compiler: Add lexical regions
9a8d09d29db1f7eebae771702e2d703635f8dadb93e45700b3b5969e17abeb6a
1 parent
0d7c8248
compiler/radiance.rad
+246 -111
| 14 | 14 | use std::lang::package; |
|
| 15 | 15 | use std::lang::il; |
|
| 16 | 16 | use std::lang::lower; |
|
| 17 | 17 | use std::arch::rv64; |
|
| 18 | 18 | use std::arch::rv64::asm; |
|
| 19 | + | use std::arch::rv64::emit; |
|
| 19 | 20 | use std::arch::rv64::printer; |
|
| 20 | 21 | use std::lang::sexpr; |
|
| 21 | 22 | use std::lang::gen::data; |
|
| 22 | 23 | use std::lang::il::binary; |
|
| 23 | 24 | use std::lang::il::binary::collect; |
|
| 24 | 25 | use std::lang::il::binary::program; |
|
| 26 | + | use std::lang::gen::labels; |
|
| 25 | 27 | use std::lang::gen::types; |
|
| 26 | 28 | use std::sys; |
|
| 27 | 29 | use std::sys::unix; |
|
| 28 | 30 | use std::collections::dict; |
|
| 29 | 31 | ||
| 30 | 32 | /// Maximum number of modules we can load per package. |
|
| 31 | - | constant MAX_LOADED_MODULES: u32 = module::MAX_MODULES; |
|
| 33 | + | constant MAX_LOADED_MODULES: u32 = 128; |
|
| 32 | 34 | /// Maximum number of packages we can compile. |
|
| 33 | 35 | constant MAX_PACKAGES: u32 = 4; |
|
| 34 | - | /// Total module entries across all packages. |
|
| 35 | - | constant MAX_TOTAL_MODULES: u32 = 192; |
|
| 36 | - | /// Source code buffer arena (2 MB). |
|
| 37 | - | constant MAX_SOURCES_SIZE: u32 = 2097152; |
|
| 36 | + | /// Source code buffer arena (4 MB). |
|
| 37 | + | constant MAX_SOURCES_SIZE: u32 = 4194304; |
|
| 38 | 38 | /// Maximum number of test functions we can discover. |
|
| 39 | 39 | constant MAX_TESTS: u32 = 1024; |
|
| 40 | 40 | /// Maximum number of assembly source paths we can load per package. |
|
| 41 | 41 | constant MAX_ASM_MODULES: u32 = 64; |
|
| 42 | 42 | ||
| 43 | 43 | /// AST arena size (64 MB) - retains parsed nodes throughout compilation. |
|
| 44 | 44 | constant TEMP_ARENA_SIZE: u32 = 67108864; |
|
| 45 | 45 | /// Per-function lowering and register-allocation arena size (16 MB). |
|
| 46 | 46 | constant FN_ARENA_SIZE: u32 = 16777216; |
|
| 47 | - | /// Main arena size (96 MB) - lives throughout compilation. |
|
| 47 | + | /// Main arena size (160 MB) - lives throughout compilation. |
|
| 48 | 48 | /// Used for: resolver data, types, symbols, global IL data, and codegen output. |
|
| 49 | - | constant MAIN_ARENA_SIZE: u32 = 100663296; |
|
| 49 | + | constant MAIN_ARENA_SIZE: u32 = 167772160; |
|
| 50 | 50 | ||
| 51 | 51 | /// AST storage arena. |
|
| 52 | 52 | static TEMP_ARENA: [u8; TEMP_ARENA_SIZE] = [0; TEMP_ARENA_SIZE]; |
|
| 53 | 53 | /// Scratch storage reclaimed after each generated function. |
|
| 54 | 54 | static FN_ARENA: [u8; FN_ARENA_SIZE] = [0; FN_ARENA_SIZE]; |
| 56 | 56 | static MAIN_ARENA: [u8; MAIN_ARENA_SIZE] = [0; MAIN_ARENA_SIZE]; |
|
| 57 | 57 | ||
| 58 | 58 | /// Module source code. |
|
| 59 | 59 | static MODULE_SOURCES: [u8; MAX_SOURCES_SIZE] = [0; MAX_SOURCES_SIZE]; |
|
| 60 | 60 | /// Module entries for all packages. |
|
| 61 | - | unsafe static MODULE_ENTRIES: [module::ModuleEntry; MAX_TOTAL_MODULES] = undefined; |
|
| 61 | + | unsafe static MODULE_ENTRIES: [?*module::ModuleEntry; module::MAX_MODULES] = [nil; module::MAX_MODULES]; |
|
| 62 | 62 | /// String pool. |
|
| 63 | 63 | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 64 | 64 | ||
| 65 | 65 | /// Package scope. |
|
| 66 | 66 | unsafe static RESOLVER_PKG_SCOPE: resolver::Scope = undefined; |
| 69 | 69 | ||
| 70 | 70 | /// Code generation storage. |
|
| 71 | 71 | unsafe static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined; |
|
| 72 | 72 | /// Hash table entries for data symbol lookup. |
|
| 73 | 73 | unsafe static CODEGEN_DATA_SYM_ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined; |
|
| 74 | + | /// Emitted instruction storage. |
|
| 75 | + | static CODEGEN_INSTRUCTIONS: [u32; emit::MAX_INSTRS] = [0; emit::MAX_INSTRS]; |
|
| 76 | + | /// Local branch patch storage. |
|
| 77 | + | unsafe static CODEGEN_PENDING_BRANCHES: [emit::PendingBranch; emit::MAX_PENDING] = undefined; |
|
| 78 | + | /// Function call patch storage. |
|
| 79 | + | unsafe static CODEGEN_PENDING_CALLS: [emit::PendingCall; emit::MAX_PENDING] = undefined; |
|
| 80 | + | /// Assembly jump patch storage. |
|
| 81 | + | unsafe static CODEGEN_PENDING_JUMPS: [emit::PendingJump; emit::MAX_PENDING] = undefined; |
|
| 82 | + | /// Address load patch storage. |
|
| 83 | + | unsafe static CODEGEN_PENDING_ADDR_LOADS: [emit::PendingAddrLoad; emit::MAX_PENDING] = undefined; |
|
| 84 | + | /// Per-function block offset storage. |
|
| 85 | + | static CODEGEN_BLOCK_OFFSETS: [i32; labels::MAX_BLOCKS_PER_FN] = [0; labels::MAX_BLOCKS_PER_FN]; |
|
| 86 | + | /// Function label hash table storage. |
|
| 87 | + | unsafe static CODEGEN_FUNC_ENTRIES: [dict::Entry; labels::FUNC_TABLE_SIZE] = undefined; |
|
| 88 | + | /// Printed function address storage. |
|
| 89 | + | unsafe static CODEGEN_FUNCS: [types::FuncAddr; emit::MAX_FUNCS] = undefined; |
|
| 90 | + | /// Debug location storage. |
|
| 91 | + | unsafe static CODEGEN_DEBUG_ENTRIES: [types::DebugEntry; emit::MAX_DEBUG_ENTRIES] = undefined; |
|
| 74 | 92 | ||
| 75 | 93 | /// Maximum number of exported definitions in one binary package. |
|
| 76 | 94 | constant MAX_PACKAGE_EXPORTS: u32 = 8192; |
|
| 77 | 95 | /// Maximum number of indexed names in one binary package. |
|
| 78 | 96 | constant MAX_PACKAGE_SYMBOLS: u32 = 16384; |
| 265 | 283 | ||
| 266 | 284 | let moduleId = try package::registerModule(pkg, graph, &mut STRING_POOL, path) catch { |
|
| 267 | 285 | throw error(&["error registering module"]); |
|
| 268 | 286 | }; |
|
| 269 | 287 | // Read file into remaining arena space. |
|
| 270 | - | let buffer = alloc::remainingBuf(sourceArena); |
|
| 288 | + | let buffer = alloc::remainingBuf(&mut *sourceArena); |
|
| 271 | 289 | if buffer.len == 0 { |
|
| 272 | 290 | throw error(&["fatal:", "source arena exhausted"]); |
|
| 273 | 291 | } |
|
| 274 | 292 | let sourceLen = unix::readFile(path, buffer) else { |
|
| 275 | 293 | throw error(&["error reading file"]); |
| 487 | 505 | throw error(&["no root module found"]); |
|
| 488 | 506 | }; |
|
| 489 | 507 | let rootEntry = module::get(graph, rootId) else { |
|
| 490 | 508 | throw error(&["root module entry not found"]); |
|
| 491 | 509 | }; |
|
| 492 | - | let rootAst = rootEntry.ast else { |
|
| 510 | + | let rootAst = module::astFor(rootEntry) else { |
|
| 493 | 511 | throw error(&["root module has no AST"]); |
|
| 494 | 512 | }; |
|
| 495 | 513 | return RootModule { entry: rootEntry, ast: rootAst }; |
|
| 496 | 514 | } |
|
| 497 | 515 |
| 510 | 528 | ast::printer::printTree(root.ast, &mut arena); |
|
| 511 | 529 | } |
|
| 512 | 530 | ||
| 513 | 531 | /// Lower all packages into a single IL program. |
|
| 514 | 532 | /// Dependencies are lowered first, then the entry package. |
|
| 515 | - | unsafe fn lowerAllPackages( |
|
| 533 | + | unsafe fn lowerAllPackages 'arena ( |
|
| 516 | 534 | ctx: *unsafe mut CompileContext, |
|
| 517 | - | res: *unsafe mut resolver::Resolver |
|
| 535 | + | res: *unsafe mut resolver::Resolver 'arena |
|
| 518 | 536 | ) -> il::Program throws (Error) { |
|
| 519 | 537 | let entryIdx = ctx.entryPkgIdx else { |
|
| 520 | 538 | panic "lowerAllPackages: no entry package"; |
|
| 521 | 539 | }; |
|
| 522 | 540 | let entryPkg = &ctx.packages[entryIdx]; |
|
| 523 | 541 | ||
| 524 | 542 | // Create the lowerer accumulator using entry package's name. |
|
| 525 | 543 | let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest }; |
|
| 526 | - | let mut low = lower::lowerer( |
|
| 527 | - | res, &ctx.graph, entryPkg.name, &mut res.arena, &mut res.arena, options |
|
| 528 | - | ); |
|
| 529 | - | try lowerAllPackagesInto(ctx, res, &mut low); |
|
| 544 | + | let arena = (&mut *res.arena) as *unsafe mut alloc::Arena; |
|
| 545 | + | let resolved: 'phase = &*res, graph = &ctx.graph where 'arena: 'phase in { |
|
| 546 | + | let mut low = lower::lowerer( |
|
| 547 | + | resolved, graph, entryPkg.name, arena, options |
|
| 548 | + | ); |
|
| 549 | + | try lowerAllPackagesInto(ctx, &mut low, &mut *arena); |
|
| 530 | 550 | ||
| 531 | - | // Finalize and return the unified program. |
|
| 532 | - | return lower::finalize(low); |
|
| 551 | + | // Finalize and return the unified program. |
|
| 552 | + | return lower::finalize(low); |
|
| 553 | + | } |
|
| 533 | 554 | } |
|
| 534 | 555 | ||
| 535 | 556 | /// Lower all packages into an existing lowerer. |
|
| 536 | - | unsafe fn lowerAllPackagesInto( |
|
| 537 | - | ctx: *unsafe mut CompileContext, |
|
| 538 | - | res: *unsafe mut resolver::Resolver, |
|
| 539 | - | low: &mut lower::Lowerer |
|
| 540 | - | ) throws (Error) { |
|
| 557 | + | unsafe fn lowerAllPackagesInto 'arena 'phase ( |
|
| 558 | + | ctx: &CompileContext, |
|
| 559 | + | low: &mut lower::Lowerer 'arena 'phase, |
|
| 560 | + | functionArena: &mut alloc::Arena |
|
| 561 | + | ) throws (Error) where 'arena: 'phase { |
|
| 541 | 562 | let entryIdx = ctx.entryPkgIdx else { |
|
| 542 | 563 | panic "lowerAllPackagesInto: no entry package"; |
|
| 543 | 564 | }; |
|
| 544 | 565 | // Lower all packages except entry. |
|
| 545 | 566 | for i in 0..ctx.packageCount { |
|
| 546 | 567 | if i <> entryIdx { |
|
| 547 | - | try lowerPackage(ctx, res, low, &ctx.packages[i], false); |
|
| 568 | + | try lowerPackage(ctx, low, &ctx.packages[i], false, functionArena); |
|
| 548 | 569 | } |
|
| 549 | 570 | } |
|
| 550 | 571 | // Lower entry package. |
|
| 551 | - | try lowerPackage(ctx, res, low, &ctx.packages[entryIdx], true); |
|
| 572 | + | try lowerPackage(ctx, low, &ctx.packages[entryIdx], true, functionArena); |
|
| 552 | 573 | } |
|
| 553 | 574 | ||
| 554 | 575 | /// Lower all modules in a package into the lowerer accumulator. |
|
| 555 | - | unsafe fn lowerPackage( |
|
| 576 | + | unsafe fn lowerPackage 'arena 'phase ( |
|
| 556 | 577 | ctx: &CompileContext, |
|
| 557 | - | res: *unsafe mut resolver::Resolver, |
|
| 558 | - | low: &mut lower::Lowerer, |
|
| 578 | + | low: &mut lower::Lowerer 'arena 'phase, |
|
| 559 | 579 | pkg: &package::Package, |
|
| 560 | - | isEntry: bool |
|
| 561 | - | ) throws (Error) { |
|
| 580 | + | isEntry: bool, |
|
| 581 | + | functionArena: &mut alloc::Arena |
|
| 582 | + | ) throws (Error) where 'arena: 'phase { |
|
| 562 | 583 | let rootId = pkg.rootModuleId else { |
|
| 563 | 584 | throw error(&["no root module found"]); |
|
| 564 | 585 | }; |
|
| 565 | 586 | // Set lowerer's package context for qualified name generation. |
|
| 566 | 587 | // TODO: We shouldn't have to call this manually. |
|
| 567 | - | lower::setPackage(low, &ctx.graph, pkg.name); |
|
| 588 | + | lower::setPackage(low, pkg.name); |
|
| 568 | 589 | ||
| 569 | - | try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg); |
|
| 590 | + | try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg, functionArena); |
|
| 570 | 591 | } |
|
| 571 | 592 | ||
| 572 | 593 | /// Recursively lower a module and all its children into the accumulator. |
|
| 573 | - | unsafe fn lowerModuleTreeInto( |
|
| 594 | + | unsafe fn lowerModuleTreeInto 'arena 'phase ( |
|
| 574 | 595 | ctx: &CompileContext, |
|
| 575 | - | low: &mut lower::Lowerer, |
|
| 596 | + | low: &mut lower::Lowerer 'arena 'phase, |
|
| 576 | 597 | graph: &module::ModuleGraph, |
|
| 577 | 598 | modId: u16, |
|
| 578 | 599 | isRoot: bool, |
|
| 579 | - | pkg: &package::Package |
|
| 580 | - | ) throws (Error) { |
|
| 600 | + | pkg: &package::Package, |
|
| 601 | + | functionArena: &mut alloc::Arena |
|
| 602 | + | ) throws (Error) where 'arena: 'phase { |
|
| 581 | 603 | let entry = module::get(graph, modId) else { |
|
| 582 | 604 | throw error(&["module entry not found"]); |
|
| 583 | 605 | }; |
|
| 584 | - | let modAst = entry.ast else { |
|
| 606 | + | let modAst = module::astFor(entry) else { |
|
| 585 | 607 | throw error(&["module has no AST"]); |
|
| 586 | 608 | }; |
|
| 587 | 609 | pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
|
| 588 | 610 | ||
| 589 | - | try lower::lowerModule(low, modId, modAst, isRoot) catch err { |
|
| 611 | + | try lower::lowerModule(low, modId, modAst, isRoot, functionArena) catch err { |
|
| 590 | 612 | io::printError("radiance: "); |
|
| 591 | 613 | io::printError("internal error during lowering: "); |
|
| 592 | 614 | lower::printError(err); |
|
| 593 | 615 | io::printError("\n"); |
|
| 594 | 616 | ||
| 595 | 617 | throw Error::Other; |
|
| 596 | 618 | }; |
|
| 597 | 619 | // Recurse into children. |
|
| 598 | - | for i in 0..entry.childrenLen { |
|
| 620 | + | for i in 0..module::childCount(entry) { |
|
| 599 | 621 | let childId = module::childAt(entry, i); |
|
| 600 | - | try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg); |
|
| 622 | + | try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg, functionArena); |
|
| 601 | 623 | } |
|
| 602 | 624 | } |
|
| 603 | 625 | ||
| 604 | 626 | /// Build a scope access chain: a::b::c from a slice of identifiers. |
|
| 605 | 627 | unsafe fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node { |
| 635 | 657 | fn collectModuleTests( |
|
| 636 | 658 | entry: *module::ModuleEntry, |
|
| 637 | 659 | tests: &mut [TestDesc], |
|
| 638 | 660 | testCount: &mut u32 |
|
| 639 | 661 | ) { |
|
| 640 | - | let modAst = entry.ast else { |
|
| 662 | + | let modAst = module::astFor(entry) else { |
|
| 641 | 663 | return; |
|
| 642 | 664 | }; |
|
| 643 | 665 | let case ast::NodeValue::Block(block) = modAst.value else { |
|
| 644 | 666 | return; |
|
| 645 | 667 | }; |
| 687 | 709 | return ast::synthNode(arena, ast::NodeValue::Call(ast::Call { callee, args })); |
|
| 688 | 710 | } |
|
| 689 | 711 | ||
| 690 | 712 | /// Inject a test runner into the entry package's root module. |
|
| 691 | 713 | /// |
|
| 692 | - | /// Scans all parsed modules for `@test fn` declarations, then appends |
|
| 714 | + | /// Scans the entry package for `@test fn` declarations, then appends |
|
| 693 | 715 | /// a synthetic entry point to the root module's AST block: |
|
| 694 | 716 | /// |
|
| 695 | 717 | /// ``` |
|
| 696 | 718 | /// @default fn #testMain() -> i32 { |
|
| 697 | 719 | /// return testing::runAllTests(&[ |
| 707 | 729 | arena: &mut ast::NodeArena |
|
| 708 | 730 | ) throws (Error) { |
|
| 709 | 731 | let entryPkg = try getEntryPackage(ctx); |
|
| 710 | 732 | let root = try getRootModule(&entryPkg, &ctx.graph); |
|
| 711 | 733 | ||
| 712 | - | // Collect all test functions across all modules. |
|
| 734 | + | // Collect test functions from the entry package's modules. |
|
| 713 | 735 | let mut tests: [TestDesc; MAX_TESTS] = undefined; |
|
| 714 | 736 | let mut testCount: u32 = 0; |
|
| 715 | 737 | ||
| 716 | 738 | for modIdx in 0..ctx.graph.entriesLen { |
|
| 717 | 739 | if let entry = module::get(&ctx.graph, modIdx as u16) { |
|
| 718 | - | collectModuleTests(entry, &mut tests[..], &mut testCount); |
|
| 740 | + | if entry.packageId == entryPkg.id { |
|
| 741 | + | collectModuleTests(entry, &mut tests[..], &mut testCount); |
|
| 742 | + | } |
|
| 719 | 743 | } |
|
| 720 | 744 | } |
|
| 721 | 745 | if testCount == 0 { |
|
| 722 | 746 | throw error(&["fatal:", "no test functions found"]); |
|
| 723 | 747 | } |
| 781 | 805 | let unsafeAttr = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Unsafe)); |
|
| 782 | 806 | let attrList = ast::nodeSlice(arena, 2).append(attrNode, a).append(unsafeAttr, a); |
|
| 783 | 807 | let fnAttrs = ast::Attributes { list: attrList }; |
|
| 784 | 808 | ||
| 785 | 809 | return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl { |
|
| 786 | - | name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs, |
|
| 810 | + | name: fnName, regions: &[], sig: fnSig, body: fnBody, attrs: fnAttrs, |
|
| 787 | 811 | })); |
|
| 788 | 812 | } |
|
| 789 | 813 | ||
| 790 | 814 | /// Build a block node with a declaration appended to its statement list. |
|
| 791 | 815 | unsafe fn injectIntoBlock( |
| 856 | 880 | ) throws (Error) { |
|
| 857 | 881 | if entries.len == 0 { |
|
| 858 | 882 | return; |
|
| 859 | 883 | } |
|
| 860 | 884 | // Use remaining arena space as serialization buffer. |
|
| 861 | - | let buf = alloc::remainingBuf(arena); |
|
| 885 | + | let buf = alloc::remainingBuf(&mut *arena); |
|
| 862 | 886 | let mut pos: u32 = 0; |
|
| 863 | 887 | ||
| 864 | 888 | for i in 0..entries.len { |
|
| 865 | 889 | let entry = &entries[i]; |
|
| 866 | 890 | let modEntry = module::get(graph, entry.moduleId) else { |
| 875 | 899 | } |
|
| 876 | 900 | try writeDataWithExt(&buf[..pos], basePath, DEBUG_EXT); |
|
| 877 | 901 | } |
|
| 878 | 902 | ||
| 879 | 903 | /// Run the resolver on the parsed modules. |
|
| 880 | - | unsafe fn runResolver(ctx: *unsafe mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) { |
|
| 881 | - | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 904 | + | unsafe fn runResolver 'arena (ctx: *unsafe mut CompileContext, mainArena: &'arena mut alloc::Arena, nodeCount: u32) -> resolver::Resolver 'arena throws (Error) { |
|
| 882 | 905 | let entryPkg = try getEntryPackage(ctx); |
|
| 883 | 906 | ||
| 884 | 907 | pkgLog(&entryPkg, &["resolving", ".."]); |
|
| 885 | 908 | ||
| 886 | 909 | let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData); |
|
| 887 | - | let nodeDataPtr = try! alloc::alloc(&mut mainArena, nodeDataSize, @alignOf(resolver::NodeData)); |
|
| 910 | + | let nodeDataPtr = try! alloc::alloc(&mut *mainArena, nodeDataSize, @alignOf(resolver::NodeData)); |
|
| 888 | 911 | let nodeData = @sliceOf(nodeDataPtr as *mut resolver::NodeData, nodeCount); |
|
| 889 | 912 | let storage = resolver::ResolverStorage { |
|
| 890 | - | arena: mainArena, |
|
| 891 | 913 | nodeData, |
|
| 892 | 914 | pkgScope: &mut RESOLVER_PKG_SCOPE, |
|
| 893 | 915 | errors: &mut RESOLVER_ERRORS[..], |
|
| 894 | 916 | }; |
|
| 895 | - | let mut res = resolver::resolver(storage, ctx.config); |
|
| 917 | + | let mut res = resolver::resolver(mainArena, storage, ctx.config); |
|
| 896 | 918 | ||
| 897 | 919 | // Build the semantic package list consumed by the resolver. |
|
| 898 | 920 | let mut resolverPkgs: [resolver::Pkg; MAX_PACKAGES] = undefined; |
|
| 899 | 921 | let mut resolverPackageCount: u32 = 0; |
|
| 900 | 922 | for i in 0..ctx.packageCount { |
| 942 | 964 | pkgLog(pkg, &["asm:", "parsing", "(", path, ")", ".."]); |
|
| 943 | 965 | ||
| 944 | 966 | let sourceLen = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else { |
|
| 945 | 967 | throw error(&["error reading assembly file"]); |
|
| 946 | 968 | }; |
|
| 947 | - | let source = &ASM_SOURCE_BUF[..sourceLen]; |
|
| 948 | - | if source.len == ASM_SOURCE_BUF.len { |
|
| 969 | + | let input = &ASM_SOURCE_BUF[..sourceLen]; |
|
| 970 | + | if input.len == ASM_SOURCE_BUF.len { |
|
| 949 | 971 | throw error(&["fatal:", "assembly source too large:", path]); |
|
| 950 | 972 | } |
|
| 973 | + | // Assembly symbols borrow source bytes until final linking. |
|
| 974 | + | let buffer = try alloc::allocSlice(arena, 1, 1, input.len) catch { |
|
| 975 | + | throw error(&["assembly source workspace exhausted"]); |
|
| 976 | + | }; |
|
| 977 | + | let source = buffer as *mut [u8]; |
|
| 978 | + | try! mem::copy(source, input); |
|
| 951 | 979 | let program = try asm::assemble( |
|
| 952 | 980 | asm::scanner::SourceKind::File { path }, |
|
| 953 | 981 | source, |
|
| 954 | 982 | &mut ASM_TEXT_BUF[..], |
|
| 955 | 983 | &mut ASM_DATA_BUF[..], |
| 987 | 1015 | } |
|
| 988 | 1016 | } |
|
| 989 | 1017 | return &ASM_RO_DATA_BUF[..*asmDataLen]; |
|
| 990 | 1018 | } |
|
| 991 | 1019 | ||
| 1020 | + | /// Generate dependency packages before the entry package. |
|
| 1021 | + | unsafe fn generateAllPackagesInto 'arena 'phase ( |
|
| 1022 | + | ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase, |
|
| 1023 | + | generator: &mut rv64::Generator, fnArena: &mut alloc::Arena |
|
| 1024 | + | ) throws (Error) where 'arena: 'phase { |
|
| 1025 | + | let entryIdx = ctx.entryPkgIdx else panic "generateAllPackagesInto: no entry package"; |
|
| 1026 | + | for i in 0..ctx.packageCount { |
|
| 1027 | + | if i <> entryIdx { |
|
| 1028 | + | try generatePackageInto(ctx, low, &ctx.packages[i], false, generator, fnArena); |
|
| 1029 | + | } |
|
| 1030 | + | } |
|
| 1031 | + | try generatePackageInto(ctx, low, &ctx.packages[entryIdx], true, generator, fnArena); |
|
| 1032 | + | } |
|
| 1033 | + | ||
| 1034 | + | /// Generate all functions in one package. |
|
| 1035 | + | unsafe fn generatePackageInto 'arena 'phase ( |
|
| 1036 | + | ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase, |
|
| 1037 | + | pkg: &package::Package, isEntry: bool, |
|
| 1038 | + | generator: &mut rv64::Generator, fnArena: &mut alloc::Arena |
|
| 1039 | + | ) throws (Error) where 'arena: 'phase { |
|
| 1040 | + | let rootId = pkg.rootModuleId else throw error(&["no root module found"]); |
|
| 1041 | + | lower::setPackage(low, pkg.name); |
|
| 1042 | + | try generateModuleTree(ctx, low, rootId, isEntry, pkg, generator, fnArena); |
|
| 1043 | + | } |
|
| 1044 | + | ||
| 1045 | + | /// Lower and consume each function before visiting the next declaration. |
|
| 1046 | + | unsafe fn generateModuleTree 'arena 'phase ( |
|
| 1047 | + | ctx: &CompileContext, low: &mut lower::Lowerer 'arena 'phase, |
|
| 1048 | + | modId: u16, isRoot: bool, pkg: &package::Package, |
|
| 1049 | + | generator: &mut rv64::Generator, fnArena: &mut alloc::Arena |
|
| 1050 | + | ) throws (Error) where 'arena: 'phase { |
|
| 1051 | + | let entry = module::get(&ctx.graph, modId) else throw error(&["module entry not found"]); |
|
| 1052 | + | let modAst = module::astFor(entry) else throw error(&["module has no AST"]); |
|
| 1053 | + | pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
|
| 1054 | + | set low.currentMod = modId; |
|
| 1055 | + | let mut cursor = try lower::moduleCursor(modAst, isRoot) catch err { |
|
| 1056 | + | io::printError("radiance: internal error during lowering: "); |
|
| 1057 | + | lower::printError(err); |
|
| 1058 | + | io::printError("\n"); |
|
| 1059 | + | throw Error::Other; |
|
| 1060 | + | }; |
|
| 1061 | + | loop { |
|
| 1062 | + | let result = try lower::lowerNext(low, &mut cursor, fnArena) catch err { |
|
| 1063 | + | io::printError("radiance: internal error during lowering: "); |
|
| 1064 | + | lower::printError(err); |
|
| 1065 | + | io::printError("\n"); |
|
| 1066 | + | throw Error::Other; |
|
| 1067 | + | }; |
|
| 1068 | + | let next = result else break; |
|
| 1069 | + | codegen::emit(generator, fnArena, &*next.function, next.role); |
|
| 1070 | + | } |
|
| 1071 | + | for i in 0..module::childCount(entry) { |
|
| 1072 | + | let childId = module::childAt(entry, i); |
|
| 1073 | + | try generateModuleTree(ctx, low, childId, false, pkg, generator, fnArena); |
|
| 1074 | + | } |
|
| 1075 | + | } |
|
| 1076 | + | ||
| 992 | 1077 | /// Lower all packages while streaming each lowered function into RV64 codegen. |
|
| 993 | - | unsafe fn lowerAndGenerateAllPackages( |
|
| 1078 | + | unsafe fn lowerAndGenerateAllPackages 'arena ( |
|
| 994 | 1079 | ctx: *unsafe mut CompileContext, |
|
| 995 | - | res: *unsafe mut resolver::Resolver, |
|
| 1080 | + | res: *unsafe mut resolver::Resolver 'arena, |
|
| 996 | 1081 | fnArena: &mut alloc::Arena, |
|
| 997 | 1082 | codegenOptions: CodegenOptions |
|
| 998 | 1083 | ) -> rv64::Program throws (Error) { |
|
| 999 | 1084 | let entryIdx = ctx.entryPkgIdx else { |
|
| 1000 | 1085 | panic "lowerAndGenerateAllPackages: no entry package"; |
| 1011 | 1096 | case CodegenEntryMode::DefaultEntry => { |
|
| 1012 | 1097 | set entryPatch = rv64::EntryPatch::Reserved(nil); |
|
| 1013 | 1098 | } |
|
| 1014 | 1099 | else => {} |
|
| 1015 | 1100 | } |
|
| 1016 | - | let mut generator = try rv64::beginProgram( |
|
| 1017 | - | rv64::ProgramOptions { entryPatch, debug: codegenOptions.debug, placement: rv64::image::Placement::Hosted }, |
|
| 1018 | - | &mut res.arena |
|
| 1019 | - | ) catch { throw error(&["code generation workspace exhausted"]); }; |
|
| 1020 | - | let mut codegenCtx = codegen::Context { |
|
| 1021 | - | generator: &mut generator, |
|
| 1022 | - | fnArena: (&mut *fnArena) as *unsafe mut alloc::Arena, |
|
| 1101 | + | let emitterStorage = emit::Storage { |
|
| 1102 | + | code: &mut CODEGEN_INSTRUCTIONS[..], |
|
| 1103 | + | pendingBranches: &mut CODEGEN_PENDING_BRANCHES[..], |
|
| 1104 | + | pendingCalls: &mut CODEGEN_PENDING_CALLS[..], |
|
| 1105 | + | pendingJumps: &mut CODEGEN_PENDING_JUMPS[..], |
|
| 1106 | + | pendingAddrLoads: &mut CODEGEN_PENDING_ADDR_LOADS[..], |
|
| 1107 | + | blockOffsets: &mut CODEGEN_BLOCK_OFFSETS[..], |
|
| 1108 | + | funcEntries: &mut CODEGEN_FUNC_ENTRIES[..], |
|
| 1109 | + | funcs: &mut CODEGEN_FUNCS[..], |
|
| 1110 | + | debugEntries: &mut CODEGEN_DEBUG_ENTRIES[..], |
|
| 1023 | 1111 | }; |
|
| 1024 | - | let mut low = lower::lowerer( |
|
| 1025 | - | res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena as *unsafe mut alloc::Arena, options |
|
| 1112 | + | let mut generator = rv64::beginProgramWithStorage( |
|
| 1113 | + | rv64::ProgramOptions { |
|
| 1114 | + | entryPatch, |
|
| 1115 | + | debug: codegenOptions.debug, |
|
| 1116 | + | placement: rv64::image::Placement::Hosted, |
|
| 1117 | + | }, |
|
| 1118 | + | emitterStorage |
|
| 1026 | 1119 | ); |
|
| 1027 | - | set low.output = lower::FnOutput::Stream(lower::FnSink { |
|
| 1028 | - | ctx: &mut codegenCtx as *unsafe mut opaque, |
|
| 1029 | - | emitFn: codegen::emit, |
|
| 1030 | - | }); |
|
| 1031 | - | let mut asmDataLen: u32 = 0; |
|
| 1032 | - | if let path = startupPath { |
|
| 1033 | - | try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, &mut res.arena); |
|
| 1034 | - | } |
|
| 1035 | - | try lowerAllPackagesInto(ctx, res, &mut low); |
|
| 1036 | - | let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, &mut res.arena); |
|
| 1120 | + | let arena = (&mut *res.arena) as *unsafe mut alloc::Arena; |
|
| 1121 | + | let resolved: 'phase = &*res, graph = &ctx.graph where 'arena: 'phase in { |
|
| 1122 | + | let mut low = lower::lowerer( |
|
| 1123 | + | resolved, graph, entryPkg.name, arena, options |
|
| 1124 | + | ); |
|
| 1125 | + | let mut asmDataLen: u32 = 0; |
|
| 1126 | + | if let path = startupPath { |
|
| 1127 | + | try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, arena); |
|
| 1128 | + | } |
|
| 1129 | + | try generateAllPackagesInto(ctx, &mut low, &mut generator, fnArena); |
|
| 1130 | + | let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, arena); |
|
| 1037 | 1131 | ||
| 1038 | - | match generator.entryPatch { |
|
| 1039 | - | case rv64::EntryPatch::Reserved(targetName) => { |
|
| 1040 | - | if targetName == nil { |
|
| 1041 | - | throw error(&["fatal:", "no default function found"]); |
|
| 1132 | + | match generator.entryPatch { |
|
| 1133 | + | case rv64::EntryPatch::Reserved(targetName) => { |
|
| 1134 | + | if targetName == nil { |
|
| 1135 | + | throw error(&["fatal:", "no default function found"]); |
|
| 1136 | + | } |
|
| 1137 | + | } |
|
| 1138 | + | else => { |
|
| 1042 | 1139 | } |
|
| 1043 | 1140 | } |
|
| 1044 | - | else => {} |
|
| 1045 | - | } |
|
| 1046 | - | if let path = codegenOptions.logPath { |
|
| 1047 | - | pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]); |
|
| 1141 | + | if let path = codegenOptions.logPath { |
|
| 1142 | + | pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]); |
|
| 1143 | + | } |
|
| 1144 | + | return try rv64::finishProgram( |
|
| 1145 | + | generator, low.data, storage, asmData, |
|
| 1146 | + | &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..] |
|
| 1147 | + | ) catch err { |
|
| 1148 | + | match err { |
|
| 1149 | + | case rv64::Error::Allocation => throw error(&["code generation workspace exhausted"]), |
|
| 1150 | + | case rv64::Error::Capacity => throw error(&["code generation output capacity exceeded"]), |
|
| 1151 | + | case rv64::Error::Symbol => throw error(&["code generation has an unresolved symbol"]), |
|
| 1152 | + | case rv64::Error::Relocation => throw error(&["code generation relocation is out of range"]), |
|
| 1153 | + | case rv64::Error::Image(_) => throw error(&["code generation image layout is invalid"]), |
|
| 1154 | + | case rv64::Error::Data(_) => throw error(&["code generation data layout is invalid"]), |
|
| 1155 | + | } |
|
| 1156 | + | }; |
|
| 1048 | 1157 | } |
|
| 1049 | - | return try rv64::finishProgram(&mut generator, low.data, storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]) catch { |
|
| 1050 | - | throw error(&["code generation failed: capacity, symbol, relocation, or image layout"]); |
|
| 1051 | - | }; |
|
| 1052 | 1158 | } |
|
| 1053 | 1159 | ||
| 1054 | 1160 | /// Source exports selected for one binary RIL package. |
|
| 1055 | 1161 | record PackageExports: Copy { |
|
| 1056 | 1162 | /// Number of initialized entries in the caller's export table. |
| 1069 | 1175 | ) -> PackageExports throws (Error) { |
|
| 1070 | 1176 | let mut count: u32 = 0; |
|
| 1071 | 1177 | let mut entryName: ?*[u8] = nil; |
|
| 1072 | 1178 | for i in 0..ctx.graph.entriesLen { |
|
| 1073 | 1179 | let modEntry = module::get(&ctx.graph, i as u16) else continue; |
|
| 1074 | - | if modEntry.packageId <> pkg.id { continue; } |
|
| 1075 | - | let root = modEntry.ast else continue; |
|
| 1180 | + | if modEntry.packageId <> pkg.id { |
|
| 1181 | + | continue; |
|
| 1182 | + | } |
|
| 1183 | + | let root = module::astFor(modEntry) else continue; |
|
| 1076 | 1184 | let case ast::NodeValue::Block(block) = root.value else continue; |
|
| 1077 | 1185 | for node in block.statements { |
|
| 1078 | 1186 | let mut ident: ?*ast::Node = nil; |
|
| 1079 | 1187 | let mut attrs: ?ast::Attributes = nil; |
|
| 1080 | 1188 | let mut kind = binary::ExportKind::Function; |
|
| 1081 | 1189 | match node.value { |
|
| 1082 | - | case ast::NodeValue::FnDecl(decl) => { set ident = decl.name; set attrs = decl.attrs; }, |
|
| 1190 | + | case ast::NodeValue::FnDecl(decl) => { |
|
| 1191 | + | set ident = decl.name; |
|
| 1192 | + | set attrs = decl.attrs; |
|
| 1193 | + | }, |
|
| 1083 | 1194 | case ast::NodeValue::ConstDecl(decl) => { |
|
| 1084 | 1195 | set ident = decl.ident; set attrs = decl.attrs; set kind = binary::ExportKind::Data; |
|
| 1085 | 1196 | }, |
|
| 1086 | 1197 | case ast::NodeValue::StaticDecl(decl) => { |
|
| 1087 | 1198 | set ident = decl.ident; set attrs = decl.attrs; set kind = binary::ExportKind::Data; |
|
| 1088 | 1199 | }, |
|
| 1089 | 1200 | else => continue, |
|
| 1090 | 1201 | } |
|
| 1091 | 1202 | let attributes = attrs else continue; |
|
| 1203 | + | if ast::attributesContains(&attributes, ast::Attribute::Intrinsic) { |
|
| 1204 | + | continue; |
|
| 1205 | + | } |
|
| 1092 | 1206 | let isDefault = ast::attributesContains(&attributes, ast::Attribute::Default) |
|
| 1093 | 1207 | and pkg.rootModuleId == modEntry.id; |
|
| 1094 | 1208 | if not isDefault and not ast::attributesContains(&attributes, ast::Attribute::Export) { |
|
| 1095 | 1209 | continue; |
|
| 1096 | 1210 | } |
| 1099 | 1213 | let qualified = il::formatQualifiedName(arena, module::moduleQualifiedPath(modEntry), name); |
|
| 1100 | 1214 | let mut present = false; |
|
| 1101 | 1215 | match kind { |
|
| 1102 | 1216 | case binary::ExportKind::Function => { |
|
| 1103 | 1217 | for func in ilProgram.fns { |
|
| 1104 | - | if mem::eq(func.name, qualified) { set present = true; } |
|
| 1218 | + | if mem::eq(func.name, qualified) { |
|
| 1219 | + | set present = true; |
|
| 1220 | + | } |
|
| 1105 | 1221 | } |
|
| 1106 | 1222 | }, |
|
| 1107 | 1223 | case binary::ExportKind::Data => { |
|
| 1108 | 1224 | for item in ilProgram.data { |
|
| 1109 | - | if mem::eq(item.name, qualified) { set present = true; } |
|
| 1225 | + | if mem::eq(item.name, qualified) { |
|
| 1226 | + | set present = true; |
|
| 1227 | + | } |
|
| 1110 | 1228 | } |
|
| 1111 | 1229 | }, |
|
| 1112 | 1230 | } |
|
| 1113 | - | if not present { continue; } |
|
| 1114 | - | if count == exports.len { throw error(&["too many package exports"]); } |
|
| 1231 | + | if not present { |
|
| 1232 | + | continue; |
|
| 1233 | + | } |
|
| 1234 | + | if count == exports.len { |
|
| 1235 | + | throw error(&["too many package exports"]); |
|
| 1236 | + | } |
|
| 1115 | 1237 | set exports[count] = binary::Export { name: qualified, kind }; |
|
| 1116 | 1238 | set count += 1; |
|
| 1117 | - | if isDefault { set entryName = qualified; } |
|
| 1239 | + | if isDefault { |
|
| 1240 | + | set entryName = qualified; |
|
| 1241 | + | } |
|
| 1118 | 1242 | } |
|
| 1119 | 1243 | } |
|
| 1120 | 1244 | return PackageExports { count, entry: entryName }; |
|
| 1121 | 1245 | } |
|
| 1122 | 1246 | ||
| 1123 | 1247 | /// Emit one binary RIL file per package into an existing directory. |
|
| 1124 | - | unsafe fn emitPackages(ctx: *unsafe mut CompileContext, res: *unsafe mut resolver::Resolver, directory: *[u8]) throws (Error) { |
|
| 1248 | + | unsafe fn emitPackages 'arena ( |
|
| 1249 | + | ctx: *unsafe mut CompileContext, |
|
| 1250 | + | res: *unsafe mut resolver::Resolver 'arena, |
|
| 1251 | + | directory: *[u8] |
|
| 1252 | + | ) throws (Error) { |
|
| 1125 | 1253 | for i in 0..ctx.packageCount { |
|
| 1126 | 1254 | if ctx.inputs[i].asmPathCount > 0 or ctx.inputs[i].startupPath <> nil { |
|
| 1127 | 1255 | throw error(&["binary RIL output requires Radiance source modules"]); |
|
| 1128 | 1256 | } |
|
| 1129 | 1257 | } |
|
| 1130 | 1258 | let unified = try lowerAllPackages(ctx, res); |
|
| 1131 | - | let allocator = alloc::arenaAllocator(&mut res.arena); |
|
| 1259 | + | let allocator = alloc::arenaAllocator(&mut *res.arena); |
|
| 1132 | 1260 | for i in 0..ctx.packageCount { |
|
| 1133 | 1261 | let pkg = &ctx.packages[i]; |
|
| 1134 | 1262 | let mut dataItems: *mut [il::Data] = &mut []; |
|
| 1135 | 1263 | let mut functions: *unsafe mut [*unsafe il::Fn] = &mut []; |
|
| 1136 | 1264 | for item in unified.data { |
|
| 1137 | - | if ownsSymbol(pkg.name, item.name) { dataItems.append(item, allocator); } |
|
| 1265 | + | if ownsSymbol(pkg.name, item.name) { |
|
| 1266 | + | dataItems.append(item, allocator); |
|
| 1267 | + | } |
|
| 1138 | 1268 | } |
|
| 1139 | 1269 | for func in unified.fns { |
|
| 1140 | - | if ownsSymbol(pkg.name, func.name) { functions.append(func, allocator); } |
|
| 1270 | + | if ownsSymbol(pkg.name, func.name) { |
|
| 1271 | + | functions.append(func, allocator); |
|
| 1272 | + | } |
|
| 1141 | 1273 | } |
|
| 1142 | 1274 | let local = il::Program { data: &dataItems[..], fns: functions }; |
|
| 1143 | - | let selected = try packageExports(ctx, pkg, &local, &mut PACKAGE_EXPORTS[..], &mut res.arena); |
|
| 1275 | + | let selected = try packageExports(ctx, pkg, &local, &mut PACKAGE_EXPORTS[..], &mut *res.arena); |
|
| 1144 | 1276 | let mut dependencies: [*[u8]; MAX_PACKAGES] = undefined; |
|
| 1145 | 1277 | let mut names = collect::new(&mut PACKAGE_SYMBOLS[..], &mut dependencies[..]); |
|
| 1146 | 1278 | let image = try collect::package(&mut names, pkg.name, local, &PACKAGE_EXPORTS[..selected.count], selected.entry) catch { |
|
| 1147 | 1279 | throw error(&["cannot collect binary RIL package", pkg.name]); |
|
| 1148 | 1280 | }; |
| 1168 | 1300 | let suffix = mem::stripPrefix(owner, name) else return false; |
|
| 1169 | 1301 | return suffix.len > 2 and suffix[0] == ':' and suffix[1] == ':'; |
|
| 1170 | 1302 | } |
|
| 1171 | 1303 | ||
| 1172 | 1304 | /// Lower, optionally dump, and optionally generate binary output. |
|
| 1173 | - | unsafe fn compile( |
|
| 1305 | + | unsafe fn compile 'arena ( |
|
| 1174 | 1306 | ctx: *unsafe mut CompileContext, |
|
| 1175 | - | res: *unsafe mut resolver::Resolver, |
|
| 1307 | + | res: *unsafe mut resolver::Resolver 'arena, |
|
| 1176 | 1308 | fnArena: &mut alloc::Arena |
|
| 1177 | 1309 | ) throws (Error) { |
|
| 1178 | 1310 | let entryPkg = try getEntryPackage(ctx); |
|
| 1179 | 1311 | if let directory = ctx.rilDirectory { |
|
| 1180 | 1312 | try emitPackages(ctx, res, directory); |
| 1193 | 1325 | let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions { |
|
| 1194 | 1326 | logPath: nil, |
|
| 1195 | 1327 | debug: false, |
|
| 1196 | 1328 | entryMode: CodegenEntryMode::None, |
|
| 1197 | 1329 | }); |
|
| 1198 | - | printer::printCodeTo(&mut out, entryPkg.name, result.code, result.funcs, &mut res.arena); |
|
| 1330 | + | printer::printCodeTo(&mut out, entryPkg.name, result.code, result.funcs, res.arena); |
|
| 1199 | 1331 | io::print("\n"); |
|
| 1200 | 1332 | ||
| 1201 | 1333 | return; |
|
| 1202 | 1334 | } |
|
| 1203 | 1335 | // Generate binary output if path specified. |
| 1223 | 1355 | throw error(&["fatal:", "failed to write output file"]); |
|
| 1224 | 1356 | } |
|
| 1225 | 1357 | ||
| 1226 | 1358 | // Write debug info file if enabled. |
|
| 1227 | 1359 | if ctx.debug { |
|
| 1228 | - | try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, &mut res.arena); |
|
| 1360 | + | try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, res.arena); |
|
| 1229 | 1361 | } |
|
| 1230 | 1362 | pkgLog(&entryPkg, &["ok", "(", outPath, ")"]); |
|
| 1231 | 1363 | } |
|
| 1232 | 1364 | ||
| 1233 | 1365 | @default unsafe fn main(env: *sys::Env) -> i32 { |
| 1253 | 1385 | try generateTestRunner(&mut ctx, &mut arena) catch { |
|
| 1254 | 1386 | return 1; |
|
| 1255 | 1387 | }; |
|
| 1256 | 1388 | } |
|
| 1257 | 1389 | // Run resolution phase. |
|
| 1258 | - | let mut res = try runResolver(&mut ctx, arena.nextId) catch { |
|
| 1259 | - | return 1; |
|
| 1260 | - | }; |
|
| 1261 | - | let mut fnArena = alloc::new(&mut FN_ARENA[..]); |
|
| 1390 | + | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 1391 | + | let arenaRef: 'arena = &mut mainArena in { |
|
| 1392 | + | let mut res = try runResolver(&mut ctx, arenaRef, arena.nextId) catch { |
|
| 1393 | + | return 1; |
|
| 1394 | + | }; |
|
| 1395 | + | let mut fnArena = alloc::new(&mut FN_ARENA[..]); |
|
| 1262 | 1396 | ||
| 1263 | - | // Lower, dump, and/or generate output. |
|
| 1264 | - | try compile(&mut ctx, &mut res, &mut fnArena) catch { |
|
| 1265 | - | return 1; |
|
| 1266 | - | }; |
|
| 1267 | - | return 0; |
|
| 1397 | + | // Lower, dump, and/or generate output. |
|
| 1398 | + | try compile(&mut ctx, &mut res, &mut fnArena) catch { |
|
| 1399 | + | return 1; |
|
| 1400 | + | }; |
|
| 1401 | + | return 0; |
|
| 1402 | + | } |
|
| 1268 | 1403 | } |
compiler/radiance/codegen.rad
+9 -18
| 3 | 3 | use std::arch::rv64; |
|
| 4 | 4 | use std::lang::alloc; |
|
| 5 | 5 | use std::lang::il; |
|
| 6 | 6 | use std::lang::lower; |
|
| 7 | 7 | ||
| 8 | - | /// State carried by the streaming lowerer/codegen callback. |
|
| 9 | - | export record Context: Copy { |
|
| 10 | - | /// Generator receiving functions. It must remain valid for every callback. |
|
| 11 | - | generator: *unsafe mut rv64::Generator, |
|
| 12 | - | /// Arena for function IL. It must remain valid for every callback. |
|
| 13 | - | fnArena: *unsafe mut alloc::Arena, |
|
| 14 | - | } |
|
| 15 | - | ||
| 16 | 8 | /// Emit one lowered function to machine code and reclaim its IL arena. |
|
| 17 | - | /// The context must point to a valid `Context` with live generator and arena storage. |
|
| 18 | - | export unsafe fn emit(ctxPtr: *unsafe mut opaque, func: *unsafe il::Fn, role: lower::FnRole) { |
|
| 19 | - | let ctx = ctxPtr as *unsafe mut Context; |
|
| 20 | - | ||
| 9 | + | /// The function's storage can be reclaimed after code generation returns. |
|
| 10 | + | export unsafe fn emit( |
|
| 11 | + | generator: &mut rv64::Generator, arena: &mut alloc::Arena, |
|
| 12 | + | func: &il::Fn, role: lower::FnRole |
|
| 13 | + | ) { |
|
| 21 | 14 | match role { |
|
| 22 | 15 | case lower::FnRole::Default => { |
|
| 23 | - | rv64::recordFunctionAlias(ctx.generator, super::DEFAULT_ENTRY_SYMBOL); |
|
| 24 | - | match ctx.generator.entryPatch { |
|
| 16 | + | rv64::recordFunctionAlias(generator, super::DEFAULT_ENTRY_SYMBOL); |
|
| 17 | + | match generator.entryPatch { |
|
| 25 | 18 | case rv64::EntryPatch::Reserved(_) => { |
|
| 26 | - | set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name); |
|
| 19 | + | set generator.entryPatch = rv64::EntryPatch::Reserved(func.name); |
|
| 27 | 20 | } |
|
| 28 | 21 | // Startup assembly calls the default function through its |
|
| 29 | 22 | // default entry symbol when no entry jump is reserved. |
|
| 30 | 23 | case rv64::EntryPatch::None => {} |
|
| 31 | 24 | } |
|
| 32 | 25 | } |
|
| 33 | 26 | else => {} |
|
| 34 | 27 | } |
|
| 35 | - | let generator = ctx.generator; |
|
| 36 | - | let arena = ctx.fnArena; |
|
| 37 | 28 | rv64::generateFunction(generator, func, arena); |
|
| 38 | - | alloc::reset(ctx.fnArena); |
|
| 29 | + | alloc::reset(arena); |
|
| 39 | 30 | } |
lib/std/arch/rv64.rad
+73 -25
| 10 | 10 | //! * isel: Instruction selection (IL to RV64 instructions) |
|
| 11 | 11 | //! * printer: Assembly text output |
|
| 12 | 12 | ||
| 13 | 13 | export mod image; |
|
| 14 | 14 | export mod atomics; |
|
| 15 | - | export mod shared; |
|
| 16 | 15 | export mod encode; |
|
| 17 | 16 | export mod decode; |
|
| 18 | 17 | export mod emit; |
|
| 19 | 18 | export mod isel; |
|
| 20 | 19 | export mod printer; |
|
| 21 | 20 | export mod asm; |
|
| 21 | + | export mod shared; |
|
| 22 | 22 | ||
| 23 | 23 | @test mod tests; |
|
| 24 | 24 | @test mod bounds; |
|
| 25 | 25 | @test mod atomicTests; |
|
| 26 | 26 |
| 110 | 110 | export constant INSTR_SIZE: i32 = 4; |
|
| 111 | 111 | /// Stack alignment requirement in bytes. |
|
| 112 | 112 | export constant STACK_ALIGNMENT: i32 = 16; |
|
| 113 | 113 | ||
| 114 | 114 | /// Minimum blit size (in bytes) to use a loop instead of inline copy. |
|
| 115 | - | /// Blits below this threshold are fully unrolled as LD/SD pairs. |
|
| 116 | - | export constant BLIT_LOOP_THRESHOLD: i32 = 256; |
|
| 115 | + | /// Blits below this threshold use unrolled byte loads and stores. |
|
| 116 | + | export constant BLIT_LOOP_THRESHOLD: i32 = 33; |
|
| 117 | 117 | ||
| 118 | 118 | ///////////////////////// |
|
| 119 | 119 | // Codegen Allocation // |
|
| 120 | 120 | ///////////////////////// |
|
| 121 | 121 |
| 247 | 247 | let mut e = try emit::emitter(arena, options.debug) catch { |
|
| 248 | 248 | alloc::restore(arena, checkpoint); |
|
| 249 | 249 | throw Error::Allocation; |
|
| 250 | 250 | }; |
|
| 251 | 251 | ||
| 252 | + | return beginProgramWithEmitter(options, e); |
|
| 253 | + | } |
|
| 254 | + | ||
| 255 | + | /// Begin RV64 code generation with caller-owned emitter storage. |
|
| 256 | + | export fn beginProgramWithStorage( |
|
| 257 | + | options: ProgramOptions, |
|
| 258 | + | storage: emit::Storage |
|
| 259 | + | ) -> Generator { |
|
| 260 | + | let e = emit::emitterWithStorage(storage, options.debug); |
|
| 261 | + | return beginProgramWithEmitter(options, e); |
|
| 262 | + | } |
|
| 263 | + | ||
| 264 | + | /// Initialize a program generator from an emitter. |
|
| 265 | + | fn beginProgramWithEmitter(options: ProgramOptions, input: emit::Emitter) -> Generator { |
|
| 266 | + | let mut e = input; |
|
| 267 | + | ||
| 252 | 268 | // Emit placeholder entry jump when requested. |
|
| 253 | 269 | // We'll patch this at the end once we know where the function is. |
|
| 254 | 270 | match options.entryPatch { |
|
| 255 | 271 | case EntryPatch::Reserved(_) => { |
|
| 256 | 272 | emit::emit(&mut e, encode::nop()); // Placeholder for two-instruction jump. |
| 268 | 284 | ||
| 269 | 285 | /// Generate code for one IL function. |
|
| 270 | 286 | /// Record failures in the emitter and restore the function arena offset. |
|
| 271 | 287 | export unsafe fn generateFunction( |
|
| 272 | 288 | generator: &mut Generator, |
|
| 273 | - | func: *unsafe il::Fn, |
|
| 289 | + | func: &il::Fn, |
|
| 274 | 290 | arena: &mut alloc::Arena |
|
| 275 | 291 | ) { |
|
| 276 | 292 | if generator.e.error <> nil or func.isExtern { |
|
| 277 | 293 | return; |
|
| 278 | 294 | } |
|
| 279 | 295 | let checkpoint = alloc::save(arena); |
|
| 280 | 296 | let config = targetConfig(); |
|
| 281 | - | let ralloc = try regalloc::allocate(func, &config, arena) catch { |
|
| 282 | - | alloc::restore(arena, checkpoint); |
|
| 283 | - | set generator.e.error = Error::Allocation; |
|
| 284 | - | return; |
|
| 285 | - | }; |
|
| 286 | - | ||
| 287 | - | isel::selectFn(&mut generator.e, &ralloc, func); |
|
| 297 | + | use *arena as scratch in { |
|
| 298 | + | try generateFunctionWithStorage(generator, func, &config, &scratch) catch { |
|
| 299 | + | set generator.e.error = Error::Allocation; |
|
| 300 | + | }; |
|
| 301 | + | } |
|
| 288 | 302 | ||
| 289 | 303 | // Reclaim unused memory after instruction selection. |
|
| 290 | 304 | alloc::restore(arena, checkpoint); |
|
| 291 | 305 | } |
|
| 292 | 306 | ||
| 307 | + | /// Allocate registers and select one function within a scratch session. |
|
| 308 | + | unsafe fn generateFunctionWithStorage 'scratch ( |
|
| 309 | + | generator: &mut Generator, |
|
| 310 | + | func: &il::Fn, |
|
| 311 | + | config: ®alloc::TargetConfig, |
|
| 312 | + | storage: &Session 'scratch |
|
| 313 | + | ) throws (alloc::AllocError) { |
|
| 314 | + | let ralloc = try regalloc::allocate(func, config, storage); |
|
| 315 | + | isel::selectFn(&mut generator.e, &ralloc, func); |
|
| 316 | + | } |
|
| 317 | + | ||
| 293 | 318 | /// Record an alternate name for the next function emitted. |
|
| 294 | 319 | export fn recordFunctionAlias(generator: &mut Generator, name: *[u8]) { |
|
| 295 | 320 | let codeLen = generator.e.codeLen; |
|
| 296 | 321 | emit::recordFuncOffsetAt(&mut generator.e, name, codeLen); |
|
| 297 | 322 | } |
| 336 | 361 | } |
|
| 337 | 362 | } |
|
| 338 | 363 | ||
| 339 | 364 | /// Finish RV64 code generation and return the emitted program. |
|
| 340 | 365 | export unsafe fn finishProgram( |
|
| 341 | - | generator: &mut Generator, |
|
| 366 | + | input: Generator, |
|
| 342 | 367 | globalData: &[il::Data], |
|
| 343 | 368 | storage: Storage, |
|
| 344 | 369 | roDataPrefix: *[u8], |
|
| 345 | 370 | roDataBuf: &mut [u8], |
|
| 346 | 371 | rwDataBuf: &mut [u8] |
|
| 347 | 372 | ) -> Program throws (Error) { |
|
| 373 | + | let mut generator = input; |
|
| 348 | 374 | try emit::check(&generator.e); |
|
| 349 | 375 | let mut roBase: u64 = RO_DATA_BASE as u64; |
|
| 350 | 376 | let mut rwBase: u64 = RW_DATA_BASE as u64; |
|
| 351 | 377 | match generator.placement { |
|
| 352 | 378 | case image::Placement::Physical { roData, rwData, .. } => { |
|
| 353 | 379 | set roBase = roData; |
|
| 354 | 380 | set rwBase = rwData; |
|
| 355 | 381 | }, |
|
| 356 | - | else => {}, |
|
| 382 | + | else => { |
|
| 383 | + | }, |
|
| 357 | 384 | } |
|
| 358 | 385 | // Build data map after function lowering. Function-local literals can add |
|
| 359 | 386 | // global data while functions are lowered, so final layout belongs here. |
|
| 360 | 387 | let case Storage { dataSyms: symbolBuf, dataSymEntries } = storage |
|
| 361 | 388 | else panic "expected code generation storage"; |
|
| 362 | 389 | let mut dataSymCount: u32 = 0; |
|
| 363 | 390 | let roLayoutSize = try data::layoutSectionAtOffset( |
|
| 364 | 391 | globalData, symbolBuf, &mut dataSymCount, roBase, roDataPrefix.len, true |
|
| 365 | - | ) catch err { throw Error::Data(err); }; |
|
| 366 | - | let rwLayoutSize = try data::layoutSection(globalData, symbolBuf, &mut dataSymCount, rwBase, false) catch err { throw Error::Data(err); }; |
|
| 392 | + | ) catch err { |
|
| 393 | + | throw Error::Data(err); |
|
| 394 | + | }; |
|
| 395 | + | let rwLayoutSize = try data::layoutSection(globalData, symbolBuf, &mut dataSymCount, rwBase, false) catch err { |
|
| 396 | + | throw Error::Data(err); |
|
| 397 | + | }; |
|
| 367 | 398 | ||
| 368 | 399 | let dataSyms = &symbolBuf[..dataSymCount]; |
|
| 369 | - | let dataSymMap = try data::buildMap(dataSyms, dataSymEntries) catch err { throw Error::Data(err); }; |
|
| 400 | + | let dataSymMap = try data::buildMap(dataSyms, dataSymEntries) catch err { |
|
| 401 | + | throw Error::Data(err); |
|
| 402 | + | }; |
|
| 370 | 403 | if roBase > 0xffffffffffffffff - roLayoutSize as u64 - 7 { |
|
| 371 | 404 | throw Error::Image(image::Error::Overflow); |
|
| 372 | 405 | } |
|
| 373 | 406 | let mut codeBase: u64 = (roBase + roLayoutSize as u64 + 7) & ~7; |
|
| 374 | 407 | let mut entry = codeBase; |
|
| 375 | 408 | match generator.placement { |
|
| 376 | 409 | case image::Placement::Physical { code, entry: address, .. } => { |
|
| 377 | 410 | set codeBase = code; |
|
| 378 | 411 | set entry = address; |
|
| 379 | 412 | }, |
|
| 380 | - | else => {}, |
|
| 413 | + | else => { |
|
| 414 | + | }, |
|
| 381 | 415 | } |
|
| 382 | 416 | let codeBytes = generator.e.codeLen * 4; |
|
| 383 | 417 | let mut layout = image::Layout { |
|
| 384 | 418 | entry, |
|
| 385 | 419 | code: image::Segment { address: codeBase, initialized: codeBytes, memory: codeBytes }, |
|
| 386 | 420 | roData: image::Segment { address: roBase, initialized: 0, memory: roLayoutSize }, |
|
| 387 | 421 | rwData: image::Segment { address: rwBase, initialized: 0, memory: rwLayoutSize }, |
|
| 388 | 422 | }; |
|
| 389 | - | try image::validate(layout) catch err { throw Error::Image(err); }; |
|
| 423 | + | try image::validate(layout) catch err { |
|
| 424 | + | throw Error::Image(err); |
|
| 425 | + | }; |
|
| 390 | 426 | ||
| 391 | 427 | match generator.entryPatch { |
|
| 392 | 428 | case EntryPatch::Reserved(targetName) => { |
|
| 393 | 429 | let target = targetName else { |
|
| 394 | 430 | throw Error::Symbol; |
| 407 | 443 | try emit::patchAddrLoads(&mut generator.e, &dataSymMap, codeBase); |
|
| 408 | 444 | ||
| 409 | 445 | try emit::check(&generator.e); |
|
| 410 | 446 | ||
| 411 | 447 | // Emit data sections. |
|
| 412 | - | if roDataPrefix.len > roDataBuf.len { throw Error::Capacity; } |
|
| 448 | + | if roDataPrefix.len > roDataBuf.len { |
|
| 449 | + | throw Error::Capacity; |
|
| 450 | + | } |
|
| 413 | 451 | try! mem::copy(roDataBuf, roDataPrefix); |
|
| 414 | 452 | ||
| 415 | 453 | let roDataSize = try data::emitSectionAtOffset( |
|
| 416 | 454 | globalData, &dataSymMap, &generator.e.labels, codeBase, roDataBuf, true, roDataPrefix.len |
|
| 417 | - | ) catch err { throw Error::Data(err); }; |
|
| 455 | + | ) catch err { |
|
| 456 | + | throw Error::Data(err); |
|
| 457 | + | }; |
|
| 418 | 458 | let rwDataSize = try data::emitSection( |
|
| 419 | 459 | globalData, &dataSymMap, &generator.e.labels, codeBase, rwDataBuf, false |
|
| 420 | - | ) catch err { throw Error::Data(err); }; |
|
| 460 | + | ) catch err { |
|
| 461 | + | throw Error::Data(err); |
|
| 462 | + | }; |
|
| 421 | 463 | set layout.roData.initialized = roDataSize; |
|
| 422 | 464 | set layout.rwData.initialized = rwDataSize; |
|
| 423 | - | try image::validate(layout) catch err { throw Error::Image(err); }; |
|
| 465 | + | try image::validate(layout) catch err { |
|
| 466 | + | throw Error::Image(err); |
|
| 467 | + | }; |
|
| 468 | + | let case Generator { e, .. } = generator else panic "expected program generator"; |
|
| 469 | + | let case emit::Emitter { |
|
| 470 | + | code, codeLen, funcs, funcsLen, debugEntries, debugEntriesLen, .. |
|
| 471 | + | } = e else panic "expected program emitter"; |
|
| 424 | 472 | return Program { |
|
| 425 | - | code: emit::getCode(&generator.e), |
|
| 426 | - | funcs: &generator.e.funcs[..], |
|
| 473 | + | code: &code[..codeLen], |
|
| 474 | + | funcs: &funcs[..funcsLen], |
|
| 427 | 475 | roDataSize, |
|
| 428 | 476 | rwDataSize, |
|
| 429 | - | debugEntries: emit::getDebugEntries(&generator.e), |
|
| 477 | + | debugEntries: &debugEntries[..debugEntriesLen], |
|
| 430 | 478 | layout, |
|
| 431 | 479 | }; |
|
| 432 | 480 | } |
lib/std/arch/rv64/asm.rad
+50 -9
| 406 | 406 | { name: "tp", reg: rv64::TP }, |
|
| 407 | 407 | { name: "zero", reg: rv64::ZERO }, |
|
| 408 | 408 | ]; |
|
| 409 | 409 | ||
| 410 | 410 | /// Sorted CSR-name lookup table used by the assembler parser. |
|
| 411 | - | export constant CSRS: [CsrEntry; 9] = [ |
|
| 411 | + | export constant CSRS: [CsrEntry; 10] = [ |
|
| 412 | + | { name: "instret", csr: 0xC02 }, |
|
| 412 | 413 | { name: "mcause", csr: 0x342 }, |
|
| 413 | 414 | { name: "mepc", csr: 0x341 }, |
|
| 414 | 415 | { name: "mhartid", csr: 0xF14 }, |
|
| 415 | 416 | { name: "mie", csr: 0x304 }, |
|
| 416 | 417 | { name: "mip", csr: 0x344 }, |
| 460 | 461 | arena: *unsafe mut alloc::Arena, |
|
| 461 | 462 | /// Assembler lexical scanner. |
|
| 462 | 463 | scan: scanner::Scanner, |
|
| 463 | 464 | /// Output text buffer. |
|
| 464 | 465 | text: *mut [u32], |
|
| 466 | + | /// Number of emitted text words. |
|
| 467 | + | textLen: u32, |
|
| 465 | 468 | /// Output data buffer. |
|
| 466 | 469 | data: *mut [u8], |
|
| 470 | + | /// Number of emitted data bytes. |
|
| 471 | + | dataLen: u32, |
|
| 467 | 472 | /// Current output section. |
|
| 468 | 473 | section: Section, |
|
| 469 | 474 | /// Defined symbols. |
|
| 470 | 475 | symbols: *mut [Symbol], |
|
| 476 | + | /// Number of defined symbols. |
|
| 477 | + | symbolsLen: u32, |
|
| 471 | 478 | /// Name-to-symbol index map. |
|
| 472 | 479 | symbolMap: dict::Dict, |
|
| 473 | 480 | /// Name-to-integer map. |
|
| 474 | 481 | constMap: dict::Dict, |
|
| 475 | 482 | /// Names marked by `.export`. |
|
| 476 | 483 | exportMap: dict::Dict, |
|
| 477 | 484 | /// Pending fixups. |
|
| 478 | 485 | fixups: *mut [Fixup], |
|
| 486 | + | /// Number of pending fixups. |
|
| 487 | + | fixupsLen: u32, |
|
| 479 | 488 | /// Fixups that reference text outside this assembly fragment. |
|
| 480 | 489 | externalFixups: *mut [Fixup], |
|
| 490 | + | /// Number of external fixups. |
|
| 491 | + | externalFixupsLen: u32, |
|
| 481 | 492 | /// Absolute runtime address of data-section offset zero. |
|
| 482 | 493 | dataBase: u32, |
|
| 483 | 494 | } |
|
| 484 | 495 | ||
| 485 | 496 | /// Assemble source using `dataBase` as the runtime address of the data-section. |
| 490 | 501 | dataBuf: *mut [u8], |
|
| 491 | 502 | arena: &mut alloc::Arena, |
|
| 492 | 503 | pool: *unsafe mut strings::Pool, |
|
| 493 | 504 | dataBase: u32 |
|
| 494 | 505 | ) -> Program throws (Error) { |
|
| 495 | - | let slotCap = source.len + SOURCE_CAP_PADDING; |
|
| 506 | + | let slotCap = tokenCapacity(sourceKind, source, pool); |
|
| 496 | 507 | let tableCap = nextPowerOfTwo(slotCap * TABLE_CAPACITY_SCALE); |
|
| 497 | 508 | ||
| 498 | 509 | let symbols = try! alloc::allocSlice(arena, @sizeOf(Symbol), @alignOf(Symbol), slotCap); |
|
| 499 | 510 | let fixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap); |
|
| 500 | 511 | let externalFixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap); |
| 506 | 517 | let fixupBuf = fixups as *mut [Fixup]; |
|
| 507 | 518 | let externalBuf = externalFixups as *mut [Fixup]; |
|
| 508 | 519 | let mut a = Assembler { |
|
| 509 | 520 | arena: arena as *unsafe mut alloc::Arena, |
|
| 510 | 521 | scan: scanner::scanner(sourceKind, source, pool), |
|
| 511 | - | text: &mut textBuf[..0], |
|
| 512 | - | data: &mut dataBuf[..0], |
|
| 522 | + | text: textBuf, |
|
| 523 | + | textLen: 0, |
|
| 524 | + | data: dataBuf, |
|
| 525 | + | dataLen: 0, |
|
| 513 | 526 | section: Section::Text, |
|
| 514 | - | symbols: &mut symbolBuf[..0], |
|
| 527 | + | symbols: symbolBuf, |
|
| 528 | + | symbolsLen: 0, |
|
| 515 | 529 | symbolMap: dict::init(entries as *mut [dict::Entry]), |
|
| 516 | 530 | constMap: dict::init(constEntries as *mut [dict::Entry]), |
|
| 517 | 531 | exportMap: dict::init(exportEntries as *mut [dict::Entry]), |
|
| 518 | - | fixups: &mut fixupBuf[..0], |
|
| 519 | - | externalFixups: &mut externalBuf[..0], |
|
| 532 | + | fixups: fixupBuf, |
|
| 533 | + | fixupsLen: 0, |
|
| 534 | + | externalFixups: externalBuf, |
|
| 535 | + | externalFixupsLen: 0, |
|
| 520 | 536 | dataBase, |
|
| 521 | 537 | }; |
|
| 522 | 538 | // Parse assembly source and emit instructions. |
|
| 523 | 539 | try parser::parseProgram(&mut a); |
|
| 524 | 540 | // Resolve fixups and finalize program. |
|
| 525 | 541 | try emit::finishProgram(&mut a); |
|
| 526 | 542 | ||
| 527 | - | let case Assembler { text, data, symbols: definedSymbols, externalFixups: pendingFixups, .. } = a |
|
| 543 | + | let case Assembler { |
|
| 544 | + | text, textLen, data, dataLen, symbols: definedSymbols, symbolsLen, |
|
| 545 | + | externalFixups: pendingFixups, externalFixupsLen, .. |
|
| 546 | + | } = a |
|
| 528 | 547 | else panic "expected assembler state"; |
|
| 529 | - | return Program { text, data, symbols: definedSymbols, externalFixups: pendingFixups }; |
|
| 548 | + | return Program { |
|
| 549 | + | text: &text[..textLen], |
|
| 550 | + | data: &data[..dataLen], |
|
| 551 | + | symbols: &definedSymbols[..symbolsLen], |
|
| 552 | + | externalFixups: &pendingFixups[..externalFixupsLen], |
|
| 553 | + | }; |
|
| 554 | + | } |
|
| 555 | + | ||
| 556 | + | /// Bound symbol and fixup counts by lexical tokens, including one spare slot. |
|
| 557 | + | unsafe fn tokenCapacity(kind: scanner::SourceKind, source: *[u8], pool: *unsafe mut strings::Pool) -> u32 { |
|
| 558 | + | let mut scan = scanner::scanner(kind, source, pool); |
|
| 559 | + | let mut count = SOURCE_CAP_PADDING; |
|
| 560 | + | while true { |
|
| 561 | + | let token = scanner::next(&mut scan); |
|
| 562 | + | if token.kind == scanner::TokenKind::Eof { |
|
| 563 | + | break; |
|
| 564 | + | } |
|
| 565 | + | set count += 1; |
|
| 566 | + | if token.kind == scanner::TokenKind::Invalid { |
|
| 567 | + | break; |
|
| 568 | + | } |
|
| 569 | + | } |
|
| 570 | + | return count; |
|
| 530 | 571 | } |
|
| 531 | 572 | ||
| 532 | 573 | /// Return the next power of two at least as large as `value`. |
|
| 533 | 574 | fn nextPowerOfTwo(value: u32) -> u32 { |
|
| 534 | 575 | let mut n: u32 = MIN_TABLE_CAPACITY; |
lib/std/arch/rv64/asm/emit.rad
+25 -12
| 3 | 3 | use std::arch::rv64::encode; |
|
| 4 | 4 | use std::arch::rv64; |
|
| 5 | 5 | use std::fmt; |
|
| 6 | 6 | ||
| 7 | 7 | use std::collections::dict; |
|
| 8 | - | use std::lang::alloc; |
|
| 9 | 8 | use std::lang::gen; |
|
| 10 | 9 | ||
| 11 | 10 | /// Define a symbol at the current text or data offset. |
|
| 12 | 11 | export unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8]) { |
|
| 13 | - | let idx = a.symbols.len; |
|
| 14 | - | let offset: i32 = a.data.len as i32 |
|
| 12 | + | let idx = a.symbolsLen; |
|
| 13 | + | let offset: i32 = a.dataLen as i32 |
|
| 15 | 14 | if a.section == super::Section::Data |
|
| 16 | - | else a.text.len as i32 * rv64::INSTR_SIZE; |
|
| 15 | + | else a.textLen as i32 * rv64::INSTR_SIZE; |
|
| 17 | 16 | ||
| 18 | - | a.symbols.append(super::Symbol { |
|
| 17 | + | assert a.symbolsLen < a.symbols.len, "defineSymbol: symbol buffer full"; |
|
| 18 | + | set a.symbols[a.symbolsLen] = super::Symbol { |
|
| 19 | 19 | name, |
|
| 20 | 20 | section: a.section, |
|
| 21 | 21 | offset, |
|
| 22 | 22 | isExported: dict::get(&a.exportMap, name) <> nil, |
|
| 23 | - | }, alloc::arenaAllocator(a.arena)); |
|
| 23 | + | }; |
|
| 24 | + | set a.symbolsLen += 1; |
|
| 24 | 25 | dict::insert(&mut a.symbolMap, name, idx as i32); |
|
| 25 | 26 | } |
|
| 26 | 27 | ||
| 27 | 28 | /// Append one encoded instruction word to the text section. |
|
| 28 | 29 | export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) { |
|
| 29 | - | a.text.append(word, alloc::arenaAllocator(a.arena)); |
|
| 30 | + | if a.textLen >= a.text.len { |
|
| 31 | + | throw super::Error::TextOverflow; |
|
| 32 | + | } |
|
| 33 | + | set a.text[a.textLen] = word; |
|
| 34 | + | set a.textLen += 1; |
|
| 30 | 35 | } |
|
| 31 | 36 | ||
| 32 | 37 | /// Append `words` no-op instructions to the text section. |
|
| 33 | 38 | export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) { |
|
| 34 | 39 | for _ in 0..words { |
| 36 | 41 | } |
|
| 37 | 42 | } |
|
| 38 | 43 | ||
| 39 | 44 | /// Append one byte to the data section. |
|
| 40 | 45 | export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) { |
|
| 41 | - | a.data.append(byte, alloc::arenaAllocator(a.arena)); |
|
| 46 | + | if a.dataLen >= a.data.len { |
|
| 47 | + | throw super::Error::DataOverflow; |
|
| 48 | + | } |
|
| 49 | + | set a.data[a.dataLen] = byte; |
|
| 50 | + | set a.dataLen += 1; |
|
| 42 | 51 | } |
|
| 43 | 52 | ||
| 44 | 53 | /// Emit a little-endian integer with `bytes` bytes. |
|
| 45 | 54 | unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) { |
|
| 46 | 55 | for i in 0..bytes { |
| 63 | 72 | } |
|
| 64 | 73 | } |
|
| 65 | 74 | ||
| 66 | 75 | /// Record a data-section symbol fixup and reserve its bytes. |
|
| 67 | 76 | export unsafe fn recordDataFixup(a: &mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) { |
|
| 68 | - | let offset = a.data.len; |
|
| 77 | + | let offset = a.dataLen; |
|
| 69 | 78 | match width { |
|
| 70 | 79 | case super::DataWidth::Word => { |
|
| 71 | 80 | recordFixup(a, target, super::FixupInfo::Word { offset }); |
|
| 72 | 81 | try emitDataInt(a, 0, rv64::WORD_SIZE as u32); |
|
| 73 | 82 | } |
| 78 | 87 | } |
|
| 79 | 88 | } |
|
| 80 | 89 | ||
| 81 | 90 | /// Record a pending symbol fixup. |
|
| 82 | 91 | unsafe fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) { |
|
| 83 | - | a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena)); |
|
| 92 | + | assert a.fixupsLen < a.fixups.len, "recordFixup: fixup buffer full"; |
|
| 93 | + | set a.fixups[a.fixupsLen] = super::Fixup { symbol, info }; |
|
| 94 | + | set a.fixupsLen += 1; |
|
| 84 | 95 | } |
|
| 85 | 96 | ||
| 86 | 97 | /// Record a text fixup that must be resolved after all program text is known. |
|
| 87 | 98 | unsafe fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) { |
|
| 88 | - | a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena)); |
|
| 99 | + | assert a.externalFixupsLen < a.externalFixups.len, "recordExternalFixup: fixup buffer full"; |
|
| 100 | + | set a.externalFixups[a.externalFixupsLen] = fixup; |
|
| 101 | + | set a.externalFixupsLen += 1; |
|
| 89 | 102 | } |
|
| 90 | 103 | ||
| 91 | 104 | /// Record a text-section symbol fixup and reserve its instruction words. |
|
| 92 | 105 | export unsafe fn recordTextFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) { |
|
| 93 | 106 | recordFixup(a, symbol, info); |
| 109 | 122 | return symbol.offset + (a.dataBase as i32); |
|
| 110 | 123 | } |
|
| 111 | 124 | ||
| 112 | 125 | /// Resolve final symbol references and patch all delayed output. |
|
| 113 | 126 | export unsafe fn finishProgram(a: &mut super::Assembler) throws (super::Error) { |
|
| 114 | - | for i in 0..a.fixups.len { |
|
| 127 | + | for i in 0..a.fixupsLen { |
|
| 115 | 128 | let fixup = a.fixups[i]; |
|
| 116 | 129 | let symbol = findSymbol(a, fixup.symbol) else { |
|
| 117 | 130 | match fixup.info { |
|
| 118 | 131 | case super::FixupInfo::Jal { .. }, super::FixupInfo::Addr { .. } => { |
|
| 119 | 132 | recordExternalFixup(a, fixup); |
lib/std/arch/rv64/asm/parser.rad
+29 -13
| 286 | 286 | throw failOnToken(tok, "instructions are only valid in the text section"); |
|
| 287 | 287 | } |
|
| 288 | 288 | if let format = atomics::parse(name) { |
|
| 289 | 289 | let rd = try parseRegister(a); |
|
| 290 | 290 | let mut rs2 = rv64::ZERO; |
|
| 291 | - | if format.operation <> 2 { set rs2 = try parseRegister(a); } |
|
| 291 | + | if format.operation <> 2 { |
|
| 292 | + | set rs2 = try parseRegister(a); |
|
| 293 | + | } |
|
| 292 | 294 | let memory = try parseMemory(a); |
|
| 293 | - | if memory.offset <> 0 { throw fail(a, "atomic memory offset must be zero"); } |
|
| 295 | + | if memory.offset <> 0 { |
|
| 296 | + | throw fail(a, "atomic memory offset must be zero"); |
|
| 297 | + | } |
|
| 294 | 298 | try emit::emitText(a, atomics::encode(atomics::Instruction { format, rd, rs1: memory.base, rs2 })); |
|
| 295 | 299 | return; |
|
| 296 | 300 | } |
|
| 297 | 301 | let form = lookupInstruction(name) else { |
|
| 298 | 302 | throw failOnToken(tok, "unknown instruction"); |
| 346 | 350 | ||
| 347 | 351 | /// Parse the `la` pseudo-instruction. |
|
| 348 | 352 | unsafe fn parseLa(a: &mut super::Assembler) throws (super::Error) { |
|
| 349 | 353 | let rd = try parseRegister(a); |
|
| 350 | 354 | let target = try parseLabelName(a); |
|
| 351 | - | let index = a.text.len; |
|
| 355 | + | let index = a.textLen; |
|
| 352 | 356 | ||
| 353 | 357 | try emit::recordTextFixup(a, target, super::FixupInfo::Addr { rd, index }, 2); |
|
| 354 | 358 | } |
|
| 355 | 359 | ||
| 356 | 360 | /// Parse a CSR read-like instruction with destination register then CSR. |
| 468 | 472 | return try parseLabelName(a); |
|
| 469 | 473 | } |
|
| 470 | 474 | ||
| 471 | 475 | /// Parse a branch target as either a label fixup or immediate offset. |
|
| 472 | 476 | unsafe fn parseBranchLabel(a: &mut super::Assembler, op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg) throws (super::Error) { |
|
| 473 | - | let index = a.text.len; |
|
| 477 | + | let index = a.textLen; |
|
| 474 | 478 | if let target = try parseOptionalLabel(a) { |
|
| 475 | 479 | try emit::recordTextFixup(a, target, super::FixupInfo::Branch { op, rs1, rs2, index }, 1); |
|
| 476 | 480 | return; |
|
| 477 | 481 | } |
|
| 478 | 482 | let imm = try parseBranchImm(a); |
| 491 | 495 | try parseJ(a, rd); |
|
| 492 | 496 | } |
|
| 493 | 497 | ||
| 494 | 498 | /// Parse a jump target for `jal` or a jump pseudo-instruction. |
|
| 495 | 499 | unsafe fn parseJ(a: &mut super::Assembler, rd: gen::Reg) throws (super::Error) { |
|
| 496 | - | let index = a.text.len; |
|
| 500 | + | let index = a.textLen; |
|
| 497 | 501 | if let target = try parseOptionalLabel(a) { |
|
| 498 | 502 | try emit::recordTextFixup(a, target, super::FixupInfo::Jal { rd, index }, 1); |
|
| 499 | 503 | return; |
|
| 500 | 504 | } |
|
| 501 | 505 | let imm = try parseJumpImm(a); |
| 580 | 584 | if count < 0 { |
|
| 581 | 585 | throw fail(a, "space size must be non-negative"); |
|
| 582 | 586 | } |
|
| 583 | 587 | // The data section grows on demand; only reject sizes that cannot be |
|
| 584 | 588 | // represented as a section offset. |
|
| 585 | - | if count > super::U32_MAX_VALUE - a.data.len as i64 { |
|
| 589 | + | if count > super::U32_MAX_VALUE - a.dataLen as i64 { |
|
| 586 | 590 | throw super::Error::DataOverflow; |
|
| 587 | 591 | } |
|
| 588 | 592 | for _ in 0..count as u32 { |
|
| 589 | 593 | try emit::emitByte(a, 0); |
|
| 590 | 594 | } |
| 606 | 610 | match a.section { |
|
| 607 | 611 | case super::Section::Text => { |
|
| 608 | 612 | if amount % rv64::INSTR_SIZE as u32 <> 0 { |
|
| 609 | 613 | throw fail(a, "text alignment must be a multiple of 4"); |
|
| 610 | 614 | } |
|
| 611 | - | let bytes = a.text.len * rv64::INSTR_SIZE as u32; |
|
| 615 | + | let bytes = a.textLen * rv64::INSTR_SIZE as u32; |
|
| 612 | 616 | let aligned = checkedAlignUp(bytes, amount) else { |
|
| 613 | 617 | throw super::Error::TextOverflow; |
|
| 614 | 618 | }; |
|
| 615 | 619 | let words = (aligned - bytes) / rv64::INSTR_SIZE as u32; |
|
| 616 | 620 | try emit::emitTextPadding(a, words); |
|
| 617 | 621 | } |
|
| 618 | 622 | case super::Section::Data => { |
|
| 619 | - | let aligned = checkedAlignUp(a.data.len, amount) else { |
|
| 623 | + | let aligned = checkedAlignUp(a.dataLen, amount) else { |
|
| 620 | 624 | throw super::Error::DataOverflow; |
|
| 621 | 625 | }; |
|
| 622 | - | for _ in a.data.len..aligned { |
|
| 626 | + | for _ in a.dataLen..aligned { |
|
| 623 | 627 | try emit::emitByte(a, 0); |
|
| 624 | 628 | } |
|
| 625 | 629 | } |
|
| 626 | 630 | } |
|
| 627 | 631 | } |
| 871 | 875 | ||
| 872 | 876 | /// Parse an access-class mask without duplicate fields. |
|
| 873 | 877 | unsafe fn parseFenceMask(a: &mut super::Assembler) -> u32 throws (super::Error) { |
|
| 874 | 878 | if a.scan.current.kind == scanner::TokenKind::Number { |
|
| 875 | 879 | let value = try parseValue(a); |
|
| 876 | - | if value <> 0 { throw fail(a, "numeric fence mask must be zero"); } |
|
| 880 | + | if value <> 0 { |
|
| 881 | + | throw fail(a, "numeric fence mask must be zero"); |
|
| 882 | + | } |
|
| 877 | 883 | return 0; |
|
| 878 | 884 | } |
|
| 879 | 885 | let token = try expectToken(a, scanner::TokenKind::Ident, "expected fence access classes"); |
|
| 880 | 886 | let mut mask: u32 = 0; |
|
| 881 | 887 | for ch in token.source { |
|
| 882 | 888 | let mut bit: u32 = 0; |
|
| 883 | 889 | match ch { |
|
| 884 | - | case 'i' => { set bit = 8; }, case 'o' => { set bit = 4; }, |
|
| 885 | - | case 'r' => { set bit = 2; }, case 'w' => { set bit = 1; }, |
|
| 890 | + | case 'i' => { |
|
| 891 | + | set bit = 8; |
|
| 892 | + | }, case 'o' => { |
|
| 893 | + | set bit = 4; |
|
| 894 | + | }, |
|
| 895 | + | case 'r' => { |
|
| 896 | + | set bit = 2; |
|
| 897 | + | }, case 'w' => { |
|
| 898 | + | set bit = 1; |
|
| 899 | + | }, |
|
| 886 | 900 | else => throw failOnToken(token, "invalid fence access class"), |
|
| 887 | 901 | } |
|
| 888 | - | if (mask & bit) <> 0 { throw failOnToken(token, "duplicate fence access class"); } |
|
| 902 | + | if (mask & bit) <> 0 { |
|
| 903 | + | throw failOnToken(token, "duplicate fence access class"); |
|
| 904 | + | } |
|
| 889 | 905 | set mask |= bit; |
|
| 890 | 906 | } |
|
| 891 | 907 | return mask; |
|
| 892 | 908 | } |
|
| 893 | 909 |
lib/std/arch/rv64/asm/tests.rad
+62 -0
| 16 | 16 | static ASM_DATA_STORAGE: [u8; 1024] = [0; 1024]; |
|
| 17 | 17 | unsafe static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 18 | 18 | static PRINT_ARENA_STORAGE: [u8; 1024] = [0; 1024]; |
|
| 19 | 19 | static PRINT_BUFFER: [u8; 128] = [0; 128]; |
|
| 20 | 20 | ||
| 21 | + | /// The retired-instruction CSR assembles to its exact architectural encoding. |
|
| 22 | + | @test unsafe fn retiredCounter() throws (testing::TestError) { |
|
| 23 | + | let program = try assembleSource(".text;\ncsrr %a0 instret;\n"); |
|
| 24 | + | assert program.text.len == 1 and program.text[0] == 0xc0202573; |
|
| 25 | + | } |
|
| 26 | + | ||
| 27 | + | /// Source buffer for comment capacity tests. |
|
| 28 | + | static SOURCE: [u8; 4096] = [0; 4096]; |
|
| 29 | + | ||
| 30 | + | /// Long comments consume source space while leaving symbol and fixup demand small. |
|
| 31 | + | @test unsafe fn commentStorage() throws (testing::TestError) { |
|
| 32 | + | let source = &mut SOURCE[..]; |
|
| 33 | + | for i in 0..source.len { |
|
| 34 | + | set source[i] = 32; |
|
| 35 | + | } |
|
| 36 | + | set source[0] = '/'; set source[1] = '/'; |
|
| 37 | + | let text = "\n.text;\nret;\n"; |
|
| 38 | + | for byte, i in text { |
|
| 39 | + | set source[source.len - text.len + i] = byte; |
|
| 40 | + | } |
|
| 41 | + | let program = try assembleSource(&SOURCE[..]); |
|
| 42 | + | try testing::expect(program.text.len == 1 and program.text[0] == encode::ret()); |
|
| 43 | + | } |
|
| 44 | + | ||
| 21 | 45 | unsafe fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) { |
|
| 22 | 46 | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
|
| 23 | 47 | return try super::assemble( |
|
| 24 | 48 | scanner::SourceKind::String, |
|
| 25 | 49 | source, |
| 122 | 146 | try testing::expect(mem::eq(program.externalFixups[1].symbol, "::default")); |
|
| 123 | 147 | try testing::expect(addrRd == rv64::T0); |
|
| 124 | 148 | try testing::expect(addrIndex == 1); |
|
| 125 | 149 | } |
|
| 126 | 150 | ||
| 151 | + | @test unsafe fn testAssembleTextOverflow() throws (testing::TestError) { |
|
| 152 | + | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
|
| 153 | + | try super::assemble( |
|
| 154 | + | scanner::SourceKind::String, |
|
| 155 | + | ".text;\nret;\nret;\n", |
|
| 156 | + | &mut ASM_TEXT_STORAGE[..1], |
|
| 157 | + | &mut ASM_DATA_STORAGE[..1], |
|
| 158 | + | &mut arena, |
|
| 159 | + | &mut ASM_STRING_POOL, |
|
| 160 | + | rv64::RO_DATA_BASE |
|
| 161 | + | ) catch err { |
|
| 162 | + | match err { |
|
| 163 | + | case super::Error::TextOverflow => return, |
|
| 164 | + | else => throw testing::TestError::Failed, |
|
| 165 | + | } |
|
| 166 | + | }; |
|
| 167 | + | throw testing::TestError::Failed; |
|
| 168 | + | } |
|
| 169 | + | ||
| 170 | + | @test unsafe fn testAssembleDataOverflow() throws (testing::TestError) { |
|
| 171 | + | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
|
| 172 | + | try super::assemble( |
|
| 173 | + | scanner::SourceKind::String, |
|
| 174 | + | ".data;\n.byte 1, 2;\n", |
|
| 175 | + | &mut ASM_TEXT_STORAGE[..1], |
|
| 176 | + | &mut ASM_DATA_STORAGE[..1], |
|
| 177 | + | &mut arena, |
|
| 178 | + | &mut ASM_STRING_POOL, |
|
| 179 | + | rv64::RO_DATA_BASE |
|
| 180 | + | ) catch err { |
|
| 181 | + | match err { |
|
| 182 | + | case super::Error::DataOverflow => return, |
|
| 183 | + | else => throw testing::TestError::Failed, |
|
| 184 | + | } |
|
| 185 | + | }; |
|
| 186 | + | throw testing::TestError::Failed; |
|
| 187 | + | } |
|
| 188 | + | ||
| 127 | 189 | @test unsafe fn testAssembleInvalidOperandsFail() throws (testing::TestError) { |
|
| 128 | 190 | try expectAssembleFail( |
|
| 129 | 191 | ".text;\nbeq %a0 %a1 @missing;\n" |
|
| 130 | 192 | ); |
|
| 131 | 193 | try expectAssembleFail( |
lib/std/arch/rv64/atomicTests.rad
+18 -6
| 30 | 30 | format: atomics::Format { operation, width: 2 + wide, order }, |
|
| 31 | 31 | rd: super::A0, rs1: super::A1, rs2: super::ZERO if operation == 2 else super::A2, |
|
| 32 | 32 | }; |
|
| 33 | 33 | let word = words[i] | (wide << 12) | (order << 25); |
|
| 34 | 34 | try testing::expect(atomics::encode(item) == word); |
|
| 35 | - | let decoded = atomics::decode(word) else { throw testing::TestError::Failed; }; |
|
| 35 | + | let decoded = atomics::decode(word) else { |
|
| 36 | + | throw testing::TestError::Failed; |
|
| 37 | + | }; |
|
| 36 | 38 | try testing::expect(decoded == item); |
|
| 37 | - | let case decode::Instr::Atomic(instruction) = decode::decode(word) else { throw testing::TestError::Failed; }; |
|
| 39 | + | let case decode::Instr::Atomic(instruction) = decode::decode(word) else { |
|
| 40 | + | throw testing::TestError::Failed; |
|
| 41 | + | }; |
|
| 38 | 42 | try testing::expect(instruction == item); |
|
| 39 | 43 | } |
|
| 40 | 44 | } |
|
| 41 | 45 | } |
|
| 42 | 46 | } |
| 47 | 51 | try testing::expect(atomics::decode(word) == nil); |
|
| 48 | 52 | } |
|
| 49 | 53 | for name in &["lr", "lr.q", "lr.w.aq.rl", "sc.d.rl.aq", "amoswap.d.bad", "amoadd.wextra"] { |
|
| 50 | 54 | try testing::expect(atomics::parse(name) == nil); |
|
| 51 | 55 | } |
|
| 52 | - | let format = atomics::parse("amoswap.d.aqrl") else { throw testing::TestError::Failed; }; |
|
| 56 | + | let format = atomics::parse("amoswap.d.aqrl") else { |
|
| 57 | + | throw testing::TestError::Failed; |
|
| 58 | + | }; |
|
| 53 | 59 | try testing::expect(format.operation == 1 and format.width == 3 and format.order == 3); |
|
| 54 | 60 | } |
|
| 55 | 61 | ||
| 56 | 62 | /// Assemble one instruction and return its exact word. |
|
| 57 | 63 | unsafe fn assemble(source: *[u8]) -> u32 throws (testing::TestError) { |
|
| 58 | 64 | let mut arena = alloc::new(&mut MEMORY[..]); |
|
| 59 | 65 | let words = &mut WORDS[..]; |
|
| 60 | 66 | let data: *mut [u8] = &mut []; |
|
| 61 | 67 | let result = try asm::assemble(asm::scanner::SourceKind::String, source, |
|
| 62 | 68 | &mut words[..], &mut data[..], &mut arena, &mut STRINGS, 0) |
|
| 63 | - | catch { throw testing::TestError::Failed; }; |
|
| 69 | + | catch { |
|
| 70 | + | throw testing::TestError::Failed; |
|
| 71 | + | }; |
|
| 64 | 72 | try testing::expect(result.text.len == 1); |
|
| 65 | 73 | return result.text[0]; |
|
| 66 | 74 | } |
|
| 67 | 75 | ||
| 68 | 76 | /// Check instruction suffix scanning and assembler operand order. |
| 81 | 89 | /// Decode fence classes and local instruction synchronization. |
|
| 82 | 90 | @test fn fences() throws (testing::TestError) { |
|
| 83 | 91 | try testing::expect(encode::fenceI() == 0x0000100f); |
|
| 84 | 92 | try testing::expect(decode::decode(encode::fenceI()) == decode::Instr::FenceI); |
|
| 85 | 93 | let case decode::Instr::Fence { predecessor, successor } = decode::decode(encode::fenceOrder(15, 15)) |
|
| 86 | - | else { throw testing::TestError::Failed; }; |
|
| 94 | + | else { |
|
| 95 | + | throw testing::TestError::Failed; |
|
| 96 | + | }; |
|
| 87 | 97 | try testing::expect(predecessor == 15 and successor == 15); |
|
| 88 | 98 | let case decode::Instr::Unknown { .. } = decode::decode(0x0000200f) |
|
| 89 | - | else { throw testing::TestError::Failed; }; |
|
| 99 | + | else { |
|
| 100 | + | throw testing::TestError::Failed; |
|
| 101 | + | }; |
|
| 90 | 102 | } |
|
| 91 | 103 | ||
| 92 | 104 | /// Reject invalid atomic operands and fence access classes. |
|
| 93 | 105 | @test unsafe fn invalidAssembly() throws (testing::TestError) { |
|
| 94 | 106 | for source in &[ |
lib/std/arch/rv64/atomics.rad
+32 -10
| 43 | 43 | { name: "amomaxu", code: 28 }, |
|
| 44 | 44 | ]; |
|
| 45 | 45 | ||
| 46 | 46 | /// Look up an operation's canonical mnemonic stem. |
|
| 47 | 47 | export fn name(code: u32) -> ?*[u8] { |
|
| 48 | - | for operation in &OPERATIONS[..] { if operation.code == code { return operation.name; } } |
|
| 48 | + | for operation in &OPERATIONS[..] { |
|
| 49 | + | if operation.code == code { |
|
| 50 | + | return operation.name; |
|
| 51 | + | } |
|
| 52 | + | } |
|
| 49 | 53 | return nil; |
|
| 50 | 54 | } |
|
| 51 | 55 | ||
| 52 | 56 | /// Parse an atomic mnemonic with mandatory width and optional ordering suffix. |
|
| 53 | 57 | export fn parse(text: *[u8]) -> ?Format { |
|
| 54 | 58 | for operation in &OPERATIONS[..] { |
|
| 55 | 59 | let n = operation.name.len; |
|
| 56 | - | if text.len < n + 2 or not mem::eq(&text[..n], operation.name) or text[n] <> '.' { continue; } |
|
| 60 | + | if text.len < n + 2 or not mem::eq(&text[..n], operation.name) or text[n] <> '.' { |
|
| 61 | + | continue; |
|
| 62 | + | } |
|
| 57 | 63 | let mut width: u32 = 2; |
|
| 58 | - | if text[n + 1] == 'd' { set width = 3; } |
|
| 59 | - | else if text[n + 1] <> 'w' { return nil; } |
|
| 64 | + | if text[n + 1] == 'd' { |
|
| 65 | + | set width = 3; |
|
| 66 | + | } |
|
| 67 | + | else if text[n + 1] <> 'w' { |
|
| 68 | + | return nil; |
|
| 69 | + | } |
|
| 60 | 70 | let suffix = &text[n + 2..]; |
|
| 61 | 71 | let mut order: u32 = 0; |
|
| 62 | - | if mem::eq(suffix, ".aq") { set order = 2; } |
|
| 63 | - | else if mem::eq(suffix, ".rl") { set order = 1; } |
|
| 64 | - | else if mem::eq(suffix, ".aqrl") { set order = 3; } |
|
| 65 | - | else if suffix.len <> 0 { return nil; } |
|
| 72 | + | if mem::eq(suffix, ".aq") { |
|
| 73 | + | set order = 2; |
|
| 74 | + | } |
|
| 75 | + | else if mem::eq(suffix, ".rl") { |
|
| 76 | + | set order = 1; |
|
| 77 | + | } |
|
| 78 | + | else if mem::eq(suffix, ".aqrl") { |
|
| 79 | + | set order = 3; |
|
| 80 | + | } |
|
| 81 | + | else if suffix.len <> 0 { |
|
| 82 | + | return nil; |
|
| 83 | + | } |
|
| 66 | 84 | return Format { operation: operation.code, width, order }; |
|
| 67 | 85 | } |
|
| 68 | 86 | return nil; |
|
| 69 | 87 | } |
|
| 70 | 88 |
| 81 | 99 | /// Decode a supported atomic word and reject reserved width and LR fields. |
|
| 82 | 100 | export fn decode(word: u32) -> ?Instruction { |
|
| 83 | 101 | let operation = word >> 27; |
|
| 84 | 102 | let width = (word >> 12) & 7; |
|
| 85 | 103 | let rs2 = super::reg(((word >> 20) & 31) as u8); |
|
| 86 | - | if (word & 127) <> 0x2f or name(operation) == nil or (width <> 2 and width <> 3) { return nil; } |
|
| 87 | - | if operation == 2 and rs2 <> super::ZERO { return nil; } |
|
| 104 | + | if (word & 127) <> 0x2f or name(operation) == nil or (width <> 2 and width <> 3) { |
|
| 105 | + | return nil; |
|
| 106 | + | } |
|
| 107 | + | if operation == 2 and rs2 <> super::ZERO { |
|
| 108 | + | return nil; |
|
| 109 | + | } |
|
| 88 | 110 | return Instruction { |
|
| 89 | 111 | format: Format { operation, width, order: (word >> 25) & 3 }, |
|
| 90 | 112 | rd: super::reg(((word >> 7) & 31) as u8), rs1: super::reg(((word >> 15) & 31) as u8), rs2, |
|
| 91 | 113 | }; |
|
| 92 | 114 | } |
lib/std/arch/rv64/bounds.rad
+39 -24
| 34 | 34 | @test unsafe fn emissionCapacity() throws (testing::TestError) { |
|
| 35 | 35 | for kind in 0..8 { |
|
| 36 | 36 | let mut arena = alloc::new(&mut MEMORY[..]); |
|
| 37 | 37 | let mut e = try! emit::emitter(&mut arena, false); |
|
| 38 | 38 | match kind { |
|
| 39 | - | case 0 => { set e.code = &mut e.code[..0]; emit::emit(&mut e, encode::nop()); }, |
|
| 39 | + | case 0 => { |
|
| 40 | + | set e.code = &mut e.code[..0]; |
|
| 41 | + | emit::emit(&mut e, encode::nop()); |
|
| 42 | + | }, |
|
| 40 | 43 | case 1 => { |
|
| 41 | - | set e.pendingBranches.len = 0; set e.pendingBranches.cap = 0; |
|
| 44 | + | set e.pendingBranchesLen = e.pendingBranches.len; |
|
| 42 | 45 | emit::recordBranch(&mut e, 0, emit::BranchKind::Jump); |
|
| 43 | 46 | }, |
|
| 44 | 47 | case 2 => { |
|
| 45 | - | set e.pendingCalls.len = 0; set e.pendingCalls.cap = 0; |
|
| 48 | + | set e.pendingCallsLen = e.pendingCalls.len; |
|
| 46 | 49 | emit::recordCall(&mut e, "p::call"); |
|
| 47 | 50 | }, |
|
| 48 | 51 | case 3 => { |
|
| 49 | - | set e.pendingJumps.len = 0; set e.pendingJumps.cap = 0; |
|
| 52 | + | set e.pendingJumpsLen = e.pendingJumps.len; |
|
| 50 | 53 | emit::recordJumpAt(&mut e, "p::jump", super::ZERO, 0); |
|
| 51 | 54 | }, |
|
| 52 | 55 | case 4 => { |
|
| 53 | - | set e.pendingAddrLoads.len = 0; set e.pendingAddrLoads.cap = 0; |
|
| 56 | + | set e.pendingAddrLoadsLen = e.pendingAddrLoads.len; |
|
| 54 | 57 | emit::recordDataAddrLoad(&mut e, "p::data", super::A0); |
|
| 55 | 58 | }, |
|
| 56 | 59 | case 5 => { |
|
| 57 | - | set e.funcs.len = 0; set e.funcs.cap = 0; |
|
| 60 | + | set e.funcsLen = e.funcs.len; |
|
| 58 | 61 | emit::recordFunc(&mut e, "p::call"); |
|
| 59 | 62 | }, |
|
| 60 | 63 | case 6 => { |
|
| 61 | 64 | let entries = &mut ENTRIES[..2]; |
|
| 62 | 65 | set e.labels.funcs = dict::init(&mut entries[..]); |
|
| 63 | 66 | emit::recordFuncOffset(&mut e, "p::first"); |
|
| 64 | 67 | emit::recordFuncOffset(&mut e, "p::second"); |
|
| 65 | 68 | }, |
|
| 66 | - | else => { emit::recordSrcLoc(&mut e, il::SrcLoc { moduleId: 0, offset: 0 }); }, |
|
| 69 | + | else => { |
|
| 70 | + | emit::recordSrcLoc(&mut e, il::SrcLoc { moduleId: 0, offset: 0 }); |
|
| 71 | + | }, |
|
| 67 | 72 | } |
|
| 68 | 73 | try testing::expect(e.error == super::Error::Capacity); |
|
| 69 | 74 | let count = e.codeLen; |
|
| 70 | 75 | emit::emit(&mut e, encode::ebreak()); |
|
| 71 | 76 | try testing::expect(e.codeLen == count); |
| 136 | 141 | } |
|
| 137 | 142 | ||
| 138 | 143 | /// Spill candidate storage fails explicitly when too many values are live. |
|
| 139 | 144 | @test unsafe fn registerStorage() throws (testing::TestError) { |
|
| 140 | 145 | let mut arena = alloc::new(&mut SCRATCH[..]); |
|
| 141 | - | let mut liveSet = try! bitset::allocate(&mut arena, 257); |
|
| 142 | - | for i in 0..257 { bitset::put(&mut liveSet, i); } |
|
| 143 | - | let mut out = [liveSet]; |
|
| 144 | - | let live = regalloc::liveness::LiveInfo { |
|
| 145 | - | liveIn: &mut [], liveOut: &mut out[..], defs: &mut [], uses: &mut [], blockCount: 1, maxReg: 257, |
|
| 146 | - | }; |
|
| 147 | - | let func = il::Fn { |
|
| 148 | - | name: "p::pressure", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true, |
|
| 149 | - | blocks: &[il::Block { label: "entry", params: &[], instrs: &mut [], locs: &[], preds: &[], loopDepth: 0 }], |
|
| 150 | - | }; |
|
| 151 | - | let mut failed = false; |
|
| 152 | - | try regalloc::spill::analyze(&func, &live, 23, 11, 8, &mut arena) catch { |
|
| 153 | - | set failed = true; |
|
| 154 | - | }; |
|
| 155 | - | try testing::expect(failed); |
|
| 146 | + | use arena as bits in { |
|
| 147 | + | let liveSet = try! bitset::allocate(&bits, 257); |
|
| 148 | + | for i in 0..257 { |
|
| 149 | + | bitset::put(liveSet, i); |
|
| 150 | + | } |
|
| 151 | + | let live = regalloc::liveness::LiveInfo 'bits { |
|
| 152 | + | liveIn: &liveSet[..0], liveOut: &liveSet[..], |
|
| 153 | + | defs: &liveSet[..0], uses: &liveSet[..0], |
|
| 154 | + | words: bitset::wordsFor(257), blockCount: 1, maxReg: 257, |
|
| 155 | + | }; |
|
| 156 | + | let func = il::Fn { |
|
| 157 | + | name: "p::pressure", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true, |
|
| 158 | + | blocks: &[il::Block { label: "entry", params: &[], instrs: &mut [], locs: &[], preds: &[], loopDepth: 0 }], |
|
| 159 | + | }; |
|
| 160 | + | let mut failed = false; |
|
| 161 | + | try regalloc::spill::analyze(&func, &live, 23, 11, 8, &bits) catch { |
|
| 162 | + | set failed = true; |
|
| 163 | + | }; |
|
| 164 | + | try testing::expect(failed); |
|
| 165 | + | } |
|
| 156 | 166 | } |
|
| 157 | 167 | ||
| 158 | 168 | /// Data output and symbol maps reject insufficient or ambiguous storage. |
|
| 159 | 169 | @test unsafe fn dataStorage() throws (testing::TestError) { |
|
| 160 | 170 | let mut arena = alloc::new(&mut MEMORY[..]); |
| 180 | 190 | try data::buildMap(&duplicate[..], &mut larger[..]) catch err { |
|
| 181 | 191 | try testing::expect(err == data::Error::Symbol); set failed += 1; |
|
| 182 | 192 | }; |
|
| 183 | 193 | try testing::expect(failed == 3); |
|
| 184 | 194 | let length = try data::emitSection(items, &map, &e.labels, 0x80000000, &mut bytes[..], false) |
|
| 185 | - | catch { throw testing::TestError::Failed; }; |
|
| 195 | + | catch { |
|
| 196 | + | throw testing::TestError::Failed; |
|
| 197 | + | }; |
|
| 186 | 198 | try testing::expect(length == 8 and bytes[0] == 1); |
|
| 187 | 199 | } |
|
| 188 | 200 | ||
| 189 | 201 | /// Name the failed invariant before returning to the test runner. |
|
| 190 | 202 | fn check(condition: bool, name: *[u8]) throws (testing::TestError) { |
|
| 191 | - | if not condition { io::printLn(name); throw testing::TestError::Failed; } |
|
| 203 | + | if not condition { |
|
| 204 | + | io::printLn(name); |
|
| 205 | + | throw testing::TestError::Failed; |
|
| 206 | + | } |
|
| 192 | 207 | } |
lib/std/arch/rv64/decode.rad
+6 -2
| 216 | 216 | let rs1 = super::reg(rs1(instr)); |
|
| 217 | 217 | let rs2 = super::reg(rs2(instr)); |
|
| 218 | 218 | ||
| 219 | 219 | match op { |
|
| 220 | 220 | case 0x2f => { |
|
| 221 | - | if let atomic = atomics::decode(instr) { return Instr::Atomic(atomic); } |
|
| 221 | + | if let atomic = atomics::decode(instr) { |
|
| 222 | + | return Instr::Atomic(atomic); |
|
| 223 | + | } |
|
| 222 | 224 | return Instr::Unknown { bits: instr }; |
|
| 223 | 225 | }, |
|
| 224 | 226 | case 0x0f => { |
|
| 225 | - | if instr == 0x100f { return Instr::FenceI; } |
|
| 227 | + | if instr == 0x100f { |
|
| 228 | + | return Instr::FenceI; |
|
| 229 | + | } |
|
| 226 | 230 | if (instr & 0xf00fffff) == 0x0f { |
|
| 227 | 231 | return Instr::Fence { predecessor: (instr >> 24) & 15, successor: (instr >> 20) & 15 }; |
|
| 228 | 232 | } |
|
| 229 | 233 | return Instr::Unknown { bits: instr }; |
|
| 230 | 234 | }, |
lib/std/arch/rv64/emit.rad
+223 -82
| 12 | 12 | use std::mem; |
|
| 13 | 13 | ||
| 14 | 14 | use super::encode; |
|
| 15 | 15 | ||
| 16 | 16 | /// Maximum number of instructions in code buffer. |
|
| 17 | - | constant MAX_INSTRS: u32 = 2097152; |
|
| 17 | + | export constant MAX_INSTRS: u32 = 2097152; |
|
| 18 | 18 | /// Maximum code length before byte offset overflows signed 32-bits. |
|
| 19 | 19 | constant MAX_CODE_LEN: u32 = 0x7FFFFFFF / super::INSTR_SIZE as u32; |
|
| 20 | 20 | /// Maximum number of pending branches awaiting patching. |
|
| 21 | - | constant MAX_PENDING: u32 = 65536; |
|
| 21 | + | export constant MAX_PENDING: u32 = 65536; |
|
| 22 | 22 | /// Maximum number of function entries. |
|
| 23 | - | constant MAX_FUNCS: u32 = 4096; |
|
| 23 | + | export constant MAX_FUNCS: u32 = 4096; |
|
| 24 | 24 | /// Maximum number of debug entries. |
|
| 25 | - | constant MAX_DEBUG_ENTRIES: u32 = 524288; |
|
| 25 | + | export constant MAX_DEBUG_ENTRIES: u32 = 524288; |
|
| 26 | 26 | ||
| 27 | 27 | ////////////////////// |
|
| 28 | 28 | // Emission Context // |
|
| 29 | 29 | ////////////////////// |
|
| 30 | 30 |
| 104 | 104 | code: *mut [u32], |
|
| 105 | 105 | /// Current number of emitted instructions. |
|
| 106 | 106 | codeLen: u32, |
|
| 107 | 107 | /// Local branches needing offset patching. |
|
| 108 | 108 | pendingBranches: *mut [PendingBranch], |
|
| 109 | + | /// Number of local branches that need patching. |
|
| 110 | + | pendingBranchesLen: u32, |
|
| 109 | 111 | /// Function calls needing offset patching. |
|
| 110 | 112 | pendingCalls: *mut [PendingCall], |
|
| 113 | + | /// Number of function calls that need patching. |
|
| 114 | + | pendingCallsLen: u32, |
|
| 111 | 115 | /// Assembly jumps needing offset patching. |
|
| 112 | 116 | pendingJumps: *mut [PendingJump], |
|
| 117 | + | /// Number of assembly jumps that need patching. |
|
| 118 | + | pendingJumpsLen: u32, |
|
| 113 | 119 | /// Function address loads needing offset patching. |
|
| 114 | 120 | pendingAddrLoads: *mut [PendingAddrLoad], |
|
| 121 | + | /// Number of function address loads that need patching. |
|
| 122 | + | pendingAddrLoadsLen: u32, |
|
| 115 | 123 | /// Block label tracking. |
|
| 116 | 124 | labels: labels::Labels, |
|
| 117 | 125 | /// Function start positions for printing. |
|
| 118 | 126 | funcs: *mut [types::FuncAddr], |
|
| 127 | + | /// Number of function start positions recorded. |
|
| 128 | + | funcsLen: u32, |
|
| 119 | 129 | /// Debug entries mapping PCs to source locations. |
|
| 120 | 130 | debugEntries: *mut [types::DebugEntry], |
|
| 121 | 131 | /// Number of debug entries recorded. |
|
| 122 | 132 | debugEntriesLen: u32, |
|
| 123 | 133 | } |
|
| 124 | 134 | ||
| 135 | + | /// Caller-owned storage for program emission. |
|
| 136 | + | export record Storage { |
|
| 137 | + | /// Emitted instruction buffer. |
|
| 138 | + | code: *mut [u32], |
|
| 139 | + | /// Local branch patch buffer. |
|
| 140 | + | pendingBranches: *mut [PendingBranch], |
|
| 141 | + | /// Function call patch buffer. |
|
| 142 | + | pendingCalls: *mut [PendingCall], |
|
| 143 | + | /// Assembly jump patch buffer. |
|
| 144 | + | pendingJumps: *mut [PendingJump], |
|
| 145 | + | /// Address load patch buffer. |
|
| 146 | + | pendingAddrLoads: *mut [PendingAddrLoad], |
|
| 147 | + | /// Per-function block offset buffer. |
|
| 148 | + | blockOffsets: *mut [i32], |
|
| 149 | + | /// Function label hash table storage. |
|
| 150 | + | funcEntries: *mut [dict::Entry], |
|
| 151 | + | /// Printed function address buffer. |
|
| 152 | + | funcs: *mut [types::FuncAddr], |
|
| 153 | + | /// Debug location buffer. |
|
| 154 | + | debugEntries: *mut [types::DebugEntry], |
|
| 155 | + | } |
|
| 156 | + | ||
| 125 | 157 | /// Computed stack frame layout for a function. |
|
| 126 | 158 | export record Frame: Copy { |
|
| 127 | 159 | /// Total frame size in bytes (aligned). |
|
| 128 | 160 | totalSize: i32, |
|
| 129 | 161 | /// Callee-saved registers and their offsets. |
| 181 | 213 | return frame; |
|
| 182 | 214 | } |
|
| 183 | 215 | ||
| 184 | 216 | /// Create a new emitter. |
|
| 185 | 217 | export unsafe fn emitter(arena: &mut alloc::Arena, debug: bool) -> Emitter throws (alloc::AllocError) { |
|
| 218 | + | let storage = try allocateStorage(arena, debug); |
|
| 219 | + | return emitterWithStorage(storage, debug); |
|
| 220 | + | } |
|
| 221 | + | ||
| 222 | + | /// Allocate emitter storage from an arena. |
|
| 223 | + | export unsafe fn allocateStorage(arena: &mut alloc::Arena, debug: bool) -> Storage throws (alloc::AllocError) { |
|
| 186 | 224 | let code = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_INSTRS); |
|
| 187 | 225 | let pendingBranches = try alloc::allocSlice(arena, @sizeOf(PendingBranch), @alignOf(PendingBranch), MAX_PENDING); |
|
| 188 | 226 | let pendingCalls = try alloc::allocSlice(arena, @sizeOf(PendingCall), @alignOf(PendingCall), MAX_PENDING); |
|
| 189 | 227 | let pendingJumps = try alloc::allocSlice(arena, @sizeOf(PendingJump), @alignOf(PendingJump), MAX_PENDING); |
|
| 190 | 228 | let pendingAddrLoads = try alloc::allocSlice(arena, @sizeOf(PendingAddrLoad), @alignOf(PendingAddrLoad), MAX_PENDING); |
| 196 | 234 | if debug { |
|
| 197 | 235 | set debugEntries = try alloc::allocSlice( |
|
| 198 | 236 | arena, @sizeOf(types::DebugEntry), @alignOf(types::DebugEntry), MAX_DEBUG_ENTRIES |
|
| 199 | 237 | ) as *mut [types::DebugEntry]; |
|
| 200 | 238 | } |
|
| 201 | - | let mut pendingBranchesBuf = pendingBranches as *mut [PendingBranch]; |
|
| 202 | - | let mut pendingCallsBuf = pendingCalls as *mut [PendingCall]; |
|
| 203 | - | let mut pendingJumpsBuf = pendingJumps as *mut [PendingJump]; |
|
| 204 | - | let mut pendingAddrLoadsBuf = pendingAddrLoads as *mut [PendingAddrLoad]; |
|
| 205 | - | let mut funcsBuf = funcs as *mut [types::FuncAddr]; |
|
| 206 | - | set pendingBranchesBuf.len = 0; |
|
| 207 | - | set pendingCallsBuf.len = 0; |
|
| 208 | - | set pendingJumpsBuf.len = 0; |
|
| 209 | - | set pendingAddrLoadsBuf.len = 0; |
|
| 210 | - | set funcsBuf.len = 0; |
|
| 239 | + | return Storage { |
|
| 240 | + | code: code as *mut [u32], |
|
| 241 | + | pendingBranches: pendingBranches as *mut [PendingBranch], |
|
| 242 | + | pendingCalls: pendingCalls as *mut [PendingCall], |
|
| 243 | + | pendingJumps: pendingJumps as *mut [PendingJump], |
|
| 244 | + | pendingAddrLoads: pendingAddrLoads as *mut [PendingAddrLoad], |
|
| 245 | + | blockOffsets: blockOffsets as *mut [i32], |
|
| 246 | + | funcEntries: funcEntries as *mut [dict::Entry], |
|
| 247 | + | funcs: funcs as *mut [types::FuncAddr], |
|
| 248 | + | debugEntries, |
|
| 249 | + | }; |
|
| 250 | + | } |
|
| 251 | + | ||
| 252 | + | /// Create an emitter from caller-owned storage. |
|
| 253 | + | export fn emitterWithStorage(storage: Storage, debug: bool) -> Emitter { |
|
| 254 | + | let case Storage { |
|
| 255 | + | code, pendingBranches, pendingCalls, pendingJumps, pendingAddrLoads, |
|
| 256 | + | blockOffsets, funcEntries, funcs, debugEntries, |
|
| 257 | + | } = storage else panic "expected emitter storage"; |
|
| 258 | + | assert code.len >= MAX_INSTRS, "emitterWithStorage: code buffer too small"; |
|
| 259 | + | assert pendingBranches.len >= MAX_PENDING, "emitterWithStorage: branch buffer too small"; |
|
| 260 | + | assert pendingCalls.len >= MAX_PENDING, "emitterWithStorage: call buffer too small"; |
|
| 261 | + | assert pendingJumps.len >= MAX_PENDING, "emitterWithStorage: jump buffer too small"; |
|
| 262 | + | assert pendingAddrLoads.len >= MAX_PENDING, "emitterWithStorage: address buffer too small"; |
|
| 263 | + | assert blockOffsets.len >= labels::MAX_BLOCKS_PER_FN, "emitterWithStorage: block buffer too small"; |
|
| 264 | + | assert funcEntries.len >= labels::FUNC_TABLE_SIZE, "emitterWithStorage: label table too small"; |
|
| 265 | + | assert funcs.len >= MAX_FUNCS, "emitterWithStorage: function buffer too small"; |
|
| 266 | + | let mut activeDebugEntries: *mut [types::DebugEntry] = &mut []; |
|
| 267 | + | if debug { |
|
| 268 | + | assert debugEntries.len >= MAX_DEBUG_ENTRIES, "emitterWithStorage: debug buffer too small"; |
|
| 269 | + | set activeDebugEntries = debugEntries; |
|
| 270 | + | } |
|
| 211 | 271 | return Emitter { |
|
| 212 | 272 | error: nil, |
|
| 213 | 273 | sharedData: false, |
|
| 214 | - | code: code as *mut [u32], |
|
| 274 | + | code, |
|
| 215 | 275 | codeLen: 0, |
|
| 216 | - | pendingBranches: pendingBranchesBuf, |
|
| 217 | - | pendingCalls: pendingCallsBuf, |
|
| 218 | - | pendingJumps: pendingJumpsBuf, |
|
| 219 | - | pendingAddrLoads: pendingAddrLoadsBuf, |
|
| 220 | - | labels: labels::init(blockOffsets as *mut [i32], funcEntries as *mut [dict::Entry]), |
|
| 221 | - | funcs: funcsBuf, |
|
| 222 | - | debugEntries, |
|
| 276 | + | pendingBranches, |
|
| 277 | + | pendingBranchesLen: 0, |
|
| 278 | + | pendingCalls, |
|
| 279 | + | pendingCallsLen: 0, |
|
| 280 | + | pendingJumps, |
|
| 281 | + | pendingJumpsLen: 0, |
|
| 282 | + | pendingAddrLoads, |
|
| 283 | + | pendingAddrLoadsLen: 0, |
|
| 284 | + | labels: labels::init(blockOffsets, funcEntries), |
|
| 285 | + | funcs, |
|
| 286 | + | funcsLen: 0, |
|
| 287 | + | debugEntries: activeDebugEntries, |
|
| 223 | 288 | debugEntriesLen: 0, |
|
| 224 | 289 | }; |
|
| 225 | 290 | } |
|
| 226 | 291 | ||
| 227 | 292 | /////////////////////// |
|
| 228 | 293 | // Emission Helpers // |
|
| 229 | 294 | /////////////////////// |
|
| 230 | 295 | ||
| 231 | 296 | /// Emit a single instruction. |
|
| 232 | 297 | export fn emit(e: &mut Emitter, instr: u32) { |
|
| 233 | - | if e.error <> nil { return; } |
|
| 234 | - | if e.codeLen == e.code.len { set e.error = super::Error::Capacity; return; } |
|
| 298 | + | if e.error <> nil { |
|
| 299 | + | return; |
|
| 300 | + | } |
|
| 301 | + | if e.codeLen == e.code.len { |
|
| 302 | + | set e.error = super::Error::Capacity; |
|
| 303 | + | return; |
|
| 304 | + | } |
|
| 235 | 305 | set e.code[e.codeLen] = instr; |
|
| 236 | 306 | set e.codeLen += 1; |
|
| 237 | 307 | } |
|
| 238 | 308 | ||
| 239 | 309 | /// Compute branch offset to a function by name. |
|
| 240 | 310 | export fn branchOffsetToFunc(e: &mut Emitter, srcIndex: u32, name: *[u8]) -> i32 { |
|
| 241 | - | if e.error <> nil { return 0; } |
|
| 311 | + | if e.error <> nil { |
|
| 312 | + | return 0; |
|
| 313 | + | } |
|
| 242 | 314 | let target = dict::get(&e.labels.funcs, name) else { |
|
| 243 | 315 | set e.error = super::Error::Symbol; return 0; |
|
| 244 | 316 | }; |
|
| 245 | 317 | return target - srcIndex as i32 * super::INSTR_SIZE; |
|
| 246 | 318 | } |
|
| 247 | 319 | ||
| 248 | 320 | /// Patch an instruction at a given index. |
|
| 249 | 321 | export fn patch(e: &mut Emitter, index: u32, instr: u32) { |
|
| 250 | - | if e.error <> nil { return; } |
|
| 251 | - | if index >= e.codeLen { set e.error = super::Error::Capacity; return; } |
|
| 322 | + | if e.error <> nil { |
|
| 323 | + | return; |
|
| 324 | + | } |
|
| 325 | + | if index >= e.codeLen { |
|
| 326 | + | set e.error = super::Error::Capacity; |
|
| 327 | + | return; |
|
| 328 | + | } |
|
| 252 | 329 | set e.code[index] = instr; |
|
| 253 | 330 | } |
|
| 254 | 331 | ||
| 255 | 332 | /// Record a block's address for branch resolution. |
|
| 256 | 333 | export fn recordBlock(e: &mut Emitter, blockIdx: u32) { |
|
| 257 | - | if e.error <> nil { return; } |
|
| 334 | + | if e.error <> nil { |
|
| 335 | + | return; |
|
| 336 | + | } |
|
| 258 | 337 | if e.codeLen > MAX_CODE_LEN or blockIdx >= e.labels.blockOffsets.len { |
|
| 259 | 338 | set e.error = super::Error::Capacity; return; |
|
| 260 | 339 | } |
|
| 261 | 340 | labels::recordBlock(&mut e.labels, blockIdx, e.codeLen as i32 * super::INSTR_SIZE); |
|
| 262 | 341 | } |
| 267 | 346 | recordFuncOffsetAt(e, name, codeLen); |
|
| 268 | 347 | } |
|
| 269 | 348 | ||
| 270 | 349 | /// Record a function's code offset at `index` for call resolution. |
|
| 271 | 350 | export fn recordFuncOffsetAt(e: &mut Emitter, name: *[u8], index: u32) { |
|
| 272 | - | if e.error <> nil { return; } |
|
| 273 | - | if index > MAX_CODE_LEN { set e.error = super::Error::Capacity; return; } |
|
| 274 | - | if name.len == 0 { set e.error = super::Error::Symbol; return; } |
|
| 351 | + | if e.error <> nil { |
|
| 352 | + | return; |
|
| 353 | + | } |
|
| 354 | + | if index > MAX_CODE_LEN { |
|
| 355 | + | set e.error = super::Error::Capacity; |
|
| 356 | + | return; |
|
| 357 | + | } |
|
| 358 | + | if name.len == 0 { |
|
| 359 | + | set e.error = super::Error::Symbol; |
|
| 360 | + | return; |
|
| 361 | + | } |
|
| 275 | 362 | if e.labels.funcs.count >= e.labels.funcs.entries.len / 2 and dict::get(&e.labels.funcs, name) == nil { |
|
| 276 | 363 | set e.error = super::Error::Capacity; return; |
|
| 277 | 364 | } |
|
| 278 | 365 | dict::insert(&mut e.labels.funcs, name, index as i32 * super::INSTR_SIZE); |
|
| 279 | 366 | } |
| 284 | 371 | recordFuncAt(e, name, codeLen); |
|
| 285 | 372 | } |
|
| 286 | 373 | ||
| 287 | 374 | /// Record a function's start position at `index` for printing. |
|
| 288 | 375 | export fn recordFuncAt(e: &mut Emitter, name: *[u8], index: u32) { |
|
| 289 | - | if e.error <> nil { return; } |
|
| 290 | - | if e.funcs.len == e.funcs.cap { set e.error = super::Error::Capacity; return; } |
|
| 291 | - | let count = e.funcs.len; |
|
| 292 | - | unsafe { set e.funcs.len = count + 1; } |
|
| 293 | - | set e.funcs[count] = types::FuncAddr { name, index }; |
|
| 376 | + | if e.error <> nil { |
|
| 377 | + | return; |
|
| 378 | + | } |
|
| 379 | + | if e.funcsLen == e.funcs.len { |
|
| 380 | + | set e.error = super::Error::Capacity; |
|
| 381 | + | return; |
|
| 382 | + | } |
|
| 383 | + | set e.funcs[e.funcsLen] = types::FuncAddr { name, index }; |
|
| 384 | + | set e.funcsLen += 1; |
|
| 294 | 385 | } |
|
| 295 | 386 | ||
| 296 | 387 | /// Record a local branch needing later patching. |
|
| 297 | 388 | /// Unconditional jumps use a single slot (J-type, +-1MB range). |
|
| 298 | 389 | /// Conditional branches use two slots (B-type has only +-4KB range, |
|
| 299 | 390 | /// so large functions may need the inverted-branch + JAL fallback). |
|
| 300 | 391 | export fn recordBranch(e: &mut Emitter, targetBlock: u32, kind: BranchKind) { |
|
| 301 | - | if e.error <> nil { return; } |
|
| 302 | - | if e.pendingBranches.len == e.pendingBranches.cap { set e.error = super::Error::Capacity; return; } |
|
| 303 | - | let count = e.pendingBranches.len; |
|
| 304 | - | unsafe { set e.pendingBranches.len = count + 1; } |
|
| 305 | - | set e.pendingBranches[count] = PendingBranch { |
|
| 392 | + | if e.error <> nil { |
|
| 393 | + | return; |
|
| 394 | + | } |
|
| 395 | + | if e.pendingBranchesLen == e.pendingBranches.len { |
|
| 396 | + | set e.error = super::Error::Capacity; |
|
| 397 | + | return; |
|
| 398 | + | } |
|
| 399 | + | set e.pendingBranches[e.pendingBranchesLen] = PendingBranch { |
|
| 306 | 400 | index: e.codeLen, |
|
| 307 | 401 | target: targetBlock, |
|
| 308 | - | kind: kind, |
|
| 402 | + | kind, |
|
| 309 | 403 | }; |
|
| 404 | + | set e.pendingBranchesLen += 1; |
|
| 310 | 405 | ||
| 311 | 406 | emit(e, encode::nop()); // First slot, always needed. |
|
| 312 | 407 | ||
| 313 | 408 | match kind { |
|
| 314 | 409 | case BranchKind::Jump => {}, |
| 318 | 413 | ||
| 319 | 414 | /// Record a function call needing later patching. |
|
| 320 | 415 | /// Emits placeholder instructions that will be patched later. |
|
| 321 | 416 | /// Uses two slots to support long-distance calls. |
|
| 322 | 417 | export fn recordCall(e: &mut Emitter, target: *[u8]) { |
|
| 323 | - | if e.error <> nil { return; } |
|
| 324 | - | if e.pendingCalls.len == e.pendingCalls.cap { set e.error = super::Error::Capacity; return; } |
|
| 325 | - | let count = e.pendingCalls.len; |
|
| 326 | - | unsafe { set e.pendingCalls.len = count + 1; } |
|
| 327 | - | set e.pendingCalls[count] = PendingCall { |
|
| 418 | + | if e.error <> nil { |
|
| 419 | + | return; |
|
| 420 | + | } |
|
| 421 | + | if e.pendingCallsLen == e.pendingCalls.len { |
|
| 422 | + | set e.error = super::Error::Capacity; |
|
| 423 | + | return; |
|
| 424 | + | } |
|
| 425 | + | set e.pendingCalls[e.pendingCallsLen] = PendingCall { |
|
| 328 | 426 | index: e.codeLen, |
|
| 329 | 427 | target, |
|
| 330 | 428 | }; |
|
| 429 | + | set e.pendingCallsLen += 1; |
|
| 331 | 430 | ||
| 332 | 431 | emit(e, encode::nop()); // Placeholder for AUIPC. |
|
| 333 | 432 | emit(e, encode::nop()); // Placeholder for JALR. |
|
| 334 | 433 | } |
|
| 335 | 434 | ||
| 336 | 435 | /// Record a jump emitted by assembly that needs whole-program patching. |
|
| 337 | 436 | export fn recordJumpAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) { |
|
| 338 | - | if e.error <> nil { return; } |
|
| 339 | - | if e.pendingJumps.len == e.pendingJumps.cap { set e.error = super::Error::Capacity; return; } |
|
| 340 | - | let count = e.pendingJumps.len; |
|
| 341 | - | unsafe { set e.pendingJumps.len = count + 1; } |
|
| 342 | - | set e.pendingJumps[count] = PendingJump { |
|
| 437 | + | if e.error <> nil { |
|
| 438 | + | return; |
|
| 439 | + | } |
|
| 440 | + | if e.pendingJumpsLen == e.pendingJumps.len { |
|
| 441 | + | set e.error = super::Error::Capacity; |
|
| 442 | + | return; |
|
| 443 | + | } |
|
| 444 | + | set e.pendingJumps[e.pendingJumpsLen] = PendingJump { |
|
| 343 | 445 | index, |
|
| 344 | 446 | target, |
|
| 345 | 447 | rd, |
|
| 346 | 448 | }; |
|
| 449 | + | set e.pendingJumpsLen += 1; |
|
| 347 | 450 | } |
|
| 348 | 451 | ||
| 349 | 452 | /// Record a function address load needing later patching. |
|
| 350 | 453 | /// Emits placeholder instructions that will be patched to load the function's address. |
|
| 351 | 454 | /// Uses two slots to compute long-distance addresses. |
| 357 | 460 | emit(e, encode::nop()); // Placeholder for ADDI. |
|
| 358 | 461 | } |
|
| 359 | 462 | ||
| 360 | 463 | /// Record a function address load already reserved by assembly. |
|
| 361 | 464 | export fn recordAddrLoadAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) { |
|
| 362 | - | if e.error <> nil { return; } |
|
| 363 | - | if e.pendingAddrLoads.len == e.pendingAddrLoads.cap { set e.error = super::Error::Capacity; return; } |
|
| 364 | - | let count = e.pendingAddrLoads.len; |
|
| 365 | - | unsafe { set e.pendingAddrLoads.len = count + 1; } |
|
| 366 | - | set e.pendingAddrLoads[count] = PendingAddrLoad { |
|
| 465 | + | if e.error <> nil { |
|
| 466 | + | return; |
|
| 467 | + | } |
|
| 468 | + | if e.pendingAddrLoadsLen == e.pendingAddrLoads.len { |
|
| 469 | + | set e.error = super::Error::Capacity; |
|
| 470 | + | return; |
|
| 471 | + | } |
|
| 472 | + | set e.pendingAddrLoads[e.pendingAddrLoadsLen] = PendingAddrLoad { |
|
| 367 | 473 | index, |
|
| 368 | 474 | target, |
|
| 369 | - | rd: rd, |
|
| 475 | + | rd, |
|
| 370 | 476 | isData: false, |
|
| 371 | 477 | }; |
|
| 478 | + | set e.pendingAddrLoadsLen += 1; |
|
| 372 | 479 | } |
|
| 373 | 480 | ||
| 374 | 481 | /// Record a data address load needing later patching. |
|
| 375 | 482 | /// Reserves two instructions for a PC-relative address load. |
|
| 376 | 483 | export fn recordDataAddrLoad(e: &mut Emitter, target: *[u8], rd: gen::Reg) { |
|
| 377 | - | if e.error <> nil { return; } |
|
| 378 | - | if e.pendingAddrLoads.len == e.pendingAddrLoads.cap { set e.error = super::Error::Capacity; return; } |
|
| 379 | - | let count = e.pendingAddrLoads.len; |
|
| 380 | - | unsafe { set e.pendingAddrLoads.len = count + 1; } |
|
| 381 | - | set e.pendingAddrLoads[count] = PendingAddrLoad { |
|
| 484 | + | if e.error <> nil { |
|
| 485 | + | return; |
|
| 486 | + | } |
|
| 487 | + | if e.pendingAddrLoadsLen == e.pendingAddrLoads.len { |
|
| 488 | + | set e.error = super::Error::Capacity; |
|
| 489 | + | return; |
|
| 490 | + | } |
|
| 491 | + | set e.pendingAddrLoads[e.pendingAddrLoadsLen] = PendingAddrLoad { |
|
| 382 | 492 | index: e.codeLen, |
|
| 383 | 493 | target, |
|
| 384 | - | rd: rd, |
|
| 494 | + | rd, |
|
| 385 | 495 | isData: true, |
|
| 386 | 496 | }; |
|
| 497 | + | set e.pendingAddrLoadsLen += 1; |
|
| 387 | 498 | ||
| 388 | 499 | emit(e, encode::nop()); // Address-load upper instruction. |
|
| 389 | 500 | emit(e, encode::nop()); // Address-load lower instruction. |
|
| 390 | 501 | if e.sharedData { |
|
| 391 | 502 | emit(e, encode::nop()); // Package offset addition. |
| 398 | 509 | /// Called after each function. |
|
| 399 | 510 | /// |
|
| 400 | 511 | /// Uses two-instruction sequences: short branches use `branch` and `nop`, |
|
| 401 | 512 | /// long branches use inverted branch and `jal` or `auipc` and `jalr`. |
|
| 402 | 513 | export unsafe fn patchLocalBranches(e: &mut Emitter) { |
|
| 403 | - | if e.error <> nil { return; } |
|
| 404 | - | for i in 0..e.pendingBranches.len { |
|
| 514 | + | if e.error <> nil { |
|
| 515 | + | return; |
|
| 516 | + | } |
|
| 517 | + | for i in 0..e.pendingBranchesLen { |
|
| 405 | 518 | let p = e.pendingBranches[i]; |
|
| 406 | - | if p.target >= e.labels.blockCount { set e.error = super::Error::Symbol; return; } |
|
| 519 | + | if p.target >= e.labels.blockCount { |
|
| 520 | + | set e.error = super::Error::Symbol; |
|
| 521 | + | return; |
|
| 522 | + | } |
|
| 407 | 523 | let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE); |
|
| 408 | 524 | match p.kind { |
|
| 409 | 525 | case BranchKind::Cond { op, rs1, rs2 } => { |
|
| 410 | 526 | if encode::isBranchImm(offset) { |
|
| 411 | 527 | patch(e, p.index, encodeCondBranch(op, rs1, rs2, offset)); |
|
| 412 | 528 | patch(e, p.index + 1, encode::nop()); |
|
| 413 | 529 | } else { |
|
| 414 | 530 | let adj = offset - super::INSTR_SIZE; |
|
| 415 | - | if not encode::isJumpImm(adj) { set e.error = super::Error::Relocation; return; } |
|
| 531 | + | if not encode::isJumpImm(adj) { |
|
| 532 | + | set e.error = super::Error::Relocation; |
|
| 533 | + | return; |
|
| 534 | + | } |
|
| 416 | 535 | patch(e, p.index, encodeInvertedBranch(op, rs1, rs2, super::INSTR_SIZE * 2)); |
|
| 417 | 536 | patch(e, p.index + 1, encode::jal(super::ZERO, adj)); |
|
| 418 | 537 | } |
|
| 419 | 538 | }, |
|
| 420 | 539 | case BranchKind::InvertedCond { op, rs1, rs2 } => { |
|
| 421 | 540 | if encode::isBranchImm(offset) { |
|
| 422 | 541 | patch(e, p.index, encodeInvertedBranch(op, rs1, rs2, offset)); |
|
| 423 | 542 | patch(e, p.index + 1, encode::nop()); |
|
| 424 | 543 | } else { |
|
| 425 | 544 | let adj = offset - super::INSTR_SIZE; |
|
| 426 | - | if not encode::isJumpImm(adj) { set e.error = super::Error::Relocation; return; } |
|
| 545 | + | if not encode::isJumpImm(adj) { |
|
| 546 | + | set e.error = super::Error::Relocation; |
|
| 547 | + | return; |
|
| 548 | + | } |
|
| 427 | 549 | patch(e, p.index, encodeCondBranch(op, rs1, rs2, super::INSTR_SIZE * 2)); |
|
| 428 | 550 | patch(e, p.index + 1, encode::jal(super::ZERO, adj)); |
|
| 429 | 551 | } |
|
| 430 | 552 | }, |
|
| 431 | 553 | case BranchKind::Jump => { |
|
| 432 | 554 | // Single-slot jump (J-type, +-1MB range). |
|
| 433 | - | if not encode::isJumpImm(offset) { set e.error = super::Error::Relocation; return; } |
|
| 555 | + | if not encode::isJumpImm(offset) { |
|
| 556 | + | set e.error = super::Error::Relocation; |
|
| 557 | + | return; |
|
| 558 | + | } |
|
| 434 | 559 | patch(e, p.index, encode::jal(super::ZERO, offset)); |
|
| 435 | 560 | }, |
|
| 436 | 561 | } |
|
| 437 | 562 | } |
|
| 438 | - | set e.pendingBranches.len = 0; |
|
| 563 | + | set e.pendingBranchesLen = 0; |
|
| 439 | 564 | } |
|
| 440 | 565 | ||
| 441 | 566 | /// Encode a conditional branch instruction. |
|
| 442 | 567 | fn encodeCondBranch(op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg, offset: i32) -> u32 { |
|
| 443 | 568 | match op { |
| 459 | 584 | } |
|
| 460 | 585 | ||
| 461 | 586 | /// Patch all pending function calls. |
|
| 462 | 587 | /// Called after all functions have been generated. |
|
| 463 | 588 | export fn patchCalls(e: &mut Emitter) { |
|
| 464 | - | for i in 0..e.pendingCalls.len { |
|
| 589 | + | for i in 0..e.pendingCallsLen { |
|
| 465 | 590 | let p = e.pendingCalls[i]; |
|
| 466 | 591 | let offset = branchOffsetToFunc(e, p.index, p.target); |
|
| 467 | - | if offset > 0x7ffff7ff { set e.error = super::Error::Relocation; return; } |
|
| 592 | + | if offset > 0x7ffff7ff { |
|
| 593 | + | set e.error = super::Error::Relocation; |
|
| 594 | + | return; |
|
| 595 | + | } |
|
| 468 | 596 | let s = splitImm(offset); |
|
| 469 | 597 | ||
| 470 | 598 | // `AUIPC scratch, hi(offset)`. |
|
| 471 | 599 | patch(e, p.index, encode::auipc(super::SCRATCH1, s.hi)); |
|
| 472 | 600 | // `JALR ra, scratch, lo(offset)`. |
| 474 | 602 | } |
|
| 475 | 603 | } |
|
| 476 | 604 | ||
| 477 | 605 | /// Patch all pending assembly jumps. |
|
| 478 | 606 | export fn patchJumps(e: &mut Emitter) { |
|
| 479 | - | for i in 0..e.pendingJumps.len { |
|
| 607 | + | for i in 0..e.pendingJumpsLen { |
|
| 480 | 608 | let p = e.pendingJumps[i]; |
|
| 481 | 609 | let offset = branchOffsetToFunc(e, p.index, p.target); |
|
| 482 | 610 | ||
| 483 | - | if not encode::isJumpImm(offset) { set e.error = super::Error::Relocation; return; } |
|
| 611 | + | if not encode::isJumpImm(offset) { |
|
| 612 | + | set e.error = super::Error::Relocation; |
|
| 613 | + | return; |
|
| 614 | + | } |
|
| 484 | 615 | patch(e, p.index, encode::jal(p.rd, offset)); |
|
| 485 | 616 | } |
|
| 486 | 617 | } |
|
| 487 | 618 | ||
| 488 | 619 | /// Patch all pending function and data address loads. |
|
| 489 | 620 | /// Called after all functions have been generated and data layout is known. |
|
| 490 | 621 | export fn patchAddrLoads(e: &mut Emitter, dataSymMap: &data::DataSymMap, codeBase: u64) throws (super::Error) { |
|
| 491 | 622 | try check(e); |
|
| 492 | - | for i in 0..e.pendingAddrLoads.len { |
|
| 623 | + | for i in 0..e.pendingAddrLoadsLen { |
|
| 493 | 624 | let p = e.pendingAddrLoads[i]; |
|
| 494 | 625 | if p.isData { |
|
| 495 | 626 | let addr = data::lookupAddr(dataSymMap, p.target) else { |
|
| 496 | 627 | throw super::Error::Symbol; |
|
| 497 | 628 | }; |
| 505 | 636 | ||
| 506 | 637 | continue; |
|
| 507 | 638 | } |
|
| 508 | 639 | ||
| 509 | 640 | let offset = branchOffsetToFunc(e, p.index, p.target); |
|
| 510 | - | if offset > 0x7ffff7ff { set e.error = super::Error::Relocation; return; } |
|
| 641 | + | if offset > 0x7ffff7ff { |
|
| 642 | + | set e.error = super::Error::Relocation; |
|
| 643 | + | return; |
|
| 644 | + | } |
|
| 511 | 645 | let s = splitImm(offset); |
|
| 512 | 646 | // `AUIPC rd, hi(offset)`. |
|
| 513 | 647 | patch(e, p.index, encode::auipc(p.rd, s.hi)); |
|
| 514 | 648 | // `ADDI rd, rd, lo(offset)`. |
|
| 515 | 649 | patch(e, p.index + 1, encode::addi(p.rd, p.rd, s.lo)); |
| 780 | 914 | ////////////////// |
|
| 781 | 915 | // Code Access // |
|
| 782 | 916 | ////////////////// |
|
| 783 | 917 | ||
| 784 | 918 | /// Get emitted code as a slice. |
|
| 785 | - | export fn getCode(e: &Emitter) -> *[u32] { |
|
| 919 | + | export fn getCode 'code (e: &'code Emitter) -> &'code [u32] { |
|
| 786 | 920 | return &e.code[..e.codeLen]; |
|
| 787 | 921 | } |
|
| 788 | 922 | ||
| 789 | 923 | /// Record a debug entry mapping the current PC to a source location. |
|
| 790 | 924 | /// Deduplicates consecutive entries with the same location. |
|
| 791 | 925 | export fn recordSrcLoc(e: &mut Emitter, loc: il::SrcLoc) { |
|
| 792 | - | if e.error <> nil { return; } |
|
| 926 | + | if e.error <> nil { |
|
| 927 | + | return; |
|
| 928 | + | } |
|
| 793 | 929 | let pc = e.codeLen * super::INSTR_SIZE as u32; |
|
| 794 | 930 | ||
| 795 | 931 | // Skip if this is the same location as the previous entry. |
|
| 796 | 932 | if e.debugEntriesLen > 0 { |
|
| 797 | 933 | let prev = &e.debugEntries[e.debugEntriesLen - 1]; |
|
| 798 | 934 | if prev.offset == loc.offset and prev.moduleId == loc.moduleId { |
|
| 799 | 935 | return; |
|
| 800 | 936 | } |
|
| 801 | 937 | } |
|
| 802 | - | if e.debugEntriesLen == e.debugEntries.len { set e.error = super::Error::Capacity; return; } |
|
| 938 | + | if e.debugEntriesLen == e.debugEntries.len { |
|
| 939 | + | set e.error = super::Error::Capacity; |
|
| 940 | + | return; |
|
| 941 | + | } |
|
| 803 | 942 | set e.debugEntries[e.debugEntriesLen] = types::DebugEntry { |
|
| 804 | 943 | pc, |
|
| 805 | 944 | moduleId: loc.moduleId, |
|
| 806 | 945 | offset: loc.offset, |
|
| 807 | 946 | }; |
|
| 808 | 947 | set e.debugEntriesLen += 1; |
|
| 809 | 948 | } |
|
| 810 | 949 | ||
| 811 | 950 | /// Get debug entries as a slice. |
|
| 812 | - | export fn getDebugEntries(e: &Emitter) -> *[types::DebugEntry] { |
|
| 951 | + | export fn getDebugEntries 'code (e: &'code Emitter) -> &'code [types::DebugEntry] { |
|
| 813 | 952 | return &e.debugEntries[..e.debugEntriesLen]; |
|
| 814 | 953 | } |
|
| 815 | 954 | ||
| 816 | 955 | /// Return the first emission failure before any generated output is published. |
|
| 817 | 956 | export fn check(e: &Emitter) throws (super::Error) { |
|
| 818 | - | if let error = e.error { throw error; } |
|
| 957 | + | if let error = e.error { |
|
| 958 | + | throw error; |
|
| 959 | + | } |
|
| 819 | 960 | } |
lib/std/arch/rv64/image.rad
+6 -2
| 125 | 125 | /// Compute a signed displacement for a two-instruction AUIPC/ADDI load. |
|
| 126 | 126 | export fn displacement(source: u64, target: u64) -> ?i32 { |
|
| 127 | 127 | if target >= source { |
|
| 128 | 128 | let distance = target - source; |
|
| 129 | 129 | // AUIPC sign-extends its upper immediate before ADDI applies the low part. |
|
| 130 | - | if distance > 0x7ffff7ff { return nil; } |
|
| 130 | + | if distance > 0x7ffff7ff { |
|
| 131 | + | return nil; |
|
| 132 | + | } |
|
| 131 | 133 | return distance as i32; |
|
| 132 | 134 | } |
|
| 133 | 135 | let distance = source - target; |
|
| 134 | - | if distance > 0x80000000 { return nil; } |
|
| 136 | + | if distance > 0x80000000 { |
|
| 137 | + | return nil; |
|
| 138 | + | } |
|
| 135 | 139 | return (-(distance as i64)) as i32; |
|
| 136 | 140 | } |
lib/std/arch/rv64/image/tests.rad
+14 -6
| 25 | 25 | }; |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | /// Check the exact 64-byte little-endian header. |
|
| 29 | 29 | @test fn header() throws (testing::TestError) { |
|
| 30 | - | let bytes = try image::header(layout()) catch { throw testing::TestError::Failed; }; |
|
| 30 | + | let bytes = try image::header(layout()) catch { |
|
| 31 | + | throw testing::TestError::Failed; |
|
| 32 | + | }; |
|
| 31 | 33 | try testing::expectBytesEq(&bytes[..], &[ |
|
| 32 | 34 | 82, 65, 68, 48, 2, 0, 0, 0, 4, 0, 0, 128, 0, 0, 0, 0, |
|
| 33 | 35 | 0, 0, 0, 128, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, |
|
| 34 | 36 | 0, 16, 0, 128, 0, 0, 0, 0, 3, 0, 0, 0, 16, 0, 0, 0, |
|
| 35 | 37 | 0, 32, 0, 128, 0, 0, 0, 0, 4, 0, 0, 0, 0, 16, 0, 0, |
| 69 | 71 | set item = layout(); |
|
| 70 | 72 | set item.rwData.address = 0xfffffffffffffff8; |
|
| 71 | 73 | try invalid(item, image::Error::Overflow); |
|
| 72 | 74 | set item = layout(); |
|
| 73 | 75 | set item.roData.address = item.code.address + 8; |
|
| 74 | - | try image::validate(item) catch { throw testing::TestError::Failed; }; |
|
| 76 | + | try image::validate(item) catch { |
|
| 77 | + | throw testing::TestError::Failed; |
|
| 78 | + | }; |
|
| 75 | 79 | } |
|
| 76 | 80 | ||
| 77 | 81 | /// Check AUIPC/ADDI limits without overflowing unsigned address arithmetic. |
|
| 78 | 82 | @test fn displacements() throws (testing::TestError) { |
|
| 79 | 83 | try testing::expect(image::displacement(0x180000000, 0x180002000) == 8192); |
| 108 | 112 | }, |
|
| 109 | 113 | ]; |
|
| 110 | 114 | unsafe static syms: [data::DataSym; 2] = undefined; |
|
| 111 | 115 | let mut ro: [u8; 8] = [0; 8]; |
|
| 112 | 116 | let mut rw: [u8; 8] = [0; 8]; |
|
| 113 | - | let result = try rv64::finishProgram(&mut generator, globals, |
|
| 117 | + | let result = try rv64::finishProgram(generator, globals, |
|
| 114 | 118 | rv64::Storage { dataSyms: &mut syms[..], dataSymEntries: &mut ENTRIES[..] }, |
|
| 115 | 119 | &[], &mut ro[..], &mut rw[..] |
|
| 116 | - | ) catch { throw testing::TestError::Failed; }; |
|
| 120 | + | ) catch { |
|
| 121 | + | throw testing::TestError::Failed; |
|
| 122 | + | }; |
|
| 117 | 123 | try testing::expect(result.layout.code.address == 0x180000000); |
|
| 118 | 124 | try testing::expect(result.layout.rwData.initialized == 8); |
|
| 119 | 125 | try testing::expect(result.layout.rwData.memory == 4104); |
|
| 120 | 126 | try testing::expect(syms[1].addr == 0x180002008); |
|
| 121 | 127 | try testing::expectBytesEq(&rw[..], &[0, 0, 0, 128, 1, 0, 0, 0]); |
| 171 | 177 | }, |
|
| 172 | 178 | ]; |
|
| 173 | 179 | unsafe static syms: [data::DataSym; 2] = undefined; |
|
| 174 | 180 | let mut ro: [u8; 0] = []; |
|
| 175 | 181 | let mut rw: [u8; 16] = [255; 16]; |
|
| 176 | - | let result = try rv64::finishProgram(&mut generator, globals, |
|
| 182 | + | let result = try rv64::finishProgram(generator, globals, |
|
| 177 | 183 | rv64::Storage { dataSyms: &mut syms[..], dataSymEntries: &mut ENTRIES[..] }, |
|
| 178 | 184 | &[], &mut ro[..], &mut rw[..] |
|
| 179 | - | ) catch { throw testing::TestError::Failed; }; |
|
| 185 | + | ) catch { |
|
| 186 | + | throw testing::TestError::Failed; |
|
| 187 | + | }; |
|
| 180 | 188 | try testing::expect(result.layout.rwData.initialized == 16); |
|
| 181 | 189 | try testing::expect(syms[1].addr == 0x80002008); |
|
| 182 | 190 | try testing::expectBytesEq(&rw[..], &[97, 0, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0]); |
|
| 183 | 191 | } |
lib/std/arch/rv64/isel.rad
+172 -198
| 82 | 82 | //////////////////// |
|
| 83 | 83 | // Selector State // |
|
| 84 | 84 | //////////////////// |
|
| 85 | 85 | ||
| 86 | 86 | /// Instruction selector state. |
|
| 87 | - | export record Selector: Copy { |
|
| 87 | + | export record Selector: 'scratch + 'selection where 'scratch: 'selection { |
|
| 88 | 88 | /// Emitter for outputting instructions. |
|
| 89 | - | e: *unsafe mut emit::Emitter, |
|
| 89 | + | e: &'selection mut emit::Emitter, |
|
| 90 | 90 | /// Register allocation result. |
|
| 91 | - | ralloc: *unsafe regalloc::AllocResult, |
|
| 91 | + | ralloc: &'selection regalloc::AllocResult 'scratch, |
|
| 92 | 92 | /// Total stack frame size. |
|
| 93 | 93 | frameSize: i32, |
|
| 94 | 94 | /// Running offset into the reserve region of the frame. |
|
| 95 | 95 | /// Tracks current position within the pre-allocated reserve slots. |
|
| 96 | 96 | reserveOffset: i32, |
| 105 | 105 | ///////////////////////// |
|
| 106 | 106 | // Register Allocation // |
|
| 107 | 107 | ///////////////////////// |
|
| 108 | 108 | ||
| 109 | 109 | /// Get the physical register for an already-allocated SSA register. |
|
| 110 | - | unsafe fn getReg(s: &Selector, ssa: il::Reg) -> gen::Reg { |
|
| 110 | + | unsafe fn getReg 'scratch 'selection (s: &Selector 'scratch 'selection, ssa: il::Reg) -> gen::Reg where 'scratch: 'selection { |
|
| 111 | 111 | let phys = s.ralloc.assignments[ssa.n] else { |
|
| 112 | 112 | panic "getReg: spilled register has no physical assignment"; |
|
| 113 | 113 | }; |
|
| 114 | 114 | return phys; |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | 117 | /// Compute the offset for a spill slot. |
|
| 118 | 118 | /// When using FP (dynamic): offset from FP = `slot - totalSize`. |
|
| 119 | 119 | /// When using SP: offset from SP = `slot`. |
|
| 120 | - | fn spillOffset(s: &Selector, slot: i32) -> i32 { |
|
| 120 | + | fn spillOffset 'scratch 'selection (s: &Selector 'scratch 'selection, slot: i32) -> i32 where 'scratch: 'selection { |
|
| 121 | 121 | if s.isDynamic { |
|
| 122 | 122 | return slot - s.frameSize; |
|
| 123 | 123 | } |
|
| 124 | 124 | return slot; |
|
| 125 | 125 | } |
|
| 126 | 126 | ||
| 127 | 127 | /// Get the base register for spill slot addressing (FP or SP). |
|
| 128 | - | fn spillBase(s: &Selector) -> gen::Reg { |
|
| 128 | + | fn spillBase 'scratch 'selection (s: &Selector 'scratch 'selection) -> gen::Reg where 'scratch: 'selection { |
|
| 129 | 129 | if s.isDynamic { |
|
| 130 | 130 | return super::FP; |
|
| 131 | 131 | } |
|
| 132 | 132 | return super::SP; |
|
| 133 | 133 | } |
|
| 134 | 134 | ||
| 135 | 135 | /// Get the destination register for an SSA register. |
|
| 136 | 136 | /// If the register is spilled, records a pending spill and returns the scratch |
|
| 137 | 137 | /// register. The pending spill is auto-committed by [`selectBlock`] after each |
|
| 138 | 138 | /// instruction. If not spilled, returns the physical register. |
|
| 139 | - | unsafe fn getDstReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg { |
|
| 139 | + | unsafe fn getDstReg 'scratch 'selection (s: &mut Selector 'scratch 'selection, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg where 'scratch: 'selection { |
|
| 140 | 140 | if let _ = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) { |
|
| 141 | 141 | set s.pendingSpill = PendingSpill { ssa, rd: scratch }; |
|
| 142 | 142 | return scratch; |
|
| 143 | 143 | } |
|
| 144 | 144 | return getReg(s, ssa); |
|
| 145 | 145 | } |
|
| 146 | 146 | ||
| 147 | 147 | /// Get the source register for an SSA register. |
|
| 148 | 148 | /// If the register is spilled, loads the value from the spill slot into the |
|
| 149 | 149 | /// scratch register and returns it. Otherwise returns the physical register. |
|
| 150 | - | unsafe fn getSrcReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg { |
|
| 150 | + | unsafe fn getSrcReg 'scratch 'selection (s: &mut Selector 'scratch 'selection, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg where 'scratch: 'selection { |
|
| 151 | 151 | if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) { |
|
| 152 | 152 | emit::emitLd(s.e, scratch, spillBase(s), spillOffset(s, slot)); |
|
| 153 | 153 | return scratch; |
|
| 154 | 154 | } |
|
| 155 | 155 | return getReg(s, ssa); |
|
| 156 | 156 | } |
|
| 157 | 157 | ||
| 158 | 158 | /// Resolve an IL value to the physical register holding it. |
|
| 159 | 159 | /// For non-spilled register values, returns the physical register directly. |
|
| 160 | 160 | /// For immediates, symbols, and spilled registers, materializes into `scratch`. |
|
| 161 | - | unsafe fn resolveVal(s: &mut Selector, scratch: gen::Reg, val: il::Val) -> gen::Reg { |
|
| 161 | + | unsafe fn resolveVal 'scratch 'selection (s: &mut Selector 'scratch 'selection, scratch: gen::Reg, val: il::Val) -> gen::Reg where 'scratch: 'selection { |
|
| 162 | 162 | match val { |
|
| 163 | 163 | case il::Val::Reg(r) => { |
|
| 164 | 164 | return getSrcReg(s, r, scratch); |
|
| 165 | 165 | }, |
|
| 166 | 166 | case il::Val::Imm(imm) => { |
| 184 | 184 | } |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | 187 | /// Load an IL value into a specific physical register. |
|
| 188 | 188 | /// Like [`resolveVal`], but ensures the value ends up in `rd`. |
|
| 189 | - | unsafe fn loadVal(s: &mut Selector, rd: gen::Reg, val: il::Val) -> gen::Reg { |
|
| 189 | + | unsafe fn loadVal 'scratch 'selection (s: &mut Selector 'scratch 'selection, rd: gen::Reg, val: il::Val) -> gen::Reg where 'scratch: 'selection { |
|
| 190 | 190 | let rs = resolveVal(s, rd, val); |
|
| 191 | 191 | emitMv(s, rd, rs); |
|
| 192 | 192 | return rd; |
|
| 193 | 193 | } |
|
| 194 | 194 | ||
| 195 | 195 | /// Emit a move instruction if source and destination differ. |
|
| 196 | - | unsafe fn emitMv(s: &mut Selector, rd: gen::Reg, rs: gen::Reg) { |
|
| 196 | + | unsafe fn emitMv 'scratch 'selection (s: &mut Selector 'scratch 'selection, rd: gen::Reg, rs: gen::Reg) where 'scratch: 'selection { |
|
| 197 | 197 | if *rd <> *rs { |
|
| 198 | 198 | emit::emit(s.e, encode::mv(rd, rs)); |
|
| 199 | 199 | } |
|
| 200 | 200 | } |
|
| 201 | 201 |
| 233 | 233 | } |
|
| 234 | 234 | } |
|
| 235 | 235 | ||
| 236 | 236 | /// Resolve a divisor in its declared width, trap if it becomes zero, and |
|
| 237 | 237 | /// return the canonicalized register. |
|
| 238 | - | unsafe fn resolveAndTrapIfZero( |
|
| 239 | - | s: &mut Selector, |
|
| 238 | + | unsafe fn resolveAndTrapIfZero 'scratch 'selection ( |
|
| 239 | + | s: &mut Selector 'scratch 'selection, |
|
| 240 | 240 | b: il::Val, |
|
| 241 | 241 | typ: il::Type, |
|
| 242 | 242 | signed: bool |
|
| 243 | - | ) -> gen::Reg { |
|
| 243 | + | ) -> gen::Reg where 'scratch: 'selection { |
|
| 244 | 244 | let mut divisor = b; |
|
| 245 | 245 | if let case il::Val::Imm(imm) = b { |
|
| 246 | 246 | set divisor = il::Val::Imm(canonicalCmpImm(imm, typ, signed)); |
|
| 247 | 247 | } |
|
| 248 | 248 | let rs2 = resolveVal(s, super::SCRATCH2, divisor); |
| 272 | 272 | isDynamic: bool, |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// Pre-scan all blocks for constant-sized reserve instructions. |
|
| 276 | 276 | /// Returns the total size needed for all static reserves, respecting alignment. |
|
| 277 | - | unsafe fn computeReserveInfo(func: *unsafe il::Fn) -> ?ReserveInfo { |
|
| 277 | + | unsafe fn computeReserveInfo(func: &il::Fn) -> ?ReserveInfo { |
|
| 278 | 278 | let mut offset: i32 = 0; |
|
| 279 | 279 | let mut isDynamic = false; |
|
| 280 | 280 | ||
| 281 | 281 | for b in 0..func.blocks.len { |
|
| 282 | 282 | let block = &func.blocks[b]; |
|
| 283 | 283 | for instr in block.instrs { |
|
| 284 | 284 | match instr { |
|
| 285 | 285 | case il::Instr::Reserve { size, alignment, .. } => { |
|
| 286 | 286 | if let case il::Val::Imm(sz) = size { |
|
| 287 | - | if alignment == 0 or (alignment & (alignment - 1)) <> 0 or sz < 0 { return nil; } |
|
| 287 | + | if alignment == 0 or (alignment & (alignment - 1)) <> 0 or sz < 0 { |
|
| 288 | + | return nil; |
|
| 289 | + | } |
|
| 288 | 290 | let aligned = (offset as u64 + alignment as u64 - 1) & ~(alignment as u64 - 1); |
|
| 289 | - | if aligned > 0x7fff0000 or sz as u64 > 0x7fff0000 - aligned { return nil; } |
|
| 291 | + | if aligned > 0x7fff0000 or sz as u64 > 0x7fff0000 - aligned { |
|
| 292 | + | return nil; |
|
| 293 | + | } |
|
| 290 | 294 | set offset = (aligned + sz as u64) as i32; |
|
| 291 | 295 | } else { |
|
| 292 | 296 | set isDynamic = true; |
|
| 293 | 297 | } |
|
| 294 | 298 | }, |
| 298 | 302 | } |
|
| 299 | 303 | return ReserveInfo { size: offset, isDynamic }; |
|
| 300 | 304 | } |
|
| 301 | 305 | ||
| 302 | 306 | /// Select instructions for a function. |
|
| 303 | - | export unsafe fn selectFn( |
|
| 307 | + | export unsafe fn selectFn 'scratch ( |
|
| 304 | 308 | e: &mut emit::Emitter, |
|
| 305 | - | ralloc: ®alloc::AllocResult, |
|
| 306 | - | func: *unsafe il::Fn |
|
| 309 | + | ralloc: ®alloc::AllocResult 'scratch, |
|
| 310 | + | func: &il::Fn |
|
| 307 | 311 | ) { |
|
| 308 | - | if e.error <> nil { return; } |
|
| 312 | + | if e.error <> nil { |
|
| 313 | + | return; |
|
| 314 | + | } |
|
| 309 | 315 | // Reset block offsets for this function. |
|
| 310 | 316 | labels::resetBlocks(&mut e.labels); |
|
| 311 | 317 | // Pre-scan for constant-sized reserves to promote to fixed frame slots. |
|
| 312 | - | let reserveInfo = computeReserveInfo(func) else { set e.error = super::Error::Capacity; return; }; |
|
| 318 | + | let reserveInfo = computeReserveInfo(func) else { |
|
| 319 | + | set e.error = super::Error::Capacity; |
|
| 320 | + | return; |
|
| 321 | + | }; |
|
| 313 | 322 | if reserveInfo.size as u64 + ralloc.spill.frameSize as u64 > 0x7fffff00 { |
|
| 314 | 323 | set e.error = super::Error::Capacity; return; |
|
| 315 | 324 | } |
|
| 316 | 325 | let isLeaf = func.isLeaf; |
|
| 317 | 326 | // Compute frame layout from spill slots, reserve slots, and used callee-saved registers. |
| 321 | 330 | func.blocks.len, |
|
| 322 | 331 | isLeaf, |
|
| 323 | 332 | reserveInfo.isDynamic |
|
| 324 | 333 | ); |
|
| 325 | 334 | // Synthetic block indices start after real blocks and the epilogue block. |
|
| 326 | - | let mut s = Selector { |
|
| 327 | - | e: e as *unsafe mut emit::Emitter, |
|
| 328 | - | ralloc: ralloc as *unsafe regalloc::AllocResult, |
|
| 329 | - | frameSize: frame.totalSize, |
|
| 330 | - | reserveOffset: 0, pendingSpill: nil, |
|
| 331 | - | nextSynthBlock: func.blocks.len + 1, |
|
| 332 | - | isDynamic: frame.isDynamic, |
|
| 333 | - | }; |
|
| 334 | - | // Record function name for printing. |
|
| 335 | - | emit::recordFunc(s.e, func.name); |
|
| 336 | - | // Record function code offset for call patching. |
|
| 337 | - | emit::recordFuncOffset(s.e, func.name); |
|
| 338 | - | // Emit prologue. |
|
| 339 | - | emit::emitPrologue(s.e, &frame); |
|
| 340 | - | ||
| 341 | - | // Move function params from arg registers to assigned registers. |
|
| 342 | - | // Cross-call params may have been assigned to callee-saved registers |
|
| 343 | - | // instead of their natural arg registers. Spilled params are stored |
|
| 344 | - | // directly to their spill slots. |
|
| 345 | - | for funcParam, i in func.params { |
|
| 346 | - | if i < super::ARG_REGS.len { |
|
| 347 | - | let param = funcParam.value; |
|
| 348 | - | let argReg = super::ARG_REGS[i]; |
|
| 349 | - | ||
| 350 | - | if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) { |
|
| 351 | - | // Spilled parameter: store arg register to spill slot. |
|
| 352 | - | emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot)); |
|
| 353 | - | } else if let assigned = ralloc.assignments[param.n] { |
|
| 354 | - | emitMv(&mut s, assigned, argReg); |
|
| 335 | + | let resultRef: 'selection = &*ralloc, emitterRef = &mut *e where 'scratch: 'selection in { |
|
| 336 | + | let mut s = Selector 'scratch 'selection { |
|
| 337 | + | e: emitterRef, |
|
| 338 | + | ralloc: resultRef, |
|
| 339 | + | frameSize: frame.totalSize, |
|
| 340 | + | reserveOffset: 0, pendingSpill: nil, |
|
| 341 | + | nextSynthBlock: func.blocks.len + 1, |
|
| 342 | + | isDynamic: frame.isDynamic, |
|
| 343 | + | }; |
|
| 344 | + | // Record function name for printing. |
|
| 345 | + | emit::recordFunc(s.e, func.name); |
|
| 346 | + | // Record function code offset for call patching. |
|
| 347 | + | emit::recordFuncOffset(s.e, func.name); |
|
| 348 | + | // Emit prologue. |
|
| 349 | + | emit::emitPrologue(s.e, &frame); |
|
| 350 | + | ||
| 351 | + | // Move function params from arg registers to assigned registers. |
|
| 352 | + | // Cross-call params may have been assigned to callee-saved registers |
|
| 353 | + | // instead of their natural arg registers. Spilled params are stored |
|
| 354 | + | // directly to their spill slots. |
|
| 355 | + | for funcParam, i in func.params { |
|
| 356 | + | if i < super::ARG_REGS.len { |
|
| 357 | + | let param = funcParam.value; |
|
| 358 | + | let argReg = super::ARG_REGS[i]; |
|
| 359 | + | ||
| 360 | + | if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) { |
|
| 361 | + | // Spilled parameter: store arg register to spill slot. |
|
| 362 | + | emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot)); |
|
| 363 | + | } else if let assigned = ralloc.assignments[param.n] { |
|
| 364 | + | emitMv(&mut s, assigned, argReg); |
|
| 365 | + | } |
|
| 355 | 366 | } |
|
| 356 | 367 | } |
|
| 357 | - | } |
|
| 358 | 368 | ||
| 359 | - | // Emit each block. |
|
| 360 | - | for i in 0..func.blocks.len { |
|
| 361 | - | if s.e.error <> nil { return; } |
|
| 362 | - | selectBlock(&mut s, i, &func.blocks[i], &frame, func); |
|
| 369 | + | // Emit each block. |
|
| 370 | + | for i in 0..func.blocks.len { |
|
| 371 | + | if s.e.error <> nil { |
|
| 372 | + | return; |
|
| 373 | + | } |
|
| 374 | + | selectBlock(&mut s, i, &func.blocks[i], &frame, func); |
|
| 375 | + | } |
|
| 376 | + | // Emit epilogue. |
|
| 377 | + | emit::emitEpilogue(s.e, &frame); |
|
| 378 | + | // Patch local branches now that all blocks are emitted. |
|
| 379 | + | emit::patchLocalBranches(s.e); |
|
| 363 | 380 | } |
|
| 364 | - | // Emit epilogue. |
|
| 365 | - | emit::emitEpilogue(s.e, &frame); |
|
| 366 | - | // Patch local branches now that all blocks are emitted. |
|
| 367 | - | emit::patchLocalBranches(s.e); |
|
| 368 | 381 | } |
|
| 369 | 382 | ||
| 370 | 383 | /// Select instructions for a block. |
|
| 371 | - | unsafe fn selectBlock(s: &mut Selector, blockIdx: u32, block: *unsafe il::Block, frame: &emit::Frame, func: *unsafe il::Fn) { |
|
| 384 | + | unsafe fn selectBlock 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, block: &il::Block, frame: &emit::Frame, func: &il::Fn) where 'scratch: 'selection { |
|
| 372 | 385 | // Record block address for branch patching. |
|
| 373 | 386 | emit::recordBlock(s.e, blockIdx); |
|
| 374 | 387 | ||
| 375 | 388 | // Block parameters are handled at jump sites (in `Jmp`/`Br`). |
|
| 376 | 389 | // By the time we enter the block, the arguments have already been |
|
| 377 | 390 | // moved to the parameter registers by the predecessor's terminator. |
|
| 378 | 391 | ||
| 379 | 392 | // Process each instruction, auto-committing any pending spill after each. |
|
| 380 | 393 | let hasLocs = block.locs.len > 0; |
|
| 381 | 394 | for instr, i in block.instrs { |
|
| 382 | - | if s.e.error <> nil { return; } |
|
| 395 | + | if s.e.error <> nil { |
|
| 396 | + | return; |
|
| 397 | + | } |
|
| 383 | 398 | // Record debug location before emitting machine instructions. |
|
| 384 | 399 | if hasLocs { |
|
| 385 | 400 | emit::recordSrcLoc(s.e, block.locs[i]); |
|
| 386 | 401 | } |
|
| 387 | 402 | set s.pendingSpill = nil; |
| 396 | 411 | } |
|
| 397 | 412 | } |
|
| 398 | 413 | } |
|
| 399 | 414 | ||
| 400 | 415 | /// Select instructions for a single IL instruction. |
|
| 401 | - | unsafe fn selectInstr(s: &mut Selector, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: *unsafe il::Fn) { |
|
| 416 | + | unsafe fn selectInstr 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: &il::Fn) where 'scratch: 'selection { |
|
| 402 | 417 | match instr { |
|
| 403 | 418 | case il::Instr::BinOp { op, typ, dst, a, b } => { |
|
| 404 | 419 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 405 | 420 | let rs1 = resolveVal(s, super::SCRATCH1, a); |
|
| 406 | 421 | selectAluBinOp(s, op, typ, rd, rs1, b); |
| 461 | 476 | else => |
|
| 462 | 477 | panic "selectInstr: invalid reserve operand", |
|
| 463 | 478 | } |
|
| 464 | 479 | }, |
|
| 465 | 480 | case il::Instr::Blit { dst, src, size } => { |
|
| 466 | - | let case il::Val::Imm(staticSize) = size |
|
| 467 | - | else panic "selectInstr: blit requires immediate size"; |
|
| 468 | - | ||
| 469 | - | let bothSpilled = regalloc::spill::isSpilled(&s.ralloc.spill, dst) |
|
| 470 | - | and regalloc::spill::isSpilled(&s.ralloc.spill, src); |
|
| 471 | - | ||
| 472 | - | // When both are spilled, offsets must fit 12-bit immediates |
|
| 473 | - | // since we can't advance base registers (they live in spill |
|
| 474 | - | // slots, not real registers we can mutate). |
|
| 475 | - | assert not (bothSpilled and staticSize as i32 > super::MAX_IMM), "selectInstr: blit both-spilled with large size"; |
|
| 476 | - | ||
| 477 | - | // Resolve dst/src base registers. |
|
| 478 | - | let mut rdst = super::SCRATCH2; |
|
| 479 | - | let mut rsrc = super::SCRATCH1; |
|
| 480 | - | let mut srcReload: ?i32 = nil; |
|
| 481 | - | ||
| 482 | - | if bothSpilled { |
|
| 483 | - | let dstSlot = regalloc::spill::spillSlot(&s.ralloc.spill, dst) else { |
|
| 484 | - | panic "selectInstr: blit dst not spilled"; |
|
| 485 | - | }; |
|
| 486 | - | let srcSlot = regalloc::spill::spillSlot(&s.ralloc.spill, src) else { |
|
| 487 | - | panic "selectInstr: blit src not spilled"; |
|
| 488 | - | }; |
|
| 489 | - | emit::emitLd(s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot)); |
|
| 490 | - | set srcReload = spillOffset(s, srcSlot); |
|
| 491 | - | } else { |
|
| 492 | - | set rdst = getSrcReg(s, dst, super::SCRATCH2); |
|
| 493 | - | set rsrc = getSrcReg(s, src, super::SCRATCH2); |
|
| 494 | - | } |
|
| 495 | - | let mut offset: i32 = 0; |
|
| 496 | - | let mut remaining = staticSize as i32; |
|
| 497 | - | ||
| 498 | - | // For large blits where both pointers are in real registers, |
|
| 499 | - | // use an inline loop instead of unrolled LD/SD pairs. |
|
| 500 | - | let dwordBytes = remaining & ~(super::DWORD_SIZE - 1); |
|
| 501 | - | let canLoop = not bothSpilled |
|
| 502 | - | and *rsrc <> *super::SCRATCH1 and *rsrc <> *super::SCRATCH2 |
|
| 503 | - | and *rdst <> *super::SCRATCH1 and *rdst <> *super::SCRATCH2; |
|
| 504 | - | ||
| 505 | - | if canLoop and dwordBytes >= super::BLIT_LOOP_THRESHOLD { |
|
| 506 | - | emit::emitAddImm(s.e, super::SCRATCH1, rsrc, dwordBytes); |
|
| 507 | - | ||
| 508 | - | let loopStart = s.e.codeLen; |
|
| 509 | - | ||
| 510 | - | emit::emitLd(s.e, super::SCRATCH2, rsrc, 0); |
|
| 511 | - | emit::emitSd(s.e, super::SCRATCH2, rdst, 0); |
|
| 512 | - | emit::emit(s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE)); |
|
| 513 | - | ||
| 514 | - | if *rdst <> *rsrc { |
|
| 515 | - | emit::emit(s.e, encode::addi(rdst, rdst, super::DWORD_SIZE)); |
|
| 516 | - | } |
|
| 517 | - | let brOff = (loopStart as i32 - s.e.codeLen as i32) * super::INSTR_SIZE; |
|
| 518 | - | ||
| 519 | - | emit::emit(s.e, encode::bne(rsrc, super::SCRATCH1, brOff)); |
|
| 520 | - | set remaining -= dwordBytes; |
|
| 521 | - | } |
|
| 522 | - | ||
| 523 | - | // Copy remaining: 8 bytes, then 4 bytes, then 1 byte at a time. |
|
| 524 | - | // Before each load/store pair, check whether the offset is |
|
| 525 | - | // about to exceed the 12-bit signed immediate range. When |
|
| 526 | - | // it does, advance the base registers by the accumulated |
|
| 527 | - | // offset and reset to zero. |
|
| 528 | - | while remaining >= super::DWORD_SIZE { |
|
| 529 | - | if offset > super::MAX_IMM - super::DWORD_SIZE { |
|
| 530 | - | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 531 | - | if *rdst <> *rsrc { |
|
| 532 | - | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 533 | - | } |
|
| 534 | - | set offset = 0; |
|
| 535 | - | } |
|
| 536 | - | if let off = srcReload { |
|
| 537 | - | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 538 | - | emit::emitLd(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 539 | - | } else { |
|
| 540 | - | emit::emitLd(s.e, super::SCRATCH1, rsrc, offset); |
|
| 541 | - | } |
|
| 542 | - | emit::emitSd(s.e, super::SCRATCH1, rdst, offset); |
|
| 543 | - | set offset += super::DWORD_SIZE; |
|
| 544 | - | set remaining -= super::DWORD_SIZE; |
|
| 545 | - | } |
|
| 546 | - | if remaining >= super::WORD_SIZE { |
|
| 547 | - | if offset > super::MAX_IMM - super::WORD_SIZE { |
|
| 548 | - | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 549 | - | if *rdst <> *rsrc { |
|
| 550 | - | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 551 | - | } |
|
| 552 | - | set offset = 0; |
|
| 553 | - | } |
|
| 554 | - | if let off = srcReload { |
|
| 555 | - | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 556 | - | emit::emitLw(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 557 | - | } else { |
|
| 558 | - | emit::emitLw(s.e, super::SCRATCH1, rsrc, offset); |
|
| 559 | - | } |
|
| 560 | - | emit::emitSw(s.e, super::SCRATCH1, rdst, offset); |
|
| 561 | - | set offset += super::WORD_SIZE; |
|
| 562 | - | set remaining -= super::WORD_SIZE; |
|
| 481 | + | let case il::Val::Imm(staticSize) = size else { |
|
| 482 | + | set s.e.error = super::Error::Capacity; return; |
|
| 483 | + | }; |
|
| 484 | + | if staticSize < 0 or staticSize > 0x7fffffff { |
|
| 485 | + | set s.e.error = super::Error::Capacity; return; |
|
| 563 | 486 | } |
|
| 564 | - | while remaining > 0 { |
|
| 565 | - | if offset > super::MAX_IMM - 1 { |
|
| 566 | - | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 567 | - | if *rdst <> *rsrc { |
|
| 568 | - | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 569 | - | } |
|
| 570 | - | set offset = 0; |
|
| 571 | - | } |
|
| 572 | - | if let off = srcReload { |
|
| 573 | - | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 574 | - | emit::emitLb(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 575 | - | } else { |
|
| 576 | - | emit::emitLb(s.e, super::SCRATCH1, rsrc, offset); |
|
| 577 | - | } |
|
| 578 | - | emit::emitSb(s.e, super::SCRATCH1, rdst, offset); |
|
| 579 | - | set offset += 1; |
|
| 580 | - | set remaining -= 1; |
|
| 487 | + | if staticSize == 0 { |
|
| 488 | + | return; |
|
| 581 | 489 | } |
|
| 582 | - | // Restore base registers if they were advanced (never happens |
|
| 583 | - | // in the both-spilled case since size <= MAX_IMM). |
|
| 584 | - | if not bothSpilled { |
|
| 585 | - | let advanced = staticSize as i32 - offset; |
|
| 586 | - | if advanced <> 0 { |
|
| 587 | - | emit::emitAddImm(s.e, rsrc, rsrc, 0 - advanced); |
|
| 588 | - | if *rdst <> *rsrc { |
|
| 589 | - | emit::emitAddImm(s.e, rdst, rdst, 0 - advanced); |
|
| 590 | - | } |
|
| 490 | + | let rdst = getSrcReg(s, dst, super::SCRATCH2); |
|
| 491 | + | let rsrc = getSrcReg(s, src, super::SCRATCH1); |
|
| 492 | + | // Blit addresses have byte alignment. Small copies need no loop state. |
|
| 493 | + | if staticSize < super::BLIT_LOOP_THRESHOLD as i64 { |
|
| 494 | + | for offset in 0..staticSize as u32 { |
|
| 495 | + | emit::emitLb(s.e, super::ADDR_SCRATCH, rsrc, offset as i32); |
|
| 496 | + | emit::emitSb(s.e, super::ADDR_SCRATCH, rdst, offset as i32); |
|
| 591 | 497 | } |
|
| 498 | + | } else { |
|
| 499 | + | // Private cursors preserve both input pointers. Save the count |
|
| 500 | + | // register because it can hold a live allocated value. |
|
| 501 | + | emit::emit(s.e, encode::addi(super::ADDR_SCRATCH, rsrc, 0)); |
|
| 502 | + | emit::emit(s.e, encode::addi(super::SCRATCH2, rdst, 0)); |
|
| 503 | + | emit::emit(s.e, encode::addi(super::SP, super::SP, -16)); |
|
| 504 | + | emit::emitSd(s.e, super::T3, super::SP, 0); |
|
| 505 | + | emit::loadImm(s.e, super::T3, staticSize); |
|
| 506 | + | let start = s.e.codeLen; |
|
| 507 | + | emit::emitLb(s.e, super::SCRATCH1, super::ADDR_SCRATCH, 0); |
|
| 508 | + | emit::emitSb(s.e, super::SCRATCH1, super::SCRATCH2, 0); |
|
| 509 | + | emit::emit(s.e, encode::addi(super::ADDR_SCRATCH, super::ADDR_SCRATCH, 1)); |
|
| 510 | + | emit::emit(s.e, encode::addi(super::SCRATCH2, super::SCRATCH2, 1)); |
|
| 511 | + | emit::emit(s.e, encode::addi(super::T3, super::T3, -1)); |
|
| 512 | + | let offset = (start as i32 - s.e.codeLen as i32) * super::INSTR_SIZE; |
|
| 513 | + | emit::emit(s.e, encode::bne(super::T3, super::ZERO, offset)); |
|
| 514 | + | emit::emitLd(s.e, super::T3, super::SP, 0); |
|
| 515 | + | emit::emit(s.e, encode::addi(super::SP, super::SP, 16)); |
|
| 592 | 516 | } |
|
| 593 | 517 | }, |
|
| 594 | 518 | case il::Instr::Zext { typ, dst, val } => { |
|
| 595 | 519 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 596 | 520 | let rs = resolveVal(s, super::SCRATCH1, val); |
| 717 | 641 | if let case il::Val::Reg(r) = func { |
|
| 718 | 642 | let target = getSrcReg(s, r, super::SCRATCH2); |
|
| 719 | 643 | emitMv(s, super::SCRATCH2, target); |
|
| 720 | 644 | } |
|
| 721 | 645 | // Move arguments to A0-A7 using parallel move resolution. |
|
| 722 | - | if args.len > super::ARG_REGS.len { set s.e.error = super::Error::Capacity; return; } |
|
| 646 | + | if args.len > super::ARG_REGS.len { |
|
| 647 | + | set s.e.error = super::Error::Capacity; |
|
| 648 | + | return; |
|
| 649 | + | } |
|
| 723 | 650 | emitParallelMoves(s, &super::ARG_REGS[..], args); |
|
| 724 | 651 | ||
| 725 | 652 | // Emit call. |
|
| 726 | 653 | match func { |
|
| 727 | 654 | case il::Val::FnAddr(name) => { |
| 752 | 679 | ||
| 753 | 680 | // Result in A0. |
|
| 754 | 681 | let ecallRd = getDstReg(s, dst, super::SCRATCH1); |
|
| 755 | 682 | emitMv(s, ecallRd, super::A0); |
|
| 756 | 683 | }, |
|
| 684 | + | case il::Instr::DeviceRead { typ, dst, handle, offset } => { |
|
| 685 | + | deviceAddress(s, typ, handle, offset, il::Val::Imm(0), false); |
|
| 686 | + | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 687 | + | match typ { |
|
| 688 | + | case il::Type::W8 => emit::emit(s.e, encode::lbu(rd, super::A0, 0)), |
|
| 689 | + | case il::Type::W16 => emit::emit(s.e, encode::lhu(rd, super::A0, 0)), |
|
| 690 | + | case il::Type::W32 => emit::emit(s.e, encode::lwu(rd, super::A0, 0)), |
|
| 691 | + | case il::Type::W64 => emit::emit(s.e, encode::ld(rd, super::A0, 0)), |
|
| 692 | + | } |
|
| 693 | + | emit::emit(s.e, encode::fence()); |
|
| 694 | + | }, |
|
| 695 | + | case il::Instr::DeviceWrite { typ, handle, offset, value } => { |
|
| 696 | + | deviceAddress(s, typ, handle, offset, value, true); |
|
| 697 | + | match typ { |
|
| 698 | + | case il::Type::W8 => emit::emit(s.e, encode::sb(super::A3, super::A0, 0)), |
|
| 699 | + | case il::Type::W16 => emit::emit(s.e, encode::sh(super::A3, super::A0, 0)), |
|
| 700 | + | case il::Type::W32 => emit::emit(s.e, encode::sw(super::A3, super::A0, 0)), |
|
| 701 | + | case il::Type::W64 => emit::emit(s.e, encode::sd(super::A3, super::A0, 0)), |
|
| 702 | + | } |
|
| 703 | + | emit::emit(s.e, encode::fence()); |
|
| 704 | + | }, |
|
| 757 | 705 | case il::Instr::Ebreak => { |
|
| 758 | 706 | emit::emit(s.e, encode::ebreak()); |
|
| 759 | 707 | }, |
|
| 760 | 708 | case il::Instr::MemoryFence => { |
|
| 761 | 709 | emit::emit(s.e, encode::fence()); |
| 840 | 788 | return false; |
|
| 841 | 789 | } |
|
| 842 | 790 | ||
| 843 | 791 | /// Select a binary ALU operation, dispatching to the appropriate |
|
| 844 | 792 | /// instruction pattern based on the operation kind and type. |
|
| 845 | - | unsafe fn selectAluBinOp(s: &mut Selector, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) { |
|
| 793 | + | unsafe fn selectAluBinOp 'scratch 'selection (s: &mut Selector 'scratch 'selection, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) where 'scratch: 'selection { |
|
| 846 | 794 | match op { |
|
| 847 | 795 | case il::BinOp::Add => { |
|
| 848 | 796 | if typ == il::Type::W32 { |
|
| 849 | 797 | // Inline W32 ADD with immediate optimization. |
|
| 850 | 798 | if let case il::Val::Imm(imm) = b { |
| 968 | 916 | selectCmp(s, typ, rd, rs1, b, CmpOp::Ult, true, super::SCRATCH2), |
|
| 969 | 917 | } |
|
| 970 | 918 | } |
|
| 971 | 919 | ||
| 972 | 920 | /// Select a unary ALU operation. |
|
| 973 | - | unsafe fn selectAluUnOp(s: &mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) { |
|
| 921 | + | unsafe fn selectAluUnOp 'scratch 'selection (s: &mut Selector 'scratch 'selection, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) where 'scratch: 'selection { |
|
| 974 | 922 | match op { |
|
| 975 | 923 | case il::UnOp::Neg => { |
|
| 976 | 924 | if typ == il::Type::W32 { |
|
| 977 | 925 | emit::emit(s.e, encode::subw(rd, super::ZERO, rs)); |
|
| 978 | 926 | } else { |
| 983 | 931 | emit::emit(s.e, encode::not_(rd, rs)), |
|
| 984 | 932 | } |
|
| 985 | 933 | } |
|
| 986 | 934 | ||
| 987 | 935 | /// Select binary operation with immediate optimization. |
|
| 988 | - | unsafe fn selectBinOp(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) { |
|
| 936 | + | unsafe fn selectBinOp 'scratch 'selection (s: &mut Selector 'scratch 'selection, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) where 'scratch: 'selection { |
|
| 989 | 937 | // Try immediate optimization first. |
|
| 990 | 938 | if let case il::Val::Imm(imm) = b { |
|
| 991 | 939 | if encode::isSmallImm64(imm) { |
|
| 992 | 940 | let simm = imm as i32; |
|
| 993 | 941 | match op { |
| 1010 | 958 | } |
|
| 1011 | 959 | ||
| 1012 | 960 | /// Select shift operation with immediate optimization. |
|
| 1013 | 961 | /// For 32-bit operations, uses the `*w` variants that operate on the lower 32 bits |
|
| 1014 | 962 | /// and sign-extend the result. |
|
| 1015 | - | unsafe fn selectShift(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) { |
|
| 963 | + | unsafe fn selectShift 'scratch 'selection (s: &mut Selector 'scratch 'selection, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) where 'scratch: 'selection { |
|
| 1016 | 964 | let isW32: bool = typ == il::Type::W32; |
|
| 1017 | 965 | ||
| 1018 | 966 | // Try immediate optimization first. |
|
| 1019 | 967 | if let case il::Val::Imm(shamt) = b { |
|
| 1020 | 968 | // Keep immediate forms only for encodable shift amounts. |
| 1063 | 1011 | /// 1. Identifies "ready" moves. |
|
| 1064 | 1012 | /// 2. Executes ready moves. |
|
| 1065 | 1013 | /// 3. Breaks cycles using scratch register. |
|
| 1066 | 1014 | /// |
|
| 1067 | 1015 | /// Entries with `ZERO` destination are skipped, as they are handled by caller. |
|
| 1068 | - | unsafe fn emitParallelMoves(s: &mut Selector, dsts: &[gen::Reg], args: &[il::Val]) { |
|
| 1016 | + | unsafe fn emitParallelMoves 'scratch 'selection (s: &mut Selector 'scratch 'selection, dsts: &[gen::Reg], args: &[il::Val]) where 'scratch: 'selection { |
|
| 1069 | 1017 | let n: u32 = args.len; |
|
| 1070 | 1018 | if n == 0 { |
|
| 1071 | 1019 | return; |
|
| 1072 | 1020 | } |
|
| 1073 | - | if n > MAX_BLOCK_ARGS { set s.e.error = super::Error::Capacity; return; } |
|
| 1021 | + | if n > MAX_BLOCK_ARGS { |
|
| 1022 | + | set s.e.error = super::Error::Capacity; |
|
| 1023 | + | return; |
|
| 1024 | + | } |
|
| 1074 | 1025 | // Source registers for each arg. |
|
| 1075 | 1026 | let mut srcRegs: [gen::Reg; MAX_BLOCK_ARGS] = [super::ZERO; MAX_BLOCK_ARGS]; |
|
| 1076 | 1027 | // If this is a register-to-register move. |
|
| 1077 | 1028 | let mut isRegMove: [bool; MAX_BLOCK_ARGS] = [false; MAX_BLOCK_ARGS]; |
|
| 1078 | 1029 | // If this move still needs to be executed. |
| 1174 | 1125 | /// Emit moves from block arguments to target block's parameter registers. |
|
| 1175 | 1126 | /// |
|
| 1176 | 1127 | /// Handles spilled destinations directly, then delegates to [`emitParallelMoves`] |
|
| 1177 | 1128 | /// for the remaining register-to-register parallel move resolution. Edges that |
|
| 1178 | 1129 | /// would overwrite an unconsumed spill source are unsupported. |
|
| 1179 | - | unsafe fn emitBlockArgs(s: &mut Selector, func: *unsafe il::Fn, target: u32, args: &[il::Val]) { |
|
| 1130 | + | unsafe fn emitBlockArgs 'scratch 'selection (s: &mut Selector 'scratch 'selection, func: &il::Fn, target: u32, args: &[il::Val]) where 'scratch: 'selection { |
|
| 1180 | 1131 | if args.len == 0 { |
|
| 1181 | 1132 | return; |
|
| 1182 | 1133 | } |
|
| 1183 | 1134 | let block = &func.blocks[target]; |
|
| 1184 | 1135 | assert args.len == block.params.len, "emitBlockArgs: argument/parameter count mismatch"; |
|
| 1185 | - | if args.len > MAX_BLOCK_ARGS { set s.e.error = super::Error::Capacity; return; } |
|
| 1136 | + | if args.len > MAX_BLOCK_ARGS { |
|
| 1137 | + | set s.e.error = super::Error::Capacity; |
|
| 1138 | + | return; |
|
| 1139 | + | } |
|
| 1186 | 1140 | ||
| 1187 | 1141 | // The parallel-move resolver only handles register destinations. Keep eager |
|
| 1188 | 1142 | // stores for independent spill slots, but reject dependencies that would |
|
| 1189 | 1143 | // require stack staging rather than silently miscompiling them. |
|
| 1190 | 1144 | for arg, i in args { |
| 1240 | 1194 | } |
|
| 1241 | 1195 | emitParallelMoves(s, &dsts[..], args); |
|
| 1242 | 1196 | } |
|
| 1243 | 1197 | ||
| 1244 | 1198 | /// Select a comparison with immediate optimization. |
|
| 1245 | - | unsafe fn selectCmp( |
|
| 1246 | - | s: &mut Selector, |
|
| 1199 | + | unsafe fn selectCmp 'scratch 'selection ( |
|
| 1200 | + | s: &mut Selector 'scratch 'selection, |
|
| 1247 | 1201 | typ: il::Type, |
|
| 1248 | 1202 | rd: gen::Reg, |
|
| 1249 | 1203 | rs1: gen::Reg, |
|
| 1250 | 1204 | b: il::Val, |
|
| 1251 | 1205 | op: CmpOp, |
|
| 1252 | 1206 | invert: bool, |
|
| 1253 | 1207 | scratch: gen::Reg |
|
| 1254 | - | ) { |
|
| 1208 | + | ) where 'scratch: 'selection { |
|
| 1255 | 1209 | let mut signed = false; |
|
| 1256 | 1210 | if let case CmpOp::Slt = op { |
|
| 1257 | 1211 | set signed = true; |
|
| 1258 | 1212 | } |
|
| 1259 | 1213 | let useSext = cmpUsesSext(typ, signed); |
| 1288 | 1242 | } |
|
| 1289 | 1243 | if invert { |
|
| 1290 | 1244 | emit::emit(s.e, encode::xori(rd, rd, 1)); |
|
| 1291 | 1245 | } |
|
| 1292 | 1246 | } |
|
| 1247 | + | ||
| 1248 | + | /// Resolve one live Device access before an ordered user-mode register instruction. |
|
| 1249 | + | unsafe fn deviceAddress 'scratch 'selection ( |
|
| 1250 | + | s: &mut Selector 'scratch 'selection, |
|
| 1251 | + | typ: il::Type, |
|
| 1252 | + | handle: il::Val, |
|
| 1253 | + | offset: il::Val, |
|
| 1254 | + | value: il::Val, |
|
| 1255 | + | writing: bool |
|
| 1256 | + | ) where 'scratch: 'selection { |
|
| 1257 | + | let mut flags = il::typeSize(typ) as i64; |
|
| 1258 | + | if writing { |
|
| 1259 | + | set flags |= 0x100; |
|
| 1260 | + | } |
|
| 1261 | + | let dsts: [gen::Reg; 5] = [super::A7, super::A0, super::A1, super::A2, super::A3]; |
|
| 1262 | + | let args: [il::Val; 5] = [il::Val::Imm(il::DEVICE_ACCESS as i64), handle, offset, il::Val::Imm(flags), value]; |
|
| 1263 | + | emitParallelMoves(s, &dsts[..], &args[..]); |
|
| 1264 | + | emit::emit(s.e, encode::ecall()); |
|
| 1265 | + | emit::emit(s.e, encode::fence()); |
|
| 1266 | + | } |
lib/std/arch/rv64/printer.rad
+22 -7
| 301 | 301 | let stem = atomics::name(instruction.format.operation) else panic "invalid atomic operation"; |
|
| 302 | 302 | write(out, stem); |
|
| 303 | 303 | write(out, ".w" if instruction.format.width == 2 else ".d"); |
|
| 304 | 304 | match instruction.format.order { |
|
| 305 | 305 | case 1 => write(out, ".rl"), case 2 => write(out, ".aq"), case 3 => write(out, ".aqrl"), |
|
| 306 | - | else => {}, |
|
| 306 | + | else => { |
|
| 307 | + | }, |
|
| 307 | 308 | } |
|
| 308 | 309 | write(out, " "); write(out, regNameR(instruction.rd)); write(out, ", "); |
|
| 309 | - | if instruction.format.operation <> 2 { write(out, regNameR(instruction.rs2)); write(out, ", "); } |
|
| 310 | + | if instruction.format.operation <> 2 { |
|
| 311 | + | write(out, regNameR(instruction.rs2)); |
|
| 312 | + | write(out, ", "); |
|
| 313 | + | } |
|
| 310 | 314 | write(out, "0("); write(out, regNameR(instruction.rs1)); write(out, ")"); |
|
| 311 | 315 | }, |
|
| 312 | 316 | case decode::Instr::Fence { predecessor, successor } => { |
|
| 313 | 317 | write(out, "fence "); fenceMask(out, predecessor); write(out, ", "); fenceMask(out, successor); |
|
| 314 | 318 | }, |
| 350 | 354 | return nil; |
|
| 351 | 355 | } |
|
| 352 | 356 | ||
| 353 | 357 | /// Print a memory-ordering mask in canonical order. |
|
| 354 | 358 | unsafe fn fenceMask(out: &mut sexpr::Output, mask: u32) { |
|
| 355 | - | if mask == 0 { write(out, "0"); return; } |
|
| 356 | - | if (mask & 8) <> 0 { write(out, "i"); } |
|
| 357 | - | if (mask & 4) <> 0 { write(out, "o"); } |
|
| 358 | - | if (mask & 2) <> 0 { write(out, "r"); } |
|
| 359 | - | if (mask & 1) <> 0 { write(out, "w"); } |
|
| 359 | + | if mask == 0 { |
|
| 360 | + | write(out, "0"); |
|
| 361 | + | return; |
|
| 362 | + | } |
|
| 363 | + | if (mask & 8) <> 0 { |
|
| 364 | + | write(out, "i"); |
|
| 365 | + | } |
|
| 366 | + | if (mask & 4) <> 0 { |
|
| 367 | + | write(out, "o"); |
|
| 368 | + | } |
|
| 369 | + | if (mask & 2) <> 0 { |
|
| 370 | + | write(out, "r"); |
|
| 371 | + | } |
|
| 372 | + | if (mask & 1) <> 0 { |
|
| 373 | + | write(out, "w"); |
|
| 374 | + | } |
|
| 360 | 375 | } |
lib/std/arch/rv64/shared.rad
+191 -56
| 1 | 1 | //! Shared package code, private data templates, and qualified symbol linking. |
|
| 2 | 2 | ||
| 3 | 3 | @test export mod tests; |
|
| 4 | + | export mod catalog; |
|
| 4 | 5 | ||
| 5 | 6 | use std::mem; |
|
| 6 | 7 | use std::lang::alloc; |
|
| 7 | 8 | use std::lang::il; |
|
| 8 | 9 | use std::lang::il::binary; |
|
| 9 | 10 | use std::lang::gen; |
|
| 10 | 11 | use std::lang::gen::data; |
|
| 11 | 12 | use super::emit; |
|
| 12 | 13 | use super::encode; |
|
| 13 | 14 | use super::image; |
|
| 15 | + | use super::asm; |
|
| 14 | 16 | ||
| 15 | 17 | /// Maximum number of resident package slots in a domain state table. |
|
| 16 | 18 | export constant MAX_PACKAGES: u32 = 256; |
|
| 17 | 19 | ||
| 18 | 20 | /// Package linking or instance storage failure. |
| 106 | 108 | } |
|
| 107 | 109 | ||
| 108 | 110 | /// Find exactly one qualified symbol in a bounded table. |
|
| 109 | 111 | export fn lookup(symbols: &[Symbol], name: &[u8]) -> ?Target { |
|
| 110 | 112 | for symbol in symbols { |
|
| 111 | - | if mem::eq(symbol.name, name) { return symbol.target; } |
|
| 113 | + | if mem::eq(symbol.name, name) { |
|
| 114 | + | return symbol.target; |
|
| 115 | + | } |
|
| 112 | 116 | } |
|
| 113 | 117 | return nil; |
|
| 114 | 118 | } |
|
| 115 | 119 | ||
| 116 | 120 | /// Resolve a local or imported symbol. |
|
| 117 | 121 | fn resolve(local: &[Symbol], imports: &[Symbol], name: *[u8]) -> Target throws (Error) { |
|
| 118 | - | if let target = lookup(local, name) { return target; } |
|
| 119 | - | let target = lookup(imports, name) else { throw Error::Symbol; }; |
|
| 122 | + | if let target = lookup(local, name) { |
|
| 123 | + | return target; |
|
| 124 | + | } |
|
| 125 | + | let target = lookup(imports, name) else { |
|
| 126 | + | throw Error::Symbol; |
|
| 127 | + | }; |
|
| 120 | 128 | return target; |
|
| 121 | 129 | } |
|
| 122 | 130 | ||
| 123 | 131 | /// Append a unique local definition to bounded symbol storage. |
|
| 124 | 132 | fn define(symbols: &mut [Symbol], count: &mut u32, name: *[u8], target: Target) throws (Error) { |
|
| 125 | - | if lookup(&symbols[..*count], name) <> nil { throw Error::Symbol; } |
|
| 126 | - | if *count == symbols.len { throw Error::Capacity; } |
|
| 133 | + | if lookup(&symbols[..*count], name) <> nil { |
|
| 134 | + | throw Error::Symbol; |
|
| 135 | + | } |
|
| 136 | + | if *count == symbols.len { |
|
| 137 | + | throw Error::Capacity; |
|
| 138 | + | } |
|
| 127 | 139 | set symbols[*count] = Symbol { name, target }; |
|
| 128 | 140 | set *count += 1; |
|
| 129 | 141 | } |
|
| 130 | 142 | ||
| 131 | 143 | /// Resolve a shared function address with its required kind. |
| 146 | 158 | ||
| 147 | 159 | /// Patch a PC-relative call or function address load. |
|
| 148 | 160 | unsafe fn relative(e: &mut emit::Emitter, index: u32, base: u64, target: u64, rd: gen::Reg, call: bool) |
|
| 149 | 161 | throws (Error) |
|
| 150 | 162 | { |
|
| 151 | - | let delta = image::displacement(base + index as u64 * 4, target) else { throw Error::Range; }; |
|
| 163 | + | let delta = image::displacement(base + index as u64 * 4, target) else { |
|
| 164 | + | throw Error::Range; |
|
| 165 | + | }; |
|
| 152 | 166 | let parts = emit::splitImm(delta); |
|
| 153 | 167 | emit::patch(e, index, encode::auipc(rd, parts.hi)); |
|
| 154 | 168 | if call { |
|
| 155 | 169 | emit::patch(e, index + 1, encode::jalr(super::RA, rd, parts.lo)); |
|
| 156 | 170 | } else { |
| 158 | 172 | } |
|
| 159 | 173 | } |
|
| 160 | 174 | ||
| 161 | 175 | /// Link shared code against local definitions and dependency exports. |
|
| 162 | 176 | unsafe fn link(e: &mut emit::Emitter, base: u64, local: &[Symbol], imports: &[Symbol]) throws (Error) { |
|
| 163 | - | for i in 0..e.pendingCalls.len { |
|
| 177 | + | for i in 0..e.pendingCallsLen { |
|
| 164 | 178 | let pending = e.pendingCalls[i]; |
|
| 165 | 179 | let address = try function(local, imports, pending.target); |
|
| 166 | 180 | try relative(e, pending.index, base, address, super::SCRATCH1, true); |
|
| 167 | 181 | } |
|
| 168 | - | for i in 0..e.pendingJumps.len { |
|
| 182 | + | for i in 0..e.pendingJumpsLen { |
|
| 169 | 183 | let pending = e.pendingJumps[i]; |
|
| 170 | 184 | let address = try function(local, imports, pending.target); |
|
| 171 | - | let delta = image::displacement(base + pending.index as u64 * 4, address) else { throw Error::Range; }; |
|
| 172 | - | if not encode::isJumpImm(delta) { throw Error::Range; } |
|
| 185 | + | let delta = image::displacement(base + pending.index as u64 * 4, address) else { |
|
| 186 | + | throw Error::Range; |
|
| 187 | + | }; |
|
| 188 | + | if not encode::isJumpImm(delta) { |
|
| 189 | + | throw Error::Range; |
|
| 190 | + | } |
|
| 173 | 191 | emit::patch(e, pending.index, encode::jal(pending.rd, delta)); |
|
| 174 | 192 | } |
|
| 175 | - | for i in 0..e.pendingAddrLoads.len { |
|
| 193 | + | for i in 0..e.pendingAddrLoadsLen { |
|
| 176 | 194 | let pending = e.pendingAddrLoads[i]; |
|
| 177 | 195 | if not pending.isData { |
|
| 178 | 196 | try relative(e, pending.index, base, try function(local, imports, pending.target), pending.rd, false); |
|
| 179 | 197 | continue; |
|
| 180 | 198 | } |
|
| 181 | 199 | let target = try resolve(local, imports, pending.target); |
|
| 182 | 200 | let location = try dataRef(target); |
|
| 183 | - | if location.slot >= MAX_PACKAGES or location.offset > 0x7ffff7ff { throw Error::Range; } |
|
| 201 | + | if location.slot >= MAX_PACKAGES or location.offset > 0x7ffff7ff { |
|
| 202 | + | throw Error::Range; |
|
| 203 | + | } |
|
| 184 | 204 | if pending.rd == super::ADDR_SCRATCH or pending.rd == super::GP or pending.rd == super::ZERO { |
|
| 185 | 205 | throw Error::Symbol; |
|
| 186 | 206 | } |
|
| 187 | 207 | let parts = emit::splitImm(location.offset as i32); |
|
| 188 | 208 | emit::patch(e, pending.index, encode::ld(pending.rd, super::GP, location.slot as i32 * 8)); |
| 192 | 212 | } |
|
| 193 | 213 | } |
|
| 194 | 214 | ||
| 195 | 215 | /// Write a little-endian integer into a validated byte extent. |
|
| 196 | 216 | fn integer(bytes: &mut [u8], offset: u32, value: u64, width: u32) { |
|
| 197 | - | for i in 0..width { set bytes[offset + i] = (value >> (i as u64 * 8)) as u8; } |
|
| 217 | + | for i in 0..width { |
|
| 218 | + | set bytes[offset + i] = (value >> (i as u64 * 8)) as u8; |
|
| 219 | + | } |
|
| 198 | 220 | } |
|
| 199 | 221 | ||
| 200 | 222 | /// Initialized output extents. |
|
| 201 | 223 | record TemplateSize: Copy { |
|
| 202 | 224 | /// Initialized byte count. |
| 210 | 232 | -> TemplateSize throws (Error) |
|
| 211 | 233 | { |
|
| 212 | 234 | let mut initialized: u32 = 0; |
|
| 213 | 235 | let mut count: u32 = 0; |
|
| 214 | 236 | for item in items { |
|
| 215 | - | if item.isZeroInit { continue; } |
|
| 237 | + | if item.isZeroInit { |
|
| 238 | + | continue; |
|
| 239 | + | } |
|
| 216 | 240 | let location = try dataRef(try resolve(local, &[], item.name)); |
|
| 217 | - | if location.offset > bytes.len or item.size > bytes.len - location.offset { throw Error::Capacity; } |
|
| 241 | + | if location.offset > bytes.len or item.size > bytes.len - location.offset { |
|
| 242 | + | throw Error::Capacity; |
|
| 243 | + | } |
|
| 218 | 244 | let end = location.offset + item.size; |
|
| 219 | - | if end > initialized { set initialized = end; } |
|
| 245 | + | if end > initialized { |
|
| 246 | + | set initialized = end; |
|
| 247 | + | } |
|
| 248 | + | } |
|
| 249 | + | for i in 0..initialized { |
|
| 250 | + | set bytes[i] = 0; |
|
| 220 | 251 | } |
|
| 221 | - | for i in 0..initialized { set bytes[i] = 0; } |
|
| 222 | 252 | for item in items { |
|
| 223 | - | if item.isZeroInit { continue; } |
|
| 253 | + | if item.isZeroInit { |
|
| 254 | + | continue; |
|
| 255 | + | } |
|
| 224 | 256 | let location = try dataRef(try resolve(local, &[], item.name)); |
|
| 225 | 257 | let mut offset = location.offset; |
|
| 226 | 258 | let end = offset + item.size; |
|
| 227 | 259 | for value in item.values { |
|
| 228 | 260 | let mut width: u32 = 1; |
|
| 229 | 261 | let mut number: u64 = 0; |
|
| 230 | 262 | match value.item { |
|
| 231 | - | case il::DataItem::Val { typ, val } => { set width = il::typeSize(typ); set number = val as u64; }, |
|
| 232 | - | case il::DataItem::Fn(name) => { set width = 8; set number = try function(local, imports, name); }, |
|
| 263 | + | case il::DataItem::Val { typ, val } => { |
|
| 264 | + | set width = il::typeSize(typ); |
|
| 265 | + | set number = val as u64; |
|
| 266 | + | }, |
|
| 267 | + | case il::DataItem::Fn(name) => { |
|
| 268 | + | set width = 8; |
|
| 269 | + | set number = try function(local, imports, name); |
|
| 270 | + | }, |
|
| 233 | 271 | case il::DataItem::Sym(name) => { |
|
| 234 | 272 | set width = 8; |
|
| 235 | 273 | let target = try dataRef(try resolve(local, imports, name)); |
|
| 236 | 274 | if value.count > 0 { |
|
| 237 | - | if count == relocs.len { throw Error::Capacity; } |
|
| 275 | + | if count == relocs.len { |
|
| 276 | + | throw Error::Capacity; |
|
| 277 | + | } |
|
| 238 | 278 | set relocs[count] = Relocation { offset, count: value.count, target }; |
|
| 239 | 279 | set count += 1; |
|
| 240 | 280 | } |
|
| 241 | 281 | }, |
|
| 242 | - | case il::DataItem::Str(s) => { set width = s.len; }, |
|
| 243 | - | case il::DataItem::Undef => {}, |
|
| 282 | + | case il::DataItem::Str(s) => { |
|
| 283 | + | set width = s.len; |
|
| 284 | + | }, |
|
| 285 | + | case il::DataItem::Undef => { |
|
| 286 | + | }, |
|
| 287 | + | } |
|
| 288 | + | if width > 0 and value.count > (end - offset) / width { |
|
| 289 | + | throw Error::Range; |
|
| 290 | + | } |
|
| 291 | + | if width == 0 { |
|
| 292 | + | continue; |
|
| 244 | 293 | } |
|
| 245 | - | if width > 0 and value.count > (end - offset) / width { throw Error::Range; } |
|
| 246 | - | if width == 0 { continue; } |
|
| 247 | 294 | for _ in 0..value.count { |
|
| 248 | 295 | match value.item { |
|
| 249 | - | case il::DataItem::Str(s) => { try! mem::copy(&mut bytes[offset..offset + width], s); }, |
|
| 250 | - | else => { integer(bytes, offset, number, width); }, |
|
| 296 | + | case il::DataItem::Str(s) => { |
|
| 297 | + | try! mem::copy(&mut bytes[offset..offset + width], s); |
|
| 298 | + | }, |
|
| 299 | + | else => { |
|
| 300 | + | integer(bytes, offset, number, width); |
|
| 301 | + | }, |
|
| 251 | 302 | } |
|
| 252 | 303 | set offset += width; |
|
| 253 | 304 | } |
|
| 254 | 305 | } |
|
| 255 | 306 | } |
|
| 256 | 307 | return TemplateSize { bytes: initialized, relocations: count }; |
|
| 257 | 308 | } |
|
| 258 | 309 | ||
| 310 | + | /// Package definitions and their native assembly boundaries. |
|
| 311 | + | export record AssemblyInput: Copy { |
|
| 312 | + | /// Trusted binary RIL definitions that remain valid during compilation. |
|
| 313 | + | package: *unsafe binary::Package, |
|
| 314 | + | /// Text-only assembly prefix with exported native boundaries. |
|
| 315 | + | assembly: asm::Program, |
|
| 316 | + | } |
|
| 317 | + | ||
| 259 | 318 | /// Compile one package into caller-owned code, symbol, and private template storage. |
|
| 260 | 319 | /// Imports must contain unique exports from the package's admitted dependencies. |
|
| 261 | 320 | /// Arena storage and binary package names must outlive the returned catalog entry. |
|
| 262 | 321 | export unsafe fn compile(input: &binary::Package, slot: u32, codeAddress: u64, imports: &[Symbol], |
|
| 263 | 322 | storage: Storage, arena: &mut alloc::Arena, scratch: &mut alloc::Arena) -> Package throws (Error) |
|
| 323 | + | { |
|
| 324 | + | return try compileAssembly(AssemblyInput { package: input as *unsafe binary::Package, |
|
| 325 | + | assembly: asm::Program { text: &[], data: &[], symbols: &[], externalFixups: &[] } }, |
|
| 326 | + | slot, codeAddress, imports, storage, arena, scratch); |
|
| 327 | + | } |
|
| 328 | + | ||
| 329 | + | /// Compile a package with a text-only assembly prefix and its exported boundaries. |
|
| 330 | + | /// Assembly and RIL definitions share one native code extent and package-state slot. |
|
| 331 | + | export unsafe fn compileAssembly(source: AssemblyInput, slot: u32, codeAddress: u64, imports: &[Symbol], |
|
| 332 | + | storage: Storage, arena: &mut alloc::Arena, scratch: &mut alloc::Arena) -> Package throws (Error) |
|
| 264 | 333 | { |
|
| 265 | 334 | let case Storage { data: dataStorage, symbols: symbolStorage, exports: exportStorage, template: templateStorage, relocations: relocationStorage } = storage |
|
| 266 | 335 | else panic "expected shared linker storage"; |
|
| 267 | - | if slot >= MAX_PACKAGES { throw Error::Range; } |
|
| 268 | - | if (codeAddress & 3) <> 0 { throw Error::Alignment; } |
|
| 336 | + | let input = source.package; |
|
| 337 | + | let assembly = source.assembly; |
|
| 338 | + | if assembly.data.len <> 0 { |
|
| 339 | + | throw Error::Range; |
|
| 340 | + | } |
|
| 341 | + | if slot >= MAX_PACKAGES { |
|
| 342 | + | throw Error::Range; |
|
| 343 | + | } |
|
| 344 | + | if (codeAddress & 3) <> 0 { |
|
| 345 | + | throw Error::Alignment; |
|
| 346 | + | } |
|
| 269 | 347 | for imported, i in imports { |
|
| 270 | - | if lookup(&imports[..i], imported.name) <> nil { throw Error::Symbol; } |
|
| 348 | + | if lookup(&imports[..i], imported.name) <> nil { |
|
| 349 | + | throw Error::Symbol; |
|
| 350 | + | } |
|
| 271 | 351 | } |
|
| 272 | 352 | let mut generator = try super::beginProgram(super::ProgramOptions { |
|
| 273 | 353 | entryPatch: super::EntryPatch::None, debug: false, placement: image::Placement::Hosted, |
|
| 274 | - | }, arena) catch err { throw Error::Codegen(err); }; |
|
| 354 | + | }, arena) catch err { |
|
| 355 | + | throw Error::Codegen(err); |
|
| 356 | + | }; |
|
| 275 | 357 | set generator.e.sharedData = true; |
|
| 276 | - | for func in input.program.fns { super::generateFunction(&mut generator, func, scratch); } |
|
| 277 | - | try emit::check(&generator.e) catch err { throw Error::Codegen(err); }; |
|
| 278 | - | if codeAddress > 0xffffffffffffffff - generator.e.codeLen as u64 * 4 { throw Error::Range; } |
|
| 358 | + | super::addAssembly(&mut generator, assembly); |
|
| 359 | + | for func in input.program.fns { |
|
| 360 | + | super::generateFunction(&mut generator, func, scratch); |
|
| 361 | + | } |
|
| 362 | + | try emit::check(&generator.e) catch err { |
|
| 363 | + | throw Error::Codegen(err); |
|
| 364 | + | }; |
|
| 365 | + | if codeAddress > 0xffffffffffffffff - generator.e.codeLen as u64 * 4 { |
|
| 366 | + | throw Error::Range; |
|
| 367 | + | } |
|
| 279 | 368 | let mut symbols: u32 = 0; |
|
| 280 | - | for func in &generator.e.funcs[..] { |
|
| 369 | + | for func in &generator.e.funcs[..generator.e.funcsLen] { |
|
| 281 | 370 | try define(symbolStorage, &mut symbols, func.name, Target::Function(codeAddress + func.index as u64 * 4)); |
|
| 282 | 371 | } |
|
| 283 | 372 | let mut dataCount: u32 = 0; |
|
| 284 | 373 | let roSize = try data::layoutSection(input.program.data, dataStorage, &mut dataCount, 0, true) |
|
| 285 | - | catch { throw Error::Range; }; |
|
| 374 | + | catch { |
|
| 375 | + | throw Error::Range; |
|
| 376 | + | }; |
|
| 286 | 377 | let memory = try data::layoutSectionAtOffset(input.program.data, dataStorage, &mut dataCount, 0, roSize, false) |
|
| 287 | - | catch { throw Error::Range; }; |
|
| 288 | - | if memory > 0x7ffff7ff { throw Error::Range; } |
|
| 378 | + | catch { |
|
| 379 | + | throw Error::Range; |
|
| 380 | + | }; |
|
| 381 | + | if memory > 0x7ffff7ff { |
|
| 382 | + | throw Error::Range; |
|
| 383 | + | } |
|
| 289 | 384 | for item in &dataStorage[..dataCount] { |
|
| 290 | 385 | try define(symbolStorage, &mut symbols, item.name, Target::Data(DataRef { slot, offset: item.addr as u32 })); |
|
| 291 | 386 | } |
|
| 292 | 387 | let local = &symbolStorage[..symbols]; |
|
| 293 | - | for symbol in local { if lookup(imports, symbol.name) <> nil { throw Error::Symbol; } } |
|
| 388 | + | for symbol in local { |
|
| 389 | + | if lookup(imports, symbol.name) <> nil { |
|
| 390 | + | throw Error::Symbol; |
|
| 391 | + | } |
|
| 392 | + | } |
|
| 294 | 393 | try link(&mut generator.e, codeAddress, local, imports); |
|
| 295 | - | try emit::check(&generator.e) catch err { throw Error::Codegen(err); }; |
|
| 394 | + | try emit::check(&generator.e) catch err { |
|
| 395 | + | throw Error::Codegen(err); |
|
| 396 | + | }; |
|
| 296 | 397 | let size = try template(input.program.data, local, imports, templateStorage, relocationStorage); |
|
| 297 | - | if input.exports.len > exportStorage.len { throw Error::Capacity; } |
|
| 398 | + | if input.exports.len > exportStorage.len { |
|
| 399 | + | throw Error::Capacity; |
|
| 400 | + | } |
|
| 298 | 401 | for exported, i in input.exports { |
|
| 299 | 402 | let target = try resolve(local, &[], exported.name); |
|
| 300 | 403 | match exported.kind { |
|
| 301 | - | case binary::ExportKind::Function => { try function(local, &[], exported.name); }, |
|
| 302 | - | case binary::ExportKind::Data => { try dataRef(target); }, |
|
| 404 | + | case binary::ExportKind::Function => { |
|
| 405 | + | try function(local, &[], exported.name); |
|
| 406 | + | }, |
|
| 407 | + | case binary::ExportKind::Data => { |
|
| 408 | + | try dataRef(target); |
|
| 409 | + | }, |
|
| 410 | + | } |
|
| 411 | + | if lookup(&exportStorage[..i], exported.name) <> nil { |
|
| 412 | + | throw Error::Symbol; |
|
| 303 | 413 | } |
|
| 304 | - | if lookup(&exportStorage[..i], exported.name) <> nil { throw Error::Symbol; } |
|
| 305 | 414 | set exportStorage[i] = Symbol { name: exported.name, target }; |
|
| 306 | 415 | } |
|
| 307 | 416 | let mut entry: ?u64 = nil; |
|
| 308 | - | if let name = input.entry { set entry = try function(&exportStorage[..input.exports.len], &[], name); } |
|
| 417 | + | if let name = input.entry { |
|
| 418 | + | set entry = try function(&exportStorage[..input.exports.len], &[], name); |
|
| 419 | + | } |
|
| 309 | 420 | let mut alignment: u32 = 8; |
|
| 310 | - | for item in input.program.data { if item.alignment > alignment { set alignment = item.alignment; } } |
|
| 421 | + | for item in input.program.data { |
|
| 422 | + | if item.alignment > alignment { |
|
| 423 | + | set alignment = item.alignment; |
|
| 424 | + | } |
|
| 425 | + | } |
|
| 426 | + | let case super::Generator { e, .. } = generator else panic "expected package generator"; |
|
| 427 | + | let case emit::Emitter { code, codeLen, .. } = e else panic "expected package emitter"; |
|
| 311 | 428 | return Package { |
|
| 312 | 429 | name: input.name, dependencies: input.dependencies, slot, codeAddress, |
|
| 313 | - | code: emit::getCode(&generator.e), exports: &exportStorage[..input.exports.len], entry, |
|
| 430 | + | code: &code[..codeLen], exports: &exportStorage[..input.exports.len], entry, |
|
| 314 | 431 | template: &templateStorage[..size.bytes], memory, alignment, |
|
| 315 | 432 | relocations: &relocationStorage[..size.relocations], |
|
| 316 | 433 | }; |
|
| 317 | 434 | } |
|
| 318 | 435 | ||
| 319 | 436 | /// Initialize private package memory after all graph bases have been assigned. |
|
| 320 | 437 | /// Validate every fixup before writing bytes so a rejected instance stays intact. |
|
| 321 | 438 | export fn instantiate(package: &Package, bases: &[u64], memory: &mut [u8]) throws (Error) { |
|
| 322 | - | if package.slot >= bases.len or bases[package.slot] == 0 { throw Error::Instance; } |
|
| 323 | - | if memory.len < package.memory or package.template.len > package.memory { throw Error::Capacity; } |
|
| 324 | - | if (bases[package.slot] & (package.alignment as u64 - 1)) <> 0 { throw Error::Alignment; } |
|
| 325 | - | if bases[package.slot] > 0xffffffffffffffff - package.memory as u64 { throw Error::Range; } |
|
| 439 | + | if package.slot >= bases.len or bases[package.slot] == 0 { |
|
| 440 | + | throw Error::Instance; |
|
| 441 | + | } |
|
| 442 | + | if memory.len < package.memory or package.template.len > package.memory { |
|
| 443 | + | throw Error::Capacity; |
|
| 444 | + | } |
|
| 445 | + | if (bases[package.slot] & (package.alignment as u64 - 1)) <> 0 { |
|
| 446 | + | throw Error::Alignment; |
|
| 447 | + | } |
|
| 448 | + | if bases[package.slot] > 0xffffffffffffffff - package.memory as u64 { |
|
| 449 | + | throw Error::Range; |
|
| 450 | + | } |
|
| 326 | 451 | for fixup in package.relocations { |
|
| 327 | - | if fixup.target.slot >= bases.len or bases[fixup.target.slot] == 0 { throw Error::Instance; } |
|
| 328 | - | if bases[fixup.target.slot] > 0xffffffffffffffff - fixup.target.offset as u64 { throw Error::Range; } |
|
| 329 | - | if fixup.offset > package.memory or fixup.count > (package.memory - fixup.offset) / 8 { throw Error::Range; } |
|
| 452 | + | if fixup.target.slot >= bases.len or bases[fixup.target.slot] == 0 { |
|
| 453 | + | throw Error::Instance; |
|
| 454 | + | } |
|
| 455 | + | if bases[fixup.target.slot] > 0xffffffffffffffff - fixup.target.offset as u64 { |
|
| 456 | + | throw Error::Range; |
|
| 457 | + | } |
|
| 458 | + | if fixup.offset > package.memory or fixup.count > (package.memory - fixup.offset) / 8 { |
|
| 459 | + | throw Error::Range; |
|
| 460 | + | } |
|
| 461 | + | } |
|
| 462 | + | for i in 0..package.memory { |
|
| 463 | + | set memory[i] = 0; |
|
| 330 | 464 | } |
|
| 331 | - | for i in 0..package.memory { set memory[i] = 0; } |
|
| 332 | 465 | try! mem::copy(memory, package.template); |
|
| 333 | 466 | for fixup in package.relocations { |
|
| 334 | 467 | let address = bases[fixup.target.slot] + fixup.target.offset as u64; |
|
| 335 | - | for i in 0..fixup.count { integer(memory, fixup.offset + i * 8, address, 8); } |
|
| 468 | + | for i in 0..fixup.count { |
|
| 469 | + | integer(memory, fixup.offset + i * 8, address, 8); |
|
| 470 | + | } |
|
| 336 | 471 | } |
|
| 337 | 472 | } |
lib/std/arch/rv64/shared/catalog.rad
added
+131 -0
| 1 | + | //! Persistent native boot catalogs for the RV64 record ABI. |
|
| 2 | + | ||
| 3 | + | use std::mem; |
|
| 4 | + | use std::arch::rv64::shared; |
|
| 5 | + | ||
| 6 | + | /// Binary package identity and its resident native descriptor. |
|
| 7 | + | export record Entry: Copy { |
|
| 8 | + | /// Complete immutable binary RIL bytes. |
|
| 9 | + | source: *[u8], |
|
| 10 | + | /// Shared code, exports, and private-state template. |
|
| 11 | + | package: shared::Package, |
|
| 12 | + | } |
|
| 13 | + | ||
| 14 | + | /// Reserve aligned bytes in the native catalog output. |
|
| 15 | + | fn reserve(output: &mut [u8], used: &mut u32, size: u32, alignment: u32) -> u32 throws (shared::Error) { |
|
| 16 | + | let padding = (alignment - (*used & (alignment - 1))) & (alignment - 1); |
|
| 17 | + | if padding > output.len - *used { |
|
| 18 | + | throw shared::Error::Capacity; |
|
| 19 | + | } |
|
| 20 | + | let offset = *used + padding; |
|
| 21 | + | if size > output.len - offset { |
|
| 22 | + | throw shared::Error::Capacity; |
|
| 23 | + | } |
|
| 24 | + | for i in *used..offset { |
|
| 25 | + | set output[i] = 0; |
|
| 26 | + | } |
|
| 27 | + | set *used = offset + size; |
|
| 28 | + | return offset; |
|
| 29 | + | } |
|
| 30 | + | ||
| 31 | + | /// View the native bytes of a caller-owned record or array. |
|
| 32 | + | unsafe fn raw(value: &opaque, size: u32) -> *unsafe [u8] { return @sliceOf((value as &u8) as *unsafe u8, size); } |
|
| 33 | + | ||
| 34 | + | /// Obtain a field's byte offset from the compiler's native record layout. |
|
| 35 | + | unsafe fn field(container: &opaque, value: &opaque) -> u32 { return (value as u64 - container as u64) as u32; } |
|
| 36 | + | ||
| 37 | + | /// Write one little-endian integer into a reserved native field. |
|
| 38 | + | fn integer(output: &mut [u8], offset: u32, value: u64, width: u32) { |
|
| 39 | + | for i in 0..width { |
|
| 40 | + | set output[offset + i] = (value >> (i as u64 * 8)) as u8; |
|
| 41 | + | } |
|
| 42 | + | } |
|
| 43 | + | ||
| 44 | + | /// Write the RV64 pointer, length, and capacity of a native immutable slice. |
|
| 45 | + | fn slice(output: &mut [u8], offset: u32, address: u64, length: u32) { |
|
| 46 | + | integer(output, offset, address, 8); |
|
| 47 | + | integer(output, offset + 8, length as u64, 4); |
|
| 48 | + | integer(output, offset + 12, length as u64, 4); |
|
| 49 | + | } |
|
| 50 | + | ||
| 51 | + | /// Copy bytes into the catalog and return their target-relative offset. |
|
| 52 | + | fn bytes(input: *[u8], output: &mut [u8], used: &mut u32) -> u32 throws (shared::Error) { |
|
| 53 | + | let offset = try reserve(output, used, input.len, 1); |
|
| 54 | + | try! mem::copy(&mut output[offset..offset + input.len], input); |
|
| 55 | + | return offset; |
|
| 56 | + | } |
|
| 57 | + | ||
| 58 | + | /// Copy a dependency list and each name into native catalog storage. |
|
| 59 | + | unsafe fn dependencies(input: *unsafe [*[u8]], base: u64, output: &mut [u8], used: &mut u32) -> u32 throws (shared::Error) { |
|
| 60 | + | if input.len > output.len / @sizeOf(*[u8]) { |
|
| 61 | + | throw shared::Error::Capacity; |
|
| 62 | + | } |
|
| 63 | + | let offset = try reserve(output, used, input.len * @sizeOf(*[u8]), @alignOf(*[u8])); |
|
| 64 | + | for name, i in input { |
|
| 65 | + | let at = try bytes(name, output, used); |
|
| 66 | + | slice(output, offset + i * @sizeOf(*[u8]), base + at as u64, name.len); |
|
| 67 | + | } |
|
| 68 | + | return offset; |
|
| 69 | + | } |
|
| 70 | + | ||
| 71 | + | /// Copy public symbols and their names into native catalog storage. |
|
| 72 | + | unsafe fn symbols(input: *[shared::Symbol], base: u64, output: &mut [u8], used: &mut u32) -> u32 throws (shared::Error) { |
|
| 73 | + | if input.len > output.len / @sizeOf(shared::Symbol) { |
|
| 74 | + | throw shared::Error::Capacity; |
|
| 75 | + | } |
|
| 76 | + | let offset = try reserve(output, used, input.len * @sizeOf(shared::Symbol), @alignOf(shared::Symbol)); |
|
| 77 | + | for symbol, i in input { |
|
| 78 | + | let at = offset + i * @sizeOf(shared::Symbol); |
|
| 79 | + | try! mem::copy(&mut output[at..at + @sizeOf(shared::Symbol)], raw(&symbol, @sizeOf(shared::Symbol))); |
|
| 80 | + | let name = try bytes(symbol.name, output, used); |
|
| 81 | + | slice(output, at + field(&symbol, &symbol.name), base + name as u64, symbol.name.len); |
|
| 82 | + | } |
|
| 83 | + | return offset; |
|
| 84 | + | } |
|
| 85 | + | ||
| 86 | + | /// Copy private-state relocation records into native catalog storage. |
|
| 87 | + | unsafe fn relocations(input: *[shared::Relocation], output: &mut [u8], used: &mut u32) -> u32 throws (shared::Error) { |
|
| 88 | + | if input.len > output.len / @sizeOf(shared::Relocation) { |
|
| 89 | + | throw shared::Error::Capacity; |
|
| 90 | + | } |
|
| 91 | + | let size = input.len * @sizeOf(shared::Relocation); |
|
| 92 | + | let offset = try reserve(output, used, size, @alignOf(shared::Relocation)); |
|
| 93 | + | try! mem::copy(&mut output[offset..offset + size], raw(input.ptr, size)); |
|
| 94 | + | return offset; |
|
| 95 | + | } |
|
| 96 | + | ||
| 97 | + | /// Pack native Entry records followed by all referenced metadata and source bytes. |
|
| 98 | + | /// Code is placed separately at each package's codeAddress. The target base must |
|
| 99 | + | /// have Entry alignment. Return the used extent. Failure leaves partial output. |
|
| 100 | + | export unsafe fn pack(entries: &[Entry], base: u64, output: &mut [u8]) -> u32 throws (shared::Error) { |
|
| 101 | + | assert @sizeOf(*[u8]) == 16; |
|
| 102 | + | if (base & (@alignOf(Entry) as u64 - 1)) <> 0 { |
|
| 103 | + | throw shared::Error::Alignment; |
|
| 104 | + | } |
|
| 105 | + | if base > 0xffffffffffffffff - output.len as u64 { |
|
| 106 | + | throw shared::Error::Range; |
|
| 107 | + | } |
|
| 108 | + | if entries.len > output.len / @sizeOf(Entry) { |
|
| 109 | + | throw shared::Error::Capacity; |
|
| 110 | + | } |
|
| 111 | + | let mut used: u32 = 0; |
|
| 112 | + | let start = try reserve(output, &mut used, entries.len * @sizeOf(Entry), @alignOf(Entry)); |
|
| 113 | + | for entry, i in entries { |
|
| 114 | + | let at = i * @sizeOf(Entry); |
|
| 115 | + | try! mem::copy(&mut output[at..at + @sizeOf(Entry)], raw(&entry, @sizeOf(Entry))); |
|
| 116 | + | let source = try bytes(entry.source, output, &mut used); |
|
| 117 | + | let name = try bytes(entry.package.name, output, &mut used); |
|
| 118 | + | let required = try dependencies(entry.package.dependencies, base, output, &mut used); |
|
| 119 | + | let exports = try symbols(entry.package.exports, base, output, &mut used); |
|
| 120 | + | let template = try bytes(entry.package.template, output, &mut used); |
|
| 121 | + | let fixups = try relocations(entry.package.relocations, output, &mut used); |
|
| 122 | + | slice(output, at + field(&entry, &entry.source), base + source as u64, entry.source.len); |
|
| 123 | + | slice(output, at + field(&entry, &entry.package.name), base + name as u64, entry.package.name.len); |
|
| 124 | + | slice(output, at + field(&entry, &entry.package.dependencies), base + required as u64, entry.package.dependencies.len); |
|
| 125 | + | slice(output, at + field(&entry, &entry.package.code), entry.package.codeAddress, entry.package.code.len); |
|
| 126 | + | slice(output, at + field(&entry, &entry.package.exports), base + exports as u64, entry.package.exports.len); |
|
| 127 | + | slice(output, at + field(&entry, &entry.package.template), base + template as u64, entry.package.template.len); |
|
| 128 | + | slice(output, at + field(&entry, &entry.package.relocations), base + fixups as u64, entry.package.relocations.len); |
|
| 129 | + | } |
|
| 130 | + | return used; |
|
| 131 | + | } |
lib/std/arch/rv64/shared/tests.rad
+115 -8
| 1 | 1 | //! Private template relocation and bounded linking checks. |
|
| 2 | 2 | ||
| 3 | 3 | use std::testing; |
|
| 4 | + | use std::mem; |
|
| 5 | + | use std::arch::rv64::shared::catalog; |
|
| 6 | + | use std::arch::rv64::asm; |
|
| 4 | 7 | use std::lang::alloc; |
|
| 5 | 8 | use std::lang::il; |
|
| 6 | 9 | use std::lang::il::binary; |
|
| 7 | 10 | use std::lang::gen::data; |
|
| 8 | 11 |
| 18 | 21 | unsafe static DATA: [data::DataSym; 8] = undefined; |
|
| 19 | 22 | /// Persistent initialized bytes. |
|
| 20 | 23 | static TEMPLATE: [u8; 64] = [0; 64]; |
|
| 21 | 24 | /// Persistent private data relocations. |
|
| 22 | 25 | unsafe static RELOCS: [super::Relocation; 8] = undefined; |
|
| 26 | + | /// Aligned storage for a native boot catalog and its retained payloads. |
|
| 27 | + | static CATALOG: [u64; 512] = [0; 512]; |
|
| 28 | + | ||
| 29 | + | /// Exported assembly boundaries occupy the same code extent as their package. |
|
| 30 | + | @test unsafe fn assemblyPrefix() throws (testing::TestError) { |
|
| 31 | + | let input = binary::Package { |
|
| 32 | + | symbols: &[], name: "p", dependencies: &[], |
|
| 33 | + | exports: &[binary::Export { name: "p::entry", kind: binary::ExportKind::Function }], |
|
| 34 | + | entry: "p::entry", program: il::Program { data: &[], fns: &[] }, |
|
| 35 | + | }; |
|
| 36 | + | let assembly = asm::Program { |
|
| 37 | + | text: &[0x02a00513 as u32, 0x00008067], data: &[], |
|
| 38 | + | symbols: &[asm::Symbol { name: "p::entry", section: asm::Section::Text, offset: 0, isExported: true }], |
|
| 39 | + | externalFixups: &[], |
|
| 40 | + | }; |
|
| 41 | + | let mut arena = alloc::new(&mut ARENA[..]); |
|
| 42 | + | let mut scratch = alloc::new(&mut SCRATCH[..]); |
|
| 43 | + | let package = try super::compileAssembly(super::AssemblyInput { package: &input, assembly }, 0, 0x180000000, &[], |
|
| 44 | + | super::Storage { |
|
| 45 | + | data: &mut DATA[..], symbols: &mut SYMBOLS[..], exports: &mut EXPORTS[..], |
|
| 46 | + | template: &mut TEMPLATE[..], relocations: &mut RELOCS[..], |
|
| 47 | + | }, &mut arena, &mut scratch) catch { |
|
| 48 | + | throw testing::TestError::Failed; |
|
| 49 | + | }; |
|
| 50 | + | let entry = package.entry else { |
|
| 51 | + | throw testing::TestError::Failed; |
|
| 52 | + | }; |
|
| 53 | + | try testing::expect(entry == 0x180000000 and package.code.len == 2); |
|
| 54 | + | try testing::expect(package.code[0] == 0x02a00513 and package.exports[0].target == super::Target::Function(entry)); |
|
| 55 | + | } |
|
| 56 | + | ||
| 57 | + | /// Packed catalog pointers resolve to retained metadata at the supplied native base. |
|
| 58 | + | @test unsafe fn nativeCatalog() throws (testing::TestError) { |
|
| 59 | + | let package = try compile(imports(), 64) catch { |
|
| 60 | + | throw testing::TestError::Failed; |
|
| 61 | + | }; |
|
| 62 | + | let output = @sliceOf(&mut CATALOG[0] as *mut u8, @sizeOf([u64; 512])); |
|
| 63 | + | let base = output.ptr as u64; |
|
| 64 | + | let used = try catalog::pack(&[catalog::Entry { source: "trusted binary", package }], base, output) |
|
| 65 | + | catch { |
|
| 66 | + | throw testing::TestError::Failed; |
|
| 67 | + | }; |
|
| 68 | + | let storage = &CATALOG[0] as *opaque; |
|
| 69 | + | let entry = storage as *catalog::Entry; |
|
| 70 | + | try testing::expect(used > @sizeOf(catalog::Entry) and used <= output.len); |
|
| 71 | + | try testing::expect(mem::eq(entry.source, "trusted binary") and mem::eq(entry.package.name, "p")); |
|
| 72 | + | try testing::expect(mem::eq(entry.package.dependencies[0], "dep")); |
|
| 73 | + | try testing::expect(entry.package.code.ptr as u64 == package.codeAddress and entry.package.code.len == package.code.len); |
|
| 74 | + | try testing::expect(entry.source.ptr as u64 >= base and entry.source.ptr as u64 < base + used as u64); |
|
| 75 | + | set TEMPLATE[0] = 91; |
|
| 76 | + | set EXPORTS[0].name = "changed"; |
|
| 77 | + | try testing::expect(entry.package.template[0] == 0 and mem::eq(entry.package.exports[0].name, "p::refs")); |
|
| 78 | + | let mut state: [u8; 48] = [255; 48]; |
|
| 79 | + | try super::instantiate(&entry.package, &[0, 0x180002000, 0x180008000], &mut state[..]) |
|
| 80 | + | catch { |
|
| 81 | + | throw testing::TestError::Failed; |
|
| 82 | + | }; |
|
| 83 | + | try testing::expect(pointer(&state[..], 0) == 0x180004000 and pointer(&state[..], 16) == 0x180001234); |
|
| 84 | + | let relocated = try catalog::pack(&[catalog::Entry { source: "trusted binary", package }], 0x180000000, output) |
|
| 85 | + | catch { |
|
| 86 | + | throw testing::TestError::Failed; |
|
| 87 | + | }; |
|
| 88 | + | try testing::expect(entry.source.ptr as u64 >= 0x180000000 and entry.source.ptr as u64 < 0x180000000 + relocated as u64); |
|
| 89 | + | } |
|
| 90 | + | ||
| 91 | + | /// Catalog bounds and native alignment failures are recoverable. |
|
| 92 | + | @test unsafe fn nativeCatalogBounds() throws (testing::TestError) { |
|
| 93 | + | let package = try compile(imports(), 64) catch { |
|
| 94 | + | throw testing::TestError::Failed; |
|
| 95 | + | }; |
|
| 96 | + | let entries = [catalog::Entry { source: "binary", package }]; |
|
| 97 | + | let output = @sliceOf(&mut CATALOG[0] as *mut u8, @sizeOf([u64; 512])); |
|
| 98 | + | let mut failures: u32 = 0; |
|
| 99 | + | try catalog::pack(&entries[..], 0x80000000, &mut output[..@sizeOf(catalog::Entry)]) catch err { |
|
| 100 | + | try testing::expect(err == super::Error::Capacity); set failures += 1; |
|
| 101 | + | }; |
|
| 102 | + | try catalog::pack(&entries[..], 0x80000001, output) catch err { |
|
| 103 | + | try testing::expect(err == super::Error::Alignment); set failures += 1; |
|
| 104 | + | }; |
|
| 105 | + | try catalog::pack(&entries[..], 0xfffffffffffffff8, output) catch err { |
|
| 106 | + | try testing::expect(err == super::Error::Range); set failures += 1; |
|
| 107 | + | }; |
|
| 108 | + | try testing::expect(failures == 3); |
|
| 109 | + | let used = try catalog::pack(&entries[..], 0x80000000, output) catch { |
|
| 110 | + | throw testing::TestError::Failed; |
|
| 111 | + | }; |
|
| 112 | + | try testing::expect(used > @sizeOf(catalog::Entry)); |
|
| 113 | + | } |
|
| 23 | 114 | ||
| 24 | 115 | /// Build a package with repeated dependency pointers and a high code pointer. |
|
| 25 | 116 | unsafe fn input() -> binary::Package { |
|
| 26 | 117 | return binary::Package { |
|
| 27 | 118 | symbols: &[], name: "p", dependencies: &["dep"], |
| 66 | 157 | } |
|
| 67 | 158 | ||
| 68 | 159 | /// Read a little-endian pointer without requiring buffer alignment. |
|
| 69 | 160 | fn pointer(bytes: &[u8], offset: u32) -> u64 { |
|
| 70 | 161 | let mut value: u64 = 0; |
|
| 71 | - | for i in 0..8 { set value |= bytes[offset + i] as u64 << (i as u64 * 8); } |
|
| 162 | + | for i in 0..8 { |
|
| 163 | + | set value |= bytes[offset + i] as u64 << (i as u64 * 8); |
|
| 164 | + | } |
|
| 72 | 165 | return value; |
|
| 73 | 166 | } |
|
| 74 | 167 | ||
| 75 | 168 | /// Check repeated pointers, full-width code addresses, padding, and zero-filled tails. |
|
| 76 | 169 | @test unsafe fn privateRelocations() throws (testing::TestError) { |
|
| 77 | - | let package = try compile(imports(), 64) catch { throw testing::TestError::Failed; }; |
|
| 170 | + | let package = try compile(imports(), 64) catch { |
|
| 171 | + | throw testing::TestError::Failed; |
|
| 172 | + | }; |
|
| 78 | 173 | try testing::expect(package.template.len == 32 and package.memory == 48 and package.alignment == 16); |
|
| 79 | 174 | try testing::expect(package.relocations.len == 1 and package.relocations[0].count == 2); |
|
| 80 | 175 | try testing::expect(pointer(package.template, 16) == 0x180001234); |
|
| 81 | 176 | let mut first: [u8; 48] = [255; 48]; |
|
| 82 | 177 | let mut second: [u8; 48] = [255; 48]; |
|
| 83 | 178 | try super::instantiate(&package, &[0, 0x180002000, 0x180008000], &mut first[..]) |
|
| 84 | - | catch { throw testing::TestError::Failed; }; |
|
| 179 | + | catch { |
|
| 180 | + | throw testing::TestError::Failed; |
|
| 181 | + | }; |
|
| 85 | 182 | try super::instantiate(&package, &[0, 0x280002000, 0x280008000], &mut second[..]) |
|
| 86 | - | catch { throw testing::TestError::Failed; }; |
|
| 183 | + | catch { |
|
| 184 | + | throw testing::TestError::Failed; |
|
| 185 | + | }; |
|
| 87 | 186 | try testing::expect(pointer(&first[..], 0) == 0x180004000 and pointer(&first[..], 8) == 0x180004000); |
|
| 88 | 187 | try testing::expect(pointer(&second[..], 0) == 0x280004000 and pointer(&second[..], 8) == 0x280004000); |
|
| 89 | 188 | try testing::expect(pointer(&first[..], 16) == pointer(&second[..], 16)); |
|
| 90 | - | for i in 24..48 { try testing::expect(first[i] == 0 and second[i] == 0); } |
|
| 189 | + | for i in 24..48 { |
|
| 190 | + | try testing::expect(first[i] == 0 and second[i] == 0); |
|
| 191 | + | } |
|
| 91 | 192 | } |
|
| 92 | 193 | ||
| 93 | 194 | /// A missing instance, bad base, or short buffer leaves instance bytes untouched. |
|
| 94 | 195 | @test unsafe fn instanceFailures() throws (testing::TestError) { |
|
| 95 | - | let package = try compile(imports(), 64) catch { throw testing::TestError::Failed; }; |
|
| 196 | + | let package = try compile(imports(), 64) catch { |
|
| 197 | + | throw testing::TestError::Failed; |
|
| 198 | + | }; |
|
| 96 | 199 | let mut bytes: [u8; 48] = [255; 48]; |
|
| 97 | 200 | let mut failed: u32 = 0; |
|
| 98 | 201 | try super::instantiate(&package, &[0, 0, 0x8000], &mut bytes[..]) catch err { |
|
| 99 | 202 | try testing::expect(err == super::Error::Instance); set failed += 1; |
|
| 100 | 203 | }; |
| 106 | 209 | }; |
|
| 107 | 210 | try super::instantiate(&package, &[0, 0xffffffffffffffff, 0x8000], &mut bytes[..]) catch err { |
|
| 108 | 211 | try testing::expect(err == super::Error::Range); set failed += 1; |
|
| 109 | 212 | }; |
|
| 110 | 213 | try testing::expect(failed == 4); |
|
| 111 | - | for byte in &bytes[..] { try testing::expect(byte == 255); } |
|
| 214 | + | for byte in &bytes[..] { |
|
| 215 | + | try testing::expect(byte == 255); |
|
| 216 | + | } |
|
| 112 | 217 | } |
|
| 113 | 218 | ||
| 114 | 219 | /// Missing symbols, duplicate imports, kind mismatches, and short output fail explicitly. |
|
| 115 | 220 | @test unsafe fn linkFailures() throws (testing::TestError) { |
|
| 116 | 221 | let mut failed: u32 = 0; |
| 128 | 233 | }; |
|
| 129 | 234 | try compile(names, 31) catch err { |
|
| 130 | 235 | try testing::expect(err == super::Error::Capacity); set failed += 1; |
|
| 131 | 236 | }; |
|
| 132 | 237 | try testing::expect(failed == 4); |
|
| 133 | - | let package = try compile(names, 64) catch { throw testing::TestError::Failed; }; |
|
| 238 | + | let package = try compile(names, 64) catch { |
|
| 239 | + | throw testing::TestError::Failed; |
|
| 240 | + | }; |
|
| 134 | 241 | try testing::expect(package.exports.len == 1); |
|
| 135 | 242 | } |
lib/std/collections/dict.rad
+2 -2
| 34 | 34 | export fn insert(m: &mut Dict, key: *[u8], value: i32) { |
|
| 35 | 35 | let mask = m.entries.len - 1; |
|
| 36 | 36 | let mut idx = hash(key) & mask; |
|
| 37 | 37 | ||
| 38 | 38 | loop { |
|
| 39 | - | let entry = &m.entries[idx]; |
|
| 39 | + | let entry = m.entries[idx]; |
|
| 40 | 40 | if entry.key.len == 0 { |
|
| 41 | 41 | assert m.count < m.entries.len / 2, "dict::insert: table full"; |
|
| 42 | 42 | set m.entries[idx] = Entry { key, value }; |
|
| 43 | 43 | set m.count += 1; |
|
| 44 | 44 | return; |
| 55 | 55 | export fn get(m: &Dict, key: *[u8]) -> ?i32 { |
|
| 56 | 56 | let mask = m.entries.len - 1; |
|
| 57 | 57 | let mut idx = hash(key) & mask; |
|
| 58 | 58 | ||
| 59 | 59 | loop { |
|
| 60 | - | let entry = &m.entries[idx]; |
|
| 60 | + | let entry = m.entries[idx]; |
|
| 61 | 61 | if entry.key.len == 0 { |
|
| 62 | 62 | return nil; |
|
| 63 | 63 | } |
|
| 64 | 64 | if mem::eq(entry.key, key) { |
|
| 65 | 65 | return entry.value; |
lib/std/lang/alloc.rad
+54 -12
| 4 | 4 | //! byte buffer. Memory is never freed individually - the entire arena is |
|
| 5 | 5 | //! reset at once. This is ideal for compiler passes where all allocations |
|
| 6 | 6 | //! have the same lifetime. |
|
| 7 | 7 | @test mod tests; |
|
| 8 | 8 | ||
| 9 | - | use std::mem; |
|
| 10 | - | ||
| 11 | 9 | /// Error thrown by allocator. |
|
| 12 | 10 | export union AllocError: Copy { |
|
| 13 | 11 | /// Allocator is out of memory. |
|
| 14 | 12 | OutOfMemory, |
|
| 15 | 13 | } |
| 33 | 31 | /// Allocate `size` bytes with the given alignment. |
|
| 34 | 32 | /// |
|
| 35 | 33 | /// Returns an opaque pointer to the allocated memory. Throws `AllocError` if |
|
| 36 | 34 | /// the arena is exhausted. The caller is responsible for casting to the |
|
| 37 | 35 | /// appropriate type and initializing the memory. |
|
| 38 | - | export fn alloc(arena: &mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
|
| 36 | + | /// Size must be positive. Alignment must be a positive power of two. |
|
| 37 | + | /// Failure leaves the allocation offset unchanged. |
|
| 38 | + | /// The caller must keep the storage live and must not reclaim live allocations. |
|
| 39 | + | export unsafe fn alloc(arena: *unsafe mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
|
| 39 | 40 | assert alignment > 0; |
|
| 40 | 41 | assert size > 0; |
|
| 41 | 42 | ||
| 42 | - | let aligned64 = (arena.offset as u64 + alignment as u64 - 1) & ~(alignment as u64 - 1); |
|
| 43 | - | if aligned64 > arena.data.len as u64 or size as u64 > arena.data.len as u64 - aligned64 { |
|
| 43 | + | assert (alignment & (alignment - 1)) == 0; |
|
| 44 | + | ||
| 45 | + | let capacity = arena.data.len; |
|
| 46 | + | if arena.offset >= capacity { |
|
| 47 | + | throw AllocError::OutOfMemory; |
|
| 48 | + | } |
|
| 49 | + | let address = (&arena.data[0]) as u64; |
|
| 50 | + | let mask = (alignment - 1) as u64; |
|
| 51 | + | let remainder = ((address & mask) + (arena.offset as u64 & mask)) & mask; |
|
| 52 | + | let padding = ((alignment as u64 - remainder) & mask) as u32; |
|
| 53 | + | let available = capacity - arena.offset; |
|
| 54 | + | if padding > available or size > available - padding { |
|
| 44 | 55 | throw AllocError::OutOfMemory; |
|
| 45 | 56 | } |
|
| 46 | - | let aligned = aligned64 as u32; |
|
| 57 | + | let aligned = arena.offset + padding; |
|
| 47 | 58 | let newOffset = aligned + size; |
|
| 48 | 59 | ||
| 49 | 60 | let base: *mut u8 = &mut arena.data[aligned]; |
|
| 50 | 61 | set arena.offset = newOffset; |
|
| 51 | 62 |
| 79 | 90 | export fn remaining(arena: &Arena) -> u32 { |
|
| 80 | 91 | return arena.data.len as u32 - arena.offset; |
|
| 81 | 92 | } |
|
| 82 | 93 | ||
| 83 | 94 | /// Returns the remaining buffer as a mutable slice. |
|
| 84 | - | export fn remainingBuf(arena: &mut Arena) -> *mut [u8] { |
|
| 95 | + | /// The caller must keep the storage live and must commit each written prefix |
|
| 96 | + | /// before another allocation can use that prefix. |
|
| 97 | + | export unsafe fn remainingBuf(arena: *unsafe mut Arena) -> *mut [u8] { |
|
| 85 | 98 | return &mut arena.data[arena.offset..]; |
|
| 86 | 99 | } |
|
| 87 | 100 | ||
| 88 | 101 | /// Commits `size` bytes of allocation, advancing the offset. |
|
| 89 | 102 | /// Use after writing to the buffer returned by [`remainingBuf`]. |
| 98 | 111 | /// Throws `AllocError` if the arena is exhausted. |
|
| 99 | 112 | export unsafe fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
|
| 100 | 113 | if count == 0 { |
|
| 101 | 114 | return &mut []; |
|
| 102 | 115 | } |
|
| 103 | - | if size > 0xffffffff / count { throw AllocError::OutOfMemory; } |
|
| 104 | - | let ptr = try alloc(arena, size * count, alignment); |
|
| 116 | + | let bytes = try allocationSize(size, count); |
|
| 117 | + | let ptr = try alloc(&mut *arena, bytes, alignment); |
|
| 105 | 118 | ||
| 106 | 119 | return @sliceOf(ptr, count); |
|
| 107 | 120 | } |
|
| 108 | 121 | ||
| 109 | 122 | /// Generic allocator interface. |
| 130 | 143 | } |
|
| 131 | 144 | ||
| 132 | 145 | /// Arena allocation function conforming to the `Allocator` interface. |
|
| 133 | 146 | unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque { |
|
| 134 | 147 | let arena = ctx as *unsafe mut Arena; |
|
| 135 | - | return try! alloc(arena, size, alignment); |
|
| 148 | + | return try! alloc(&mut *arena, size, alignment); |
|
| 136 | 149 | } |
|
| 137 | 150 | ||
| 138 | 151 | /// Allocate raw storage that remains valid until the arena is reset. |
|
| 139 | 152 | export unsafe fn allocRaw(arena: &mut Arena, size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { |
|
| 140 | - | let owner = try alloc(arena, size, alignment); |
|
| 153 | + | let owner = try alloc(&mut *arena, size, alignment); |
|
| 141 | 154 | let byte = owner as *mut u8; |
|
| 142 | 155 | let raw: *unsafe mut u8 = &mut *byte; |
|
| 143 | 156 | return raw as *unsafe mut opaque; |
|
| 144 | 157 | } |
|
| 145 | 158 | ||
| 146 | 159 | /// Allocate a raw slice that remains valid until the arena is reset. |
|
| 147 | 160 | export unsafe fn allocRawSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { |
|
| 148 | 161 | if count == 0 { |
|
| 149 | 162 | return &mut []; |
|
| 150 | 163 | } |
|
| 151 | - | let raw = try allocRaw(arena, size * count, alignment); |
|
| 164 | + | let bytes = try allocationSize(size, count); |
|
| 165 | + | let raw = try allocRaw(arena, bytes, alignment); |
|
| 152 | 166 | return @sliceOf(raw, count); |
|
| 153 | 167 | } |
|
| 168 | + | ||
| 169 | + | /// Raw storage provider for allocation sessions. |
|
| 170 | + | export trait Alloc { |
|
| 171 | + | /// Reserve one uninitialized object. |
|
| 172 | + | unsafe fn (&mut Alloc) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError); |
|
| 173 | + | /// Reserve an uninitialized slice. |
|
| 174 | + | unsafe fn (&mut Alloc) reserveSlice(itemSize: u32, itemAlignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError); |
|
| 175 | + | } |
|
| 176 | + | ||
| 177 | + | instance Alloc for Arena { |
|
| 178 | + | /// Reserve one uninitialized object in the arena. |
|
| 179 | + | unsafe fn (arena: &mut Arena) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { |
|
| 180 | + | return try allocRaw(arena, size, alignment); |
|
| 181 | + | } |
|
| 182 | + | ||
| 183 | + | /// Reserve an uninitialized slice in the arena. |
|
| 184 | + | unsafe fn (arena: &mut Arena) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { |
|
| 185 | + | return try allocRawSlice(arena, size, alignment, count); |
|
| 186 | + | } |
|
| 187 | + | } |
|
| 188 | + | ||
| 189 | + | /// Compute a slice allocation size without unsigned multiplication overflow. |
|
| 190 | + | fn allocationSize(size: u32, count: u32) -> u32 throws (AllocError) { |
|
| 191 | + | if size <> 0 and count > 4294967295 / size { |
|
| 192 | + | throw AllocError::OutOfMemory; |
|
| 193 | + | } |
|
| 194 | + | return size * count; |
|
| 195 | + | } |
lib/std/lang/alloc/tests.rad
+83 -14
| 1 | 1 | //! Tests for the bump allocator. |
|
| 2 | 2 | ||
| 3 | 3 | use std::testing; |
|
| 4 | 4 | ||
| 5 | 5 | /// Test basic allocation. |
|
| 6 | - | @test fn testAllocBasic() throws (testing::TestError) { |
|
| 6 | + | @test unsafe fn testAllocBasic() throws (testing::TestError) { |
|
| 7 | 7 | static STORAGE: [u8; 64] = [0; 64]; |
|
| 8 | 8 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 9 | 9 | ||
| 10 | 10 | let ptr = try! super::alloc(&mut arena, 4, 4); |
|
| 11 | 11 | try testing::expect(super::used(&arena) == 4); |
|
| 12 | 12 | try testing::expect(super::remaining(&arena) == 60); |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Test that allocations are properly aligned. |
|
| 16 | - | @test fn testAllocAlignment() throws (testing::TestError) { |
|
| 16 | + | @test unsafe fn testAllocAlignment() throws (testing::TestError) { |
|
| 17 | 17 | static STORAGE: [u8; 64] = [0; 64]; |
|
| 18 | 18 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 19 | 19 | ||
| 20 | 20 | // Allocate 1 byte with 1-byte alignment. |
|
| 21 | 21 | let p1 = try! super::alloc(&mut arena, 1, 1); |
| 25 | 25 | let p2 = try! super::alloc(&mut arena, 4, 4); |
|
| 26 | 26 | try testing::expect(super::used(&arena) == 8); // 1 + 3 padding + 4 |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// Test multiple allocations. |
|
| 30 | - | @test fn testAllocMultiple() throws (testing::TestError) { |
|
| 30 | + | @test unsafe fn testAllocMultiple() throws (testing::TestError) { |
|
| 31 | 31 | static STORAGE: [u8; 128] = [0; 128]; |
|
| 32 | 32 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 33 | 33 | ||
| 34 | 34 | let p1 = try! super::alloc(&mut arena, 8, 4); |
|
| 35 | 35 | let p2 = try! super::alloc(&mut arena, 16, 4); |
| 37 | 37 | ||
| 38 | 38 | try testing::expect(super::used(&arena) == 28); // 8 + 16 + 4 |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Test that arena throws when exhausted. |
|
| 42 | - | @test fn testAllocExhausted() throws (testing::TestError) { |
|
| 42 | + | @test unsafe fn testAllocExhausted() throws (testing::TestError) { |
|
| 43 | 43 | static STORAGE: [u8; 16] = [0; 16]; |
|
| 44 | 44 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 45 | 45 | ||
| 46 | 46 | // This should succeed. |
|
| 47 | 47 | let p1 = try! super::alloc(&mut arena, 8, 4); |
| 56 | 56 | }; |
|
| 57 | 57 | try testing::expect(failed); |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | /// Test that reset allows reuse of memory. |
|
| 61 | - | @test fn testAllocReset() throws (testing::TestError) { |
|
| 61 | + | @test unsafe fn testAllocReset() throws (testing::TestError) { |
|
| 62 | 62 | static STORAGE: [u8; 32] = [0; 32]; |
|
| 63 | 63 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 64 | 64 | ||
| 65 | 65 | let p1 = try! super::alloc(&mut arena, 16, 4); |
|
| 66 | 66 | try testing::expect(super::used(&arena) == 16); |
| 72 | 72 | // Should be able to allocate again. |
|
| 73 | 73 | let p2 = try! super::alloc(&mut arena, 32, 4); |
|
| 74 | 74 | } |
|
| 75 | 75 | ||
| 76 | 76 | /// Test alignment when offset is already aligned. |
|
| 77 | - | @test fn testAllocAlreadyAligned() throws (testing::TestError) { |
|
| 77 | + | @test unsafe fn testAllocAlreadyAligned() throws (testing::TestError) { |
|
| 78 | 78 | static STORAGE: [u8; 64] = [0; 64]; |
|
| 79 | 79 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 80 | 80 | ||
| 81 | 81 | // Allocate 4 bytes - offset becomes 4, already aligned for next 4-byte alloc. |
|
| 82 | 82 | let p1 = try! super::alloc(&mut arena, 4, 4); |
| 86 | 86 | let p2 = try! super::alloc(&mut arena, 4, 4); |
|
| 87 | 87 | try testing::expect(super::used(&arena) == 8); |
|
| 88 | 88 | } |
|
| 89 | 89 | ||
| 90 | 90 | /// Test allocation that would overflow with alignment padding. |
|
| 91 | - | @test fn testAllocOverflowWithPadding() throws (testing::TestError) { |
|
| 91 | + | @test unsafe fn testAllocOverflowWithPadding() throws (testing::TestError) { |
|
| 92 | 92 | static STORAGE: [u8; 16] = [0; 16]; |
|
| 93 | 93 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 94 | 94 | ||
| 95 | 95 | // Allocate 1 byte, offset is now 1. |
|
| 96 | 96 | let p1 = try! super::alloc(&mut arena, 1, 1); |
| 110 | 110 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 111 | 111 | let a = super::arenaAllocator(&mut arena); |
|
| 112 | 112 | ||
| 113 | 113 | // Allocate through the Allocator indirection. |
|
| 114 | 114 | let p1 = a.func(a.ctx, 16, 4); |
|
| 115 | - | try testing::expect(super::used(&arena) == 16); |
|
| 115 | + | try testing::expect((p1 as u64 & 3) == 0); |
|
| 116 | 116 | ||
| 117 | 117 | let p2 = a.func(a.ctx, 8, 8); |
|
| 118 | - | try testing::expect(super::used(&arena) == 24); |
|
| 118 | + | try testing::expect((p2 as u64 & 7) == 0); |
|
| 119 | + | try testing::expect(super::used(&arena) as u64 == p2 as u64 - &STORAGE[0] as u64 + 8); |
|
| 119 | 120 | ||
| 120 | 121 | // Verify the pointers are distinct. |
|
| 121 | 122 | try testing::expect(p1 as u64 <> p2 as u64); |
|
| 122 | 123 | } |
|
| 123 | 124 |
| 125 | 126 | @test unsafe fn testAllocOverflow() throws (testing::TestError) { |
|
| 126 | 127 | static bytes: [u8; 64] = [0; 64]; |
|
| 127 | 128 | let mut arena = super::new(&mut bytes[..]); |
|
| 128 | 129 | set arena.offset = 8; |
|
| 129 | 130 | let mut failed: u32 = 0; |
|
| 130 | - | try super::allocSlice(&mut arena, 8, 8, 0x20000000) catch { set failed += 1; }; |
|
| 131 | - | try super::alloc(&mut arena, 0xffffffff, 8) catch { set failed += 1; }; |
|
| 131 | + | try super::allocSlice(&mut arena, 8, 8, 0x20000000) catch { |
|
| 132 | + | set failed += 1; |
|
| 133 | + | }; |
|
| 134 | + | try super::alloc(&mut arena, 0xffffffff, 8) catch { |
|
| 135 | + | set failed += 1; |
|
| 136 | + | }; |
|
| 132 | 137 | try testing::expect(failed == 2 and arena.offset == 8); |
|
| 133 | 138 | set arena.offset = 0xfffffff8; |
|
| 134 | - | try super::alloc(&mut arena, 16, 16) catch { set failed += 1; }; |
|
| 139 | + | try super::alloc(&mut arena, 16, 16) catch { |
|
| 140 | + | set failed += 1; |
|
| 141 | + | }; |
|
| 135 | 142 | try testing::expect(failed == 3 and arena.offset == 0xfffffff8); |
|
| 136 | 143 | set arena.offset = 8; |
|
| 137 | - | let storage = try super::alloc(&mut arena, 8, 8) catch { throw testing::TestError::Failed; }; |
|
| 138 | - | try testing::expect(arena.offset == 16); |
|
| 144 | + | let storage = try super::alloc(&mut arena, 8, 8) catch { |
|
| 145 | + | throw testing::TestError::Failed; |
|
| 146 | + | }; |
|
| 147 | + | try testing::expect((storage as u64 & 7) == 0); |
|
| 148 | + | try testing::expect(arena.offset as u64 == storage as u64 - &bytes[0] as u64 + 8); |
|
| 149 | + | } |
|
| 150 | + | ||
| 151 | + | /// Allocation alignment uses the backing address and preserves distinct objects. |
|
| 152 | + | @test unsafe fn testAllocUnalignedBacking() throws (testing::TestError) { |
|
| 153 | + | static STORAGE: [u8; 96] = [0; 96]; |
|
| 154 | + | for start in 0..16 { |
|
| 155 | + | let mut arena = super::new(&mut STORAGE[start..]); |
|
| 156 | + | let first = try! super::allocRaw(&mut arena, 8, 16) as *unsafe mut u64; |
|
| 157 | + | set *first = 123; |
|
| 158 | + | let second = try! super::allocRaw(&mut arena, 8, 16) as *unsafe mut u64; |
|
| 159 | + | set *second = 456; |
|
| 160 | + | try testing::expect((first as u64 & 15) == 0); |
|
| 161 | + | try testing::expect((second as u64 & 15) == 0); |
|
| 162 | + | try testing::expect(second as u64 >= first as u64 + 8); |
|
| 163 | + | try testing::expect(*first == 123); |
|
| 164 | + | try testing::expect(*second == 456); |
|
| 165 | + | } |
|
| 166 | + | } |
|
| 167 | + | ||
| 168 | + | /// Capacity failure and arithmetic overflow leave the arena and live data intact. |
|
| 169 | + | @test unsafe fn testAllocFailureAtomicity() throws (testing::TestError) { |
|
| 170 | + | static STORAGE: [u8; 64] = [0; 64]; |
|
| 171 | + | let mut arena = super::new(&mut STORAGE[..]); |
|
| 172 | + | let first = try! super::allocRaw(&mut arena, 1, 1) as *unsafe mut u8; |
|
| 173 | + | set *first = 42; |
|
| 174 | + | for size in [64 as u32, 4294967295 as u32] { |
|
| 175 | + | let saved = super::used(&arena); |
|
| 176 | + | let mut failed = false; |
|
| 177 | + | try super::alloc(&mut arena, size, 1) catch { |
|
| 178 | + | set failed = true; |
|
| 179 | + | }; |
|
| 180 | + | try testing::expect(failed); |
|
| 181 | + | try testing::expect(super::used(&arena) == saved); |
|
| 182 | + | try testing::expect(*first == 42); |
|
| 183 | + | } |
|
| 184 | + | let next = try! super::allocRaw(&mut arena, 1, 1) as *unsafe mut u8; |
|
| 185 | + | try testing::expect(next as u64 == first as u64 + 1); |
|
| 186 | + | } |
|
| 187 | + | ||
| 188 | + | /// Slice count multiplication fails before either allocation API changes state. |
|
| 189 | + | @test unsafe fn testAllocSliceOverflowAtomicity() throws (testing::TestError) { |
|
| 190 | + | static STORAGE: [u8; 64] = [0; 64]; |
|
| 191 | + | let mut arena = super::new(&mut STORAGE[..]); |
|
| 192 | + | let first = try! super::allocRaw(&mut arena, 1, 1) as *unsafe mut u8; |
|
| 193 | + | set *first = 42; |
|
| 194 | + | let saved = super::used(&arena); |
|
| 195 | + | let mut failed = false; |
|
| 196 | + | try super::allocSlice(&mut arena, 4, 4, 1073741825) catch { |
|
| 197 | + | set failed = true; |
|
| 198 | + | }; |
|
| 199 | + | try testing::expect(failed); |
|
| 200 | + | try testing::expect(super::used(&arena) == saved); |
|
| 201 | + | set failed = false; |
|
| 202 | + | try super::allocRawSlice(&mut arena, 4, 4, 1073741825) catch { |
|
| 203 | + | set failed = true; |
|
| 204 | + | }; |
|
| 205 | + | try testing::expect(failed); |
|
| 206 | + | try testing::expect(super::used(&arena) == saved); |
|
| 207 | + | try testing::expect(*first == 42); |
|
| 139 | 208 | } |
lib/std/lang/ast.rad
+91 -11
| 2 | 2 | export mod printer; |
|
| 3 | 3 | ||
| 4 | 4 | use std::io; |
|
| 5 | 5 | use std::fmt; |
|
| 6 | 6 | use std::lang::alloc; |
|
| 7 | - | use std::lang::types; |
|
| 7 | + | ||
| 8 | + | /// Ownership and safety qualifier written in a pointer type. |
|
| 9 | + | /// Named reference regions are separate `TypeSig::RegionRef` nodes. |
|
| 10 | + | export union PointerClass: Copy { |
|
| 11 | + | /// Owned pointer introduced by `*`. |
|
| 12 | + | Owned, |
|
| 13 | + | /// Borrowed reference introduced by `&`. |
|
| 14 | + | Ref, |
|
| 15 | + | /// Raw pointer introduced by `*unsafe`. |
|
| 16 | + | Unsafe, |
|
| 17 | + | } |
|
| 8 | 18 | ||
| 9 | 19 | /// Maximum number of trait methods. |
|
| 10 | 20 | export constant MAX_TRAIT_METHODS: u32 = 8; |
|
| 11 | 21 | ||
| 12 | 22 | /// Arena for all parser allocations. |
| 180 | 190 | length: *Node, |
|
| 181 | 191 | }, |
|
| 182 | 192 | /// Slice type, eg. `*[i32]`, `&[i32]`, or `*unsafe [i32]`. |
|
| 183 | 193 | Slice { |
|
| 184 | 194 | /// Ownership and safety class. |
|
| 185 | - | class: types::PointerClass, |
|
| 195 | + | class: PointerClass, |
|
| 186 | 196 | /// Slice element type. |
|
| 187 | 197 | itemType: *Node, |
|
| 188 | 198 | /// Whether the slice is mutable. |
|
| 189 | 199 | mutable: bool, |
|
| 190 | 200 | }, |
|
| 191 | 201 | /// Pointer type, eg. `*i32`, `&i32`, or `*unsafe i32`. |
|
| 192 | 202 | Pointer { |
|
| 193 | 203 | /// Ownership and safety class. |
|
| 194 | - | class: types::PointerClass, |
|
| 204 | + | class: PointerClass, |
|
| 195 | 205 | /// Pointer target type. |
|
| 196 | 206 | valueType: *Node, |
|
| 197 | 207 | /// Whether the pointer is mutable. |
|
| 198 | 208 | mutable: bool, |
|
| 199 | 209 | }, |
|
| 200 | 210 | /// Optional, eg. `?i32`. |
|
| 201 | 211 | Optional { |
|
| 202 | 212 | /// Underlying type. |
|
| 203 | 213 | valueType: *Node, |
|
| 204 | 214 | }, |
|
| 215 | + | /// Shared cell pointer with value access to its payload. |
|
| 216 | + | Cell { |
|
| 217 | + | /// Storage lifetime and ownership class. |
|
| 218 | + | class: PointerClass, |
|
| 219 | + | /// Copy payload type. |
|
| 220 | + | payload: *Node, |
|
| 221 | + | }, |
|
| 205 | 222 | /// Nominal type, points to identifier node. |
|
| 206 | 223 | Nominal(*Node), |
|
| 224 | + | /// Reference qualified by a lexical region. |
|
| 225 | + | RegionRef { |
|
| 226 | + | /// Region name node. |
|
| 227 | + | region: *Node, |
|
| 228 | + | /// Reference type node. |
|
| 229 | + | type: *Node, |
|
| 230 | + | }, |
|
| 231 | + | /// Nominal type with invariant region arguments. |
|
| 232 | + | Applied { |
|
| 233 | + | /// Nominal type path. |
|
| 234 | + | name: *Node, |
|
| 235 | + | /// Region arguments in declaration order. |
|
| 236 | + | regions: *[*Node], |
|
| 237 | + | }, |
|
| 207 | 238 | /// Inline record type for union variant payloads. |
|
| 208 | 239 | Record { |
|
| 209 | 240 | /// Field declaration nodes. |
|
| 210 | 241 | fields: *[*Node], |
|
| 211 | 242 | /// Whether this record has labeled fields. |
| 220 | 251 | }, |
|
| 221 | 252 | /// Trait object type, eg. `*opaque Allocator`, `&opaque Allocator`, or |
|
| 222 | 253 | /// `*unsafe opaque Allocator`. |
|
| 223 | 254 | TraitObject { |
|
| 224 | 255 | /// Ownership and safety class. |
|
| 225 | - | class: types::PointerClass, |
|
| 256 | + | class: PointerClass, |
|
| 226 | 257 | /// Trait name identifier. |
|
| 227 | 258 | traitName: *Node, |
|
| 228 | 259 | /// Whether the pointer is mutable. |
|
| 229 | 260 | mutable: bool, |
|
| 230 | 261 | }, |
| 487 | 518 | name: *Node, |
|
| 488 | 519 | /// Field declaration nodes. |
|
| 489 | 520 | fields: *[*Node], |
|
| 490 | 521 | /// Optional attribute list applied to the record. |
|
| 491 | 522 | attrs: ?Attributes, |
|
| 492 | - | /// Trait derivations attached to the record. |
|
| 523 | + | /// Region parameters in declaration order. |
|
| 524 | + | regions: *[*Node], |
|
| 525 | + | /// Ownership trait derivations attached to the record. |
|
| 493 | 526 | derives: *[*Node], |
|
| 494 | 527 | /// Whether this record has labeled fields. |
|
| 495 | 528 | labeled: bool, |
|
| 496 | 529 | } |
|
| 497 | 530 |
| 501 | 534 | name: *Node, |
|
| 502 | 535 | /// Variant nodes making up the union. |
|
| 503 | 536 | variants: *[*Node], |
|
| 504 | 537 | /// Optional attribute list applied to the union. |
|
| 505 | 538 | attrs: ?Attributes, |
|
| 506 | - | /// Trait derivations attached to the union. |
|
| 539 | + | /// Region parameters in declaration order. |
|
| 540 | + | regions: *[*Node], |
|
| 541 | + | /// Ownership trait derivations attached to the union. |
|
| 507 | 542 | derives: *[*Node], |
|
| 508 | 543 | } |
|
| 509 | 544 | ||
| 510 | 545 | /// Union variant declaration. |
|
| 511 | 546 | export record UnionDeclVariant: Copy { |
| 521 | 556 | ||
| 522 | 557 | /// Function declaration. |
|
| 523 | 558 | export record FnDecl: Copy { |
|
| 524 | 559 | /// Identifier naming the function. |
|
| 525 | 560 | name: *Node, |
|
| 561 | + | /// Region parameters in declaration order. |
|
| 562 | + | regions: *[*Node], |
|
| 526 | 563 | /// Function type signature. |
|
| 527 | 564 | sig: FnSig, |
|
| 528 | 565 | /// Optional function body (`nil` for extern functions). |
|
| 529 | 566 | body: ?*Node, |
|
| 530 | 567 | /// Optional attribute list applied to the function. |
|
| 531 | 568 | attrs: ?Attributes, |
|
| 532 | 569 | } |
|
| 533 | 570 | ||
| 571 | + | /// Method declaration modifiers. |
|
| 572 | + | export record MethodModifiers: Copy { |
|
| 573 | + | /// Region parameters in declaration order. |
|
| 574 | + | regions: *[*Node], |
|
| 575 | + | /// Optional attribute list. |
|
| 576 | + | attrs: ?Attributes, |
|
| 577 | + | } |
|
| 578 | + | ||
| 534 | 579 | /// Array repeat literal metadata. |
|
| 535 | 580 | export record ArrayRepeatLit: Copy { |
|
| 536 | 581 | /// Expression providing the repeated value. |
|
| 537 | 582 | item: *Node, |
|
| 538 | 583 | /// Expression providing the repetition count. |
| 613 | 658 | Char(u8), |
|
| 614 | 659 | /// String literal like `"Hello World!"`. |
|
| 615 | 660 | String(*[u8]), |
|
| 616 | 661 | /// Identifier expression. |
|
| 617 | 662 | Ident(*[u8]), |
|
| 663 | + | /// Lexical region name, including its apostrophe prefix. |
|
| 664 | + | Region { |
|
| 665 | + | /// Source name. |
|
| 666 | + | name: *[u8], |
|
| 667 | + | /// Declared parent for a region parameter. |
|
| 668 | + | parent: ?*Node, |
|
| 669 | + | }, |
|
| 670 | + | /// Function or nominal constructor applied to explicit region arguments. |
|
| 671 | + | RegionApply { |
|
| 672 | + | /// Function or constructor expression. |
|
| 673 | + | value: *Node, |
|
| 674 | + | /// Region argument nodes in declaration order. |
|
| 675 | + | regions: *[*Node], |
|
| 676 | + | }, |
|
| 677 | + | /// Named source place for a lexical region binding. |
|
| 678 | + | RegionBinding(Arg), |
|
| 679 | + | /// Block with explicit storage bindings for a concrete region. |
|
| 680 | + | RegionBlock { |
|
| 681 | + | /// Region name introduced by this block. |
|
| 682 | + | region: *Node, |
|
| 683 | + | /// Binding nodes with address expressions as initializers. |
|
| 684 | + | bindings: *[*Node], |
|
| 685 | + | /// Statements within the region. |
|
| 686 | + | body: *Node, |
|
| 687 | + | /// Whether a `use` block supplies an allocation interface. |
|
| 688 | + | isSession: bool, |
|
| 689 | + | }, |
|
| 618 | 690 | /// Numeric literal such as `42` or `0xFF`. |
|
| 619 | 691 | Number(fmt::IntLiteral), |
|
| 620 | 692 | /// Range expression such as `0..10` or `..`. |
|
| 621 | 693 | Range(Range), |
|
| 622 | 694 | /// Array literal expression. |
| 769 | 841 | }, |
|
| 770 | 842 | /// Method signature inside a trait declaration. |
|
| 771 | 843 | TraitMethodSig { |
|
| 772 | 844 | /// Method name identifier. |
|
| 773 | 845 | name: *Node, |
|
| 846 | + | /// Region parameters and attributes. |
|
| 847 | + | modifiers: *MethodModifiers, |
|
| 774 | 848 | /// Receiver type node (eg. `*mut Allocator`). |
|
| 775 | 849 | receiver: *Node, |
|
| 776 | 850 | /// Function signature. |
|
| 777 | 851 | sig: FnSig, |
|
| 778 | - | /// Optional declaration modifiers. |
|
| 779 | - | attrs: ?Attributes, |
|
| 780 | 852 | }, |
|
| 781 | 853 | /// Instance block. |
|
| 782 | 854 | InstanceDecl { |
|
| 783 | 855 | /// Trait name identifier. |
|
| 784 | 856 | traitName: *Node, |
|
| 785 | 857 | /// Target type identifier. |
|
| 786 | 858 | targetType: *Node, |
|
| 859 | + | /// Region parameters in declaration order. |
|
| 860 | + | regions: *[*Node], |
|
| 787 | 861 | /// Method definition nodes ([`MethodDecl`]). |
|
| 788 | 862 | methods: *[*Node], |
|
| 789 | 863 | }, |
|
| 790 | 864 | /// Method definition with a receiver. |
|
| 791 | 865 | /// Used both inside `instance` blocks and as standalone methods. |
|
| 792 | 866 | MethodDecl { |
|
| 793 | 867 | /// Method name identifier. |
|
| 794 | 868 | name: *Node, |
|
| 869 | + | /// Region parameters and attributes. |
|
| 870 | + | modifiers: *MethodModifiers, |
|
| 795 | 871 | /// Receiver binding name ([`Ident`] node). |
|
| 796 | 872 | receiverName: *Node, |
|
| 797 | 873 | /// Receiver type node (eg. `*mut Arena`). |
|
| 798 | 874 | receiverType: *Node, |
|
| 799 | 875 | /// Function signature. |
|
| 800 | 876 | sig: FnSig, |
|
| 801 | 877 | /// Method body. |
|
| 802 | 878 | body: *Node, |
|
| 803 | - | /// Optional attribute list. |
|
| 804 | - | attrs: ?Attributes, |
|
| 805 | 879 | }, |
|
| 806 | 880 | } |
|
| 807 | 881 | ||
| 808 | 882 | /// Full AST node with shared metadata and variant-specific payload. |
|
| 809 | 883 | export record Node: Copy { |
| 864 | 938 | let params: *[*Node] = &[]; |
|
| 865 | 939 | let throwList: *[*Node] = &[]; |
|
| 866 | 940 | let fnSig = FnSig { params, returnType: nil, throwList }; |
|
| 867 | 941 | let fnBody: *Node = synthNode(arena, NodeValue::Block(Block { statements: bodyStmts, isUnsafe: false })); |
|
| 868 | 942 | let fnDecl = synthNode(arena, NodeValue::FnDecl(FnDecl { |
|
| 869 | - | name: fnName, sig: fnSig, body: fnBody, attrs: nil, |
|
| 943 | + | name: fnName, regions: &[], sig: fnSig, body: fnBody, attrs: nil, |
|
| 870 | 944 | })); |
|
| 871 | 945 | let mut rootStmts: *mut [*Node] = &mut []; |
|
| 872 | 946 | rootStmts.append(fnDecl, a); |
|
| 873 | 947 | let modBody = synthNode(arena, NodeValue::Block(Block { statements: rootStmts, isUnsafe: false })); |
|
| 874 | 948 | ||
| 875 | 949 | return SynthFnMod { modBody, fnBody }; |
|
| 876 | 950 | } |
|
| 951 | + | ||
| 952 | + | /// Describe the immutable binding introduced by a lexical region header. |
|
| 953 | + | export fn borrowBinding(binding: Arg) -> Let { |
|
| 954 | + | let ident = binding.label else panic "borrowBinding: missing name"; |
|
| 955 | + | return Let { ident, type: nil, value: binding.value, alignment: nil, mutable: false }; |
|
| 956 | + | } |
lib/std/lang/ast/printer.rad
+66 -10
| 1 | 1 | //! AST pretty printer using S-expression syntax. |
|
| 2 | 2 | ||
| 3 | 3 | use std::io; |
|
| 4 | 4 | use std::lang::sexpr; |
|
| 5 | 5 | use std::lang::alloc; |
|
| 6 | - | use std::lang::types; |
|
| 7 | 6 | ||
| 8 | 7 | /// Return the symbol for a binary operator. |
|
| 9 | 8 | fn binOpName(op: super::BinaryOp) -> *[u8] { |
|
| 10 | 9 | match op { |
|
| 11 | 10 | case super::BinaryOp::Add => return "+", |
| 69 | 68 | } |
|
| 70 | 69 | } |
|
| 71 | 70 | ||
| 72 | 71 | /// Return the S-expression head for a pointer class. |
|
| 73 | 72 | fn pointerClassHead( |
|
| 74 | - | class: types::PointerClass, |
|
| 73 | + | class: super::PointerClass, |
|
| 75 | 74 | ownedHead: *[u8], |
|
| 76 | 75 | refHead: *[u8], |
|
| 77 | 76 | unsafeHead: *[u8], |
|
| 78 | 77 | ) -> *[u8] { |
|
| 79 | 78 | match class { |
|
| 80 | - | case types::PointerClass::Owned => return ownedHead, |
|
| 81 | - | case types::PointerClass::Ref => return refHead, |
|
| 82 | - | case types::PointerClass::Unsafe => return unsafeHead, |
|
| 79 | + | case super::PointerClass::Owned => return ownedHead, |
|
| 80 | + | case super::PointerClass::Ref => return refHead, |
|
| 81 | + | case super::PointerClass::Unsafe => return unsafeHead, |
|
| 83 | 82 | } |
|
| 84 | 83 | } |
|
| 85 | 84 | ||
| 86 | 85 | /// Convert a type signature to an S-expression. |
|
| 87 | 86 | unsafe fn typeSigToExpr(a: &mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr { |
| 103 | 102 | return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, valueType)]) if mutable |
|
| 104 | 103 | else sexpr::list(a, head, &[toExpr(a, valueType)]); |
|
| 105 | 104 | } |
|
| 106 | 105 | case super::TypeSig::Optional { valueType } => |
|
| 107 | 106 | return sexpr::list(a, "?", &[toExpr(a, valueType)]), |
|
| 107 | + | case super::TypeSig::Cell { class, payload } => |
|
| 108 | + | return sexpr::list(a, pointerClassHead(class, "cell-ptr", "cell-ref", "unsafe-cell-ptr"), &[toExpr(a, payload)]), |
|
| 108 | 109 | case super::TypeSig::Nominal(name) => return toExpr(a, name), |
|
| 110 | + | case super::TypeSig::RegionRef { region, type } => |
|
| 111 | + | return sexpr::list(a, "region-ref", &[toExpr(a, region), toExpr(a, type)]), |
|
| 112 | + | case super::TypeSig::Applied { name, regions } => |
|
| 113 | + | return sexpr::list(a, "apply", &[toExpr(a, name), sexpr::list(a, "regions", nodeListToExprs(a, regions))]), |
|
| 109 | 114 | case super::TypeSig::Record { fields, .. } => |
|
| 110 | 115 | return sexpr::list(a, "record", nodeListToExprs(a, fields)), |
|
| 111 | 116 | case super::TypeSig::Fn { sig, isUnsafe } => { |
|
| 112 | 117 | let mut ret = sexpr::sym("void"); |
|
| 113 | 118 | if let rt = sig.returnType { |
| 448 | 453 | return sexpr::block(a, "match", &[toExpr(a, m.subject)], children); |
|
| 449 | 454 | } |
|
| 450 | 455 | case super::NodeValue::MatchProng(p) => { |
|
| 451 | 456 | return prongToExpr(a, p); |
|
| 452 | 457 | } |
|
| 458 | + | case super::NodeValue::RegionApply { value, regions } => |
|
| 459 | + | return sexpr::list(a, "apply", &[toExpr(a, value), sexpr::list(a, "regions", nodeListToExprs(a, regions))]), |
|
| 460 | + | case super::NodeValue::RegionBinding(b) => |
|
| 461 | + | return sexpr::list(a, "binding", &[toExprOrNull(a, b.label), toExpr(a, b.value)]), |
|
| 462 | + | case super::NodeValue::Region { name, parent } => { |
|
| 463 | + | if let p = parent { |
|
| 464 | + | return sexpr::list(a, "region", &[sexpr::sym(name), toExpr(a, p)]); |
|
| 465 | + | } |
|
| 466 | + | return sexpr::sym(name); |
|
| 467 | + | } |
|
| 468 | + | case super::NodeValue::RegionBlock { region, bindings, body, isSession } => { |
|
| 469 | + | let head = "use-region" if isSession else "let-region"; |
|
| 470 | + | return sexpr::block(a, head, &[toExpr(a, region), sexpr::list(a, "bindings", nodeListToExprs(a, bindings))], &[toExpr(a, body)]); |
|
| 471 | + | } |
|
| 453 | 472 | case super::NodeValue::FnDecl(f) => { |
|
| 454 | 473 | let params = sexpr::list(a, "params", nodeListToExprs(a, f.sig.params)); |
|
| 455 | 474 | let ret = toExprOrNull(a, f.sig.returnType); |
|
| 475 | + | if f.regions.len > 0 { |
|
| 476 | + | let regions = sexpr::list(a, "regions", nodeListToExprs(a, f.regions)); |
|
| 477 | + | if let body = f.body { |
|
| 478 | + | return sexpr::block(a, "fn", &[toExpr(a, f.name), regions, params, ret], &[toExpr(a, body)]); |
|
| 479 | + | } |
|
| 480 | + | return sexpr::list(a, "fn", &[toExpr(a, f.name), regions, params, ret]); |
|
| 481 | + | } |
|
| 456 | 482 | if let body = f.body { |
|
| 457 | 483 | return sexpr::block(a, "fn", &[toExpr(a, f.name), params, ret], &[toExpr(a, body)]); |
|
| 458 | 484 | } |
|
| 459 | 485 | return sexpr::list(a, "fn", &[toExpr(a, f.name), params, ret]); |
|
| 460 | 486 | } |
|
| 461 | 487 | case super::NodeValue::Mod(m) => return sexpr::list(a, "mod", &[toExpr(a, m.name)]), |
|
| 462 | 488 | case super::NodeValue::Use(u_) => return sexpr::list(a, "use", &[toExpr(a, u_.path)]), |
|
| 463 | 489 | case super::NodeValue::RecordDecl(r) => { |
|
| 464 | 490 | let children = fieldListToExprs(a, r.fields); |
|
| 491 | + | if r.regions.len > 0 or r.derives.len > 0 { |
|
| 492 | + | return sexpr::block(a, "record", &[ |
|
| 493 | + | toExpr(a, r.name), |
|
| 494 | + | sexpr::list(a, "regions", nodeListToExprs(a, r.regions)), |
|
| 495 | + | sexpr::list(a, "derives", nodeListToExprs(a, r.derives)), |
|
| 496 | + | ], children); |
|
| 497 | + | } |
|
| 465 | 498 | return sexpr::block(a, "record", &[toExpr(a, r.name)], children); |
|
| 466 | 499 | } |
|
| 467 | 500 | case super::NodeValue::RecordField { field, type, value } => { |
|
| 468 | 501 | return fieldToExpr(a, field, type, value); |
|
| 469 | 502 | } |
|
| 470 | 503 | case super::NodeValue::UnionDecl(u_) => { |
|
| 471 | 504 | let children = variantListToExprs(a, u_.variants); |
|
| 505 | + | if u_.regions.len > 0 or u_.derives.len > 0 { |
|
| 506 | + | return sexpr::block(a, "union", &[ |
|
| 507 | + | toExpr(a, u_.name), |
|
| 508 | + | sexpr::list(a, "regions", nodeListToExprs(a, u_.regions)), |
|
| 509 | + | sexpr::list(a, "derives", nodeListToExprs(a, u_.derives)), |
|
| 510 | + | ], children); |
|
| 511 | + | } |
|
| 472 | 512 | return sexpr::block(a, "union", &[toExpr(a, u_.name)], children); |
|
| 473 | 513 | } |
|
| 474 | 514 | case super::NodeValue::UnionDeclVariant(v) => { |
|
| 475 | 515 | return variantToExpr(a, v.name, v.type); |
|
| 476 | 516 | } |
| 478 | 518 | case super::NodeValue::TraitDecl { name, supertraits, methods, .. } => { |
|
| 479 | 519 | let children = nodeListToExprs(a, methods); |
|
| 480 | 520 | let supers = sexpr::list(a, "supertraits", nodeListToExprs(a, supertraits)); |
|
| 481 | 521 | return sexpr::block(a, "trait", &[toExpr(a, name), supers], children); |
|
| 482 | 522 | } |
|
| 483 | - | case super::NodeValue::TraitMethodSig { name, receiver, sig, attrs } => { |
|
| 523 | + | case super::NodeValue::TraitMethodSig { name, modifiers, receiver, sig } => { |
|
| 524 | + | let regions = modifiers.regions; |
|
| 525 | + | let attrs = modifiers.attrs; |
|
| 484 | 526 | let params = sexpr::list(a, "params", nodeListToExprs(a, sig.params)); |
|
| 485 | 527 | let ret = toExprOrNull(a, sig.returnType); |
|
| 486 | 528 | let attributes = attributesToExpr(a, attrs); |
|
| 487 | 529 | return sexpr::list( |
|
| 488 | 530 | a, |
|
| 489 | 531 | "methodSig", |
|
| 490 | - | &[attributes, toExpr(a, receiver), toExpr(a, name), params, ret], |
|
| 532 | + | &[ |
|
| 533 | + | attributes, |
|
| 534 | + | toExpr(a, receiver), |
|
| 535 | + | toExpr(a, name), |
|
| 536 | + | sexpr::list(a, "regions", nodeListToExprs(a, regions)), |
|
| 537 | + | params, |
|
| 538 | + | ret, |
|
| 539 | + | ], |
|
| 491 | 540 | ); |
|
| 492 | 541 | } |
|
| 493 | - | case super::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 542 | + | case super::NodeValue::InstanceDecl { traitName, targetType, regions, methods } => { |
|
| 494 | 543 | let children = nodeListToExprs(a, methods); |
|
| 495 | - | return sexpr::block(a, "instance", &[toExpr(a, traitName), toExpr(a, targetType)], children); |
|
| 544 | + | return sexpr::block(a, "instance", &[ |
|
| 545 | + | toExpr(a, traitName), |
|
| 546 | + | toExpr(a, targetType), |
|
| 547 | + | sexpr::list(a, "regions", nodeListToExprs(a, regions)), |
|
| 548 | + | ], children); |
|
| 496 | 549 | } |
|
| 497 | 550 | case super::NodeValue::MethodDecl { |
|
| 498 | - | name, receiverName, receiverType, sig, body, attrs, |
|
| 551 | + | name, modifiers, receiverName, receiverType, sig, body, |
|
| 499 | 552 | } => { |
|
| 553 | + | let regions = modifiers.regions; |
|
| 554 | + | let attrs = modifiers.attrs; |
|
| 500 | 555 | let params = sexpr::list(a, "params", nodeListToExprs(a, sig.params)); |
|
| 501 | 556 | let ret = toExprOrNull(a, sig.returnType); |
|
| 502 | 557 | let attributes = attributesToExpr(a, attrs); |
|
| 503 | 558 | return sexpr::block( |
|
| 504 | 559 | a, |
| 506 | 561 | &[ |
|
| 507 | 562 | attributes, |
|
| 508 | 563 | toExpr(a, receiverType), |
|
| 509 | 564 | toExpr(a, receiverName), |
|
| 510 | 565 | toExpr(a, name), |
|
| 566 | + | sexpr::list(a, "regions", nodeListToExprs(a, regions)), |
|
| 511 | 567 | params, |
|
| 512 | 568 | ret, |
|
| 513 | 569 | ], |
|
| 514 | 570 | &[toExpr(a, body)], |
|
| 515 | 571 | ); |
lib/std/lang/gen/bitset.rad
+48 -93
| 14 | 14 | return b; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Calculate the number of 32-bit words needed to store `n` bits. |
|
| 18 | 18 | export fn wordsFor(n: u32) -> u32 { |
|
| 19 | - | return (n + 31) / 32; |
|
| 19 | + | return n / 32 + (1 if n % 32 > 0 else 0); |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | /// A fixed-size bitset backed by an array of 32-bit words. |
|
| 23 | - | export record Bitset: Copy { |
|
| 24 | - | /// Backing words. The storage must outlive the bitset and its iterators. |
|
| 25 | - | bits: *unsafe mut [u32], |
|
| 26 | - | /// Number of bits this bitset can hold. |
|
| 27 | - | len: u32, |
|
| 28 | - | } |
|
| 29 | - | ||
| 30 | - | /// Create a new bitset backed by the given zero-initialized storage. |
|
| 31 | - | /// The storage must outlive the bitset and its iterators. |
|
| 32 | - | export unsafe fn new(bits: &mut [u32]) -> Bitset { |
|
| 33 | - | let len = bits.len * 32; |
|
| 34 | - | return Bitset { bits: bits as *unsafe mut [u32], len }; |
|
| 35 | - | } |
|
| 36 | - | ||
| 37 | - | /// Create a new bitset backed by the given storage, zeroing it first. |
|
| 38 | - | /// The storage must outlive the bitset and its iterators. |
|
| 39 | - | export unsafe fn init(bits: &mut [u32]) -> Bitset { |
|
| 40 | - | for i in 0..bits.len { |
|
| 41 | - | set bits[i] = 0; |
|
| 42 | - | } |
|
| 43 | - | return new(bits); |
|
| 44 | - | } |
|
| 45 | - | ||
| 46 | - | /// Create a bitset from arena allocation. |
|
| 47 | - | export unsafe fn allocate(arena: &mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) { |
|
| 48 | - | let numWords = wordsFor(len); |
|
| 49 | - | let bits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *unsafe mut [u32]; |
|
| 50 | - | ||
| 51 | - | return init(bits); |
|
| 22 | + | /// Allocate zeroed words for at least `len` bits in a session. |
|
| 23 | + | export fn allocate 'bits (storage: &Session 'bits, len: u32) -> &'bits mut [u32] throws (alloc::AllocError) { |
|
| 24 | + | return try storage.fill(0 as u32, wordsFor(len)); |
|
| 52 | 25 | } |
|
| 53 | 26 | ||
| 54 | 27 | /// Set bit `n` in the bitset. |
|
| 55 | - | export unsafe fn put(bs: &mut Bitset, n: u32) { |
|
| 56 | - | if n >= bs.len { |
|
| 28 | + | export fn put(bs: &mut [u32], n: u32) { |
|
| 29 | + | let word = n / 32; |
|
| 30 | + | if word >= bs.len { |
|
| 57 | 31 | return; |
|
| 58 | 32 | } |
|
| 59 | - | let word = n / 32; |
|
| 60 | 33 | let b = n % 32; |
|
| 61 | 34 | ||
| 62 | - | set bs.bits[word] |= (1 << b); |
|
| 35 | + | set bs[word] |= (1 << b); |
|
| 63 | 36 | } |
|
| 64 | 37 | ||
| 65 | 38 | /// Clear bit `n` in the bitset. |
|
| 66 | - | export unsafe fn clear(bs: &mut Bitset, n: u32) { |
|
| 67 | - | if n >= bs.len { |
|
| 39 | + | export fn clear(bs: &mut [u32], n: u32) { |
|
| 40 | + | let word = n / 32; |
|
| 41 | + | if word >= bs.len { |
|
| 68 | 42 | return; |
|
| 69 | 43 | } |
|
| 70 | - | let word = n / 32; |
|
| 71 | 44 | let b = n % 32; |
|
| 72 | 45 | ||
| 73 | - | set bs.bits[word] &= ~(1 << b); |
|
| 46 | + | set bs[word] &= ~(1 << b); |
|
| 74 | 47 | } |
|
| 75 | 48 | ||
| 76 | 49 | /// Check if bit `n` is set. |
|
| 77 | - | export unsafe fn contains(bs: &Bitset, n: u32) -> bool { |
|
| 78 | - | if n >= bs.len { |
|
| 50 | + | export fn contains(bs: &[u32], n: u32) -> bool { |
|
| 51 | + | let word = n / 32; |
|
| 52 | + | if word >= bs.len { |
|
| 79 | 53 | return false; |
|
| 80 | 54 | } |
|
| 81 | - | let word = n / 32; |
|
| 82 | 55 | let b = n % 32; |
|
| 83 | 56 | ||
| 84 | - | return (bs.bits[word] & (1 << b)) <> 0; |
|
| 57 | + | return (bs[word] & (1 << b)) <> 0; |
|
| 85 | 58 | } |
|
| 86 | 59 | ||
| 87 | 60 | /// Count the number of set bits. |
|
| 88 | - | export unsafe fn count(bs: &Bitset) -> u32 { |
|
| 61 | + | export fn count(bs: &[u32]) -> u32 { |
|
| 89 | 62 | let mut total: u32 = 0; |
|
| 90 | - | let numWords = bs.bits.len; |
|
| 91 | - | for i in 0..numWords { |
|
| 92 | - | let word = bs.bits[i]; |
|
| 63 | + | for word in bs { |
|
| 93 | 64 | if word <> 0 { |
|
| 94 | 65 | set total += popCount(word); |
|
| 95 | 66 | } |
|
| 96 | 67 | } |
|
| 97 | 68 | return total; |
| 108 | 79 | ||
| 109 | 80 | return n & 0x3F; |
|
| 110 | 81 | } |
|
| 111 | 82 | ||
| 112 | 83 | /// Union: `dst = dst | src`. |
|
| 113 | - | export unsafe fn union_(dst: &mut Bitset, src: &Bitset) { |
|
| 114 | - | let numWords = dst.bits.len; |
|
| 115 | - | let srcWords = src.bits.len; |
|
| 116 | - | let minWords = min(numWords, srcWords); |
|
| 117 | - | for i in 0..minWords { |
|
| 118 | - | set dst.bits[i] |= src.bits[i]; |
|
| 84 | + | export fn union_(dst: &mut [u32], src: &[u32]) { |
|
| 85 | + | for i in 0..min(dst.len, src.len) { |
|
| 86 | + | set dst[i] |= src[i]; |
|
| 119 | 87 | } |
|
| 120 | 88 | } |
|
| 121 | 89 | ||
| 122 | 90 | /// Subtract: `dst = dst - src`. |
|
| 123 | - | export unsafe fn subtract(dst: &mut Bitset, src: &Bitset) { |
|
| 124 | - | let numWords = dst.bits.len; |
|
| 125 | - | let srcWords = src.bits.len; |
|
| 126 | - | let minWords = min(numWords, srcWords); |
|
| 127 | - | for i in 0..minWords { |
|
| 128 | - | set dst.bits[i] &= ~src.bits[i]; |
|
| 91 | + | export fn subtract(dst: &mut [u32], src: &[u32]) { |
|
| 92 | + | for i in 0..min(dst.len, src.len) { |
|
| 93 | + | set dst[i] &= ~src[i]; |
|
| 129 | 94 | } |
|
| 130 | 95 | } |
|
| 131 | 96 | ||
| 132 | 97 | /// Check if two bitsets are equal. |
|
| 133 | - | export unsafe fn eq(a: &Bitset, b: &Bitset) -> bool { |
|
| 134 | - | let numWordsA = a.bits.len; |
|
| 135 | - | let numWordsB = b.bits.len; |
|
| 136 | - | let minWords = min(numWordsA, numWordsB); |
|
| 98 | + | export fn eq(a: &[u32], b: &[u32]) -> bool { |
|
| 99 | + | let minWords = min(a.len, b.len); |
|
| 137 | 100 | ||
| 138 | 101 | for i in 0..minWords { |
|
| 139 | - | if a.bits[i] <> b.bits[i] { |
|
| 102 | + | if a[i] <> b[i] { |
|
| 140 | 103 | return false; |
|
| 141 | 104 | } |
|
| 142 | 105 | } |
|
| 143 | - | for i in minWords..numWordsA { |
|
| 144 | - | if a.bits[i] <> 0 { |
|
| 106 | + | for i in minWords..a.len { |
|
| 107 | + | if a[i] <> 0 { |
|
| 145 | 108 | return false; |
|
| 146 | 109 | } |
|
| 147 | 110 | } |
|
| 148 | - | for i in minWords..numWordsB { |
|
| 149 | - | if b.bits[i] <> 0 { |
|
| 111 | + | for i in minWords..b.len { |
|
| 112 | + | if b[i] <> 0 { |
|
| 150 | 113 | return false; |
|
| 151 | 114 | } |
|
| 152 | 115 | } |
|
| 153 | 116 | return true; |
|
| 154 | 117 | } |
|
| 155 | 118 | ||
| 156 | 119 | /// Copy bits from source to destination. |
|
| 157 | - | export unsafe fn copy(dst: &mut Bitset, src: &Bitset) { |
|
| 158 | - | let numWords = dst.bits.len; |
|
| 159 | - | let srcWords = src.bits.len; |
|
| 160 | - | let minWords = min(numWords, srcWords); |
|
| 120 | + | export fn copy(dst: &mut [u32], src: &[u32]) { |
|
| 121 | + | let minWords = min(dst.len, src.len); |
|
| 161 | 122 | ||
| 162 | 123 | for i in 0..minWords { |
|
| 163 | - | set dst.bits[i] = src.bits[i]; |
|
| 124 | + | set dst[i] = src[i]; |
|
| 164 | 125 | } |
|
| 165 | 126 | // Clear remaining words if destination is larger. |
|
| 166 | - | for i in minWords..numWords { |
|
| 167 | - | set dst.bits[i] = 0; |
|
| 127 | + | for i in minWords..dst.len { |
|
| 128 | + | set dst[i] = 0; |
|
| 168 | 129 | } |
|
| 169 | 130 | } |
|
| 170 | 131 | ||
| 171 | 132 | /// Clear all bits. |
|
| 172 | - | export unsafe fn clearAll(bs: &mut Bitset) { |
|
| 173 | - | let numWords = bs.bits.len; |
|
| 174 | - | for i in 0..numWords { |
|
| 175 | - | set bs.bits[i] = 0; |
|
| 133 | + | export fn clearAll(bs: &mut [u32]) { |
|
| 134 | + | for i in 0..bs.len { |
|
| 135 | + | set bs[i] = 0; |
|
| 176 | 136 | } |
|
| 177 | 137 | } |
|
| 178 | 138 | ||
| 179 | 139 | /// Iterator state for iterating set bits. |
|
| 180 | 140 | export record BitIter: Copy { |
|
| 181 | - | /// Bitset being iterated. It must outlive this iterator. |
|
| 182 | - | bs: *unsafe Bitset, |
|
| 183 | 141 | /// Current word index. |
|
| 184 | 142 | wordIdx: u32, |
|
| 185 | 143 | /// Remaining bits in the current word (visited bits cleared). |
|
| 186 | 144 | remaining: u32, |
|
| 187 | 145 | } |
|
| 188 | 146 | ||
| 189 | 147 | /// Create an iterator over set bits. |
|
| 190 | - | /// The bitset and its backing words must outlive the iterator. |
|
| 191 | - | export unsafe fn iter(bs: &Bitset) -> BitIter { |
|
| 192 | - | let remaining = bs.bits[0] if bs.len > 0 else 0; |
|
| 193 | - | return BitIter { bs: bs as *unsafe Bitset, wordIdx: 0, remaining }; |
|
| 148 | + | /// The cursor retains the first word mask and its position. |
|
| 149 | + | export fn iter(bs: &[u32]) -> BitIter { |
|
| 150 | + | let remaining = bs[0] if bs.len > 0 else 0; |
|
| 151 | + | return BitIter { wordIdx: 0, remaining }; |
|
| 194 | 152 | } |
|
| 195 | 153 | ||
| 196 | - | /// Get the next set bit, or nil if none remain. |
|
| 197 | - | export unsafe fn iterNext(it: &mut BitIter) -> ?u32 { |
|
| 198 | - | let numWords = it.bs.bits.len; |
|
| 154 | + | /// Get the next set bit from the supplied bitset, or nil if none remain. |
|
| 155 | + | /// The current word mask is a value snapshot; later words are read on demand. |
|
| 156 | + | export fn iterNext(it: &mut BitIter, bs: &[u32]) -> ?u32 { |
|
| 199 | 157 | // Skip to next non-zero word. |
|
| 200 | 158 | while it.remaining == 0 { |
|
| 201 | 159 | set it.wordIdx += 1; |
|
| 202 | - | if it.wordIdx >= numWords { |
|
| 160 | + | if it.wordIdx >= bs.len { |
|
| 203 | 161 | return nil; |
|
| 204 | 162 | } |
|
| 205 | - | set it.remaining = it.bs.bits[it.wordIdx]; |
|
| 163 | + | set it.remaining = bs[it.wordIdx]; |
|
| 206 | 164 | } |
|
| 207 | 165 | // Find lowest set bit position using de Bruijn sequence. |
|
| 208 | 166 | let b = ctz(it.remaining); |
|
| 209 | 167 | let n = it.wordIdx * 32 + b; |
|
| 210 | - | if n >= it.bs.len { |
|
| 211 | - | return nil; |
|
| 212 | - | } |
|
| 213 | 168 | // Clear the lowest set bit. |
|
| 214 | 169 | set it.remaining &= it.remaining - 1; |
|
| 215 | 170 | ||
| 216 | 171 | return n; |
|
| 217 | 172 | } |
lib/std/lang/gen/bitset/tests.rad
+132 -112
| 1 | 1 | //! Tests for the bitset module. |
|
| 2 | 2 | ||
| 3 | 3 | use std::testing; |
|
| 4 | + | use std::lang::alloc; |
|
| 4 | 5 | ||
| 5 | - | /// Test [`super::init`] and basic set/contains operations. |
|
| 6 | + | ||
| 7 | + | /// Test word initialization and basic set/contains operations. |
|
| 6 | 8 | @test unsafe fn testInit() throws (testing::TestError) { |
|
| 7 | 9 | let mut bits: [u32; 4] = undefined; |
|
| 8 | - | let mut bs = super::init(&mut bits[..]); |
|
| 10 | + | let bs = &mut bits[..]; |
|
| 11 | + | super::clearAll(bs); |
|
| 9 | 12 | ||
| 10 | - | // `init` should zero-initialize, so all bits start unset. |
|
| 11 | - | try testing::expect(not super::contains(&bs, 0)); |
|
| 12 | - | try testing::expect(not super::contains(&bs, 31)); |
|
| 13 | - | try testing::expect(not super::contains(&bs, 32)); |
|
| 14 | - | try testing::expect(not super::contains(&bs, 127)); |
|
| 13 | + | // All bits start unset after clearing the words. |
|
| 14 | + | try testing::expect(not super::contains(bs, 0)); |
|
| 15 | + | try testing::expect(not super::contains(bs, 31)); |
|
| 16 | + | try testing::expect(not super::contains(bs, 32)); |
|
| 17 | + | try testing::expect(not super::contains(bs, 127)); |
|
| 15 | 18 | ||
| 16 | 19 | // Set various bits including word boundaries. |
|
| 17 | - | super::put(&mut bs, 0); |
|
| 18 | - | super::put(&mut bs, 31); |
|
| 19 | - | super::put(&mut bs, 32); |
|
| 20 | - | super::put(&mut bs, 127); |
|
| 20 | + | super::put(bs, 0); |
|
| 21 | + | super::put(bs, 31); |
|
| 22 | + | super::put(bs, 32); |
|
| 23 | + | super::put(bs, 127); |
|
| 21 | 24 | ||
| 22 | - | try testing::expect(super::contains(&bs, 0)); |
|
| 23 | - | try testing::expect(super::contains(&bs, 31)); |
|
| 24 | - | try testing::expect(super::contains(&bs, 32)); |
|
| 25 | - | try testing::expect(super::contains(&bs, 127)); |
|
| 25 | + | try testing::expect(super::contains(bs, 0)); |
|
| 26 | + | try testing::expect(super::contains(bs, 31)); |
|
| 27 | + | try testing::expect(super::contains(bs, 32)); |
|
| 28 | + | try testing::expect(super::contains(bs, 127)); |
|
| 26 | 29 | ||
| 27 | 30 | // Other bits still unset. |
|
| 28 | - | try testing::expect(not super::contains(&bs, 1)); |
|
| 29 | - | try testing::expect(not super::contains(&bs, 30)); |
|
| 30 | - | try testing::expect(not super::contains(&bs, 33)); |
|
| 31 | - | try testing::expect(not super::contains(&bs, 126)); |
|
| 31 | + | try testing::expect(not super::contains(bs, 1)); |
|
| 32 | + | try testing::expect(not super::contains(bs, 30)); |
|
| 33 | + | try testing::expect(not super::contains(bs, 33)); |
|
| 34 | + | try testing::expect(not super::contains(bs, 126)); |
|
| 32 | 35 | } |
|
| 33 | 36 | ||
| 34 | 37 | /// Test clear operation. |
|
| 35 | 38 | @test unsafe fn testClear() throws (testing::TestError) { |
|
| 36 | 39 | let mut bits: [u32; 2] = [0; 2]; |
|
| 37 | - | let mut bs = super::new(&mut bits[..]); |
|
| 40 | + | let bs = &mut bits[..]; |
|
| 38 | 41 | ||
| 39 | - | super::put(&mut bs, 0); |
|
| 40 | - | super::put(&mut bs, 31); |
|
| 41 | - | super::put(&mut bs, 32); |
|
| 42 | + | super::put(bs, 0); |
|
| 43 | + | super::put(bs, 31); |
|
| 44 | + | super::put(bs, 32); |
|
| 42 | 45 | ||
| 43 | - | try testing::expect(super::contains(&bs, 0)); |
|
| 44 | - | try testing::expect(super::contains(&bs, 31)); |
|
| 45 | - | try testing::expect(super::contains(&bs, 32)); |
|
| 46 | + | try testing::expect(super::contains(bs, 0)); |
|
| 47 | + | try testing::expect(super::contains(bs, 31)); |
|
| 48 | + | try testing::expect(super::contains(bs, 32)); |
|
| 46 | 49 | ||
| 47 | - | super::clear(&mut bs, 31); |
|
| 50 | + | super::clear(bs, 31); |
|
| 48 | 51 | ||
| 49 | - | try testing::expect(super::contains(&bs, 0)); |
|
| 50 | - | try testing::expect(not super::contains(&bs, 31)); |
|
| 51 | - | try testing::expect(super::contains(&bs, 32)); |
|
| 52 | + | try testing::expect(super::contains(bs, 0)); |
|
| 53 | + | try testing::expect(not super::contains(bs, 31)); |
|
| 54 | + | try testing::expect(super::contains(bs, 32)); |
|
| 52 | 55 | } |
|
| 53 | 56 | ||
| 54 | 57 | /// Test population count. |
|
| 55 | 58 | @test unsafe fn testCount() throws (testing::TestError) { |
|
| 56 | 59 | let mut bits: [u32; 2] = [0; 2]; |
|
| 57 | - | let mut bs = super::new(&mut bits[..]); |
|
| 60 | + | let bs = &mut bits[..]; |
|
| 58 | 61 | ||
| 59 | - | try testing::expect(super::count(&bs) == 0); |
|
| 62 | + | try testing::expect(super::count(bs) == 0); |
|
| 60 | 63 | ||
| 61 | - | super::put(&mut bs, 0); |
|
| 62 | - | try testing::expect(super::count(&bs) == 1); |
|
| 64 | + | super::put(bs, 0); |
|
| 65 | + | try testing::expect(super::count(bs) == 1); |
|
| 63 | 66 | ||
| 64 | - | super::put(&mut bs, 31); |
|
| 65 | - | super::put(&mut bs, 32); |
|
| 66 | - | super::put(&mut bs, 63); |
|
| 67 | - | try testing::expect(super::count(&bs) == 4); |
|
| 67 | + | super::put(bs, 31); |
|
| 68 | + | super::put(bs, 32); |
|
| 69 | + | super::put(bs, 63); |
|
| 70 | + | try testing::expect(super::count(bs) == 4); |
|
| 68 | 71 | ||
| 69 | - | super::clear(&mut bs, 31); |
|
| 70 | - | try testing::expect(super::count(&bs) == 3); |
|
| 72 | + | super::clear(bs, 31); |
|
| 73 | + | try testing::expect(super::count(bs) == 3); |
|
| 71 | 74 | } |
|
| 72 | 75 | ||
| 73 | 76 | /// Test union operation. |
|
| 74 | 77 | @test unsafe fn testUnion() throws (testing::TestError) { |
|
| 75 | 78 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 76 | 79 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 77 | - | let mut a = super::new(&mut bits_a[..]); |
|
| 78 | - | let mut b = super::new(&mut bits_b[..]); |
|
| 80 | + | let a = &mut bits_a[..]; |
|
| 81 | + | let b = &mut bits_b[..]; |
|
| 79 | 82 | ||
| 80 | - | super::put(&mut a, 0); |
|
| 81 | - | super::put(&mut a, 10); |
|
| 83 | + | super::put(a, 0); |
|
| 84 | + | super::put(a, 10); |
|
| 82 | 85 | ||
| 83 | - | super::put(&mut b, 10); |
|
| 84 | - | super::put(&mut b, 20); |
|
| 86 | + | super::put(b, 10); |
|
| 87 | + | super::put(b, 20); |
|
| 85 | 88 | ||
| 86 | - | super::union_(&mut a, &b); |
|
| 89 | + | super::union_(a, b); |
|
| 87 | 90 | ||
| 88 | - | try testing::expect(super::contains(&a, 0)); |
|
| 89 | - | try testing::expect(super::contains(&a, 10)); |
|
| 90 | - | try testing::expect(super::contains(&a, 20)); |
|
| 91 | - | try testing::expect(super::count(&a) == 3); |
|
| 91 | + | try testing::expect(super::contains(a, 0)); |
|
| 92 | + | try testing::expect(super::contains(a, 10)); |
|
| 93 | + | try testing::expect(super::contains(a, 20)); |
|
| 94 | + | try testing::expect(super::count(a) == 3); |
|
| 92 | 95 | } |
|
| 93 | 96 | ||
| 94 | 97 | /// Test subtract operation. |
|
| 95 | 98 | @test unsafe fn testSubtract() throws (testing::TestError) { |
|
| 96 | 99 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 97 | 100 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 98 | - | let mut a = super::new(&mut bits_a[..]); |
|
| 99 | - | let mut b = super::new(&mut bits_b[..]); |
|
| 101 | + | let a = &mut bits_a[..]; |
|
| 102 | + | let b = &mut bits_b[..]; |
|
| 100 | 103 | ||
| 101 | - | super::put(&mut a, 0); |
|
| 102 | - | super::put(&mut a, 10); |
|
| 103 | - | super::put(&mut a, 20); |
|
| 104 | + | super::put(a, 0); |
|
| 105 | + | super::put(a, 10); |
|
| 106 | + | super::put(a, 20); |
|
| 104 | 107 | ||
| 105 | - | super::put(&mut b, 10); |
|
| 106 | - | super::put(&mut b, 30); |
|
| 108 | + | super::put(b, 10); |
|
| 109 | + | super::put(b, 30); |
|
| 107 | 110 | ||
| 108 | - | super::subtract(&mut a, &b); |
|
| 111 | + | super::subtract(a, b); |
|
| 109 | 112 | ||
| 110 | - | try testing::expect(super::contains(&a, 0)); |
|
| 111 | - | try testing::expect(not super::contains(&a, 10)); |
|
| 112 | - | try testing::expect(super::contains(&a, 20)); |
|
| 113 | - | try testing::expect(super::count(&a) == 2); |
|
| 113 | + | try testing::expect(super::contains(a, 0)); |
|
| 114 | + | try testing::expect(not super::contains(a, 10)); |
|
| 115 | + | try testing::expect(super::contains(a, 20)); |
|
| 116 | + | try testing::expect(super::count(a) == 2); |
|
| 114 | 117 | } |
|
| 115 | 118 | ||
| 116 | 119 | /// Test equality check. |
|
| 117 | 120 | @test unsafe fn testEq() throws (testing::TestError) { |
|
| 118 | 121 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 119 | 122 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 120 | - | let mut a = super::new(&mut bits_a[..]); |
|
| 121 | - | let mut b = super::new(&mut bits_b[..]); |
|
| 123 | + | let a = &mut bits_a[..]; |
|
| 124 | + | let b = &mut bits_b[..]; |
|
| 122 | 125 | ||
| 123 | 126 | // Both empty. |
|
| 124 | - | try testing::expect(super::eq(&a, &b)); |
|
| 127 | + | try testing::expect(super::eq(a, b)); |
|
| 125 | 128 | ||
| 126 | - | super::put(&mut a, 5); |
|
| 127 | - | try testing::expect(not super::eq(&a, &b)); |
|
| 129 | + | super::put(a, 5); |
|
| 130 | + | try testing::expect(not super::eq(a, b)); |
|
| 128 | 131 | ||
| 129 | - | super::put(&mut b, 5); |
|
| 130 | - | try testing::expect(super::eq(&a, &b)); |
|
| 132 | + | super::put(b, 5); |
|
| 133 | + | try testing::expect(super::eq(a, b)); |
|
| 131 | 134 | ||
| 132 | - | super::put(&mut a, 32); |
|
| 133 | - | super::put(&mut b, 32); |
|
| 134 | - | try testing::expect(super::eq(&a, &b)); |
|
| 135 | + | super::put(a, 32); |
|
| 136 | + | super::put(b, 32); |
|
| 137 | + | try testing::expect(super::eq(a, b)); |
|
| 135 | 138 | ||
| 136 | - | super::put(&mut b, 33); |
|
| 137 | - | try testing::expect(not super::eq(&a, &b)); |
|
| 139 | + | super::put(b, 33); |
|
| 140 | + | try testing::expect(not super::eq(a, b)); |
|
| 138 | 141 | } |
|
| 139 | 142 | ||
| 140 | 143 | /// Test copy operation. |
|
| 141 | 144 | @test unsafe fn testCopy() throws (testing::TestError) { |
|
| 142 | 145 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 143 | 146 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 144 | - | let mut a = super::new(&mut bits_a[..]); |
|
| 145 | - | let mut b = super::new(&mut bits_b[..]); |
|
| 147 | + | let a = &mut bits_a[..]; |
|
| 148 | + | let b = &mut bits_b[..]; |
|
| 146 | 149 | ||
| 147 | - | super::put(&mut a, 0); |
|
| 148 | - | super::put(&mut a, 31); |
|
| 149 | - | super::put(&mut a, 63); |
|
| 150 | + | super::put(a, 0); |
|
| 151 | + | super::put(a, 31); |
|
| 152 | + | super::put(a, 63); |
|
| 150 | 153 | ||
| 151 | - | super::copy(&mut b, &a); |
|
| 154 | + | super::copy(b, a); |
|
| 152 | 155 | ||
| 153 | - | try testing::expect(super::eq(&a, &b)); |
|
| 154 | - | try testing::expect(super::contains(&b, 0)); |
|
| 155 | - | try testing::expect(super::contains(&b, 31)); |
|
| 156 | - | try testing::expect(super::contains(&b, 63)); |
|
| 156 | + | try testing::expect(super::eq(a, b)); |
|
| 157 | + | try testing::expect(super::contains(b, 0)); |
|
| 158 | + | try testing::expect(super::contains(b, 31)); |
|
| 159 | + | try testing::expect(super::contains(b, 63)); |
|
| 157 | 160 | } |
|
| 158 | 161 | ||
| 159 | 162 | /// Test clearAll operation. |
|
| 160 | 163 | @test unsafe fn testClearAll() throws (testing::TestError) { |
|
| 161 | 164 | let mut bits: [u32; 2] = [0; 2]; |
|
| 162 | - | let mut bs = super::new(&mut bits[..]); |
|
| 163 | - | ||
| 164 | - | super::put(&mut bs, 0); |
|
| 165 | - | super::put(&mut bs, 31); |
|
| 166 | - | super::put(&mut bs, 32); |
|
| 167 | - | super::put(&mut bs, 63); |
|
| 168 | - | try testing::expect(super::count(&bs) == 4); |
|
| 169 | - | ||
| 170 | - | super::clearAll(&mut bs); |
|
| 171 | - | try testing::expect(super::count(&bs) == 0); |
|
| 172 | - | try testing::expect(not super::contains(&bs, 0)); |
|
| 173 | - | try testing::expect(not super::contains(&bs, 31)); |
|
| 165 | + | let bs = &mut bits[..]; |
|
| 166 | + | ||
| 167 | + | super::put(bs, 0); |
|
| 168 | + | super::put(bs, 31); |
|
| 169 | + | super::put(bs, 32); |
|
| 170 | + | super::put(bs, 63); |
|
| 171 | + | try testing::expect(super::count(bs) == 4); |
|
| 172 | + | ||
| 173 | + | super::clearAll(bs); |
|
| 174 | + | try testing::expect(super::count(bs) == 0); |
|
| 175 | + | try testing::expect(not super::contains(bs, 0)); |
|
| 176 | + | try testing::expect(not super::contains(bs, 31)); |
|
| 174 | 177 | } |
|
| 175 | 178 | ||
| 176 | 179 | /// Test iteration over set bits. |
|
| 177 | 180 | @test unsafe fn testIter() throws (testing::TestError) { |
|
| 178 | 181 | let mut bits: [u32; 2] = [0; 2]; |
|
| 179 | - | let mut bs = super::new(&mut bits[..]); |
|
| 182 | + | let bs = &mut bits[..]; |
|
| 180 | 183 | ||
| 181 | - | super::put(&mut bs, 3); |
|
| 182 | - | super::put(&mut bs, 31); |
|
| 183 | - | super::put(&mut bs, 32); |
|
| 184 | - | super::put(&mut bs, 50); |
|
| 184 | + | super::put(bs, 3); |
|
| 185 | + | super::put(bs, 31); |
|
| 186 | + | super::put(bs, 32); |
|
| 187 | + | super::put(bs, 50); |
|
| 185 | 188 | ||
| 186 | - | let mut it = super::iter(&bs); |
|
| 189 | + | let mut it = super::iter(bs); |
|
| 187 | 190 | let mut count: u32 = 0; |
|
| 188 | 191 | let mut sum: u32 = 0; |
|
| 189 | 192 | ||
| 190 | - | while let n = super::iterNext(&mut it) { |
|
| 193 | + | while let n = super::iterNext(&mut it, bs) { |
|
| 191 | 194 | set count += 1; |
|
| 192 | 195 | set sum += n; |
|
| 193 | 196 | } |
|
| 194 | 197 | ||
| 195 | 198 | try testing::expect(count == 4); |
| 197 | 200 | } |
|
| 198 | 201 | ||
| 199 | 202 | /// Test iteration on empty bitset. |
|
| 200 | 203 | @test unsafe fn testIterEmpty() throws (testing::TestError) { |
|
| 201 | 204 | let mut bits: [u32; 2] = [0; 2]; |
|
| 202 | - | let mut bs = super::new(&mut bits[..]); |
|
| 203 | - | let mut it = super::iter(&bs); |
|
| 204 | - | let result = super::iterNext(&mut it); |
|
| 205 | + | let bs = &mut bits[..]; |
|
| 206 | + | let mut it = super::iter(bs); |
|
| 207 | + | let result = super::iterNext(&mut it, bs); |
|
| 205 | 208 | ||
| 206 | 209 | try testing::expect(result == nil); |
|
| 207 | 210 | } |
|
| 208 | 211 | ||
| 209 | 212 | /// Test [`super::wordsFor`] calculation. |
| 213 | 216 | try testing::expect(super::wordsFor(31) == 1); |
|
| 214 | 217 | try testing::expect(super::wordsFor(32) == 1); |
|
| 215 | 218 | try testing::expect(super::wordsFor(33) == 2); |
|
| 216 | 219 | try testing::expect(super::wordsFor(64) == 2); |
|
| 217 | 220 | try testing::expect(super::wordsFor(65) == 3); |
|
| 221 | + | try testing::expect(super::wordsFor(0xFFFFFFFF) == 134217728); |
|
| 218 | 222 | } |
|
| 219 | 223 | ||
| 220 | 224 | /// Test out-of-bounds access is safe. |
|
| 221 | 225 | @test unsafe fn testOutOfBounds() throws (testing::TestError) { |
|
| 222 | 226 | let mut bits: [u32; 1] = [0; 1]; |
|
| 223 | - | let mut bs = super::new(&mut bits[..]); |
|
| 227 | + | let bs = &mut bits[..]; |
|
| 224 | 228 | ||
| 225 | 229 | // Setting beyond length should be ignored. |
|
| 226 | - | super::put(&mut bs, 100); |
|
| 227 | - | try testing::expect(not super::contains(&bs, 100)); |
|
| 230 | + | super::put(bs, 100); |
|
| 231 | + | try testing::expect(not super::contains(bs, 100)); |
|
| 228 | 232 | ||
| 229 | 233 | // Clearing beyond length should be safe. |
|
| 230 | - | super::clear(&mut bs, 100); |
|
| 234 | + | super::clear(bs, 100); |
|
| 231 | 235 | ||
| 232 | 236 | // Contains beyond length should return false. |
|
| 233 | - | try testing::expect(not super::contains(&bs, 100)); |
|
| 237 | + | try testing::expect(not super::contains(bs, 100)); |
|
| 238 | + | } |
|
| 239 | + | ||
| 240 | + | /// Allocate initialized words with a retained session lifetime. |
|
| 241 | + | @test unsafe fn testSession() throws (testing::TestError) { |
|
| 242 | + | static DATA: [u8; 64] = [0; 64]; |
|
| 243 | + | let mut arena = alloc::new(&mut DATA[..]); |
|
| 244 | + | use arena as bits in { |
|
| 245 | + | let words = try! super::allocate(&bits, 65); |
|
| 246 | + | try testing::expect(words.len == 3); |
|
| 247 | + | try testing::expect(super::count(words) == 0); |
|
| 248 | + | super::put(words, 64); |
|
| 249 | + | try testing::expect(super::count(words) == 1); |
|
| 250 | + | let empty = try! super::allocate(&bits, 0); |
|
| 251 | + | try testing::expect(empty.len == 0); |
|
| 252 | + | try testing::expect(super::count(empty) == 0); |
|
| 253 | + | } |
|
| 234 | 254 | } |
lib/std/lang/gen/data.rad
+47 -16
| 90 | 90 | throws (Error) |
|
| 91 | 91 | { |
|
| 92 | 92 | if item.alignment == 0 or (item.alignment & (item.alignment - 1)) <> 0 { |
|
| 93 | 93 | throw Error::Alignment; |
|
| 94 | 94 | } |
|
| 95 | - | if (base & (item.alignment as u64 - 1)) <> 0 { throw Error::Alignment; } |
|
| 95 | + | if (base & (item.alignment as u64 - 1)) <> 0 { |
|
| 96 | + | throw Error::Alignment; |
|
| 97 | + | } |
|
| 96 | 98 | let aligned = (*offset as u64 + item.alignment as u64 - 1) & ~(item.alignment as u64 - 1); |
|
| 97 | 99 | let end = aligned + item.size as u64; |
|
| 98 | 100 | if end > 0xffffffff or base > 0xffffffffffffffff - end { |
|
| 99 | 101 | throw Error::Overflow; |
|
| 100 | 102 | } |
|
| 101 | - | if *count >= syms.len { throw Error::Capacity; } |
|
| 103 | + | if *count >= syms.len { |
|
| 104 | + | throw Error::Capacity; |
|
| 105 | + | } |
|
| 102 | 106 | set syms[*count] = DataSym { name: item.name, addr: base + aligned }; |
|
| 103 | 107 | set *count += 1; |
|
| 104 | 108 | set *offset = end as u32; |
|
| 105 | 109 | } |
|
| 106 | 110 |
| 127 | 131 | buf: &mut [u8], |
|
| 128 | 132 | readOnly: bool, |
|
| 129 | 133 | startOffset: u32 |
|
| 130 | 134 | ) -> u32 throws (Error) { |
|
| 131 | 135 | let mut offset: u32 = startOffset; |
|
| 132 | - | if offset > buf.len { throw Error::Capacity; } |
|
| 136 | + | if offset > buf.len { |
|
| 137 | + | throw Error::Capacity; |
|
| 138 | + | } |
|
| 133 | 139 | ||
| 134 | 140 | for i in 0..items.len { |
|
| 135 | 141 | let data = items[i]; |
|
| 136 | 142 | if data.readOnly == readOnly and not data.isZeroInit { |
|
| 137 | 143 | let start = offset; |
|
| 138 | - | if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 { throw Error::Alignment; } |
|
| 144 | + | if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 { |
|
| 145 | + | throw Error::Alignment; |
|
| 146 | + | } |
|
| 139 | 147 | let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1); |
|
| 140 | - | if aligned > buf.len as u64 { throw Error::Capacity; } |
|
| 148 | + | if aligned > buf.len as u64 { |
|
| 149 | + | throw Error::Capacity; |
|
| 150 | + | } |
|
| 141 | 151 | set offset = aligned as u32; |
|
| 142 | - | if data.size > buf.len - offset { throw Error::Capacity; } |
|
| 152 | + | if data.size > buf.len - offset { |
|
| 153 | + | throw Error::Capacity; |
|
| 154 | + | } |
|
| 143 | 155 | let end = offset + data.size; |
|
| 144 | - | for j in start..end { set buf[j] = 0; } |
|
| 156 | + | for j in start..end { |
|
| 157 | + | set buf[j] = 0; |
|
| 158 | + | } |
|
| 145 | 159 | for j in 0..data.values.len { |
|
| 146 | 160 | let v = &data.values[j]; |
|
| 147 | 161 | let mut width: u32 = 1; |
|
| 148 | 162 | match v.item { |
|
| 149 | - | case il::DataItem::Val { typ, .. } => { set width = il::typeSize(typ); }, |
|
| 150 | - | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { set width = 8; }, |
|
| 151 | - | case il::DataItem::Str(bytes) => { set width = bytes.len; }, |
|
| 152 | - | else => {}, |
|
| 163 | + | case il::DataItem::Val { typ, .. } => { |
|
| 164 | + | set width = il::typeSize(typ); |
|
| 165 | + | }, |
|
| 166 | + | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { |
|
| 167 | + | set width = 8; |
|
| 168 | + | }, |
|
| 169 | + | case il::DataItem::Str(bytes) => { |
|
| 170 | + | set width = bytes.len; |
|
| 171 | + | }, |
|
| 172 | + | else => { |
|
| 173 | + | }, |
|
| 174 | + | } |
|
| 175 | + | if width > 0 and v.count > (end - offset) / width { |
|
| 176 | + | throw Error::Overflow; |
|
| 177 | + | } |
|
| 178 | + | if width == 0 { |
|
| 179 | + | continue; |
|
| 153 | 180 | } |
|
| 154 | - | if width > 0 and v.count > (end - offset) / width { throw Error::Overflow; } |
|
| 155 | - | if width == 0 { continue; } |
|
| 156 | 181 | for _ in 0..v.count { |
|
| 157 | 182 | match v.item { |
|
| 158 | 183 | case il::DataItem::Val { typ, val } => { |
|
| 159 | 184 | let size = il::typeSize(typ); |
|
| 160 | 185 | try! mem::copy(&mut buf[offset..], @sliceOf(&val as &u8, size)); |
| 169 | 194 | try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8)); |
|
| 170 | 195 | ||
| 171 | 196 | set offset += @sizeOf(u64); |
|
| 172 | 197 | }, |
|
| 173 | 198 | case il::DataItem::Fn(name) => { |
|
| 174 | - | let fnOffset = dict::get(&fnLabels.funcs, name) else { throw Error::Symbol; }; |
|
| 175 | - | if fnOffset < 0 or codeBase > 0xffffffffffffffff - fnOffset as u64 { throw Error::Overflow; } |
|
| 199 | + | let fnOffset = dict::get(&fnLabels.funcs, name) else { |
|
| 200 | + | throw Error::Symbol; |
|
| 201 | + | }; |
|
| 202 | + | if fnOffset < 0 or codeBase > 0xffffffffffffffff - fnOffset as u64 { |
|
| 203 | + | throw Error::Overflow; |
|
| 204 | + | } |
|
| 176 | 205 | let addr = codeBase + fnOffset as u64; |
|
| 177 | 206 | let addr64: u64 = addr as u64; |
|
| 178 | 207 | try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8)); |
|
| 179 | 208 | ||
| 180 | 209 | set offset += @sizeOf(*u8); |
| 202 | 231 | if entries.len == 0 or (entries.len & (entries.len - 1)) <> 0 or syms.len > entries.len / 2 { |
|
| 203 | 232 | throw Error::Capacity; |
|
| 204 | 233 | } |
|
| 205 | 234 | let mut d = dict::init(entries); |
|
| 206 | 235 | for i in 0..syms.len { |
|
| 207 | - | if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil { throw Error::Symbol; } |
|
| 236 | + | if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil { |
|
| 237 | + | throw Error::Symbol; |
|
| 238 | + | } |
|
| 208 | 239 | dict::insert(&mut d, syms[i].name, i as i32); |
|
| 209 | 240 | } |
|
| 210 | 241 | return DataSymMap { dict: d, syms }; |
|
| 211 | 242 | } |
|
| 212 | 243 |
lib/std/lang/gen/regalloc.rad
+11 -11
| 33 | 33 | /// Size of a spill slot in bytes. |
|
| 34 | 34 | slotSize: u32, |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Complete register allocation result. |
|
| 38 | - | export record AllocResult: Copy { |
|
| 38 | + | export record AllocResult: 'scratch + Copy { |
|
| 39 | 39 | /// SSA register to physical register mapping. |
|
| 40 | - | assignments: *unsafe [?super::Reg], |
|
| 40 | + | assignments: &'scratch [?super::Reg], |
|
| 41 | 41 | /// Spill slot information. |
|
| 42 | - | spill: spill::SpillInfo, |
|
| 42 | + | spill: spill::SpillInfo 'scratch, |
|
| 43 | 43 | /// Bitmask of used callee-saved registers. |
|
| 44 | 44 | usedCalleeSaved: u32, |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | 47 | /// Run register allocation on a function. |
|
| 48 | 48 | /// |
|
| 49 | 49 | /// Returns a mapping from SSA registers to physical registers, plus |
|
| 50 | 50 | /// spill information. |
|
| 51 | - | export unsafe fn allocate( |
|
| 52 | - | func: *unsafe il::Fn, |
|
| 51 | + | export unsafe fn allocate 'scratch ( |
|
| 52 | + | func: &il::Fn, |
|
| 53 | 53 | config: &TargetConfig, |
|
| 54 | - | arena: &mut alloc::Arena |
|
| 55 | - | ) -> AllocResult throws (alloc::AllocError) { |
|
| 54 | + | storage: &Session 'scratch |
|
| 55 | + | ) -> AllocResult 'scratch throws (alloc::AllocError) { |
|
| 56 | 56 | // Phase 1: Liveness analysis. |
|
| 57 | - | let live = try liveness::analyze(func, arena); |
|
| 57 | + | let live = try liveness::analyze(func, storage); |
|
| 58 | 58 | // Phase 2: Spill analysis (determine which values need stack slots). |
|
| 59 | - | let spillInfo = try spill::analyze(func, &live, config.allocatable.len, config.calleeSaved.len, config.slotSize, arena); |
|
| 59 | + | let spillInfo = try spill::analyze(func, &live, config.allocatable.len, config.calleeSaved.len, config.slotSize, storage); |
|
| 60 | 60 | // Phase 3: Register assignment (map SSA registers to physical registers). |
|
| 61 | - | let assignInfo = try assign::assign(func, &live, &spillInfo, config, arena); |
|
| 61 | + | let assignInfo = try assign::assign(func, &live, &spillInfo, config, storage); |
|
| 62 | 62 | ||
| 63 | - | return AllocResult { |
|
| 63 | + | return AllocResult 'scratch { |
|
| 64 | 64 | assignments: assignInfo.assignments, |
|
| 65 | 65 | spill: spillInfo, |
|
| 66 | 66 | usedCalleeSaved: assignInfo.usedCalleeSaved, |
|
| 67 | 67 | }; |
|
| 68 | 68 | } |
lib/std/lang/gen/regalloc/assign.rad
+78 -96
| 16 | 16 | /// Maximum number of active register mappings. |
|
| 17 | 17 | constant MAX_ACTIVE: u32 = 64; |
|
| 18 | 18 | ||
| 19 | 19 | /// Register mapping at a program point. |
|
| 20 | 20 | /// Maps SSA registers to physical registers. |
|
| 21 | - | export record RegMap { |
|
| 21 | + | export record RegMap: 'scratch { |
|
| 22 | 22 | /// SSA (virtual) registers that have mappings. |
|
| 23 | - | virtRegs: *mut [u32], |
|
| 23 | + | virtRegs: &'scratch mut [u32], |
|
| 24 | 24 | /// Physical register for each virtual register. |
|
| 25 | - | physRegs: *mut [gen::Reg], |
|
| 25 | + | physRegs: &'scratch mut [gen::Reg], |
|
| 26 | 26 | /// Number of active mappings. |
|
| 27 | 27 | n: u32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Register assignment result, per function. |
|
| 31 | - | export record AssignInfo: Copy { |
|
| 31 | + | export record AssignInfo: 'scratch + Copy { |
|
| 32 | 32 | /// SSA register -> physical register mapping. |
|
| 33 | - | assignments: *unsafe mut [?gen::Reg], |
|
| 33 | + | assignments: &'scratch [?gen::Reg], |
|
| 34 | 34 | /// Bitmask of used callee-saved registers. |
|
| 35 | 35 | usedCalleeSaved: u32, |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Per-instruction context for freeing and allocating register uses. |
|
| 39 | - | record InstrCtx: Copy { |
|
| 40 | - | current: *unsafe mut RegMap, |
|
| 41 | - | usedRegs: *unsafe mut bitset::Bitset, |
|
| 39 | + | record InstrCtx: 'scratch + 'step where 'scratch: 'step { |
|
| 40 | + | current: &'step mut RegMap 'scratch, |
|
| 41 | + | usedRegs: &'step mut [u32], |
|
| 42 | 42 | /// Last operand-use index for each register used in the current block. |
|
| 43 | - | lastUse: *unsafe [u32], |
|
| 44 | - | live: *unsafe liveness::LiveInfo, |
|
| 43 | + | lastUse: &'step [u32], |
|
| 44 | + | live: &'step liveness::LiveInfo 'scratch, |
|
| 45 | 45 | blockIdx: u32, |
|
| 46 | 46 | instrIdx: u32, |
|
| 47 | 47 | allocatable: *[gen::Reg], |
|
| 48 | 48 | calleeSaved: *[gen::Reg], |
|
| 49 | - | assignments: *unsafe mut [?gen::Reg], |
|
| 50 | - | spillInfo: *unsafe spill::SpillInfo, |
|
| 51 | - | } |
|
| 52 | - | ||
| 53 | - | /// Context for recording the last operand-use index in a block. |
|
| 54 | - | record LastUseCtx: Copy { |
|
| 55 | - | /// Per-register indices, shared by all blocks in the function. |
|
| 56 | - | lastUse: *unsafe mut [u32], |
|
| 57 | - | /// Index of the instruction whose operands are being recorded. |
|
| 58 | - | index: u32, |
|
| 49 | + | assignments: &'step mut [?gen::Reg], |
|
| 50 | + | spillInfo: &'step spill::SpillInfo 'scratch, |
|
| 59 | 51 | } |
|
| 60 | 52 | ||
| 61 | 53 | /// Compute register assignment. |
|
| 62 | - | export unsafe fn assign( |
|
| 63 | - | func: *unsafe il::Fn, |
|
| 64 | - | live: &liveness::LiveInfo, |
|
| 65 | - | spillInfo: &spill::SpillInfo, |
|
| 54 | + | export unsafe fn assign 'scratch ( |
|
| 55 | + | func: &il::Fn, |
|
| 56 | + | live: &liveness::LiveInfo 'scratch, |
|
| 57 | + | spillInfo: &spill::SpillInfo 'scratch, |
|
| 66 | 58 | config: &super::TargetConfig, |
|
| 67 | - | arena: &mut alloc::Arena |
|
| 68 | - | ) -> AssignInfo throws (alloc::AllocError) { |
|
| 59 | + | storage: &Session 'scratch |
|
| 60 | + | ) -> AssignInfo 'scratch throws (alloc::AllocError) { |
|
| 69 | 61 | let maxReg = live.maxReg; |
|
| 70 | 62 | let blockCount = func.blocks.len; |
|
| 71 | 63 | let allocatable = config.allocatable; |
|
| 72 | 64 | ||
| 73 | 65 | if maxReg == 0 or blockCount == 0 { |
|
| 74 | - | return AssignInfo { |
|
| 75 | - | assignments: &mut [], |
|
| 66 | + | return AssignInfo 'scratch { |
|
| 67 | + | assignments: try storage.fill(nil as ?gen::Reg, 0), |
|
| 76 | 68 | usedCalleeSaved: 0, |
|
| 77 | 69 | }; |
|
| 78 | 70 | } |
|
| 79 | 71 | ||
| 80 | 72 | // Allocate output structures. |
|
| 81 | - | let assignments = try alloc::allocRawSlice(arena, @sizeOf(?gen::Reg), @alignOf(?gen::Reg), maxReg) as *unsafe mut [?gen::Reg]; |
|
| 82 | - | for i in 0..maxReg { |
|
| 83 | - | set assignments[i] = nil; |
|
| 84 | - | } |
|
| 85 | - | // Reuse one last-use table for all blocks in the function. |
|
| 86 | - | let lastUse = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), maxReg) as *unsafe mut [u32]; |
|
| 73 | + | let assignments = try storage.fill(nil as ?gen::Reg, maxReg); |
|
| 87 | 74 | // Pre-assign function parameters to argument registers. |
|
| 88 | 75 | // Cross-call params are NOT pre-assigned here; they will be allocated |
|
| 89 | 76 | // to callee-saved registers by the normal path, and isel emits moves |
|
| 90 | 77 | // from the arg register to the assigned register at function entry. |
|
| 91 | 78 | for param, i in func.params { |
|
| 92 | 79 | if i < config.argRegs.len { |
|
| 93 | - | if not bitset::contains(&spillInfo.calleeClass, param.value.n) { |
|
| 80 | + | if not bitset::contains(spillInfo.calleeClass, param.value.n) { |
|
| 94 | 81 | set assignments[param.value.n] = config.argRegs[i]; |
|
| 95 | 82 | } |
|
| 96 | 83 | } |
|
| 97 | 84 | } |
|
| 98 | 85 | // Allocate used registers bitset (32 physical registers per 32-bit word). |
|
| 99 | - | let usedRegsBits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), 1) as *unsafe mut [u32]; |
|
| 100 | - | let mut usedRegs = bitset::init(usedRegsBits); |
|
| 86 | + | let usedRegs = try bitset::allocate(storage, 32); |
|
| 101 | 87 | ||
| 102 | 88 | // Current register mapping. |
|
| 103 | - | let mut current = try createRegMap(arena); |
|
| 89 | + | // Reuse one last-use table for all blocks in the function. |
|
| 90 | + | let lastUse = try storage.fill(0 as u32, maxReg); |
|
| 91 | + | let mut current = try createRegMap(storage); |
|
| 104 | 92 | ||
| 105 | 93 | // Phase 2: Linear scan allocation. |
|
| 106 | 94 | for b in 0..blockCount { |
|
| 107 | 95 | let block = &func.blocks[b]; |
|
| 108 | 96 | ||
| 109 | 97 | // Record every operand before allocation. Only current-block operands |
|
| 110 | 98 | // query this table, so every read is initialized by this scan. |
|
| 111 | 99 | // Entries for other registers need not be cleared between blocks. |
|
| 112 | 100 | for instr, i in block.instrs { |
|
| 113 | - | let mut ctx = LastUseCtx { lastUse, index: i }; |
|
| 114 | - | il::forEachReg(instr, recordLastUseCb, &mut ctx as &mut opaque); |
|
| 101 | + | let mut registers = il::registers(&instr); |
|
| 102 | + | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 103 | + | recordLastUse(reg, &mut lastUse[..], i); |
|
| 104 | + | } |
|
| 115 | 105 | } |
|
| 116 | 106 | ||
| 117 | 107 | // Reset for new block. |
|
| 118 | 108 | set current.n = 0; |
|
| 119 | - | bitset::clearAll(&mut usedRegs); |
|
| 109 | + | bitset::clearAll(usedRegs); |
|
| 120 | 110 | ||
| 121 | 111 | // Mark all live-in values' registers as used. |
|
| 122 | 112 | // This ensures we don't reuse registers for values that flow in |
|
| 123 | 113 | // from predecessors, even at merge points with multiple predecessors. |
|
| 124 | 114 | // Values that are live-in but have no assignment yet (e.g. callee-saved |
|
| 125 | 115 | // function parameters not used before a phi block) are allocated now to |
|
| 126 | 116 | // prevent conflicts with block parameters. |
|
| 127 | - | let mut liveInIter = bitset::iter(&live.liveIn[b]); |
|
| 128 | - | while let ssaReg = bitset::iterNext(&mut liveInIter) { |
|
| 117 | + | let mut liveInIter = bitset::iter(liveness::liveInRow(live, b)); |
|
| 118 | + | while let ssaReg = bitset::iterNext(&mut liveInIter, liveness::liveInRow(live, b)) { |
|
| 129 | 119 | let reg = il::Reg { n: ssaReg }; |
|
| 130 | 120 | if not spill::isSpilled(spillInfo, reg) { |
|
| 131 | 121 | if let phys = assignments[ssaReg] { |
|
| 132 | - | bitset::put(&mut usedRegs, *phys as u32); |
|
| 122 | + | bitset::put(usedRegs, *phys as u32); |
|
| 133 | 123 | rmapSet(&mut current, ssaReg, phys); |
|
| 134 | 124 | } else { |
|
| 135 | - | set assignments[ssaReg] = rallocReg(&mut current, &mut usedRegs, ssaReg, allocatable, config.calleeSaved, spillInfo); |
|
| 125 | + | set assignments[ssaReg] = rallocReg(&mut current, usedRegs, ssaReg, allocatable, config.calleeSaved, spillInfo); |
|
| 136 | 126 | } |
|
| 137 | 127 | } |
|
| 138 | 128 | } |
|
| 139 | 129 | ||
| 140 | 130 | // Allocate block parameters. |
|
| 141 | 131 | for p in block.params { |
|
| 142 | 132 | if p.value.n < maxReg and not spill::isSpilled(spillInfo, p.value) { |
|
| 143 | - | set assignments[p.value.n] = rallocReg(&mut current, &mut usedRegs, p.value.n, allocatable, config.calleeSaved, spillInfo); |
|
| 133 | + | set assignments[p.value.n] = rallocReg(&mut current, usedRegs, p.value.n, allocatable, config.calleeSaved, spillInfo); |
|
| 144 | 134 | } |
|
| 145 | 135 | } |
|
| 146 | 136 | ||
| 147 | 137 | // Process each instruction. |
|
| 148 | 138 | for instr, i in block.instrs { |
|
| 149 | - | let mut ctx = InstrCtx { |
|
| 150 | - | current: &mut current, |
|
| 151 | - | usedRegs: &mut usedRegs, |
|
| 152 | - | lastUse, |
|
| 153 | - | live: live as *unsafe liveness::LiveInfo, |
|
| 154 | - | blockIdx: b, |
|
| 155 | - | instrIdx: i, |
|
| 156 | - | allocatable, |
|
| 157 | - | calleeSaved: config.calleeSaved, |
|
| 158 | - | assignments, |
|
| 159 | - | spillInfo: spillInfo as *unsafe spill::SpillInfo, |
|
| 160 | - | }; |
|
| 161 | - | il::forEachReg(instr, processInstrRegCb, &mut ctx as &mut opaque); |
|
| 139 | + | let currentRef: 'step = &mut current, usedRef = &mut usedRegs[..], |
|
| 140 | + | lastUseRef = &lastUse[..], liveRef = &*live, |
|
| 141 | + | assignmentsRef = &mut assignments[..], spillRef = &*spillInfo |
|
| 142 | + | where 'scratch: 'step in { |
|
| 143 | + | let mut ctx = InstrCtx 'scratch 'step { |
|
| 144 | + | current: currentRef, |
|
| 145 | + | usedRegs: usedRef, |
|
| 146 | + | lastUse: lastUseRef, |
|
| 147 | + | live: liveRef, |
|
| 148 | + | blockIdx: b, |
|
| 149 | + | instrIdx: i, |
|
| 150 | + | allocatable, |
|
| 151 | + | calleeSaved: config.calleeSaved, |
|
| 152 | + | assignments: assignmentsRef, |
|
| 153 | + | spillInfo: spillRef, |
|
| 154 | + | }; |
|
| 155 | + | let mut registers = il::registers(&instr); |
|
| 156 | + | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 157 | + | processInstrReg(reg, &mut ctx); |
|
| 158 | + | } |
|
| 159 | + | } |
|
| 162 | 160 | ||
| 163 | 161 | // Allocate destination. |
|
| 164 | 162 | if let dst = il::instrDst(instr) { |
|
| 165 | 163 | if dst.n < maxReg and not spill::isSpilled(spillInfo, dst) { |
|
| 166 | - | set assignments[dst.n] = rallocReg(&mut current, &mut usedRegs, dst.n, allocatable, config.calleeSaved, spillInfo); |
|
| 164 | + | set assignments[dst.n] = rallocReg(&mut current, usedRegs, dst.n, allocatable, config.calleeSaved, spillInfo); |
|
| 167 | 165 | } |
|
| 168 | 166 | } |
|
| 169 | 167 | } |
|
| 170 | 168 | } |
|
| 171 | 169 | // Compute bitmask of used callee-saved registers. |
| 178 | 176 | } |
|
| 179 | 177 | } |
|
| 180 | 178 | } |
|
| 181 | 179 | } |
|
| 182 | 180 | ||
| 183 | - | return AssignInfo { |
|
| 184 | - | assignments, |
|
| 181 | + | return AssignInfo 'scratch { |
|
| 182 | + | assignments: &assignments[..], |
|
| 185 | 183 | usedCalleeSaved, |
|
| 186 | 184 | }; |
|
| 187 | 185 | } |
|
| 188 | 186 | ||
| 189 | 187 | /// Create an empty register map. |
|
| 190 | - | unsafe fn createRegMap(arena: &mut alloc::Arena) -> RegMap throws (alloc::AllocError) { |
|
| 191 | - | let virtRegs = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_ACTIVE) as *mut [u32]; |
|
| 192 | - | let physRegs = try alloc::allocSlice(arena, @sizeOf(gen::Reg), @alignOf(gen::Reg), MAX_ACTIVE) as *mut [gen::Reg]; |
|
| 193 | - | ||
| 194 | - | return RegMap { virtRegs, physRegs, n: 0 }; |
|
| 188 | + | fn createRegMap 'scratch (storage: &Session 'scratch) -> RegMap 'scratch throws (alloc::AllocError) { |
|
| 189 | + | let virtRegs = try storage.fill(0 as u32, MAX_ACTIVE); |
|
| 190 | + | let physRegs = try storage.fill(gen::Reg(0), MAX_ACTIVE); |
|
| 191 | + | return RegMap 'scratch { virtRegs, physRegs, n: 0 }; |
|
| 195 | 192 | } |
|
| 196 | 193 | ||
| 197 | 194 | /// Find physical register for a virtual register in RegMap. |
|
| 198 | - | fn rmapFind(rmap: &RegMap, virtReg: u32) -> ?gen::Reg { |
|
| 195 | + | fn rmapFind 'scratch (rmap: &RegMap 'scratch, virtReg: u32) -> ?gen::Reg { |
|
| 199 | 196 | for i in 0..rmap.n { |
|
| 200 | 197 | if rmap.virtRegs[i] == virtReg { |
|
| 201 | 198 | return rmap.physRegs[i]; |
|
| 202 | 199 | } |
|
| 203 | 200 | } |
|
| 204 | 201 | return nil; |
|
| 205 | 202 | } |
|
| 206 | 203 | ||
| 207 | 204 | /// Add a mapping to the register map. |
|
| 208 | - | fn rmapSet(rmap: &mut RegMap, virtReg: u32, physReg: gen::Reg) { |
|
| 205 | + | fn rmapSet 'scratch (rmap: &mut RegMap 'scratch, virtReg: u32, physReg: gen::Reg) { |
|
| 209 | 206 | assert rmap.n < MAX_ACTIVE, "rmapSet: register map overflow"; |
|
| 210 | 207 | set rmap.virtRegs[rmap.n] = virtReg; |
|
| 211 | 208 | set rmap.physRegs[rmap.n] = physReg; |
|
| 212 | 209 | set rmap.n += 1; |
|
| 213 | 210 | } |
|
| 214 | 211 | ||
| 215 | 212 | /// Remove a mapping from the register map and return its physical register. |
|
| 216 | - | fn rmapRemove(rmap: &mut RegMap, virtReg: u32) -> ?gen::Reg { |
|
| 213 | + | fn rmapRemove 'scratch (rmap: &mut RegMap 'scratch, virtReg: u32) -> ?gen::Reg { |
|
| 217 | 214 | for i in 0..rmap.n { |
|
| 218 | 215 | if rmap.virtRegs[i] == virtReg { |
|
| 219 | 216 | let phys = rmap.physRegs[i]; |
|
| 220 | 217 | // Swap with last and decrement. |
|
| 221 | 218 | set rmap.n -= 1; |
| 228 | 225 | } |
|
| 229 | 226 | return nil; |
|
| 230 | 227 | } |
|
| 231 | 228 | ||
| 232 | 229 | /// Find first free register in pool, allocate it, return it. |
|
| 233 | - | unsafe fn findFreeInPool(usedRegs: &mut bitset::Bitset, current: &mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg { |
|
| 234 | - | for i in 0..pool.len { |
|
| 235 | - | let r = pool[i]; |
|
| 230 | + | fn findFreeInPool 'scratch (usedRegs: &mut [u32], current: &mut RegMap 'scratch, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg { |
|
| 231 | + | for r in pool { |
|
| 236 | 232 | if not bitset::contains(usedRegs, *r as u32) { |
|
| 237 | 233 | bitset::put(usedRegs, *r as u32); |
|
| 238 | 234 | rmapSet(current, ssaReg, r); |
|
| 239 | 235 | return r; |
|
| 240 | 236 | } |
| 242 | 238 | return nil; |
|
| 243 | 239 | } |
|
| 244 | 240 | ||
| 245 | 241 | /// Allocate a physical register for an SSA register. |
|
| 246 | 242 | /// Cross-call values are steered to callee-saved registers. |
|
| 247 | - | unsafe fn rallocReg( |
|
| 248 | - | current: &mut RegMap, |
|
| 249 | - | usedRegs: &mut bitset::Bitset, |
|
| 243 | + | fn rallocReg 'scratch ( |
|
| 244 | + | current: &mut RegMap 'scratch, |
|
| 245 | + | usedRegs: &mut [u32], |
|
| 250 | 246 | ssaReg: u32, |
|
| 251 | 247 | allocatable: *[gen::Reg], |
|
| 252 | 248 | calleeSaved: *[gen::Reg], |
|
| 253 | - | spillInfo: &spill::SpillInfo |
|
| 249 | + | spillInfo: &spill::SpillInfo 'scratch |
|
| 254 | 250 | ) -> gen::Reg { |
|
| 255 | 251 | // Check if already assigned. |
|
| 256 | 252 | if let phys = rmapFind(current, ssaReg) { |
|
| 257 | 253 | return phys; |
|
| 258 | 254 | } |
|
| 259 | - | let crossCall = bitset::contains(&spillInfo.calleeClass, ssaReg); |
|
| 260 | 255 | // Allocate from appropriate pool. Cross-call values must use callee-saved |
|
| 261 | 256 | // registers since they are live across function calls. |
|
| 262 | - | if crossCall { |
|
| 257 | + | if bitset::contains(spillInfo.calleeClass, ssaReg) { |
|
| 263 | 258 | if let r = findFreeInPool(usedRegs, current, ssaReg, calleeSaved) { |
|
| 264 | 259 | return r; |
|
| 265 | 260 | } |
|
| 266 | 261 | panic "rallocReg: no callee-saved register for cross-call value"; |
|
| 267 | 262 | } |
| 269 | 264 | return r; |
|
| 270 | 265 | } |
|
| 271 | 266 | panic "rallocReg: no free register, spilling fault"; |
|
| 272 | 267 | } |
|
| 273 | 268 | ||
| 274 | - | /// Record the current index; forward traversal leaves the last operand use. |
|
| 275 | - | unsafe fn recordLastUseCb(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 276 | - | let index = (ctxPtr as &mut LastUseCtx).index; |
|
| 277 | - | recordLastUse(reg, &mut (ctxPtr as &mut LastUseCtx).lastUse[..], index); |
|
| 278 | - | } |
|
| 279 | - | ||
| 280 | 269 | /// Record the last instruction that uses the register. |
|
| 281 | 270 | fn recordLastUse(reg: il::Reg, lastUse: &mut [u32], index: u32) { |
|
| 282 | 271 | set lastUse[reg.n] = index; |
|
| 283 | 272 | } |
|
| 284 | 273 | ||
| 285 | - | /// Free operands with no later block use or live-out use, then allocate missing uses. |
|
| 286 | - | unsafe fn processInstrRegCb(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 287 | - | processInstrReg(reg, ctxPtr as &mut InstrCtx); |
|
| 288 | - | } |
|
| 289 | - | ||
| 290 | 274 | /// Release expired registers and assign a register for this operand. |
|
| 291 | - | unsafe fn processInstrReg(reg: il::Reg, ctx: &mut InstrCtx) { |
|
| 292 | - | if not (bitset::contains(&ctx.live.liveOut[ctx.blockIdx], reg.n) or ctx.lastUse[reg.n] > ctx.instrIdx) { |
|
| 275 | + | fn processInstrReg 'scratch 'step (reg: il::Reg, ctx: &mut InstrCtx 'scratch 'step) where 'scratch: 'step { |
|
| 276 | + | if not (bitset::contains(liveness::liveOutRow(ctx.live, ctx.blockIdx), reg.n) or ctx.lastUse[reg.n] > ctx.instrIdx) { |
|
| 293 | 277 | if let phys = rmapRemove(ctx.current, reg.n) { |
|
| 294 | 278 | bitset::clear(ctx.usedRegs, *phys as u32); |
|
| 295 | 279 | } |
|
| 296 | 280 | } |
|
| 297 | - | assert reg.n < ctx.assignments.len, "processInstrRegCb: register out of bounds"; |
|
| 281 | + | assert reg.n < ctx.assignments.len, "processInstrReg: register out of bounds"; |
|
| 298 | 282 | if spill::isSpilled(ctx.spillInfo, reg) { |
|
| 299 | 283 | return; // Spilled values don't get physical registers. |
|
| 300 | 284 | } |
|
| 301 | 285 | if ctx.assignments[reg.n] == nil { |
|
| 302 | - | let current = ctx.current; |
|
| 303 | - | let usedRegs = ctx.usedRegs; |
|
| 304 | 286 | set ctx.assignments[reg.n] = rallocReg( |
|
| 305 | - | current, usedRegs, reg.n, ctx.allocatable, |
|
| 287 | + | ctx.current, ctx.usedRegs, reg.n, ctx.allocatable, |
|
| 306 | 288 | ctx.calleeSaved, ctx.spillInfo |
|
| 307 | 289 | ); |
|
| 308 | 290 | } |
|
| 309 | 291 | } |
lib/std/lang/gen/regalloc/liveness.rad
+86 -100
| 22 | 22 | //! let live = liveness::analyze(func, ...); |
|
| 23 | 23 | //! if not liveness::hasLaterUse(&live, func, blockIdx, instrIdx, reg) { |
|
| 24 | 24 | //! // `reg` dies at this instruction. |
|
| 25 | 25 | //! } |
|
| 26 | 26 | ||
| 27 | - | use std::mem; |
|
| 27 | + | @test mod tests; |
|
| 28 | + | ||
| 28 | 29 | use std::lang::il; |
|
| 29 | 30 | use std::lang::alloc; |
|
| 30 | 31 | use std::lang::gen::bitset; |
|
| 31 | 32 | ||
| 32 | 33 | /// Maximum number of SSA registers supported. |
|
| 33 | 34 | export constant MAX_SSA_REGS: u32 = 8192; |
|
| 34 | 35 | ||
| 35 | 36 | /// Liveness information for a function. |
|
| 36 | - | export record LiveInfo: Copy { |
|
| 37 | - | /// Per-block live-in sets (indexed by block index). |
|
| 38 | - | liveIn: *unsafe mut [bitset::Bitset], |
|
| 39 | - | /// Per-block live-out sets (indexed by block index). |
|
| 40 | - | liveOut: *unsafe mut [bitset::Bitset], |
|
| 41 | - | /// Per-block defs sets (registers defined in block). |
|
| 42 | - | defs: *unsafe mut [bitset::Bitset], |
|
| 43 | - | /// Per-block uses sets (registers used before defined in block). |
|
| 44 | - | uses: *unsafe mut [bitset::Bitset], |
|
| 37 | + | export record LiveInfo: 'scratch + Copy { |
|
| 38 | + | /// Per-block live-in words, stored in consecutive rows. |
|
| 39 | + | liveIn: &'scratch [u32], |
|
| 40 | + | /// Per-block live-out words, stored in consecutive rows. |
|
| 41 | + | liveOut: &'scratch [u32], |
|
| 42 | + | /// Per-block definition words, stored in consecutive rows. |
|
| 43 | + | defs: &'scratch [u32], |
|
| 44 | + | /// Per-block use-before-definition words, stored in consecutive rows. |
|
| 45 | + | uses: &'scratch [u32], |
|
| 46 | + | /// Number of words in one block row. |
|
| 47 | + | words: u32, |
|
| 45 | 48 | /// Number of blocks. |
|
| 46 | 49 | blockCount: u32, |
|
| 47 | 50 | /// Maximum register number used. |
|
| 48 | 51 | maxReg: u32, |
|
| 49 | 52 | } |
|
| 50 | 53 | ||
| 51 | - | /// Context for collecting defs and uses during block analysis. |
|
| 52 | - | record DefsUses: Copy { |
|
| 53 | - | defs: *unsafe bitset::Bitset, |
|
| 54 | - | uses: *unsafe mut bitset::Bitset, |
|
| 55 | - | } |
|
| 56 | - | ||
| 57 | - | /// Context for searching for a specific register in an instruction. |
|
| 58 | - | record FindCtx: Copy { |
|
| 59 | - | target: u32, |
|
| 60 | - | found: bool, |
|
| 61 | - | } |
|
| 62 | - | ||
| 63 | 54 | /// Compute liveness by growing live sets until no live-in set changes. |
|
| 64 | - | export unsafe fn analyze(func: *unsafe il::Fn, arena: &mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) { |
|
| 55 | + | export unsafe fn analyze 'scratch (func: &il::Fn, storage: &Session 'scratch) -> LiveInfo 'scratch throws (alloc::AllocError) { |
|
| 65 | 56 | let blockCount = func.blocks.len; |
|
| 66 | 57 | if blockCount == 0 { |
|
| 67 | - | return LiveInfo { |
|
| 68 | - | liveIn: &mut [], |
|
| 69 | - | liveOut: &mut [], |
|
| 70 | - | defs: &mut [], |
|
| 71 | - | uses: &mut [], |
|
| 72 | - | blockCount: 0, |
|
| 73 | - | maxReg: 0, |
|
| 74 | - | }; |
|
| 58 | + | let empty = try storage.fill(0 as u32, 0); |
|
| 59 | + | let frozen: &'scratch [u32] = &empty[..]; |
|
| 60 | + | return LiveInfo 'scratch { liveIn: frozen, liveOut: frozen, defs: frozen, uses: frozen, words: 0, blockCount: 0, maxReg: 0 }; |
|
| 75 | 61 | } |
|
| 76 | 62 | ||
| 77 | 63 | // Find max register number. |
|
| 78 | 64 | let mut maxReg: u32 = 0; |
|
| 79 | 65 | for p in func.params { |
| 82 | 68 | for b in 0..blockCount { |
|
| 83 | 69 | let block = &func.blocks[b]; |
|
| 84 | 70 | for p in block.params { |
|
| 85 | 71 | set maxReg = maxRegNum(p.value.n, maxReg); |
|
| 86 | 72 | } |
|
| 87 | - | for i in 0..block.instrs.len { |
|
| 88 | - | il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as &mut opaque); |
|
| 89 | - | if let dst = il::instrDst(block.instrs[i]) { |
|
| 73 | + | for instr in block.instrs { |
|
| 74 | + | let mut registers = il::registers(&instr); |
|
| 75 | + | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 76 | + | updateMaxReg(reg, &mut maxReg); |
|
| 77 | + | } |
|
| 78 | + | if let dst = il::instrDst(instr) { |
|
| 90 | 79 | set maxReg = maxRegNum(dst.n, maxReg); |
|
| 91 | 80 | } |
|
| 92 | 81 | } |
|
| 93 | 82 | } |
|
| 94 | - | if maxReg > MAX_SSA_REGS { throw alloc::AllocError::OutOfMemory; } |
|
| 95 | - | // Allocate per-block bitsets. |
|
| 96 | - | let liveIn = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset]; |
|
| 97 | - | let liveOut = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset]; |
|
| 98 | - | let defs = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset]; |
|
| 99 | - | let uses = try alloc::allocRawSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *unsafe mut [bitset::Bitset]; |
|
| 100 | - | ||
| 101 | - | for b in 0..blockCount { |
|
| 102 | - | set liveIn[b] = try bitset::allocate(arena, maxReg); |
|
| 103 | - | set liveOut[b] = try bitset::allocate(arena, maxReg); |
|
| 104 | - | set defs[b] = try bitset::allocate(arena, maxReg); |
|
| 105 | - | set uses[b] = try bitset::allocate(arena, maxReg); |
|
| 83 | + | if maxReg > MAX_SSA_REGS { |
|
| 84 | + | throw alloc::AllocError::OutOfMemory; |
|
| 106 | 85 | } |
|
| 86 | + | // Allocate one contiguous word matrix for each set class. |
|
| 87 | + | let words = bitset::wordsFor(maxReg); |
|
| 88 | + | let count64 = blockCount as u64 * words as u64; |
|
| 89 | + | if count64 > 0xFFFFFFFF { |
|
| 90 | + | throw alloc::AllocError::OutOfMemory; |
|
| 91 | + | } |
|
| 92 | + | let count = count64 as u32; |
|
| 93 | + | let liveIn = try storage.fill(0 as u32, count); |
|
| 94 | + | let liveOut = try storage.fill(0 as u32, count); |
|
| 95 | + | let defs = try storage.fill(0 as u32, count); |
|
| 96 | + | let uses = try storage.fill(0 as u32, count); |
|
| 107 | 97 | ||
| 108 | 98 | // Compute local defs and uses for each block. |
|
| 109 | 99 | for b in 0..blockCount { |
|
| 110 | - | computeLocalDefsUses(&func.blocks[b], &mut defs[b], &mut uses[b]); |
|
| 100 | + | computeLocalDefsUses(&func.blocks[b], &mut defs[b * words..(b + 1) * words], &mut uses[b * words..(b + 1) * words]); |
|
| 111 | 101 | } |
|
| 112 | 102 | // Live sets grow monotonically from empty sets; no scratch set is needed. |
|
| 113 | 103 | let mut changed = true; |
|
| 114 | 104 | ||
| 115 | 105 | while changed { |
| 120 | 110 | while b > 0 { |
|
| 121 | 111 | set b -= 1; |
|
| 122 | 112 | let block = &func.blocks[b]; |
|
| 123 | 113 | ||
| 124 | 114 | // Add successor live-in sets directly to this block's live-out set. |
|
| 125 | - | addSuccessorLiveIn(func, block, liveIn, &mut liveOut[b]); |
|
| 115 | + | addSuccessorLiveIn(func, block, liveIn, words, &mut liveOut[b * words..(b + 1) * words]); |
|
| 126 | 116 | ||
| 127 | - | if computeAndUpdateLiveIn(&mut liveIn[b], &liveOut[b], &defs[b], &uses[b]) { |
|
| 117 | + | if computeAndUpdateLiveIn(&mut liveIn[b * words..(b + 1) * words], &liveOut[b * words..(b + 1) * words], &defs[b * words..(b + 1) * words], &uses[b * words..(b + 1) * words]) { |
|
| 128 | 118 | set changed = true; |
|
| 129 | 119 | } |
|
| 130 | 120 | } |
|
| 131 | 121 | } |
|
| 132 | - | return LiveInfo { liveIn, liveOut, defs, uses, blockCount, maxReg }; |
|
| 122 | + | return LiveInfo 'scratch { liveIn: &liveIn[..], liveOut: &liveOut[..], defs: &defs[..], uses: &uses[..], words, blockCount, maxReg }; |
|
| 133 | 123 | } |
|
| 134 | 124 | ||
| 135 | 125 | /// Compute `liveIn = uses | (liveOut - defs)` and update `dst`. |
|
| 136 | 126 | /// Returns `true` if `dst` changed. Combined loop avoids multiple passes. |
|
| 137 | - | unsafe fn computeAndUpdateLiveIn( |
|
| 138 | - | dst: *unsafe mut bitset::Bitset, |
|
| 139 | - | liveOut: *unsafe bitset::Bitset, |
|
| 140 | - | defs: *unsafe bitset::Bitset, |
|
| 141 | - | uses: *unsafe bitset::Bitset |
|
| 127 | + | fn computeAndUpdateLiveIn( |
|
| 128 | + | dst: &mut [u32], |
|
| 129 | + | liveOut: &[u32], |
|
| 130 | + | defs: &[u32], |
|
| 131 | + | uses: &[u32] |
|
| 142 | 132 | ) -> bool { |
|
| 143 | - | let numWords = dst.bits.len; |
|
| 144 | 133 | let mut changed = false; |
|
| 145 | - | for i in 0..numWords { |
|
| 146 | - | let newWord = uses.bits[i] | (liveOut.bits[i] & ~defs.bits[i]); |
|
| 147 | - | if dst.bits[i] <> newWord { |
|
| 148 | - | set dst.bits[i] = newWord; |
|
| 134 | + | for i in 0..dst.len { |
|
| 135 | + | let newWord = uses[i] | (liveOut[i] & ~defs[i]); |
|
| 136 | + | if dst[i] <> newWord { |
|
| 137 | + | set dst[i] = newWord; |
|
| 149 | 138 | set changed = true; |
|
| 150 | 139 | } |
|
| 151 | 140 | } |
|
| 152 | 141 | return changed; |
|
| 153 | 142 | } |
|
| 154 | 143 | ||
| 155 | 144 | /// Compute local defs and uses for a single block. |
|
| 156 | - | unsafe fn computeLocalDefsUses(block: *unsafe il::Block, defs: *unsafe mut bitset::Bitset, uses: *unsafe mut bitset::Bitset) { |
|
| 145 | + | unsafe fn computeLocalDefsUses(block: &il::Block, defs: &mut [u32], uses: &mut [u32]) { |
|
| 157 | 146 | for p in block.params { |
|
| 158 | 147 | bitset::put(defs, p.value.n); |
|
| 159 | 148 | } |
|
| 160 | - | for i in 0..block.instrs.len { |
|
| 161 | - | let instr = block.instrs[i]; |
|
| 162 | - | let mut ctx = DefsUses { defs, uses }; |
|
| 163 | - | il::forEachReg(instr, addUseCallback, &mut ctx as &mut opaque); |
|
| 149 | + | for instr in block.instrs { |
|
| 150 | + | let mut registers = il::registers(&instr); |
|
| 151 | + | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 152 | + | addUse(reg, defs, uses); |
|
| 153 | + | } |
|
| 164 | 154 | ||
| 165 | 155 | if let dst = il::instrDst(instr) { |
|
| 166 | 156 | bitset::put(defs, dst.n); |
|
| 167 | 157 | } |
|
| 168 | 158 | } |
|
| 169 | 159 | } |
|
| 170 | 160 | ||
| 171 | - | /// Callback for [`il::forEachReg`]: adds register to uses if not already defined. |
|
| 172 | - | unsafe fn addUseCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 173 | - | addUse(reg, ctx as &mut DefsUses); |
|
| 174 | - | } |
|
| 175 | - | ||
| 176 | 161 | /// Add an undefined register to the use set. |
|
| 177 | - | unsafe fn addUse(reg: il::Reg, c: &mut DefsUses) { |
|
| 178 | - | if not bitset::contains(c.defs, reg.n) { |
|
| 179 | - | bitset::put(c.uses, reg.n); |
|
| 162 | + | fn addUse(reg: il::Reg, defs: &[u32], uses: &mut [u32]) { |
|
| 163 | + | if not bitset::contains(defs, reg.n) { |
|
| 164 | + | bitset::put(uses, reg.n); |
|
| 180 | 165 | } |
|
| 181 | 166 | } |
|
| 182 | 167 | ||
| 183 | - | /// Callback for [`il::forEachReg`]: updates max register number. |
|
| 184 | - | unsafe fn maxRegCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 185 | - | updateMaxReg(reg, ctx as &mut u32); |
|
| 186 | - | } |
|
| 187 | - | ||
| 188 | 168 | /// Update the largest register number. |
|
| 189 | 169 | fn updateMaxReg(reg: il::Reg, max: &mut u32) { |
|
| 190 | 170 | set *max = maxRegNum(reg.n, *max); |
|
| 191 | 171 | } |
|
| 192 | 172 | ||
| 193 | 173 | /// Return the larger of n+1 and current. |
|
| 194 | 174 | fn maxRegNum(n: u32, current: u32) -> u32 { |
|
| 195 | - | if n >= MAX_SSA_REGS { return MAX_SSA_REGS + 1; } |
|
| 175 | + | if n >= MAX_SSA_REGS { |
|
| 176 | + | return MAX_SSA_REGS + 1; |
|
| 177 | + | } |
|
| 196 | 178 | if n + 1 > current { |
|
| 197 | 179 | return n + 1; |
|
| 198 | 180 | } |
|
| 199 | 181 | return current; |
|
| 200 | 182 | } |
|
| 201 | 183 | ||
| 202 | 184 | /// Add successor live-in sets to the block's live-out set. |
|
| 203 | - | unsafe fn addSuccessorLiveIn(func: *unsafe il::Fn, block: *unsafe il::Block, liveIn: *unsafe [bitset::Bitset], liveOut: *unsafe mut bitset::Bitset) { |
|
| 185 | + | unsafe fn addSuccessorLiveIn(func: &il::Fn, block: &il::Block, liveIn: &[u32], words: u32, liveOut: &mut [u32]) { |
|
| 204 | 186 | if block.instrs.len == 0 { |
|
| 205 | 187 | return; |
|
| 206 | 188 | } |
|
| 207 | 189 | let term = block.instrs[block.instrs.len - 1]; |
|
| 208 | 190 | ||
| 209 | 191 | match term { |
|
| 210 | 192 | case il::Instr::Jmp { target, .. } => |
|
| 211 | - | unionBlockLiveIn(target, liveIn, liveOut), |
|
| 193 | + | unionBlockLiveIn(target, liveIn, words, liveOut), |
|
| 212 | 194 | case il::Instr::Br { thenTarget, elseTarget, .. } => { |
|
| 213 | - | unionBlockLiveIn(thenTarget, liveIn, liveOut); |
|
| 214 | - | unionBlockLiveIn(elseTarget, liveIn, liveOut); |
|
| 195 | + | unionBlockLiveIn(thenTarget, liveIn, words, liveOut); |
|
| 196 | + | unionBlockLiveIn(elseTarget, liveIn, words, liveOut); |
|
| 215 | 197 | }, |
|
| 216 | 198 | case il::Instr::Switch { defaultTarget, cases, .. } => { |
|
| 217 | - | unionBlockLiveIn(defaultTarget, liveIn, liveOut); |
|
| 199 | + | unionBlockLiveIn(defaultTarget, liveIn, words, liveOut); |
|
| 218 | 200 | for c in cases { |
|
| 219 | - | unionBlockLiveIn(c.target, liveIn, liveOut); |
|
| 201 | + | unionBlockLiveIn(c.target, liveIn, words, liveOut); |
|
| 220 | 202 | } |
|
| 221 | 203 | }, |
|
| 222 | 204 | else => {}, |
|
| 223 | 205 | } |
|
| 224 | 206 | } |
|
| 225 | 207 | ||
| 226 | 208 | /// Union a target block's live-in set into the block's live-out set. |
|
| 227 | - | unsafe fn unionBlockLiveIn(target: u32, liveIn: *unsafe [bitset::Bitset], liveOut: *unsafe mut bitset::Bitset) { |
|
| 228 | - | bitset::union_(liveOut, &liveIn[target]); |
|
| 209 | + | fn unionBlockLiveIn(target: u32, liveIn: &[u32], words: u32, liveOut: &mut [u32]) { |
|
| 210 | + | bitset::union_(liveOut, &liveIn[target * words..(target + 1) * words]); |
|
| 229 | 211 | } |
|
| 230 | 212 | ||
| 231 | 213 | /// Check if a register has any use after this instruction. |
|
| 232 | - | export unsafe fn hasLaterUse(info: *unsafe LiveInfo, func: *unsafe il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 214 | + | export unsafe fn hasLaterUse 'scratch (info: &LiveInfo 'scratch, func: &il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 233 | 215 | let block = &func.blocks[blockIdx]; |
|
| 234 | 216 | ||
| 235 | - | if bitset::contains(&info.liveOut[blockIdx], reg.n) { |
|
| 217 | + | if bitset::contains(liveOutRow(info, blockIdx), reg.n) { |
|
| 236 | 218 | return true; |
|
| 237 | 219 | } |
|
| 238 | 220 | for i in (instrIdx + 1)..block.instrs.len { |
|
| 239 | 221 | if instrUsesReg(block.instrs[i], reg) { |
|
| 240 | 222 | return true; |
| 243 | 225 | return false; |
|
| 244 | 226 | } |
|
| 245 | 227 | ||
| 246 | 228 | /// Check if an instruction uses a specific register. |
|
| 247 | 229 | unsafe fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool { |
|
| 248 | - | let mut ctx = FindCtx { target: reg.n, found: false }; |
|
| 249 | - | il::forEachReg(instr, findRegCallback, &mut ctx as &mut opaque); |
|
| 250 | - | return ctx.found; |
|
| 230 | + | let mut registers = il::registers(&instr); |
|
| 231 | + | while let source = il::nextReg(&mut registers, &instr) { |
|
| 232 | + | if source.n == reg.n { |
|
| 233 | + | return true; |
|
| 234 | + | } |
|
| 235 | + | } |
|
| 236 | + | return false; |
|
| 251 | 237 | } |
|
| 252 | 238 | ||
| 253 | - | /// Callback for [`il::forEachReg`]: sets found if register matches target. |
|
| 254 | - | unsafe fn findRegCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 255 | - | findReg(reg, ctx as &mut FindCtx); |
|
| 239 | + | /// Borrow the live-in words for one block. |
|
| 240 | + | export fn liveInRow 'scratch (info: &LiveInfo 'scratch, block: u32) -> &'scratch [u32] { |
|
| 241 | + | assert block < info.blockCount; |
|
| 242 | + | return &info.liveIn[block * info.words..(block + 1) * info.words]; |
|
| 256 | 243 | } |
|
| 257 | 244 | ||
| 258 | - | /// Set the match flag when the register equals the target. |
|
| 259 | - | fn findReg(reg: il::Reg, c: &mut FindCtx) { |
|
| 260 | - | if reg.n == c.target { |
|
| 261 | - | set c.found = true; |
|
| 262 | - | } |
|
| 245 | + | /// Borrow the live-out words for one block. |
|
| 246 | + | export fn liveOutRow 'scratch (info: &LiveInfo 'scratch, block: u32) -> &'scratch [u32] { |
|
| 247 | + | assert block < info.blockCount; |
|
| 248 | + | return &info.liveOut[block * info.words..(block + 1) * info.words]; |
|
| 263 | 249 | } |
lib/std/lang/gen/regalloc/liveness/tests.rad
added
+69 -0
| 1 | + | //! Tests for liveness analysis. |
|
| 2 | + | ||
| 3 | + | use std::testing; |
|
| 4 | + | use std::lang::alloc; |
|
| 5 | + | use std::lang::il; |
|
| 6 | + | use std::lang::gen::bitset; |
|
| 7 | + | ||
| 8 | + | ||
| 9 | + | /// Construct a block whose instruction storage belongs to the caller. |
|
| 10 | + | unsafe fn block(name: *[u8], instructions: *unsafe mut [il::Instr]) -> il::Block { |
|
| 11 | + | return il::Block { label: name, params: &[], instrs: instructions, |
|
| 12 | + | locs: &[], preds: &[], loopDepth: 0 }; |
|
| 13 | + | } |
|
| 14 | + | ||
| 15 | + | /// Check one bit in each word of a three-word row. |
|
| 16 | + | fn check(words: &[u32], first: bool, second: bool, third: bool) throws (testing::TestError) { |
|
| 17 | + | try testing::expect(words.len == 3); |
|
| 18 | + | try testing::expect(bitset::contains(words, 0) == first); |
|
| 19 | + | try testing::expect(bitset::contains(words, 33) == second); |
|
| 20 | + | try testing::expect(bitset::contains(words, 66) == third); |
|
| 21 | + | } |
|
| 22 | + | ||
| 23 | + | @test unsafe fn testLoopLiveness() throws (testing::TestError) { |
|
| 24 | + | let r0 = il::Reg { n: 0 }; |
|
| 25 | + | let r33 = il::Reg { n: 33 }; |
|
| 26 | + | let r66 = il::Reg { n: 66 }; |
|
| 27 | + | let mut entry = [ |
|
| 28 | + | il::Instr::Copy { dst: r0, val: il::Val::Imm(7) }, |
|
| 29 | + | il::Instr::Jmp { target: 1, args: &mut [] }, |
|
| 30 | + | ]; |
|
| 31 | + | let mut head = [ |
|
| 32 | + | il::Instr::Copy { dst: r33, val: il::Val::Reg(r0) }, |
|
| 33 | + | il::Instr::Br { op: il::CmpOp::Eq, typ: il::Type::W64, |
|
| 34 | + | a: il::Val::Reg(r33), b: il::Val::Imm(0), |
|
| 35 | + | thenTarget: 2, thenArgs: &mut [], elseTarget: 3, elseArgs: &mut [] }, |
|
| 36 | + | ]; |
|
| 37 | + | let mut body = [ |
|
| 38 | + | il::Instr::BinOp { op: il::BinOp::Add, typ: il::Type::W64, |
|
| 39 | + | dst: r66, a: il::Val::Reg(r33), b: il::Val::Imm(1) }, |
|
| 40 | + | il::Instr::Jmp { target: 1, args: &mut [] }, |
|
| 41 | + | ]; |
|
| 42 | + | let mut exit = [il::Instr::Ret { val: il::Val::Reg(r33) }]; |
|
| 43 | + | let blocks = [block("entry", &mut entry[..]), block("head", &mut head[..]), |
|
| 44 | + | block("body", &mut body[..]), block("exit", &mut exit[..])]; |
|
| 45 | + | let function = il::Fn { name: "loop", params: &[], returnType: il::Type::W64, |
|
| 46 | + | isExtern: false, isLeaf: true, blocks: &blocks[..] }; |
|
| 47 | + | static DATA: [u8; 8192] = [0; 8192]; |
|
| 48 | + | let mut arena = alloc::new(&mut DATA[..]); |
|
| 49 | + | let mut answer: u32 = 0; |
|
| 50 | + | use arena as analysis in { |
|
| 51 | + | let live = try! super::analyze(&function, &analysis); |
|
| 52 | + | try testing::expect(live.blockCount == 4); |
|
| 53 | + | try testing::expect(live.maxReg == 67); |
|
| 54 | + | try testing::expect(live.words == 3); |
|
| 55 | + | try check(super::liveInRow(&live, 0), false, false, false); |
|
| 56 | + | try check(super::liveOutRow(&live, 0), true, false, false); |
|
| 57 | + | try check(super::liveInRow(&live, 1), true, false, false); |
|
| 58 | + | try check(super::liveOutRow(&live, 1), true, true, false); |
|
| 59 | + | try check(super::liveInRow(&live, 2), true, true, false); |
|
| 60 | + | try check(super::liveOutRow(&live, 2), true, false, false); |
|
| 61 | + | try check(super::liveInRow(&live, 3), false, true, false); |
|
| 62 | + | try check(super::liveOutRow(&live, 3), false, false, false); |
|
| 63 | + | try check(&live.defs[6..9], false, false, true); |
|
| 64 | + | try check(&live.uses[6..9], false, true, false); |
|
| 65 | + | set answer = 42; |
|
| 66 | + | } |
|
| 67 | + | alloc::reset(&mut arena); |
|
| 68 | + | try testing::expect(answer == 42); |
|
| 69 | + | } |
lib/std/lang/gen/regalloc/spill.rad
+66 -81
| 47 | 47 | /// Number of uses (weighted by loop depth). |
|
| 48 | 48 | uses: u32, |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Spill decision for a function. |
|
| 52 | - | export record SpillInfo: Copy { |
|
| 52 | + | export record SpillInfo: 'scratch + Copy { |
|
| 53 | 53 | /// SSA register mapped to stack slot offset. `-1` means not spilled. |
|
| 54 | - | slots: *[i32], |
|
| 54 | + | slots: &'scratch [i32], |
|
| 55 | 55 | /// Total spill frame size needed in bytes. |
|
| 56 | 56 | frameSize: i32, |
|
| 57 | 57 | /// Values that must be allocated in callee-saved registers. |
|
| 58 | - | calleeClass: bitset::Bitset, |
|
| 58 | + | calleeClass: &'scratch [u32], |
|
| 59 | 59 | /// Maximum SSA register number. |
|
| 60 | 60 | maxReg: u32, |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Candidate buffer for spill decisions. |
|
| 64 | 64 | record Candidates: Copy { |
|
| 65 | - | entries: [CostEntry; 256], |
|
| 65 | + | entries: [CostEntry; MAX_CANDIDATES], |
|
| 66 | 66 | n: u32, |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | 69 | /// Entry for cost sorting. |
|
| 70 | 70 | record CostEntry: Copy { |
|
| 71 | 71 | reg: u32, |
|
| 72 | 72 | cost: u32, |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | - | /// Context for counting register uses. |
|
| 76 | - | record CountCtx: Copy { |
|
| 77 | - | costs: *unsafe mut [SpillCost], |
|
| 78 | - | weight: u32, |
|
| 79 | - | } |
|
| 80 | - | ||
| 81 | 75 | /// Analyze a function and determine which values need spill slots. |
|
| 82 | - | export unsafe fn analyze( |
|
| 83 | - | func: *unsafe il::Fn, |
|
| 84 | - | live: &liveness::LiveInfo, |
|
| 76 | + | export unsafe fn analyze 'scratch ( |
|
| 77 | + | func: &il::Fn, |
|
| 78 | + | live: &liveness::LiveInfo 'scratch, |
|
| 85 | 79 | numRegs: u32, |
|
| 86 | 80 | numCalleeSaved: u32, |
|
| 87 | 81 | slotSize: u32, |
|
| 88 | - | arena: &mut alloc::Arena |
|
| 89 | - | ) -> SpillInfo throws (alloc::AllocError) { |
|
| 82 | + | storage: &Session 'scratch |
|
| 83 | + | ) -> SpillInfo 'scratch throws (alloc::AllocError) { |
|
| 90 | 84 | let maxReg = live.maxReg; |
|
| 91 | 85 | if maxReg == 0 { |
|
| 92 | - | let calleeClass = try bitset::allocate(arena, 0); |
|
| 93 | - | return SpillInfo { |
|
| 94 | - | slots: &mut [], |
|
| 86 | + | let calleeClass = try bitset::allocate(storage, 0); |
|
| 87 | + | return SpillInfo 'scratch { |
|
| 88 | + | slots: try storage.fill(-1 as i32, 0), |
|
| 95 | 89 | frameSize: 0, |
|
| 96 | - | calleeClass, |
|
| 90 | + | calleeClass: &calleeClass[..], |
|
| 97 | 91 | maxReg: 0, |
|
| 98 | 92 | }; |
|
| 99 | 93 | } |
|
| 100 | 94 | // Allocate spill slots array. |
|
| 101 | - | let slots = try alloc::allocSlice(arena, @sizeOf(i32), @alignOf(i32), maxReg) as *mut [i32]; |
|
| 102 | - | for i in 0..maxReg { |
|
| 103 | - | set slots[i] = -1; |
|
| 104 | - | } |
|
| 95 | + | let slots = try storage.fill(-1 as i32, maxReg); |
|
| 105 | 96 | // Allocate cost array. |
|
| 106 | - | let costs = try alloc::allocRawSlice(arena, @sizeOf(SpillCost), @alignOf(SpillCost), maxReg) as *unsafe mut [SpillCost]; |
|
| 107 | - | for i in 0..maxReg { |
|
| 108 | - | set costs[i] = SpillCost { defs: 0, uses: 0 }; |
|
| 109 | - | } |
|
| 97 | + | let costs = try storage.fill(SpillCost { defs: 0, uses: 0 }, maxReg); |
|
| 110 | 98 | // Phase 1: Calculate spill costs. |
|
| 111 | 99 | fillCosts(func, costs); |
|
| 112 | 100 | ||
| 113 | 101 | // Phase 2: Find values that exceed register pressure. |
|
| 114 | - | let mut spilled = try bitset::allocate(arena, maxReg); |
|
| 115 | - | let mut calleeClass = try bitset::allocate(arena, maxReg); |
|
| 116 | - | let mut scratch = try bitset::allocate(arena, maxReg); |
|
| 102 | + | let spilled = try bitset::allocate(storage, maxReg); |
|
| 103 | + | let calleeClass = try bitset::allocate(storage, maxReg); |
|
| 104 | + | let scratch = try bitset::allocate(storage, maxReg); |
|
| 117 | 105 | ||
| 118 | 106 | for b in 0..func.blocks.len { |
|
| 119 | 107 | let block = &func.blocks[b]; |
|
| 120 | 108 | ||
| 121 | 109 | // Start with live-out set. |
|
| 122 | - | bitset::copy(&mut scratch, &live.liveOut[b]); |
|
| 110 | + | bitset::copy(scratch, liveness::liveOutRow(live, b)); |
|
| 123 | 111 | ||
| 124 | 112 | // Walk instructions backwards. |
|
| 125 | 113 | let mut i = block.instrs.len; |
|
| 126 | 114 | while i > 0 { |
|
| 127 | 115 | set i -= 1; |
|
| 128 | 116 | let instr = block.instrs[i]; |
|
| 129 | 117 | ||
| 130 | 118 | // Limit register pressure before processing this instruction. |
|
| 131 | - | try limitPressure(&mut scratch, &mut spilled, costs, numRegs); |
|
| 119 | + | try limitPressure(scratch, spilled, costs, numRegs); |
|
| 132 | 120 | ||
| 133 | 121 | // Enforce cross-call pressure at call sites. |
|
| 134 | 122 | if il::isCall(instr) { |
|
| 135 | 123 | try limitCrossCallPressure( |
|
| 136 | - | &mut scratch, &mut spilled, costs, |
|
| 137 | - | &mut calleeClass, numCalleeSaved, il::instrDst(instr) |
|
| 124 | + | scratch, spilled, costs, |
|
| 125 | + | calleeClass, numCalleeSaved, il::instrDst(instr) |
|
| 138 | 126 | ); |
|
| 139 | 127 | } |
|
| 140 | 128 | // Remove definition from live set. |
|
| 141 | 129 | if let dst = il::instrDst(instr) { |
|
| 142 | - | bitset::clear(&mut scratch, dst.n); |
|
| 130 | + | bitset::clear(scratch, dst.n); |
|
| 143 | 131 | } |
|
| 144 | 132 | // Add uses to live set. |
|
| 145 | - | il::forEachReg(instr, addRegToSetCallback, &mut scratch as &mut opaque); |
|
| 133 | + | let mut registers = il::registers(&instr); |
|
| 134 | + | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 135 | + | bitset::put(scratch, reg.n); |
|
| 136 | + | } |
|
| 146 | 137 | } |
|
| 147 | 138 | // Also limit pressure at block entry. |
|
| 148 | - | try limitPressure(&mut scratch, &mut spilled, costs, numRegs); |
|
| 139 | + | try limitPressure(scratch, spilled, costs, numRegs); |
|
| 149 | 140 | } |
|
| 150 | 141 | ||
| 151 | 142 | // Phase 3: Enforce global callee-class limit. |
|
| 152 | 143 | // The per-call-site limit may leave the callee-class set larger than |
|
| 153 | 144 | // `numCalleeSaved` when different call sites keep different subsets. |
|
| 154 | 145 | // Spill excess values to guarantee the assignment phase always finds |
|
| 155 | 146 | // a callee-saved register for cross-call values. |
|
| 156 | - | let calleeCount = bitset::count(&calleeClass); |
|
| 147 | + | let calleeCount = bitset::count(calleeClass); |
|
| 157 | 148 | if calleeCount > numCalleeSaved { |
|
| 158 | - | let mut it = bitset::iter(&calleeClass); |
|
| 149 | + | let mut it = bitset::iter(calleeClass); |
|
| 159 | 150 | for _ in 0..(calleeCount - numCalleeSaved) { |
|
| 160 | - | if let reg = bitset::iterNext(&mut it) { |
|
| 161 | - | bitset::clear(&mut calleeClass, reg); |
|
| 162 | - | bitset::put(&mut spilled, reg); |
|
| 151 | + | if let reg = bitset::iterNext(&mut it, calleeClass) { |
|
| 152 | + | bitset::clear(calleeClass, reg); |
|
| 153 | + | bitset::put(spilled, reg); |
|
| 163 | 154 | } else { |
|
| 164 | 155 | panic "spill: count > 0 but no set bits found"; |
|
| 165 | 156 | } |
|
| 166 | 157 | } |
|
| 167 | 158 | } |
|
| 168 | 159 | ||
| 169 | 160 | // Phase 4: Assign stack slots to spilled values. |
|
| 170 | 161 | let mut frameSize: i32 = 0; |
|
| 171 | - | let mut it = bitset::iter(&spilled); |
|
| 162 | + | let mut it = bitset::iter(spilled); |
|
| 172 | 163 | ||
| 173 | - | while let n = bitset::iterNext(&mut it) { |
|
| 164 | + | while let n = bitset::iterNext(&mut it, spilled) { |
|
| 174 | 165 | set slots[n] = frameSize; |
|
| 175 | 166 | set frameSize += slotSize as i32; |
|
| 176 | 167 | } |
|
| 177 | - | return SpillInfo { slots, frameSize, calleeClass, maxReg }; |
|
| 168 | + | return SpillInfo 'scratch { slots: &slots[..], frameSize, calleeClass: &calleeClass[..], maxReg }; |
|
| 178 | 169 | } |
|
| 179 | 170 | ||
| 180 | 171 | /// Calculate spill costs for all registers, weighted by loop depth. |
|
| 181 | - | unsafe fn fillCosts(func: *unsafe il::Fn, costs: *unsafe mut [SpillCost]) { |
|
| 172 | + | unsafe fn fillCosts(func: &il::Fn, costs: &mut [SpillCost]) { |
|
| 182 | 173 | for b in 0..func.blocks.len { |
|
| 183 | 174 | let block = &func.blocks[b]; |
|
| 184 | 175 | ||
| 185 | 176 | // Exponential weight for loop depth, capped to avoid overflow. |
|
| 186 | 177 | let depth = MAX_LOOP_WEIGHT if block.loopDepth > MAX_LOOP_WEIGHT else block.loopDepth; |
| 191 | 182 | if p.value.n < costs.len { |
|
| 192 | 183 | set costs[p.value.n].defs = costs[p.value.n].defs + weight; |
|
| 193 | 184 | } |
|
| 194 | 185 | } |
|
| 195 | 186 | // Count instruction defs and uses. |
|
| 196 | - | for i in 0..block.instrs.len { |
|
| 197 | - | let instr = block.instrs[i]; |
|
| 187 | + | for instr in block.instrs { |
|
| 198 | 188 | ||
| 199 | 189 | // Count definition. |
|
| 200 | 190 | if let dst = il::instrDst(instr) { |
|
| 201 | 191 | if dst.n < costs.len { |
|
| 202 | 192 | set costs[dst.n].defs = costs[dst.n].defs + weight; |
|
| 203 | 193 | } |
|
| 204 | 194 | } |
|
| 205 | 195 | // Count uses. |
|
| 206 | - | let mut ctx = CountCtx { costs, weight }; |
|
| 207 | - | il::forEachReg(instr, countRegUseCallback, &mut ctx as &mut opaque); |
|
| 196 | + | let mut registers = il::registers(&instr); |
|
| 197 | + | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 198 | + | countRegUse(reg, &mut costs[..], weight); |
|
| 199 | + | } |
|
| 208 | 200 | } |
|
| 209 | 201 | } |
|
| 210 | 202 | } |
|
| 211 | 203 | ||
| 212 | 204 | /// Sort candidates by cost (ascending) using insertion sort, then spill |
|
| 213 | 205 | /// the cheapest `excess` values: set in `spilled`, clear in `source`. |
|
| 214 | - | unsafe fn spillCheapest( |
|
| 206 | + | fn spillCheapest( |
|
| 215 | 207 | c: &mut Candidates, |
|
| 216 | 208 | excess: u32, |
|
| 217 | - | source: &mut bitset::Bitset, |
|
| 218 | - | spilled: &mut bitset::Bitset |
|
| 209 | + | source: &mut [u32], |
|
| 210 | + | spilled: &mut [u32] |
|
| 219 | 211 | ) { |
|
| 220 | 212 | // Insertion sort ascending by cost. |
|
| 221 | 213 | for i in 1..c.n { |
|
| 222 | 214 | let key = c.entries[i]; |
|
| 223 | 215 | let mut j: u32 = i; |
| 233 | 225 | bitset::clear(source, c.entries[i].reg); |
|
| 234 | 226 | } |
|
| 235 | 227 | } |
|
| 236 | 228 | ||
| 237 | 229 | /// Collect all values from a bitset into a candidates buffer with their costs. |
|
| 238 | - | unsafe fn collectCandidates(bs: &bitset::Bitset, costs: *unsafe [SpillCost]) -> Candidates throws (alloc::AllocError) { |
|
| 230 | + | unsafe fn collectCandidates(bs: &[u32], costs: &[SpillCost]) -> Candidates throws (alloc::AllocError) { |
|
| 239 | 231 | let mut c = Candidates { entries: undefined, n: 0 }; |
|
| 240 | 232 | let mut it = bitset::iter(bs); |
|
| 241 | - | while let reg = bitset::iterNext(&mut it) { |
|
| 242 | - | if c.n == MAX_CANDIDATES { throw alloc::AllocError::OutOfMemory; } |
|
| 233 | + | while let reg = bitset::iterNext(&mut it, bs) { |
|
| 234 | + | if c.n == MAX_CANDIDATES { |
|
| 235 | + | throw alloc::AllocError::OutOfMemory; |
|
| 236 | + | } |
|
| 243 | 237 | if reg < costs.len { |
|
| 244 | 238 | set c.entries[c.n] = CostEntry { reg, cost: costs[reg].defs + costs[reg].uses }; |
|
| 245 | 239 | set c.n += 1; |
|
| 246 | 240 | } |
|
| 247 | 241 | } |
|
| 248 | 242 | return c; |
|
| 249 | 243 | } |
|
| 250 | 244 | ||
| 251 | 245 | /// Limit register pressure by marking low-cost values as spilled. |
|
| 252 | 246 | unsafe fn limitPressure( |
|
| 253 | - | live: &mut bitset::Bitset, |
|
| 254 | - | spilled: &mut bitset::Bitset, |
|
| 255 | - | costs: *unsafe [SpillCost], |
|
| 247 | + | live: &mut [u32], |
|
| 248 | + | spilled: &mut [u32], |
|
| 249 | + | costs: &[SpillCost], |
|
| 256 | 250 | numRegs: u32 |
|
| 257 | 251 | ) throws (alloc::AllocError) { |
|
| 258 | 252 | let liveCount = bitset::count(live); |
|
| 259 | 253 | if liveCount <= numRegs { |
|
| 260 | 254 | return; |
| 275 | 269 | /// |
|
| 276 | 270 | /// At a call site, every live value must survive the call in a callee-saved |
|
| 277 | 271 | /// register. If the count exceeds `numCalleeSaved`, spill the cheapest |
|
| 278 | 272 | /// crossing values. |
|
| 279 | 273 | unsafe fn limitCrossCallPressure( |
|
| 280 | - | live: &mut bitset::Bitset, |
|
| 281 | - | spilled: &mut bitset::Bitset, |
|
| 282 | - | costs: *unsafe [SpillCost], |
|
| 283 | - | calleeClass: &mut bitset::Bitset, |
|
| 274 | + | live: &mut [u32], |
|
| 275 | + | spilled: &mut [u32], |
|
| 276 | + | costs: &[SpillCost], |
|
| 277 | + | calleeClass: &mut [u32], |
|
| 284 | 278 | numCalleeSaved: u32, |
|
| 285 | 279 | callDst: ?il::Reg |
|
| 286 | 280 | ) throws (alloc::AllocError) { |
|
| 287 | 281 | // Collect crossing candidates: live values excluding the call destination. |
|
| 288 | - | let mut candidates: [CostEntry; 256] = undefined; |
|
| 282 | + | let mut candidates: [CostEntry; MAX_CANDIDATES] = undefined; |
|
| 289 | 283 | let mut numCandidates: u32 = 0; |
|
| 290 | 284 | let mut it = bitset::iter(live); |
|
| 291 | - | while let n = bitset::iterNext(&mut it) { |
|
| 285 | + | while let n = bitset::iterNext(&mut it, live) { |
|
| 292 | 286 | if not isCallDst(callDst, n) and n < costs.len { |
|
| 293 | - | if numCandidates == MAX_CANDIDATES { throw alloc::AllocError::OutOfMemory; } |
|
| 287 | + | if numCandidates == MAX_CANDIDATES { |
|
| 288 | + | throw alloc::AllocError::OutOfMemory; |
|
| 289 | + | } |
|
| 294 | 290 | set candidates[numCandidates] = CostEntry { |
|
| 295 | 291 | reg: n, |
|
| 296 | 292 | cost: costs[n].defs + costs[n].uses, |
|
| 297 | 293 | }; |
|
| 298 | 294 | set numCandidates += 1; |
| 303 | 299 | let mut c = Candidates { entries: candidates, n: numCandidates }; |
|
| 304 | 300 | spillCheapest(&mut c, numCandidates - numCalleeSaved, live, spilled); |
|
| 305 | 301 | } |
|
| 306 | 302 | // Mark crossing values that remain after spilling as callee-saved class. |
|
| 307 | 303 | let mut it2 = bitset::iter(live); |
|
| 308 | - | while let n = bitset::iterNext(&mut it2) { |
|
| 304 | + | while let n = bitset::iterNext(&mut it2, live) { |
|
| 309 | 305 | if not isCallDst(callDst, n) { |
|
| 310 | 306 | bitset::put(calleeClass, n); |
|
| 311 | 307 | } |
|
| 312 | 308 | } |
|
| 313 | 309 | } |
|
| 314 | 310 | ||
| 315 | - | /// Callback for [`il::forEachReg`]: increments use count for register. |
|
| 316 | - | unsafe fn countRegUseCallback(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 317 | - | let weight = (ctxPtr as &mut CountCtx).weight; |
|
| 318 | - | countRegUse(reg, &mut (ctxPtr as &mut CountCtx).costs[..], weight); |
|
| 319 | - | } |
|
| 320 | - | ||
| 321 | 311 | /// Add the block weight to the register use count. |
|
| 322 | 312 | fn countRegUse(reg: il::Reg, costs: &mut [SpillCost], weight: u32) { |
|
| 323 | - | assert reg.n < costs.len, "countRegUseCallback: register out of bounds"; |
|
| 313 | + | assert reg.n < costs.len, "countRegUse: register out of bounds"; |
|
| 324 | 314 | set costs[reg.n].uses += weight; |
|
| 325 | 315 | } |
|
| 326 | 316 | ||
| 327 | - | /// Callback for [`il::forEachReg`]: adds register to live set. |
|
| 328 | - | unsafe fn addRegToSetCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 329 | - | bitset::put(ctx as &mut bitset::Bitset, reg.n); |
|
| 330 | - | } |
|
| 331 | - | ||
| 332 | 317 | /// Check if a register is spilled. |
|
| 333 | - | export fn isSpilled(info: &SpillInfo, reg: il::Reg) -> bool { |
|
| 318 | + | export fn isSpilled 'scratch (info: &SpillInfo 'scratch, reg: il::Reg) -> bool { |
|
| 334 | 319 | if reg.n >= info.maxReg { |
|
| 335 | 320 | return false; |
|
| 336 | 321 | } |
|
| 337 | 322 | return info.slots[reg.n] >= 0; |
|
| 338 | 323 | } |
|
| 339 | 324 | ||
| 340 | 325 | /// Get spill slot offset for a register, or `nil` if not spilled. |
|
| 341 | - | export fn spillSlot(info: &SpillInfo, reg: il::Reg) -> ?i32 { |
|
| 326 | + | export fn spillSlot 'scratch (info: &SpillInfo 'scratch, reg: il::Reg) -> ?i32 { |
|
| 342 | 327 | if isSpilled(info, reg) { |
|
| 343 | 328 | return info.slots[reg.n]; |
|
| 344 | 329 | } |
|
| 345 | 330 | return nil; |
|
| 346 | 331 | } |
lib/std/lang/il.rad
+170 -69
| 58 | 58 | // TODO: Labels should have their own type. |
|
| 59 | 59 | // TODO: Blocks should have an instruction in `Instr`. |
|
| 60 | 60 | ||
| 61 | 61 | export mod printer; |
|
| 62 | 62 | export mod binary; |
|
| 63 | + | @test mod tests; |
|
| 63 | 64 | ||
| 64 | 65 | use std::mem; |
|
| 65 | 66 | use std::lang::alloc; |
|
| 66 | 67 | ||
| 67 | 68 | /// Source location for debug info. |
| 296 | 297 | /// Environment break: `ebreak;`. |
|
| 297 | 298 | /// Triggers a breakpoint exception for debugging. |
|
| 298 | 299 | Ebreak, |
|
| 299 | 300 | /// Full acquire/release memory fence. |
|
| 300 | 301 | MemoryFence, |
|
| 302 | + | /// Read one register through a live Device handle. |
|
| 303 | + | DeviceRead { |
|
| 304 | + | /// Register access width. |
|
| 305 | + | typ: Type, |
|
| 306 | + | /// Destination for the zero-extended register value. |
|
| 307 | + | dst: Reg, |
|
| 308 | + | /// Domain-relative Device capability. |
|
| 309 | + | handle: Val, |
|
| 310 | + | /// Byte offset within the device region. |
|
| 311 | + | offset: Val, |
|
| 312 | + | }, |
|
| 313 | + | /// Write one register through a live Device handle. |
|
| 314 | + | DeviceWrite { |
|
| 315 | + | /// Register access width. |
|
| 316 | + | typ: Type, |
|
| 317 | + | /// Domain-relative Device capability. |
|
| 318 | + | handle: Val, |
|
| 319 | + | /// Byte offset within the device region. |
|
| 320 | + | offset: Val, |
|
| 321 | + | /// Value whose low bits are written at the selected width. |
|
| 322 | + | value: Val, |
|
| 323 | + | }, |
|
| 301 | 324 | } |
|
| 302 | 325 | ||
| 303 | 326 | ////////////////////////// |
|
| 304 | 327 | // Blocks and Functions // |
|
| 305 | 328 | ////////////////////////// |
| 398 | 421 | ||
| 399 | 422 | /////////////////////// |
|
| 400 | 423 | // Utility Functions // |
|
| 401 | 424 | /////////////////////// |
|
| 402 | 425 | ||
| 426 | + | /// Compiler-generated metadata call for one MMIO access. |
|
| 427 | + | /// a0 is the handle, a1 the offset, and a2 the byte width plus bit 8 for a write. |
|
| 428 | + | /// a3 holds a write value and is preserved. Success returns the address in a0. |
|
| 429 | + | /// Any failure terminates the caller before the user-mode register access. |
|
| 430 | + | export constant DEVICE_ACCESS: u32 = 74; |
|
| 431 | + | ||
| 403 | 432 | /// Get the destination register of an instruction, if any. |
|
| 404 | 433 | export fn instrDst(instr: Instr) -> ?Reg { |
|
| 405 | 434 | match instr { |
|
| 406 | 435 | case Instr::Reserve { dst, .. } => return dst, |
|
| 407 | 436 | case Instr::Load { dst, .. } => return dst, |
| 411 | 440 | case Instr::UnOp { dst, .. } => return dst, |
|
| 412 | 441 | case Instr::Zext { dst, .. } => return dst, |
|
| 413 | 442 | case Instr::Sext { dst, .. } => return dst, |
|
| 414 | 443 | case Instr::Call { dst, .. } => return dst, |
|
| 415 | 444 | case Instr::Ecall { dst, .. } => return dst, |
|
| 445 | + | case Instr::DeviceRead { dst, .. } => return dst, |
|
| 416 | 446 | else => return nil, |
|
| 417 | 447 | } |
|
| 418 | 448 | } |
|
| 419 | 449 | ||
| 420 | 450 | /// Check if an instruction is a function call. |
|
| 421 | 451 | export fn isCall(instr: Instr) -> bool { |
|
| 422 | 452 | match instr { |
|
| 423 | 453 | case Instr::Call { .. }, |
|
| 424 | - | Instr::Ecall { .. } => return true, |
|
| 454 | + | Instr::Ecall { .. }, Instr::DeviceRead { .. }, Instr::DeviceWrite { .. } => return true, |
|
| 425 | 455 | else => return false, |
|
| 426 | 456 | } |
|
| 427 | 457 | } |
|
| 428 | 458 | ||
| 429 | - | /// Call a function for each register used by an instruction. |
|
| 430 | - | /// This is called by the register allocator to analyze register usage. |
|
| 431 | - | export unsafe fn forEachReg(instr: Instr, f: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 432 | - | match instr { |
|
| 433 | - | case Instr::Reserve { size, .. } => |
|
| 434 | - | withReg(size, f, ctx), |
|
| 435 | - | case Instr::Load { src, .. } => f(src, ctx), |
|
| 436 | - | case Instr::Sload { src, .. } => f(src, ctx), |
|
| 459 | + | /// Position within an instruction's source operands. |
|
| 460 | + | export record RegCursor: Copy { |
|
| 461 | + | /// Registers in the instruction's fixed operands, in operand order. |
|
| 462 | + | fixed: [Reg; 5], |
|
| 463 | + | /// Number of initialized fixed registers. |
|
| 464 | + | count: u32, |
|
| 465 | + | /// Next fixed register. |
|
| 466 | + | next: u32, |
|
| 467 | + | /// Next argument in the current argument group. |
|
| 468 | + | argument: u32, |
|
| 469 | + | /// Argument group: zero for the first group, then one per switch case. |
|
| 470 | + | branch: u32, |
|
| 471 | + | /// Whether the instruction has variable argument groups left to scan. |
|
| 472 | + | arguments: bool, |
|
| 473 | + | } |
|
| 474 | + | ||
| 475 | + | /// Start a source-register scan in instruction operand order. |
|
| 476 | + | export unsafe fn registers(instr: &Instr) -> RegCursor { |
|
| 477 | + | let mut cursor = RegCursor { fixed: undefined, count: 0, next: 0, argument: 0, branch: 0, arguments: false }; |
|
| 478 | + | match *instr { |
|
| 479 | + | case Instr::Reserve { size, .. } => addOperand(&mut cursor, size), |
|
| 480 | + | case Instr::Load { src, .. } => addOperand(&mut cursor, Val::Reg(src)), |
|
| 481 | + | case Instr::Sload { src, .. } => addOperand(&mut cursor, Val::Reg(src)), |
|
| 437 | 482 | case Instr::Store { src, dst, .. } => { |
|
| 438 | - | withReg(src, f, ctx); |
|
| 439 | - | f(dst, ctx); |
|
| 440 | - | }, |
|
| 483 | + | addOperand(&mut cursor, src); |
|
| 484 | + | addOperand(&mut cursor, Val::Reg(dst)); |
|
| 485 | + | } |
|
| 441 | 486 | case Instr::Blit { dst, src, size } => { |
|
| 442 | - | f(dst, ctx); |
|
| 443 | - | f(src, ctx); |
|
| 444 | - | withReg(size, f, ctx); |
|
| 445 | - | }, |
|
| 446 | - | case Instr::Copy { val, .. } => |
|
| 447 | - | withReg(val, f, ctx), |
|
| 487 | + | addOperand(&mut cursor, Val::Reg(dst)); |
|
| 488 | + | addOperand(&mut cursor, Val::Reg(src)); |
|
| 489 | + | addOperand(&mut cursor, size); |
|
| 490 | + | } |
|
| 491 | + | case Instr::Copy { val, .. } => addOperand(&mut cursor, val), |
|
| 448 | 492 | case Instr::BinOp { a, b, .. } => { |
|
| 449 | - | withReg(a, f, ctx); |
|
| 450 | - | withReg(b, f, ctx); |
|
| 451 | - | }, |
|
| 452 | - | case Instr::UnOp { a, .. } => |
|
| 453 | - | withReg(a, f, ctx), |
|
| 454 | - | case Instr::Zext { val, .. } => |
|
| 455 | - | withReg(val, f, ctx), |
|
| 456 | - | case Instr::Sext { val, .. } => |
|
| 457 | - | withReg(val, f, ctx), |
|
| 458 | - | case Instr::Call { func, args, .. } => { |
|
| 459 | - | withReg(func, f, ctx); |
|
| 460 | - | for arg in args { |
|
| 461 | - | withReg(arg, f, ctx); |
|
| 462 | - | } |
|
| 463 | - | }, |
|
| 493 | + | addOperand(&mut cursor, a); |
|
| 494 | + | addOperand(&mut cursor, b); |
|
| 495 | + | } |
|
| 496 | + | case Instr::UnOp { a, .. } => addOperand(&mut cursor, a), |
|
| 497 | + | case Instr::Zext { val, .. } => addOperand(&mut cursor, val), |
|
| 498 | + | case Instr::Sext { val, .. } => addOperand(&mut cursor, val), |
|
| 499 | + | case Instr::Call { func, .. } => { |
|
| 500 | + | addOperand(&mut cursor, func); |
|
| 501 | + | set cursor.arguments = true; |
|
| 502 | + | } |
|
| 464 | 503 | case Instr::Ret { val } => { |
|
| 465 | - | if let v = val { |
|
| 466 | - | withReg(v, f, ctx); |
|
| 504 | + | if let value = val { |
|
| 505 | + | addOperand(&mut cursor, value); |
|
| 506 | + | } |
|
| 507 | + | } |
|
| 508 | + | case Instr::Jmp { .. } => set cursor.arguments = true, |
|
| 509 | + | case Instr::Br { a, b, .. } => { |
|
| 510 | + | addOperand(&mut cursor, a); |
|
| 511 | + | addOperand(&mut cursor, b); |
|
| 512 | + | set cursor.arguments = true; |
|
| 513 | + | } |
|
| 514 | + | case Instr::Switch { val, .. } => { |
|
| 515 | + | addOperand(&mut cursor, val); |
|
| 516 | + | set cursor.arguments = true; |
|
| 517 | + | } |
|
| 518 | + | case Instr::Ecall { num, a0, a1, a2, a3, .. } => { |
|
| 519 | + | addOperand(&mut cursor, num); |
|
| 520 | + | addOperand(&mut cursor, a0); |
|
| 521 | + | addOperand(&mut cursor, a1); |
|
| 522 | + | addOperand(&mut cursor, a2); |
|
| 523 | + | addOperand(&mut cursor, a3); |
|
| 524 | + | } |
|
| 525 | + | case Instr::DeviceRead { handle, offset, .. } => { |
|
| 526 | + | addOperand(&mut cursor, handle); |
|
| 527 | + | addOperand(&mut cursor, offset); |
|
| 528 | + | } |
|
| 529 | + | case Instr::DeviceWrite { handle, offset, value, .. } => { |
|
| 530 | + | addOperand(&mut cursor, handle); |
|
| 531 | + | addOperand(&mut cursor, offset); |
|
| 532 | + | addOperand(&mut cursor, value); |
|
| 533 | + | } |
|
| 534 | + | case Instr::Unreachable, Instr::Ebreak, Instr::MemoryFence => { |
|
| 535 | + | } |
|
| 536 | + | } |
|
| 537 | + | return cursor; |
|
| 538 | + | } |
|
| 539 | + | ||
| 540 | + | /// Append a fixed register operand to the cursor. |
|
| 541 | + | fn addOperand(cursor: &mut RegCursor, value: Val) { |
|
| 542 | + | if let case Val::Reg(reg) = value { |
|
| 543 | + | set cursor.fixed[cursor.count] = reg; |
|
| 544 | + | set cursor.count += 1; |
|
| 545 | + | } |
|
| 546 | + | } |
|
| 547 | + | ||
| 548 | + | /// Return the next source register, including repeated uses. |
|
| 549 | + | /// Supply the unchanged instruction for every step of a scan. |
|
| 550 | + | export unsafe fn nextReg(cursor: &mut RegCursor, instr: &Instr) -> ?Reg { |
|
| 551 | + | if cursor.next < cursor.count { |
|
| 552 | + | let reg = cursor.fixed[cursor.next]; |
|
| 553 | + | set cursor.next += 1; |
|
| 554 | + | return reg; |
|
| 555 | + | } |
|
| 556 | + | if not cursor.arguments { |
|
| 557 | + | return nil; |
|
| 558 | + | } |
|
| 559 | + | match *instr { |
|
| 560 | + | case Instr::Call { args, .. } => { |
|
| 561 | + | if let reg = nextArgument(cursor, args) { |
|
| 562 | + | return reg; |
|
| 467 | 563 | } |
|
| 468 | - | }, |
|
| 564 | + | } |
|
| 469 | 565 | case Instr::Jmp { args, .. } => { |
|
| 470 | - | for arg in args { |
|
| 471 | - | withReg(arg, f, ctx); |
|
| 566 | + | if let reg = nextArgument(cursor, args) { |
|
| 567 | + | return reg; |
|
| 472 | 568 | } |
|
| 473 | - | }, |
|
| 474 | - | case Instr::Br { a, b, thenArgs, elseArgs, .. } => { |
|
| 475 | - | withReg(a, f, ctx); |
|
| 476 | - | withReg(b, f, ctx); |
|
| 477 | - | for arg in thenArgs { |
|
| 478 | - | withReg(arg, f, ctx); |
|
| 569 | + | } |
|
| 570 | + | case Instr::Br { thenArgs, elseArgs, .. } => { |
|
| 571 | + | if cursor.branch == 0 { |
|
| 572 | + | if let reg = nextArgument(cursor, thenArgs) { |
|
| 573 | + | return reg; |
|
| 574 | + | } |
|
| 575 | + | set cursor.branch = 1; |
|
| 576 | + | set cursor.argument = 0; |
|
| 479 | 577 | } |
|
| 480 | - | for arg in elseArgs { |
|
| 481 | - | withReg(arg, f, ctx); |
|
| 578 | + | if let reg = nextArgument(cursor, elseArgs) { |
|
| 579 | + | return reg; |
|
| 482 | 580 | } |
|
| 483 | - | }, |
|
| 484 | - | case Instr::Switch { val, defaultArgs, cases, .. } => { |
|
| 485 | - | withReg(val, f, ctx); |
|
| 486 | - | for arg in defaultArgs { |
|
| 487 | - | withReg(arg, f, ctx); |
|
| 581 | + | } |
|
| 582 | + | case Instr::Switch { defaultArgs, cases, .. } => { |
|
| 583 | + | if cursor.branch == 0 { |
|
| 584 | + | if let reg = nextArgument(cursor, defaultArgs) { |
|
| 585 | + | return reg; |
|
| 586 | + | } |
|
| 587 | + | set cursor.branch = 1; |
|
| 588 | + | set cursor.argument = 0; |
|
| 488 | 589 | } |
|
| 489 | - | for c in cases { |
|
| 490 | - | for arg in c.args { |
|
| 491 | - | withReg(arg, f, ctx); |
|
| 590 | + | while cursor.branch - 1 < cases.len { |
|
| 591 | + | if let reg = nextArgument(cursor, cases[cursor.branch - 1].args) { |
|
| 592 | + | return reg; |
|
| 492 | 593 | } |
|
| 594 | + | set cursor.branch += 1; |
|
| 595 | + | set cursor.argument = 0; |
|
| 493 | 596 | } |
|
| 494 | - | }, |
|
| 495 | - | case Instr::Ecall { num, a0, a1, a2, a3, .. } => { |
|
| 496 | - | withReg(num, f, ctx); |
|
| 497 | - | withReg(a0, f, ctx); |
|
| 498 | - | withReg(a1, f, ctx); |
|
| 499 | - | withReg(a2, f, ctx); |
|
| 500 | - | withReg(a3, f, ctx); |
|
| 501 | - | }, |
|
| 502 | - | case Instr::Unreachable, |
|
| 503 | - | Instr::Ebreak, |
|
| 504 | - | Instr::MemoryFence => {}, |
|
| 597 | + | } |
|
| 598 | + | else => panic "nextReg: expected instruction argument groups", |
|
| 505 | 599 | } |
|
| 600 | + | set cursor.arguments = false; |
|
| 601 | + | return nil; |
|
| 506 | 602 | } |
|
| 507 | 603 | ||
| 508 | - | /// Call callback if value is a register. |
|
| 509 | - | unsafe fn withReg(val: Val, callback: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 510 | - | if let case Val::Reg(r) = val { |
|
| 511 | - | callback(r, ctx); |
|
| 604 | + | /// Scan an argument group from the cursor's current position. |
|
| 605 | + | unsafe fn nextArgument(cursor: &mut RegCursor, args: *unsafe [Val]) -> ?Reg { |
|
| 606 | + | while cursor.argument < args.len { |
|
| 607 | + | let value = args[cursor.argument]; |
|
| 608 | + | set cursor.argument += 1; |
|
| 609 | + | if let case Val::Reg(reg) = value { |
|
| 610 | + | return reg; |
|
| 611 | + | } |
|
| 512 | 612 | } |
|
| 613 | + | return nil; |
|
| 513 | 614 | } |
lib/std/lang/il/binary.rad
+4 -0
| 77 | 77 | export constant INSTR_ECALL: u8 = 16; |
|
| 78 | 78 | /// Wire tag for instruction Ebreak. |
|
| 79 | 79 | export constant INSTR_EBREAK: u8 = 17; |
|
| 80 | 80 | /// Wire tag for instruction MemoryFence. |
|
| 81 | 81 | export constant INSTR_MEMORYFENCE: u8 = 18; |
|
| 82 | + | /// Wire tag for a checked device read. |
|
| 83 | + | export constant INSTR_DEVICE_READ: u8 = 19; |
|
| 84 | + | /// Wire tag for a checked device write. |
|
| 85 | + | export constant INSTR_DEVICE_WRITE: u8 = 20; |
|
| 82 | 86 | ||
| 83 | 87 | /// Wire tag for data Val. |
|
| 84 | 88 | export constant DATA_VAL: u8 = 0; |
|
| 85 | 89 | /// Wire tag for data Sym. |
|
| 86 | 90 | export constant DATA_SYM: u8 = 1; |
lib/std/lang/il/binary/collect.rad
+12 -3
| 72 | 72 | /// Collect names referenced by one IL value. |
|
| 73 | 73 | unsafe fn value(names: &mut Names, owner: *[u8], item: il::Val) throws (binary::Error) { |
|
| 74 | 74 | match item { |
|
| 75 | 75 | case il::Val::DataSym(name) => try reference(names, owner, name), |
|
| 76 | 76 | case il::Val::FnAddr(name) => try reference(names, owner, name), |
|
| 77 | - | else => {}, |
|
| 77 | + | else => { |
|
| 78 | + | }, |
|
| 78 | 79 | } |
|
| 79 | 80 | } |
|
| 80 | 81 | ||
| 81 | 82 | /// Collect names from an instruction's variable-length operand sequence. |
|
| 82 | 83 | unsafe fn values(names: &mut Names, owner: *[u8], items: &[il::Val]) throws (binary::Error) { |
| 127 | 128 | try value(names, owner, a0); |
|
| 128 | 129 | try value(names, owner, a1); |
|
| 129 | 130 | try value(names, owner, a2); |
|
| 130 | 131 | try value(names, owner, a3); |
|
| 131 | 132 | }, |
|
| 133 | + | case il::Instr::DeviceRead { handle, offset, .. } => { |
|
| 134 | + | try value(names, owner, handle); try value(names, owner, offset); |
|
| 135 | + | }, |
|
| 136 | + | case il::Instr::DeviceWrite { handle, offset, value: source, .. } => { |
|
| 137 | + | try value(names, owner, handle); try value(names, owner, offset); try value(names, owner, source); |
|
| 138 | + | }, |
|
| 132 | 139 | case il::Instr::Load { .. }, il::Instr::Sload { .. }, il::Instr::Unreachable, |
|
| 133 | - | il::Instr::Ebreak, il::Instr::MemoryFence => {}, |
|
| 140 | + | il::Instr::Ebreak, il::Instr::MemoryFence => { |
|
| 141 | + | }, |
|
| 134 | 142 | } |
|
| 135 | 143 | } |
|
| 136 | 144 | ||
| 137 | 145 | /// Check that a definition belongs to the selected package. |
|
| 138 | 146 | unsafe fn definition(names: &mut Names, owner: *[u8], name: *[u8]) throws (binary::Error) { |
| 165 | 173 | try definition(names, owner, item.name); |
|
| 166 | 174 | for data in item.values { |
|
| 167 | 175 | match data.item { |
|
| 168 | 176 | case il::DataItem::Sym(name) => try reference(names, owner, name), |
|
| 169 | 177 | case il::DataItem::Fn(name) => try reference(names, owner, name), |
|
| 170 | - | else => {}, |
|
| 178 | + | else => { |
|
| 179 | + | }, |
|
| 171 | 180 | } |
|
| 172 | 181 | } |
|
| 173 | 182 | } |
|
| 174 | 183 | for func in program.fns { |
|
| 175 | 184 | try definition(names, owner, func.name); |
lib/std/lang/il/binary/decodeTests.rad
+3 -1
| 347 | 347 | ||
| 348 | 348 | /// Copy data fixtures into stable storage for the package descriptor. |
|
| 349 | 349 | unsafe fn retainData(items: &[il::Data]) -> *[il::Data] { |
|
| 350 | 350 | unsafe static DATA: [il::Data; 2] = undefined; |
|
| 351 | 351 | assert items.len <= DATA.len; |
|
| 352 | - | for item, i in items { set DATA[i] = item; } |
|
| 352 | + | for item, i in items { |
|
| 353 | + | set DATA[i] = item; |
|
| 354 | + | } |
|
| 353 | 355 | return &DATA[..items.len]; |
|
| 354 | 356 | } |
lib/std/lang/il/binary/program.rad
+12 -4
| 116 | 116 | let mut extent: u64 = 0; |
|
| 117 | 117 | for j in 0..count { |
|
| 118 | 118 | let value = try reader::dataValue(input); |
|
| 119 | 119 | let mut width: u32 = 0; |
|
| 120 | 120 | match value.item { |
|
| 121 | - | case il::DataItem::Val { typ, .. } => { set width = il::typeSize(typ); }, |
|
| 122 | - | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { set width = 8; }, |
|
| 123 | - | case il::DataItem::Str(text) => { set width = text.len; }, |
|
| 124 | - | case il::DataItem::Undef => { set width = 1; }, |
|
| 121 | + | case il::DataItem::Val { typ, .. } => { |
|
| 122 | + | set width = il::typeSize(typ); |
|
| 123 | + | }, |
|
| 124 | + | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { |
|
| 125 | + | set width = 8; |
|
| 126 | + | }, |
|
| 127 | + | case il::DataItem::Str(text) => { |
|
| 128 | + | set width = text.len; |
|
| 129 | + | }, |
|
| 130 | + | case il::DataItem::Undef => { |
|
| 131 | + | set width = 1; |
|
| 132 | + | }, |
|
| 125 | 133 | } |
|
| 126 | 134 | set extent += width as u64 * value.count as u64; |
|
| 127 | 135 | if extent > size as u64 { |
|
| 128 | 136 | throw binary::Error::Invalid; |
|
| 129 | 137 | } |
lib/std/lang/il/binary/reader.rad
+23 -4
| 314 | 314 | let va1 = try val(input); |
|
| 315 | 315 | let va2 = try val(input); |
|
| 316 | 316 | let va3 = try val(input); |
|
| 317 | 317 | return il::Instr::Ecall { dst: vdst, num: vnum, a0: va0, a1: va1, a2: va2, a3: va3 }; |
|
| 318 | 318 | }, |
|
| 319 | + | case super::INSTR_DEVICE_READ => { |
|
| 320 | + | let t = try typ(input); |
|
| 321 | + | let dst = try reg(input); |
|
| 322 | + | let handle = try val(input); let offset = try val(input); |
|
| 323 | + | return il::Instr::DeviceRead { typ: t, dst, handle, offset }; |
|
| 324 | + | }, |
|
| 325 | + | case super::INSTR_DEVICE_WRITE => { |
|
| 326 | + | let t = try typ(input); |
|
| 327 | + | let handle = try val(input); let offset = try val(input); let value = try val(input); |
|
| 328 | + | return il::Instr::DeviceWrite { typ: t, handle, offset, value }; |
|
| 329 | + | }, |
|
| 319 | 330 | case super::INSTR_EBREAK => { |
|
| 320 | 331 | return il::Instr::Ebreak; |
|
| 321 | 332 | }, |
|
| 322 | 333 | case super::INSTR_MEMORYFENCE => { |
|
| 323 | 334 | return il::Instr::MemoryFence; |
| 334 | 345 | case super::DATA_VAL => { |
|
| 335 | 346 | let t = try typ(input); |
|
| 336 | 347 | let n = try integer(input, il::typeSize(t)); |
|
| 337 | 348 | set item = il::DataItem::Val { typ: t, val: n as i64 }; |
|
| 338 | 349 | }, |
|
| 339 | - | case super::DATA_SYM => { set item = il::DataItem::Sym(try symbol(input)); }, |
|
| 340 | - | case super::DATA_FN => { set item = il::DataItem::Fn(try symbol(input)); }, |
|
| 341 | - | case super::DATA_STR => { set item = il::DataItem::Str(try bytes(input)); }, |
|
| 342 | - | case super::DATA_UNDEF => { set item = il::DataItem::Undef; }, |
|
| 350 | + | case super::DATA_SYM => { |
|
| 351 | + | set item = il::DataItem::Sym(try symbol(input)); |
|
| 352 | + | }, |
|
| 353 | + | case super::DATA_FN => { |
|
| 354 | + | set item = il::DataItem::Fn(try symbol(input)); |
|
| 355 | + | }, |
|
| 356 | + | case super::DATA_STR => { |
|
| 357 | + | set item = il::DataItem::Str(try bytes(input)); |
|
| 358 | + | }, |
|
| 359 | + | case super::DATA_UNDEF => { |
|
| 360 | + | set item = il::DataItem::Undef; |
|
| 361 | + | }, |
|
| 343 | 362 | else => throw binary::Error::Invalid, |
|
| 344 | 363 | } |
|
| 345 | 364 | let n = try integer(input, 4) as u32; |
|
| 346 | 365 | return il::DataValue { item, count: n }; |
|
| 347 | 366 | } |
lib/std/lang/il/binary/tests.rad
+78 -22
| 12 | 12 | ||
| 13 | 13 | /// Check little-endian encoding for every integer width. |
|
| 14 | 14 | @test unsafe fn integers() throws (testing::TestError) { |
|
| 15 | 15 | let mut buffer: [u8; 15] = [0; 15]; |
|
| 16 | 16 | let mut out = writer::new(&mut buffer[..], &[]); |
|
| 17 | - | try writer::integer(&mut out, 0x12, 1) catch { throw testing::TestError::Failed; }; |
|
| 18 | - | try writer::integer(&mut out, 0x3456, 2) catch { throw testing::TestError::Failed; }; |
|
| 19 | - | try writer::integer(&mut out, 0x789abcde, 4) catch { throw testing::TestError::Failed; }; |
|
| 20 | - | try writer::integer(&mut out, 0x0123456789abcdef, 8) catch { throw testing::TestError::Failed; }; |
|
| 17 | + | try writer::integer(&mut out, 0x12, 1) catch { |
|
| 18 | + | throw testing::TestError::Failed; |
|
| 19 | + | }; |
|
| 20 | + | try writer::integer(&mut out, 0x3456, 2) catch { |
|
| 21 | + | throw testing::TestError::Failed; |
|
| 22 | + | }; |
|
| 23 | + | try writer::integer(&mut out, 0x789abcde, 4) catch { |
|
| 24 | + | throw testing::TestError::Failed; |
|
| 25 | + | }; |
|
| 26 | + | try writer::integer(&mut out, 0x0123456789abcdef, 8) catch { |
|
| 27 | + | throw testing::TestError::Failed; |
|
| 28 | + | }; |
|
| 21 | 29 | try testing::expectBytesEq(&buffer[..], &[0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, 0xef, 0xcd, |
|
| 22 | 30 | 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]); |
|
| 23 | 31 | } |
|
| 24 | 32 | ||
| 25 | 33 | /// Compare one instruction with its fixed wire representation. |
|
| 26 | 34 | unsafe fn instruction(item: il::Instr, expected: &[u8]) throws (testing::TestError) { |
|
| 27 | 35 | let mut buffer: [u8; 256] = [0; 256]; |
|
| 28 | 36 | let mut out = writer::new(&mut buffer[..], &["data", "fn"]); |
|
| 29 | - | try writer::instr(&mut out, item) catch { throw testing::TestError::Failed; }; |
|
| 37 | + | try writer::instr(&mut out, item) catch { |
|
| 38 | + | throw testing::TestError::Failed; |
|
| 39 | + | }; |
|
| 30 | 40 | try testing::expectBytesEq(&buffer[..out.offset], expected); |
|
| 31 | 41 | for capacity in 0..expected.len { |
|
| 32 | 42 | let mut short = writer::new(&mut buffer[..capacity], &["data", "fn"]); |
|
| 33 | 43 | let mut failed = false; |
|
| 34 | 44 | try writer::instr(&mut short, item) catch err { |
| 41 | 51 | let memory = &mut MEMORY[..512]; |
|
| 42 | 52 | let mut arena = alloc::new(&mut memory[..]); |
|
| 43 | 53 | let mut input = reader::new(expected, &mut arena, &["data", "fn"]); |
|
| 44 | 54 | set input.registers = 16; |
|
| 45 | 55 | set input.blocks = 4; |
|
| 46 | - | let decoded = try reader::instr(&mut input) catch { throw testing::TestError::Failed; }; |
|
| 56 | + | let decoded = try reader::instr(&mut input) catch { |
|
| 57 | + | throw testing::TestError::Failed; |
|
| 58 | + | }; |
|
| 47 | 59 | try testing::expect(input.offset == expected.len); |
|
| 48 | 60 | set out.offset = 0; |
|
| 49 | - | try writer::instr(&mut out, decoded) catch { throw testing::TestError::Failed; }; |
|
| 61 | + | try writer::instr(&mut out, decoded) catch { |
|
| 62 | + | throw testing::TestError::Failed; |
|
| 63 | + | }; |
|
| 50 | 64 | try testing::expectBytesEq(&buffer[..out.offset], expected); |
|
| 51 | 65 | for length in 0..expected.len { |
|
| 52 | 66 | alloc::reset(&mut arena); |
|
| 53 | 67 | set input = reader::new(&expected[..length], &mut arena, &["data", "fn"]); |
|
| 54 | 68 | set input.registers = 16; |
| 131 | 145 | dst: il::Reg { n: 1 }, num: il::Val::Undef, a0: il::Val::Undef, a1: il::Val::Undef, |
|
| 132 | 146 | a2: il::Val::Undef, a3: il::Val::Undef, |
|
| 133 | 147 | }, &[16, 1, 0, 0, 0, 4, 4, 4, 4, 4]); |
|
| 134 | 148 | try instruction(il::Instr::Ebreak, &[17]); |
|
| 135 | 149 | try instruction(il::Instr::MemoryFence, &[18]); |
|
| 150 | + | for typ in [il::Type::W8, il::Type::W16, il::Type::W32, il::Type::W64] { |
|
| 151 | + | let mut read: [u8; 8] = [19, 0, 1, 0, 0, 0, 4, 4]; |
|
| 152 | + | let mut write: [u8; 5] = [20, 0, 4, 4, 4]; |
|
| 153 | + | set read[1] = il::typeSize(typ) as u8; |
|
| 154 | + | set write[1] = il::typeSize(typ) as u8; |
|
| 155 | + | try instruction(il::Instr::DeviceRead { |
|
| 156 | + | typ, dst: il::Reg { n: 1 }, handle: il::Val::Undef, offset: il::Val::Undef, |
|
| 157 | + | }, &read[..]); |
|
| 158 | + | try instruction(il::Instr::DeviceWrite { |
|
| 159 | + | typ, handle: il::Val::Undef, offset: il::Val::Undef, value: il::Val::Undef, |
|
| 160 | + | }, &write[..]); |
|
| 161 | + | } |
|
| 136 | 162 | } |
|
| 137 | 163 | ||
| 138 | 164 | /// Check every value tag and empty and nonempty sequences. |
|
| 139 | 165 | @test unsafe fn values() throws (testing::TestError) { |
|
| 140 | 166 | let mut buffer: [u8; 64] = [0; 64]; |
|
| 141 | 167 | let mut out = writer::new(&mut buffer[..], &["data", "fn"]); |
|
| 142 | - | try writer::values(&mut out, &[]) catch { throw testing::TestError::Failed; }; |
|
| 168 | + | try writer::values(&mut out, &[]) catch { |
|
| 169 | + | throw testing::TestError::Failed; |
|
| 170 | + | }; |
|
| 143 | 171 | try writer::values(&mut out, &[ |
|
| 144 | 172 | il::Val::Reg(il::Reg { n: 0x12345678 }), |
|
| 145 | 173 | il::Val::Imm(-2), il::Val::DataSym("data"), |
|
| 146 | 174 | il::Val::FnAddr("fn"), il::Val::Undef, |
|
| 147 | - | ]) catch { throw testing::TestError::Failed; }; |
|
| 175 | + | ]) catch { |
|
| 176 | + | throw testing::TestError::Failed; |
|
| 177 | + | }; |
|
| 148 | 178 | try testing::expectBytesEq(&buffer[..out.offset], &[ |
|
| 149 | 179 | 0, 0, 0, 0, 5, 0, 0, 0, |
|
| 150 | 180 | 0, 0x78, 0x56, 0x34, 0x12, |
|
| 151 | 181 | 1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
|
| 152 | 182 | 2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 4, |
|
| 153 | 183 | ]); |
|
| 154 | 184 | let memory = &mut MEMORY[..256]; |
|
| 155 | 185 | let mut arena = alloc::new(&mut memory[..]); |
|
| 156 | 186 | let mut input = reader::new(&buffer[..out.offset], &mut arena, &["data", "fn"]); |
|
| 157 | 187 | set input.registers = 0x12345679; |
|
| 158 | - | let empty = try reader::values(&mut input) catch { throw testing::TestError::Failed; }; |
|
| 159 | - | let decoded = try reader::values(&mut input) catch { throw testing::TestError::Failed; }; |
|
| 188 | + | let empty = try reader::values(&mut input) catch { |
|
| 189 | + | throw testing::TestError::Failed; |
|
| 190 | + | }; |
|
| 191 | + | let decoded = try reader::values(&mut input) catch { |
|
| 192 | + | throw testing::TestError::Failed; |
|
| 193 | + | }; |
|
| 160 | 194 | try testing::expect(empty.len == 0); |
|
| 161 | 195 | try testing::expect(decoded.len == 5); |
|
| 162 | 196 | try testing::expect(input.offset == out.offset); |
|
| 163 | 197 | let mut repeated: [u8; 64] = [0; 64]; |
|
| 164 | 198 | let mut copy = writer::new(&mut repeated[..], &["data", "fn"]); |
|
| 165 | - | try writer::values(&mut copy, empty) catch { throw testing::TestError::Failed; }; |
|
| 166 | - | try writer::values(&mut copy, decoded) catch { throw testing::TestError::Failed; }; |
|
| 199 | + | try writer::values(&mut copy, empty) catch { |
|
| 200 | + | throw testing::TestError::Failed; |
|
| 201 | + | }; |
|
| 202 | + | try writer::values(&mut copy, decoded) catch { |
|
| 203 | + | throw testing::TestError::Failed; |
|
| 204 | + | }; |
|
| 167 | 205 | try testing::expectBytesEq(&buffer[..out.offset], &repeated[..copy.offset]); |
|
| 168 | 206 | ||
| 169 | 207 | } |
|
| 170 | 208 | ||
| 171 | 209 | /// Check initializer bytes and repetition counts. |
|
| 172 | 210 | @test unsafe fn initializers() throws (testing::TestError) { |
|
| 173 | 211 | let mut buffer: [u8; 128] = [0; 128]; |
|
| 174 | 212 | let mut out = writer::new(&mut buffer[..], &["data", "fn"]); |
|
| 175 | 213 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W8, |
|
| 176 | - | val: -1 }, count: 0 }) catch { throw testing::TestError::Failed; }; |
|
| 214 | + | val: -1 }, count: 0 }) catch { |
|
| 215 | + | throw testing::TestError::Failed; |
|
| 216 | + | }; |
|
| 177 | 217 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W16, |
|
| 178 | - | val: -2 }, count: 1 }) catch { throw testing::TestError::Failed; }; |
|
| 218 | + | val: -2 }, count: 1 }) catch { |
|
| 219 | + | throw testing::TestError::Failed; |
|
| 220 | + | }; |
|
| 179 | 221 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W32, |
|
| 180 | - | val: -3 }, count: 2 }) catch { throw testing::TestError::Failed; }; |
|
| 222 | + | val: -3 }, count: 2 }) catch { |
|
| 223 | + | throw testing::TestError::Failed; |
|
| 224 | + | }; |
|
| 181 | 225 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W64, |
|
| 182 | - | val: -4 }, count: 3 }) catch { throw testing::TestError::Failed; }; |
|
| 226 | + | val: -4 }, count: 3 }) catch { |
|
| 227 | + | throw testing::TestError::Failed; |
|
| 228 | + | }; |
|
| 183 | 229 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Sym("data"), count: 4 }) |
|
| 184 | - | catch { throw testing::TestError::Failed; }; |
|
| 230 | + | catch { |
|
| 231 | + | throw testing::TestError::Failed; |
|
| 232 | + | }; |
|
| 185 | 233 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Fn("fn"), count: 5 }) catch |
|
| 186 | - | { throw testing::TestError::Failed; }; |
|
| 234 | + | { |
|
| 235 | + | throw testing::TestError::Failed; |
|
| 236 | + | }; |
|
| 187 | 237 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str("ab"), count: 6 }) catch |
|
| 188 | - | { throw testing::TestError::Failed; }; |
|
| 238 | + | { |
|
| 239 | + | throw testing::TestError::Failed; |
|
| 240 | + | }; |
|
| 189 | 241 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str(""), count: 7 }) catch { |
|
| 190 | 242 | throw testing::TestError::Failed; }; |
|
| 191 | 243 | try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Undef, count: 8 }) catch { |
|
| 192 | 244 | throw testing::TestError::Failed; }; |
|
| 193 | 245 | try testing::expectBytesEq(&buffer[..out.offset], &[0, 1, 255, 0, 0, 0, 0, 0, 2, 254, 255, 1, 0, |
| 208 | 260 | try writer::symbol(&mut out, "absent") catch err { |
|
| 209 | 261 | try testing::expect(err == binary::Error::Symbol); |
|
| 210 | 262 | set failures += 1; |
|
| 211 | 263 | }; |
|
| 212 | 264 | try testing::expect(out.offset == 0); |
|
| 213 | - | try writer::integer(&mut out, 0, 8) catch { throw testing::TestError::Failed; }; |
|
| 265 | + | try writer::integer(&mut out, 0, 8) catch { |
|
| 266 | + | throw testing::TestError::Failed; |
|
| 267 | + | }; |
|
| 214 | 268 | try writer::integer(&mut out, 1, 1) catch err { |
|
| 215 | 269 | try testing::expect(err == binary::Error::Capacity); |
|
| 216 | 270 | set failures += 1; |
|
| 217 | 271 | }; |
|
| 218 | 272 | try testing::expect(failures == 3); |
| 264 | 318 | }; |
|
| 265 | 319 | try testing::expect(failed); |
|
| 266 | 320 | try testing::expect(out.offset <= capacity); |
|
| 267 | 321 | } |
|
| 268 | 322 | let mut out = writer::new(&mut buffer[..], &[]); |
|
| 269 | - | try writer::bytes(&mut out, "abc") catch { throw testing::TestError::Failed; }; |
|
| 323 | + | try writer::bytes(&mut out, "abc") catch { |
|
| 324 | + | throw testing::TestError::Failed; |
|
| 325 | + | }; |
|
| 270 | 326 | try testing::expectBytesEq(&buffer[..], &[3, 0, 0, 0, 97, 98, 99]); |
|
| 271 | 327 | } |
lib/std/lang/il/binary/writer.rad
+11 -0
| 265 | 265 | try val(out, va0); |
|
| 266 | 266 | try val(out, va1); |
|
| 267 | 267 | try val(out, va2); |
|
| 268 | 268 | try val(out, va3); |
|
| 269 | 269 | }, |
|
| 270 | + | case il::Instr::DeviceRead { typ: t, dst, handle, offset } => { |
|
| 271 | + | try integer(out, super::INSTR_DEVICE_READ as u64, 1); |
|
| 272 | + | try typ(out, t); |
|
| 273 | + | try integer(out, dst.n as u64, 4); |
|
| 274 | + | try val(out, handle); try val(out, offset); |
|
| 275 | + | }, |
|
| 276 | + | case il::Instr::DeviceWrite { typ: t, handle, offset, value } => { |
|
| 277 | + | try integer(out, super::INSTR_DEVICE_WRITE as u64, 1); |
|
| 278 | + | try typ(out, t); |
|
| 279 | + | try val(out, handle); try val(out, offset); try val(out, value); |
|
| 280 | + | }, |
|
| 270 | 281 | case il::Instr::Ebreak => { |
|
| 271 | 282 | try integer(out, super::INSTR_EBREAK as u64, 1); |
|
| 272 | 283 | }, |
|
| 273 | 284 | case il::Instr::MemoryFence => { |
|
| 274 | 285 | try integer(out, super::INSTR_MEMORYFENCE as u64, 1); |
lib/std/lang/il/printer.rad
+8 -0
| 336 | 336 | write(out, " "); |
|
| 337 | 337 | writeVal(out, a2); |
|
| 338 | 338 | write(out, " "); |
|
| 339 | 339 | writeVal(out, a3); |
|
| 340 | 340 | } |
|
| 341 | + | case super::Instr::DeviceRead { typ, dst, handle, offset } => { |
|
| 342 | + | write(out, "device-read "); write(out, typeStr(typ)); write(out, " "); |
|
| 343 | + | writeReg(out, dst); write(out, " "); writeVal(out, handle); write(out, " "); writeVal(out, offset); |
|
| 344 | + | } |
|
| 345 | + | case super::Instr::DeviceWrite { typ, handle, offset, value } => { |
|
| 346 | + | write(out, "device-write "); write(out, typeStr(typ)); write(out, " "); |
|
| 347 | + | writeVal(out, handle); write(out, " "); writeVal(out, offset); write(out, " "); writeVal(out, value); |
|
| 348 | + | } |
|
| 341 | 349 | case super::Instr::Ebreak => { |
|
| 342 | 350 | write(out, "ebreak"); |
|
| 343 | 351 | } |
|
| 344 | 352 | case super::Instr::MemoryFence => { |
|
| 345 | 353 | write(out, "memory-fence"); |
lib/std/lang/il/tests.rad
added
+64 -0
| 1 | + | //! Tests for RIL source register iteration. |
|
| 2 | + | ||
| 3 | + | use std::testing; |
|
| 4 | + | ||
| 5 | + | /// Require exact source-register order and stable exhaustion. |
|
| 6 | + | unsafe fn check(instr: super::Instr, expected: &[u32]) throws (testing::TestError) { |
|
| 7 | + | let mut cursor = super::registers(&instr); |
|
| 8 | + | for value in expected { |
|
| 9 | + | let reg = super::nextReg(&mut cursor, &instr) else panic; |
|
| 10 | + | try testing::expect(reg.n == value); |
|
| 11 | + | } |
|
| 12 | + | try testing::expect(super::nextReg(&mut cursor, &instr) == nil); |
|
| 13 | + | try testing::expect(super::nextReg(&mut cursor, &instr) == nil); |
|
| 14 | + | } |
|
| 15 | + | ||
| 16 | + | @test unsafe fn testRegisterIteration() throws (testing::TestError) { |
|
| 17 | + | let dst = super::Reg { n: 99 }; |
|
| 18 | + | let r1 = super::Reg { n: 1 }; |
|
| 19 | + | let r2 = super::Reg { n: 2 }; |
|
| 20 | + | let a = super::Val::Reg(r1); |
|
| 21 | + | let b = super::Val::Reg(r2); |
|
| 22 | + | let imm = super::Val::Imm(7); |
|
| 23 | + | try check(super::Instr::Reserve { dst, size: a, alignment: 8 }, &[1]); |
|
| 24 | + | try check(super::Instr::Reserve { dst, size: imm, alignment: 8 }, &[]); |
|
| 25 | + | try check(super::Instr::Load { typ: super::Type::W64, dst, src: r1, offset: 0 }, &[1]); |
|
| 26 | + | try check(super::Instr::Sload { typ: super::Type::W32, dst, src: r2, offset: 0 }, &[2]); |
|
| 27 | + | try check(super::Instr::Store { typ: super::Type::W64, src: a, dst: r2, offset: 0 }, &[1, 2]); |
|
| 28 | + | try check(super::Instr::Store { typ: super::Type::W64, src: imm, dst: r2, offset: 0 }, &[2]); |
|
| 29 | + | try check(super::Instr::Blit { dst: r1, src: r2, size: a }, &[1, 2, 1]); |
|
| 30 | + | try check(super::Instr::Blit { dst: r1, src: r2, size: imm }, &[1, 2]); |
|
| 31 | + | try check(super::Instr::Copy { dst, val: a }, &[1]); |
|
| 32 | + | try check(super::Instr::Copy { dst, val: imm }, &[]); |
|
| 33 | + | try check(super::Instr::Copy { dst, val: super::Val::DataSym("data") }, &[]); |
|
| 34 | + | try check(super::Instr::Copy { dst, val: super::Val::Undef }, &[]); |
|
| 35 | + | try check(super::Instr::BinOp { op: super::BinOp::Add, typ: super::Type::W64, dst, a: imm, b }, &[2]); |
|
| 36 | + | try check(super::Instr::BinOp { op: super::BinOp::Add, typ: super::Type::W64, dst, a, b: a }, &[1, 1]); |
|
| 37 | + | try check(super::Instr::UnOp { op: super::UnOp::Neg, typ: super::Type::W64, dst, a }, &[1]); |
|
| 38 | + | try check(super::Instr::Zext { typ: super::Type::W8, dst, val: a }, &[1]); |
|
| 39 | + | try check(super::Instr::Sext { typ: super::Type::W8, dst, val: b }, &[2]); |
|
| 40 | + | let mut args = [imm, a, b, a]; |
|
| 41 | + | try check(super::Instr::Call { retTy: super::Type::W64, dst, func: b, args: &args[..] }, &[2, 1, 2, 1]); |
|
| 42 | + | try check(super::Instr::Call { retTy: super::Type::W64, dst: nil, func: super::Val::FnAddr("callee"), args: &[] }, &[]); |
|
| 43 | + | try check(super::Instr::Ret { val: a }, &[1]); |
|
| 44 | + | try check(super::Instr::Ret { val: nil }, &[]); |
|
| 45 | + | try check(super::Instr::Jmp { target: 0, args: &mut args[..] }, &[1, 2, 1]); |
|
| 46 | + | let mut other = [b, imm]; |
|
| 47 | + | try check(super::Instr::Br { op: super::CmpOp::Eq, typ: super::Type::W64, a, b, |
|
| 48 | + | thenTarget: 0, thenArgs: &mut args[..], elseTarget: 1, elseArgs: &mut other[..] }, &[1, 2, 1, 2, 1, 2]); |
|
| 49 | + | let mut cases = [ |
|
| 50 | + | super::SwitchCase { value: 0, target: 0, args: &mut [] }, |
|
| 51 | + | super::SwitchCase { value: 1, target: 1, args: &mut args[..] }, |
|
| 52 | + | super::SwitchCase { value: 2, target: 2, args: &mut [] }, |
|
| 53 | + | super::SwitchCase { value: 3, target: 3, args: &mut other[..] }, |
|
| 54 | + | ]; |
|
| 55 | + | try check(super::Instr::Switch { val: b, defaultTarget: 0, defaultArgs: &mut [], cases: &mut cases[..] }, &[2, 1, 2, 1, 2]); |
|
| 56 | + | try check(super::Instr::Switch { val: imm, defaultTarget: 0, defaultArgs: &mut args[..], cases: &mut [] }, &[1, 2, 1]); |
|
| 57 | + | try check(super::Instr::Switch { val: imm, defaultTarget: 0, defaultArgs: &mut [], cases: &mut [] }, &[]); |
|
| 58 | + | try check(super::Instr::Ecall { dst, num: a, a0: imm, a1: b, a2: a, a3: imm }, &[1, 2, 1]); |
|
| 59 | + | try check(super::Instr::Ecall { dst, num: a, a0: b, a1: a, a2: b, a3: a }, &[1, 2, 1, 2, 1]); |
|
| 60 | + | try check(super::Instr::Unreachable, &[]); |
|
| 61 | + | try check(super::Instr::Ebreak, &[]); |
|
| 62 | + | try check(super::Instr::MemoryFence, &[]); |
|
| 63 | + | ||
| 64 | + | } |
lib/std/lang/lower.rad
+922 -628
| 262 | 262 | Normal, |
|
| 263 | 263 | /// Program entry function marked with `@default`. |
|
| 264 | 264 | Default, |
|
| 265 | 265 | } |
|
| 266 | 266 | ||
| 267 | - | /// Function sink used by lowerers that consume functions as they are produced. |
|
| 268 | - | export record FnSink: Copy { |
|
| 269 | - | /// Opaque context. It must remain valid for every sink callback. |
|
| 270 | - | ctx: *unsafe mut opaque, |
|
| 271 | - | /// Callback invoked for each lowered function. |
|
| 272 | - | emitFn: unsafe fn(*unsafe mut opaque, *unsafe il::Fn, FnRole), |
|
| 273 | - | } |
|
| 274 | - | ||
| 275 | - | /// Destination for functions produced by the lowerer. |
|
| 276 | - | export union FnOutput: Copy { |
|
| 277 | - | /// Store lowered functions in the provided slice. |
|
| 278 | - | Accumulate(*unsafe mut [*unsafe il::Fn]), |
|
| 279 | - | /// Send lowered functions to an external consumer immediately. |
|
| 280 | - | Stream(FnSink), |
|
| 267 | + | /// One function ready for a caller to consume before lowering continues. |
|
| 268 | + | export record LoweredFunction: Copy { |
|
| 269 | + | /// Function allocated in the current function arena. |
|
| 270 | + | function: *unsafe il::Fn, |
|
| 271 | + | /// Entry-point role of the source declaration. |
|
| 272 | + | role: FnRole, |
|
| 273 | + | } |
|
| 274 | + | ||
| 275 | + | /// Position within one module's declarations and instance methods. |
|
| 276 | + | export record ModuleCursor { |
|
| 277 | + | /// Top-level declaration nodes in source order. |
|
| 278 | + | declarations: *[*ast::Node], |
|
| 279 | + | /// Next declaration to process. |
|
| 280 | + | next: u32, |
|
| 281 | + | /// Whether default functions in this module are program entry points. |
|
| 282 | + | isRoot: bool, |
|
| 283 | + | /// Instance whose methods are currently being lowered. |
|
| 284 | + | instanceState: InstanceCursor, |
|
| 285 | + | /// Whether the instance cursor contains an active declaration. |
|
| 286 | + | instanceActive: bool, |
|
| 287 | + | } |
|
| 288 | + | ||
| 289 | + | /// Persistent v-table names and position within an instance declaration. |
|
| 290 | + | export record InstanceCursor: Copy { |
|
| 291 | + | /// Trait whose method slots define the v-table. |
|
| 292 | + | traitInfo: *unsafe resolver::TraitType, |
|
| 293 | + | /// Trait source name. |
|
| 294 | + | traitName: *[u8], |
|
| 295 | + | /// Concrete type source name. |
|
| 296 | + | typeName: *[u8], |
|
| 297 | + | /// Method declarations in source order. |
|
| 298 | + | methods: *[*ast::Node], |
|
| 299 | + | /// Next method to process. |
|
| 300 | + | next: u32, |
|
| 301 | + | /// Persistent qualified function names, indexed by trait method slot. |
|
| 302 | + | methodNames: [*[u8]; ast::MAX_TRAIT_METHODS], |
|
| 303 | + | /// Slots initialized by concrete methods. |
|
| 304 | + | methodNameSet: [bool; ast::MAX_TRAIT_METHODS], |
|
| 281 | 305 | } |
|
| 282 | 306 | ||
| 283 | 307 | /// Module-level lowering context. Shared across all function lowerings. |
|
| 284 | 308 | /// Holds global state like the data section (strings, constants) and provides |
|
| 285 | 309 | /// access to the resolver for type queries. |
|
| 286 | - | export record Lowerer { |
|
| 310 | + | export record Lowerer: 'arena + 'phase where 'arena: 'phase { |
|
| 287 | 311 | /// Arena for persistent lowering state, including data and symbol names. |
|
| 288 | 312 | arena: *unsafe mut alloc::Arena, |
|
| 289 | - | /// Arena for allocations owned by the function currently being lowered. |
|
| 290 | - | fnArena: *unsafe mut alloc::Arena, |
|
| 291 | - | /// Allocator backed by the arena. |
|
| 292 | - | allocator: alloc::Allocator, |
|
| 293 | 313 | /// Resolver for type information. Used to query types, symbols, and |
|
| 294 | 314 | /// compile-time constant values during lowering. |
|
| 295 | - | resolver: *unsafe resolver::Resolver, |
|
| 315 | + | resolver: &'phase resolver::Resolver 'arena, |
|
| 296 | 316 | /// Module graph for cross-module symbol resolution. |
|
| 297 | - | moduleGraph: ?*unsafe module::ModuleGraph, |
|
| 317 | + | moduleGraph: ?&'phase module::ModuleGraph, |
|
| 298 | 318 | /// Package name for qualified symbol names. |
|
| 299 | 319 | pkgName: *[u8], |
|
| 300 | 320 | /// Current module being lowered. |
|
| 301 | 321 | currentMod: ?u16, |
|
| 302 | 322 | /// Global data items (string literals, constants, static arrays). |
|
| 303 | 323 | /// These become the data sections in the final binary. |
|
| 304 | 324 | data: *mut [il::Data], |
|
| 305 | - | /// Destination for lowered functions. |
|
| 306 | - | output: FnOutput, |
|
| 307 | - | /// Map of function symbols to qualified names. |
|
| 308 | - | fnSyms: *mut [FnSymEntry], |
|
| 325 | + | /// First data entry owned by the package currently being lowered. |
|
| 326 | + | packageDataStart: u32, |
|
| 327 | + | /// Functions retained for accumulated output. |
|
| 328 | + | functions: *unsafe mut [*unsafe il::Fn], |
|
| 329 | + | /// Function and data symbols with their emitted names. |
|
| 330 | + | symbolNames: *mut [SymbolNameEntry], |
|
| 309 | 331 | /// Global error type tag table. Maps nominal types to unique tags. |
|
| 310 | 332 | errTags: *mut [ErrTagEntry], |
|
| 311 | 333 | /// Next error tag to assign (starts at 1; 0 = success). |
|
| 312 | 334 | errTagCounter: u32, |
|
| 313 | 335 | /// Lowering options. |
|
| 314 | 336 | options: LowerOptions, |
|
| 315 | 337 | } |
|
| 316 | 338 | ||
| 317 | - | /// Entry mapping a function symbol to its qualified name. |
|
| 318 | - | record FnSymEntry: Copy { |
|
| 339 | + | /// Entry mapping a function or data symbol to its emitted name. |
|
| 340 | + | record SymbolNameEntry: Copy { |
|
| 341 | + | /// Stable resolver symbol identity. |
|
| 319 | 342 | sym: *unsafe resolver::Symbol, |
|
| 343 | + | /// Qualified name in the emitted program. |
|
| 320 | 344 | qualName: *[u8], |
|
| 321 | 345 | } |
|
| 322 | 346 | ||
| 323 | 347 | /// Entry in the global error tag table. |
|
| 324 | 348 | record ErrTagEntry: Copy { |
| 340 | 364 | return maxSize; |
|
| 341 | 365 | } |
|
| 342 | 366 | ||
| 343 | 367 | /// Get or assign a globally unique error tag for the given error type. |
|
| 344 | 368 | /// Tag `0` is reserved for success; error tags start at `1`. |
|
| 345 | - | fn getOrAssignErrorTag(self: &mut Lowerer, errType: resolver::Type) -> u32 { |
|
| 369 | + | unsafe fn getOrAssignErrorTag 'arena 'phase (self: &mut Lowerer 'arena 'phase, errType: resolver::Type) -> u32 where 'arena: 'phase { |
|
| 346 | 370 | for entry in &self.errTags[..] { |
|
| 347 | - | if entry.ty == errType { |
|
| 371 | + | if resolver::erasedTypesEqual(entry.ty, errType) { |
|
| 348 | 372 | return entry.tag; |
|
| 349 | 373 | } |
|
| 350 | 374 | } |
|
| 351 | 375 | let tag = self.errTagCounter; |
|
| 352 | 376 | ||
| 353 | 377 | set self.errTagCounter += 1; |
|
| 354 | - | self.errTags.append(ErrTagEntry { ty: errType, tag }, self.allocator); |
|
| 378 | + | self.errTags.append(ErrTagEntry { ty: errType, tag }, alloc::arenaAllocator(self.arena)); |
|
| 355 | 379 | ||
| 356 | 380 | return tag; |
|
| 357 | 381 | } |
|
| 358 | 382 | ||
| 359 | - | /// Emit one function to the active output. |
|
| 360 | - | unsafe fn emitFunction(self: &mut Lowerer, func: *unsafe il::Fn, role: FnRole) { |
|
| 361 | - | match self.output { |
|
| 362 | - | case FnOutput::Accumulate(accumulated) => { |
|
| 363 | - | let mut fns = accumulated; |
|
| 364 | - | fns.append(func, self.allocator); |
|
| 365 | - | set self.output = FnOutput::Accumulate(fns); |
|
| 366 | - | } |
|
| 367 | - | case FnOutput::Stream(sink) => { |
|
| 368 | - | sink.emitFn(sink.ctx, func, role); |
|
| 369 | - | } |
|
| 370 | - | } |
|
| 383 | + | /// Retain a lowered function for accumulated IL output. |
|
| 384 | + | unsafe fn emitFunction 'arena 'phase (self: &mut Lowerer 'arena 'phase, func: *unsafe il::Fn) where 'arena: 'phase { |
|
| 385 | + | self.functions.append(func, alloc::arenaAllocator(self.arena)); |
|
| 371 | 386 | } |
|
| 372 | 387 | ||
| 373 | 388 | /// Get the function role for a top-level function declaration. |
|
| 374 | 389 | fn lowerFnRole(isRoot: bool, attrs: ?ast::Attributes) -> FnRole { |
|
| 375 | 390 | if isRoot and checkAttr(attrs, ast::Attribute::Default) { |
| 673 | 688 | // Function Lowering State // |
|
| 674 | 689 | ///////////////////////////// |
|
| 675 | 690 | ||
| 676 | 691 | /// Per-function lowering state. Created fresh for each function and contains |
|
| 677 | 692 | /// all the mutable state needed during function body lowering. |
|
| 678 | - | record FnLowerer: Copy { |
|
| 693 | + | record FnLowerer: 'arena + 'phase + 'function where 'arena: 'phase, 'phase: 'function { |
|
| 679 | 694 | /// Reference to the module-level lowerer. |
|
| 680 | - | low: *unsafe mut Lowerer, |
|
| 681 | - | /// Allocator for IL allocations. |
|
| 682 | - | allocator: alloc::Allocator, |
|
| 695 | + | low: &'function mut Lowerer 'arena 'phase, |
|
| 696 | + | /// Arena for allocations owned by this function. |
|
| 697 | + | arena: &'function mut alloc::Arena, |
|
| 683 | 698 | /// Type signature of the function being lowered. |
|
| 684 | 699 | fnType: *resolver::FnType, |
|
| 685 | 700 | /// Number of SSA variable slots required for each block. |
|
| 686 | 701 | localCount: u32, |
|
| 687 | 702 | /// Function name, used as prefix for generated data symbols. |
| 692 | 707 | /// Metadata (name, type, mutability) for each variable. Indexed by variable |
|
| 693 | 708 | /// id. Doesn't change after declaration. For the SSA value of a variable in |
|
| 694 | 709 | /// a specific block, see [`BlockData::vars`]. |
|
| 695 | 710 | vars: *unsafe mut [VarData], |
|
| 696 | 711 | /// Parameter-to-variable bindings, initialized in the entry block. |
|
| 697 | - | params: *unsafe mut [FnParamBinding], |
|
| 712 | + | params: [FnParamBinding; resolver::MAX_FN_PARAMS], |
|
| 713 | + | /// Number of initialized parameter bindings. |
|
| 714 | + | paramsLen: u32, |
|
| 698 | 715 | ||
| 699 | 716 | // ~ Basic block management ~ // |
|
| 700 | 717 | ||
| 701 | 718 | /// Block storage array, indexed by block id. |
|
| 702 | 719 | blockData: *unsafe mut [BlockData], |
| 705 | 722 | /// The block currently receiving new instructions. |
|
| 706 | 723 | currentBlock: ?BlockId, |
|
| 707 | 724 | ||
| 708 | 725 | // ~ Loop management ~ // |
|
| 709 | 726 | ||
| 710 | - | /// Stack of loop contexts for break/continue resolution. |
|
| 711 | - | loopStack: *unsafe mut [LoopCtx], |
|
| 727 | + | /// Loop contexts indexed by nesting depth. |
|
| 728 | + | /// The active prefix contains `loopDepth` entries. |
|
| 729 | + | loopStack: [LoopCtx; MAX_LOOP_DEPTH], |
|
| 712 | 730 | /// Current nesting depth (index into loopStack). |
|
| 713 | 731 | loopDepth: u32, |
|
| 714 | 732 | ||
| 715 | 733 | // ~ Counters ~ // |
|
| 716 | 734 |
| 745 | 763 | /// 2. Iterates over top-level declarations, lowering each. |
|
| 746 | 764 | /// 3. Returns the complete IL program with functions and data section. |
|
| 747 | 765 | /// |
|
| 748 | 766 | /// The resolver must have already processed the AST -- we rely on its type |
|
| 749 | 767 | /// annotations, symbol table, and constant evaluations. |
|
| 750 | - | export unsafe fn lower( |
|
| 751 | - | res: &resolver::Resolver, |
|
| 768 | + | export unsafe fn lower 'arena ( |
|
| 769 | + | res: &resolver::Resolver 'arena, |
|
| 752 | 770 | root: *ast::Node, |
|
| 753 | 771 | pkgName: *[u8], |
|
| 754 | 772 | arena: &mut alloc::Arena |
|
| 755 | 773 | ) -> il::Program throws (LowerError) { |
|
| 756 | - | let mut low = Lowerer { |
|
| 757 | - | arena: (&mut *arena) as *unsafe mut alloc::Arena, |
|
| 758 | - | fnArena: (&mut *arena) as *unsafe mut alloc::Arena, |
|
| 759 | - | allocator: alloc::arenaAllocator(arena), |
|
| 760 | - | resolver: res as *unsafe resolver::Resolver, |
|
| 761 | - | moduleGraph: nil, |
|
| 762 | - | pkgName, |
|
| 763 | - | currentMod: nil, |
|
| 764 | - | data: &mut [], |
|
| 765 | - | output: FnOutput::Accumulate(&mut []), |
|
| 766 | - | fnSyms: &mut [], |
|
| 767 | - | errTags: &mut [], |
|
| 768 | - | errTagCounter: 1, |
|
| 769 | - | options: LowerOptions { debug: false, buildTest: false }, |
|
| 770 | - | }; |
|
| 771 | - | try lowerDecls(&mut low, root, true); |
|
| 774 | + | let functionArena = (&mut *arena) as *unsafe mut alloc::Arena; |
|
| 775 | + | let resolved: 'phase = &*res where 'arena: 'phase in { |
|
| 776 | + | let mut low = Lowerer 'arena 'phase { |
|
| 777 | + | arena: (&mut *arena) as *unsafe mut alloc::Arena, |
|
| 778 | + | resolver: resolved, |
|
| 779 | + | moduleGraph: nil, |
|
| 780 | + | pkgName, |
|
| 781 | + | currentMod: nil, |
|
| 782 | + | data: &mut [], |
|
| 783 | + | packageDataStart: 0, |
|
| 784 | + | functions: &mut [], |
|
| 785 | + | symbolNames: &mut [], |
|
| 786 | + | errTags: &mut [], |
|
| 787 | + | errTagCounter: 1, |
|
| 788 | + | options: LowerOptions { debug: false, buildTest: false }, |
|
| 789 | + | }; |
|
| 790 | + | try lowerDecls(&mut low, root, true, functionArena); |
|
| 772 | 791 | ||
| 773 | - | return finalize(low); |
|
| 792 | + | return finalize(low); |
|
| 793 | + | } |
|
| 774 | 794 | } |
|
| 775 | 795 | ||
| 776 | 796 | ///////////////////////////////// |
|
| 777 | 797 | // Multi-Module Lowering API // |
|
| 778 | 798 | ///////////////////////////////// |
|
| 779 | 799 | ||
| 780 | 800 | /// Create a lowerer for multi-module compilation. |
|
| 781 | - | /// The resolver, graph, and both arenas must outlive the returned lowerer. |
|
| 782 | - | export unsafe fn lowerer( |
|
| 783 | - | res: *unsafe resolver::Resolver, |
|
| 784 | - | graph: &module::ModuleGraph, |
|
| 801 | + | /// The resolver, graph, and persistent arena must outlive the returned lowerer. |
|
| 802 | + | export unsafe fn lowerer 'arena 'phase ( |
|
| 803 | + | res: &'phase resolver::Resolver 'arena, |
|
| 804 | + | graph: &'phase module::ModuleGraph, |
|
| 785 | 805 | pkgName: *[u8], |
|
| 786 | 806 | arena: *unsafe mut alloc::Arena, |
|
| 787 | - | fnArena: *unsafe mut alloc::Arena, |
|
| 788 | 807 | options: LowerOptions |
|
| 789 | - | ) -> Lowerer { |
|
| 790 | - | return Lowerer { |
|
| 808 | + | ) -> Lowerer 'arena 'phase where 'arena: 'phase { |
|
| 809 | + | return Lowerer 'arena 'phase { |
|
| 791 | 810 | arena, |
|
| 792 | - | fnArena, |
|
| 793 | - | allocator: alloc::arenaAllocator(arena), |
|
| 794 | 811 | resolver: res, |
|
| 795 | - | moduleGraph: graph as *unsafe module::ModuleGraph, |
|
| 812 | + | moduleGraph: graph, |
|
| 796 | 813 | pkgName, |
|
| 797 | 814 | currentMod: nil, |
|
| 798 | 815 | data: &mut [], |
|
| 799 | - | output: FnOutput::Accumulate(&mut []), |
|
| 800 | - | fnSyms: &mut [], |
|
| 816 | + | packageDataStart: 0, |
|
| 817 | + | functions: &mut [], |
|
| 818 | + | symbolNames: &mut [], |
|
| 801 | 819 | errTags: &mut [], |
|
| 802 | 820 | errTagCounter: 1, |
|
| 803 | 821 | options, |
|
| 804 | 822 | }; |
|
| 805 | 823 | } |
|
| 806 | 824 | ||
| 807 | 825 | /// Lower a module's AST into the lowerer. |
|
| 808 | 826 | /// Call this for each module in the package, then use `finalize` to get the program. |
|
| 809 | - | export unsafe fn lowerModule( |
|
| 810 | - | low: &mut Lowerer, |
|
| 827 | + | export unsafe fn lowerModule 'arena 'phase ( |
|
| 828 | + | low: &mut Lowerer 'arena 'phase, |
|
| 811 | 829 | moduleId: u16, |
|
| 812 | 830 | root: *ast::Node, |
|
| 813 | - | isRoot: bool |
|
| 814 | - | ) throws (LowerError) { |
|
| 831 | + | isRoot: bool, |
|
| 832 | + | functionArena: &mut alloc::Arena |
|
| 833 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 815 | 834 | set low.currentMod = moduleId; |
|
| 816 | - | try lowerDecls(low, root, isRoot); |
|
| 835 | + | try lowerDecls(low, root, isRoot, functionArena); |
|
| 817 | 836 | } |
|
| 818 | 837 | ||
| 819 | - | /// Lower all top-level declarations in a block. |
|
| 820 | - | unsafe fn lowerDecls(low: &mut Lowerer, root: *ast::Node, isRoot: bool) throws (LowerError) { |
|
| 838 | + | /// Start lowering the declarations of one module. |
|
| 839 | + | export unsafe fn moduleCursor(root: *ast::Node, isRoot: bool) -> ModuleCursor throws (LowerError) { |
|
| 821 | 840 | let case ast::NodeValue::Block(block) = root.value else { |
|
| 822 | 841 | throw LowerError::ExpectedBlock(root); |
|
| 823 | 842 | }; |
|
| 824 | - | let stmtsList = block.statements; |
|
| 825 | - | ||
| 826 | - | for node in stmtsList { |
|
| 843 | + | return ModuleCursor { declarations: block.statements, next: 0, isRoot, instanceState: undefined, instanceActive: false }; |
|
| 844 | + | } |
|
| 845 | + | ||
| 846 | + | /// Lower declarations until one function is ready or the module is complete. |
|
| 847 | + | /// The caller must finish using a function before reclaiming its arena storage. |
|
| 848 | + | export unsafe fn lowerNext 'arena 'phase ( |
|
| 849 | + | low: &mut Lowerer 'arena 'phase, |
|
| 850 | + | cursor: &mut ModuleCursor, |
|
| 851 | + | functionArena: &mut alloc::Arena |
|
| 852 | + | ) -> ?LoweredFunction throws (LowerError) where 'arena: 'phase { |
|
| 853 | + | loop { |
|
| 854 | + | if cursor.instanceActive { |
|
| 855 | + | if let function = try lowerNextInstance(low, &mut cursor.instanceState, functionArena) { |
|
| 856 | + | return LoweredFunction { function, role: FnRole::Normal }; |
|
| 857 | + | } |
|
| 858 | + | set cursor.instanceActive = false; |
|
| 859 | + | } |
|
| 860 | + | if cursor.next >= cursor.declarations.len { |
|
| 861 | + | return nil; |
|
| 862 | + | } |
|
| 863 | + | let node = cursor.declarations[cursor.next]; |
|
| 864 | + | set cursor.next += 1; |
|
| 827 | 865 | match node.value { |
|
| 828 | 866 | case ast::NodeValue::FnDecl(decl) => { |
|
| 829 | - | if let f = try lowerFnDecl(low, node, decl) { |
|
| 830 | - | let role = lowerFnRole(isRoot, decl.attrs); |
|
| 831 | - | emitFunction(low, f, role); |
|
| 867 | + | if let function = try lowerFnDecl(low, node, decl, functionArena) { |
|
| 868 | + | return LoweredFunction { function, role: lowerFnRole(cursor.isRoot, decl.attrs) }; |
|
| 832 | 869 | } |
|
| 833 | 870 | } |
|
| 834 | - | case ast::NodeValue::ConstDecl(decl) => { |
|
| 835 | - | try lowerDataDecl(low, node, decl.value, true); |
|
| 836 | - | } |
|
| 837 | - | case ast::NodeValue::StaticDecl(decl) => { |
|
| 838 | - | try lowerDataDecl(low, node, decl.value, false); |
|
| 839 | - | } |
|
| 840 | - | case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 841 | - | try lowerInstanceDecl(low, node, traitName, targetType, methods); |
|
| 871 | + | case ast::NodeValue::ConstDecl(decl) => |
|
| 872 | + | try lowerDataDecl(low, node, decl.value, true), |
|
| 873 | + | case ast::NodeValue::StaticDecl(decl) => |
|
| 874 | + | try lowerDataDecl(low, node, decl.value, false), |
|
| 875 | + | case ast::NodeValue::InstanceDecl { traitName, targetType, methods, .. } => { |
|
| 876 | + | set cursor.instanceState = try instanceCursor(low, traitName, targetType, methods); |
|
| 877 | + | set cursor.instanceActive = true; |
|
| 842 | 878 | } |
|
| 843 | - | case ast::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, .. } => { |
|
| 844 | - | if let f = try lowerMethodDecl(low, node, name, receiverName, sig, body) { |
|
| 845 | - | emitFunction(low, f, FnRole::Normal); |
|
| 879 | + | case ast::NodeValue::MethodDecl { name, receiverName, sig, body, .. } => { |
|
| 880 | + | if let function = try lowerMethodDecl( |
|
| 881 | + | low, node, name, receiverName, sig, body, functionArena |
|
| 882 | + | ) { |
|
| 883 | + | return LoweredFunction { function, role: FnRole::Normal }; |
|
| 846 | 884 | } |
|
| 847 | 885 | } |
|
| 848 | 886 | else => {}, |
|
| 849 | 887 | } |
|
| 850 | 888 | } |
|
| 851 | 889 | } |
|
| 852 | 890 | ||
| 891 | + | /// Lower all top-level declarations in a block. |
|
| 892 | + | unsafe fn lowerDecls 'arena 'phase ( |
|
| 893 | + | low: &mut Lowerer 'arena 'phase, |
|
| 894 | + | root: *ast::Node, |
|
| 895 | + | isRoot: bool, |
|
| 896 | + | functionArena: &mut alloc::Arena |
|
| 897 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 898 | + | let mut cursor = try moduleCursor(root, isRoot); |
|
| 899 | + | while let result = try lowerNext(low, &mut cursor, functionArena) { |
|
| 900 | + | emitFunction(low, result.function); |
|
| 901 | + | } |
|
| 902 | + | } |
|
| 903 | + | ||
| 853 | 904 | /// Consume the lowerer and publish its global data as an immutable array. |
|
| 854 | - | export fn finalize(low: Lowerer) -> il::Program { |
|
| 855 | - | let case Lowerer { data, output, .. } = low |
|
| 905 | + | export fn finalize 'arena 'phase (low: Lowerer 'arena 'phase) -> il::Program where 'arena: 'phase { |
|
| 906 | + | let case Lowerer 'arena 'phase { data, functions, .. } = low |
|
| 856 | 907 | else panic "expected lowerer"; |
|
| 857 | - | match output { |
|
| 858 | - | case FnOutput::Accumulate(accumulated) => { |
|
| 859 | - | return il::Program { |
|
| 860 | - | data, |
|
| 861 | - | fns: accumulated, |
|
| 862 | - | }; |
|
| 863 | - | } |
|
| 864 | - | case FnOutput::Stream(_) => { |
|
| 865 | - | panic "finalize: cannot finalize streaming lowerer"; |
|
| 866 | - | } |
|
| 867 | - | } |
|
| 908 | + | return il::Program { data, fns: functions }; |
|
| 868 | 909 | } |
|
| 869 | 910 | ||
| 870 | 911 | ///////////////////////////////// |
|
| 871 | 912 | // Qualified Name Construction // |
|
| 872 | 913 | ///////////////////////////////// |
|
| 873 | 914 | ||
| 874 | 915 | /// Get module path segments for the current or specified module. |
|
| 875 | 916 | /// Returns empty slice if no module graph or module not found. |
|
| 876 | - | unsafe fn getModulePath(self: &mut Lowerer, modId: ?u16) -> *[*[u8]] { |
|
| 917 | + | unsafe fn getModulePath 'arena 'phase (self: &mut Lowerer 'arena 'phase, modId: ?u16) -> *[*[u8]] where 'arena: 'phase { |
|
| 877 | 918 | let graph = self.moduleGraph else { |
|
| 878 | 919 | return &[]; |
|
| 879 | 920 | }; |
|
| 880 | 921 | let mut id = modId; |
|
| 881 | 922 | if id == nil { |
| 890 | 931 | return module::moduleQualifiedPath(entry); |
|
| 891 | 932 | } |
|
| 892 | 933 | ||
| 893 | 934 | /// Build a qualified name string for a symbol. |
|
| 894 | 935 | /// If `modId` is nil, uses current module. |
|
| 895 | - | unsafe fn qualifyName(self: &mut Lowerer, modId: ?u16, name: *[u8]) -> *[u8] { |
|
| 936 | + | unsafe fn qualifyName 'arena 'phase (self: &mut Lowerer 'arena 'phase, modId: ?u16, name: *[u8]) -> *[u8] where 'arena: 'phase { |
|
| 896 | 937 | let path = getModulePath(self, modId); |
|
| 897 | 938 | if path.len == 0 { |
|
| 898 | 939 | return name; |
|
| 899 | 940 | } |
|
| 900 | 941 | return il::formatQualifiedName(self.arena, path, name); |
|
| 901 | 942 | } |
|
| 902 | 943 | ||
| 903 | - | /// Register a function symbol with its qualified name. |
|
| 904 | - | /// Called when lowering function declarations, so cross-package calls can find |
|
| 905 | - | /// the function by name. |
|
| 906 | - | fn registerFnSym(self: &mut Lowerer, sym: *unsafe resolver::Symbol, qualName: *[u8]) { |
|
| 907 | - | self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator); |
|
| 944 | + | /// Register the emitted name for a function or data symbol. |
|
| 945 | + | /// Calls and address expressions use this name across package boundaries. |
|
| 946 | + | unsafe fn registerSymbolName 'arena 'phase (self: &mut Lowerer 'arena 'phase, sym: *unsafe resolver::Symbol, qualName: *[u8]) where 'arena: 'phase { |
|
| 947 | + | self.symbolNames.append(SymbolNameEntry { sym, qualName }, alloc::arenaAllocator(self.arena)); |
|
| 908 | 948 | } |
|
| 909 | 949 | ||
| 910 | - | /// Look up a function's qualified name by its symbol. |
|
| 911 | - | /// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered). |
|
| 950 | + | /// Look up the emitted name for a function or data symbol. |
|
| 951 | + | /// Return `nil` if its declaration has not been lowered. |
|
| 912 | 952 | // TODO: This is kind of dubious as an optimization, if it depends on the order |
|
| 913 | 953 | // in which modules are lowered. |
|
| 914 | 954 | // TODO: Use a hash table here? |
|
| 915 | - | unsafe fn lookupFnSym(self: &Lowerer, sym: *unsafe resolver::Symbol) -> ?*[u8] { |
|
| 916 | - | for entry in &self.fnSyms[..] { |
|
| 955 | + | unsafe fn lookupSymbolName 'arena 'phase (self: &Lowerer 'arena 'phase, sym: *unsafe resolver::Symbol) -> ?*[u8] where 'arena: 'phase { |
|
| 956 | + | for entry in &self.symbolNames[..] { |
|
| 917 | 957 | if entry.sym == sym { |
|
| 918 | 958 | return entry.qualName; |
|
| 919 | 959 | } |
|
| 920 | 960 | } |
|
| 921 | 961 | return nil; |
|
| 922 | 962 | } |
|
| 923 | 963 | ||
| 924 | 964 | /// Set the package context for lowering. |
|
| 925 | 965 | /// Called before lowering each package. |
|
| 926 | - | /// The graph must outlive later uses of the lowerer. |
|
| 927 | - | export unsafe fn setPackage(self: &mut Lowerer, graph: &module::ModuleGraph, pkgName: *[u8]) { |
|
| 928 | - | set self.moduleGraph = graph as *unsafe module::ModuleGraph; |
|
| 966 | + | export unsafe fn setPackage 'arena 'phase (self: &mut Lowerer 'arena 'phase, pkgName: *[u8]) where 'arena: 'phase { |
|
| 929 | 967 | set self.pkgName = pkgName; |
|
| 930 | 968 | set self.currentMod = nil; |
|
| 969 | + | set self.packageDataStart = self.data.len; |
|
| 931 | 970 | } |
|
| 932 | 971 | ||
| 933 | 972 | /// Create a new function lowerer for a given function type and name. |
|
| 934 | - | unsafe fn fnLowerer( |
|
| 935 | - | self: &mut Lowerer, |
|
| 973 | + | unsafe fn fnLowerer 'arena 'phase 'function ( |
|
| 974 | + | self: &'function mut Lowerer 'arena 'phase, |
|
| 936 | 975 | node: *ast::Node, |
|
| 937 | 976 | fnType: *resolver::FnType, |
|
| 938 | - | qualName: *[u8] |
|
| 939 | - | ) -> FnLowerer { |
|
| 940 | - | let loopStack = try! alloc::allocRawSlice(self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *unsafe mut [LoopCtx]; |
|
| 941 | - | ||
| 942 | - | let mut fnLow = FnLowerer { |
|
| 943 | - | low: (&mut *self) as *unsafe mut Lowerer, |
|
| 944 | - | allocator: alloc::arenaAllocator(self.fnArena), |
|
| 945 | - | fnType: fnType, |
|
| 946 | - | localCount: resolver::nodeData(self.resolver, node).localCount, |
|
| 977 | + | qualName: *[u8], |
|
| 978 | + | functionArena: &'function mut alloc::Arena |
|
| 979 | + | ) -> FnLowerer 'arena 'phase 'function where 'arena: 'phase, 'phase: 'function { |
|
| 980 | + | let localCount = resolver::nodeData(self.resolver, node).localCount; |
|
| 981 | + | let mut fnLow = FnLowerer 'arena 'phase 'function { |
|
| 982 | + | low: self, |
|
| 983 | + | arena: functionArena, |
|
| 984 | + | fnType, |
|
| 985 | + | localCount, |
|
| 947 | 986 | fnName: qualName, |
|
| 948 | 987 | vars: &mut [], |
|
| 949 | - | params: &mut [], |
|
| 988 | + | params: [FnParamBinding { var: Var(0), reg: il::Reg { n: 0 } }; resolver::MAX_FN_PARAMS], |
|
| 989 | + | paramsLen: 0, |
|
| 950 | 990 | blockData: &mut [], |
|
| 951 | 991 | entryBlock: nil, |
|
| 952 | 992 | currentBlock: nil, |
|
| 953 | - | loopStack, |
|
| 993 | + | loopStack: [LoopCtx { breakTarget: BlockId(0), continueTarget: nil }; MAX_LOOP_DEPTH], |
|
| 954 | 994 | loopDepth: 0, |
|
| 955 | 995 | labelCounter: 0, |
|
| 956 | 996 | dataCounter: 0, |
|
| 957 | 997 | regCounter: 0, |
|
| 958 | 998 | returnReg: nil, |
|
| 959 | 999 | isLeaf: true, |
|
| 960 | 1000 | srcLoc: undefined, |
|
| 961 | 1001 | }; |
|
| 962 | - | if self.options.debug { |
|
| 963 | - | let modId = self.currentMod else { |
|
| 1002 | + | if fnLow.low.options.debug { |
|
| 1003 | + | let modId = fnLow.low.currentMod else { |
|
| 964 | 1004 | panic "fnLowerer: debug enabled but no current module"; |
|
| 965 | 1005 | }; |
|
| 966 | 1006 | set fnLow.srcLoc = il::SrcLoc { |
|
| 967 | 1007 | moduleId: modId, |
|
| 968 | 1008 | offset: node.span.offset, |
| 976 | 1016 | /// This sets up the per-function lowering state, processes parameters, |
|
| 977 | 1017 | /// then lowers the function body into a CFG of basic blocks. |
|
| 978 | 1018 | /// |
|
| 979 | 1019 | /// For throwing functions, the return type is a result aggregate |
|
| 980 | 1020 | /// rather than the declared return type. |
|
| 981 | - | unsafe fn lowerFnDecl(self: &mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*unsafe il::Fn throws (LowerError) { |
|
| 1021 | + | unsafe fn lowerFnDecl 'arena 'phase ( |
|
| 1022 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1023 | + | node: *ast::Node, |
|
| 1024 | + | decl: ast::FnDecl, |
|
| 1025 | + | functionArena: &mut alloc::Arena |
|
| 1026 | + | ) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase { |
|
| 982 | 1027 | if not shouldLowerFn(&decl, self.options.buildTest) { |
|
| 983 | 1028 | return nil; |
|
| 984 | 1029 | } |
|
| 985 | 1030 | let case ast::NodeValue::Ident(name) = decl.name.value else { |
|
| 986 | 1031 | throw LowerError::ExpectedIdentifier; |
| 994 | 1039 | // Build qualified function name for multi-module compilation. |
|
| 995 | 1040 | let qualName = qualifyName(self, nil, name); |
|
| 996 | 1041 | ||
| 997 | 1042 | // Register function symbol for cross-package call resolution. |
|
| 998 | 1043 | if let sym = data.sym { |
|
| 999 | - | registerFnSym(self, sym, qualName); |
|
| 1000 | - | } |
|
| 1001 | - | let mut fnLow = fnLowerer(self, node, fnType, qualName); |
|
| 1002 | - | ||
| 1003 | - | // If the function returns an aggregate or is throwing, prepend a hidden |
|
| 1004 | - | // return parameter. The caller allocates the buffer and passes it |
|
| 1005 | - | // as the first argument; the callee writes the return value into it. |
|
| 1006 | - | if requiresReturnParam(fnType) and not isExtern { |
|
| 1007 | - | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1008 | - | } |
|
| 1009 | - | let lowParams = try lowerParams(&mut fnLow, *fnType, decl.sig.params, nil); |
|
| 1010 | - | let func = try! alloc::allocRaw(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn; |
|
| 1044 | + | registerSymbolName(self, sym, qualName); |
|
| 1045 | + | } |
|
| 1046 | + | let parent: 'function = &mut *self, arena = &mut *functionArena where 'phase: 'function in { |
|
| 1047 | + | let mut fnLow = fnLowerer(parent, node, fnType, qualName, arena); |
|
| 1048 | + | ||
| 1049 | + | // If the function returns an aggregate or is throwing, prepend a hidden |
|
| 1050 | + | // return parameter. The caller allocates the buffer and passes it |
|
| 1051 | + | // as the first argument; the callee writes the return value into it. |
|
| 1052 | + | if requiresReturnParam(fnType) and not isExtern { |
|
| 1053 | + | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1054 | + | } |
|
| 1055 | + | let lowParams = try lowerParams(&mut fnLow, *fnType, decl.sig.params, nil); |
|
| 1056 | + | let func = try! alloc::allocRaw(fnLow.arena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn; |
|
| 1057 | + | ||
| 1058 | + | // Throwing functions return a result aggregate (word-sized pointer). |
|
| 1059 | + | // TODO: The resolver should set an appropriate type that takes into account |
|
| 1060 | + | // the throws list. It shouldn't set the return type to the "success" |
|
| 1061 | + | // value only. |
|
| 1062 | + | let returnType = il::Type::W64 if fnType.throwList.len > 0 |
|
| 1063 | + | else ilType(fnLow.low, *fnType.returnType); |
|
| 1064 | + | set *func = il::Fn { |
|
| 1065 | + | name: qualName, |
|
| 1066 | + | params: lowParams, |
|
| 1067 | + | returnType, |
|
| 1068 | + | isExtern, |
|
| 1069 | + | isLeaf: true, |
|
| 1070 | + | blocks: &[], |
|
| 1071 | + | }; |
|
| 1072 | + | let body = decl.body else { |
|
| 1073 | + | // Extern functions have no body. |
|
| 1074 | + | assert isExtern; |
|
| 1075 | + | return func; |
|
| 1076 | + | }; |
|
| 1077 | + | set func.blocks = try lowerFnBody(&mut fnLow, body); |
|
| 1078 | + | set func.isLeaf = fnLow.isLeaf; |
|
| 1011 | 1079 | ||
| 1012 | - | set *func = il::Fn { |
|
| 1013 | - | name: qualName, |
|
| 1014 | - | params: lowParams, |
|
| 1015 | - | returnType: undefined, |
|
| 1016 | - | isExtern, |
|
| 1017 | - | isLeaf: true, |
|
| 1018 | - | blocks: &[], |
|
| 1019 | - | }; |
|
| 1020 | - | // Throwing functions return a result aggregate (word-sized pointer). |
|
| 1021 | - | // TODO: The resolver should set an appropriate type that takes into account |
|
| 1022 | - | // the throws list. It shouldn't set the return type to the "success" |
|
| 1023 | - | // value only. |
|
| 1024 | - | if fnType.throwList.len > 0 { |
|
| 1025 | - | set func.returnType = il::Type::W64; |
|
| 1026 | - | } else { |
|
| 1027 | - | set func.returnType = ilType(self, *fnType.returnType); |
|
| 1028 | - | } |
|
| 1029 | - | let body = decl.body else { |
|
| 1030 | - | // Extern functions have no body. |
|
| 1031 | - | assert isExtern; |
|
| 1032 | 1080 | return func; |
|
| 1033 | - | }; |
|
| 1034 | - | set func.blocks = try lowerFnBody(&mut fnLow, body); |
|
| 1035 | - | set func.isLeaf = fnLow.isLeaf; |
|
| 1036 | - | ||
| 1037 | - | return func; |
|
| 1081 | + | } |
|
| 1038 | 1082 | } |
|
| 1039 | 1083 | ||
| 1040 | 1084 | /// Build a qualified name of the form "Type::method". |
|
| 1041 | - | unsafe fn instanceMethodName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] { |
|
| 1085 | + | unsafe fn instanceMethodName 'arena 'phase (self: &mut Lowerer 'arena 'phase, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] where 'arena: 'phase { |
|
| 1042 | 1086 | let sepLen: u32 = 2; // "::" |
|
| 1043 | 1087 | let totalLen = typeName.len + sepLen + methodName.len; |
|
| 1044 | 1088 | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1045 | 1089 | let mut pos: u32 = 0; |
|
| 1046 | 1090 |
| 1051 | 1095 | ||
| 1052 | 1096 | return qualifyName(self, modId, &buf[..totalLen]); |
|
| 1053 | 1097 | } |
|
| 1054 | 1098 | ||
| 1055 | 1099 | /// Build a v-table data name of the form "vtable::Type::Trait". |
|
| 1056 | - | unsafe fn vtableName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] { |
|
| 1100 | + | unsafe fn vtableName 'arena 'phase (self: &mut Lowerer 'arena 'phase, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] where 'arena: 'phase { |
|
| 1057 | 1101 | let prefix = "vtable::"; |
|
| 1058 | 1102 | let sepLen: u32 = 2; // "::" |
|
| 1059 | 1103 | let totalLen = prefix.len + typeName.len + sepLen + traitName.len; |
|
| 1060 | 1104 | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1061 | 1105 | let mut pos: u32 = 0; |
| 1074 | 1118 | /// Each method in the instance block is lowered as a standalone function |
|
| 1075 | 1119 | /// with a qualified name of the form `Type::method`. A read-only v-table |
|
| 1076 | 1120 | /// data record is emitted containing pointers to these functions, ordered |
|
| 1077 | 1121 | /// by the trait's method indices. The v-table is later referenced when |
|
| 1078 | 1122 | /// constructing trait objects for dynamic dispatch. |
|
| 1079 | - | unsafe fn lowerInstanceDecl( |
|
| 1080 | - | self: &mut Lowerer, |
|
| 1081 | - | node: *ast::Node, |
|
| 1123 | + | unsafe fn instanceCursor 'arena 'phase ( |
|
| 1124 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1082 | 1125 | traitNameNode: *ast::Node, |
|
| 1083 | 1126 | targetTypeNode: *ast::Node, |
|
| 1084 | 1127 | methods: *[*ast::Node] |
|
| 1085 | - | ) throws (LowerError) { |
|
| 1128 | + | ) -> InstanceCursor throws (LowerError) where 'arena: 'phase { |
|
| 1086 | 1129 | // Look up the trait and type from the resolver. |
|
| 1087 | 1130 | let traitSym = resolver::nodeData(self.resolver, traitNameNode).sym |
|
| 1088 | 1131 | else throw LowerError::MissingSymbol(traitNameNode); |
|
| 1089 | 1132 | let case resolver::SymbolData::Trait(traitInfo) = traitSym.data |
|
| 1090 | 1133 | else throw LowerError::MissingMetadata; |
|
| 1091 | 1134 | let typeSym = resolver::nodeData(self.resolver, targetTypeNode).sym |
|
| 1092 | 1135 | else throw LowerError::MissingSymbol(targetTypeNode); |
|
| 1093 | 1136 | ||
| 1094 | - | let tName = traitSym.name; |
|
| 1095 | - | let typeName = typeSym.name; |
|
| 1096 | - | ||
| 1097 | 1137 | // Lower each instance method as a regular function. |
|
| 1098 | 1138 | // Collect qualified names for the v-table. Empty entries are filled |
|
| 1099 | 1139 | // later from inherited supertrait methods. |
|
| 1100 | - | let mut methodNames: [*[u8]; ast::MAX_TRAIT_METHODS] = undefined; |
|
| 1101 | - | let mut methodNameSet: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS]; |
|
| 1140 | + | return InstanceCursor { |
|
| 1141 | + | traitInfo, traitName: traitSym.name, typeName: typeSym.name, methods, next: 0, |
|
| 1142 | + | methodNames: undefined, methodNameSet: [false; ast::MAX_TRAIT_METHODS], |
|
| 1143 | + | }; |
|
| 1144 | + | } |
|
| 1102 | 1145 | ||
| 1103 | - | for methodNode in methods { |
|
| 1146 | + | /// Lower the next concrete method, or finish the instance's v-table. |
|
| 1147 | + | unsafe fn lowerNextInstance 'arena 'phase ( |
|
| 1148 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1149 | + | state: &mut InstanceCursor, |
|
| 1150 | + | functionArena: &mut alloc::Arena |
|
| 1151 | + | ) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase { |
|
| 1152 | + | let traitInfo = state.traitInfo; |
|
| 1153 | + | let typeName = state.typeName; |
|
| 1154 | + | let tName = state.traitName; |
|
| 1155 | + | while state.next < state.methods.len { |
|
| 1156 | + | let methodNode = state.methods[state.next]; |
|
| 1157 | + | set state.next += 1; |
|
| 1104 | 1158 | let case ast::NodeValue::MethodDecl { |
|
| 1105 | - | name, receiverName, receiverType, sig, body, .. |
|
| 1159 | + | name, receiverName, sig, body, .. |
|
| 1106 | 1160 | } = methodNode.value else continue; |
|
| 1107 | 1161 | ||
| 1108 | 1162 | let case ast::NodeValue::Ident(mName) = name.value else { |
|
| 1109 | 1163 | throw LowerError::ExpectedIdentifier; |
|
| 1110 | 1164 | }; |
|
| 1111 | 1165 | let qualName = instanceMethodName(self, nil, typeName, mName); |
|
| 1112 | - | let func = try lowerMethod(self, methodNode, qualName, receiverName, sig, body) |
|
| 1166 | + | let func = try lowerMethod(self, methodNode, qualName, receiverName, sig, body, functionArena) |
|
| 1113 | 1167 | else continue; |
|
| 1114 | - | emitFunction(self, func, FnRole::Normal); |
|
| 1115 | 1168 | ||
| 1116 | 1169 | let method = resolver::findTraitMethod(traitInfo, mName) |
|
| 1117 | 1170 | else panic "lowerInstanceDecl: method not found in trait"; |
|
| 1118 | 1171 | ||
| 1119 | - | set methodNames[method.index] = qualName; |
|
| 1120 | - | set methodNameSet[method.index] = true; |
|
| 1172 | + | set state.methodNames[method.index] = qualName; |
|
| 1173 | + | set state.methodNameSet[method.index] = true; |
|
| 1174 | + | return func; |
|
| 1121 | 1175 | } |
|
| 1122 | 1176 | ||
| 1123 | 1177 | // Fill inherited method slots from supertraits. |
|
| 1124 | 1178 | // These methods were already lowered as part of the supertrait instance |
|
| 1125 | 1179 | // declarations and use the same `Type::method` qualified name. |
|
| 1126 | 1180 | for method, i in traitInfo.methods { |
|
| 1127 | - | if not methodNameSet[i] { |
|
| 1128 | - | set methodNames[i] = instanceMethodName(self, nil, typeName, method.name); |
|
| 1181 | + | if not state.methodNameSet[i] { |
|
| 1182 | + | set state.methodNames[i] = instanceMethodName(self, nil, typeName, method.name); |
|
| 1129 | 1183 | } |
|
| 1130 | 1184 | } |
|
| 1131 | 1185 | ||
| 1132 | 1186 | // Create v-table in data section, used for dynamic dispatch. |
|
| 1133 | 1187 | let vName = vtableName(self, nil, typeName, tName); |
| 1135 | 1189 | self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32 |
|
| 1136 | 1190 | ) as *mut [il::DataValue]; |
|
| 1137 | 1191 | ||
| 1138 | 1192 | for i in 0..traitInfo.methods.len { |
|
| 1139 | 1193 | set values[i] = il::DataValue { |
|
| 1140 | - | item: il::DataItem::Fn(methodNames[i]), |
|
| 1194 | + | item: il::DataItem::Fn(state.methodNames[i]), |
|
| 1141 | 1195 | count: 1, |
|
| 1142 | 1196 | }; |
|
| 1143 | 1197 | } |
|
| 1144 | 1198 | self.data.append(il::Data { |
|
| 1145 | 1199 | name: vName, |
|
| 1146 | 1200 | size: traitInfo.methods.len as u32 * resolver::PTR_SIZE, |
|
| 1147 | 1201 | alignment: resolver::PTR_SIZE, |
|
| 1148 | 1202 | readOnly: true, |
|
| 1149 | 1203 | isZeroInit: false, |
|
| 1150 | 1204 | values: &values[..traitInfo.methods.len as u32], |
|
| 1151 | - | }, self.allocator); |
|
| 1205 | + | }, alloc::arenaAllocator(self.arena)); |
|
| 1206 | + | return nil; |
|
| 1152 | 1207 | } |
|
| 1153 | 1208 | ||
| 1154 | 1209 | /// Lower a method node into an IL function with the given qualified name. |
|
| 1155 | 1210 | /// Shared by both instance methods and standalone methods. |
|
| 1156 | - | unsafe fn lowerMethod( |
|
| 1157 | - | self: &mut Lowerer, |
|
| 1211 | + | unsafe fn lowerMethod 'arena 'phase ( |
|
| 1212 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1158 | 1213 | node: *ast::Node, |
|
| 1159 | 1214 | qualName: *[u8], |
|
| 1160 | 1215 | receiverName: *ast::Node, |
|
| 1161 | 1216 | sig: ast::FnSig, |
|
| 1162 | 1217 | body: *ast::Node, |
|
| 1163 | - | ) -> ?*unsafe il::Fn throws (LowerError) { |
|
| 1218 | + | functionArena: &mut alloc::Arena, |
|
| 1219 | + | ) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase { |
|
| 1164 | 1220 | let data = resolver::nodeData(self.resolver, node); |
|
| 1165 | 1221 | let case resolver::Type::Fn(fnType) = data.ty else { |
|
| 1166 | 1222 | throw LowerError::ExpectedFunction; |
|
| 1167 | 1223 | }; |
|
| 1168 | 1224 | let sym = data.sym else throw LowerError::MissingSymbol(node); |
|
| 1169 | - | registerFnSym(self, sym, qualName); |
|
| 1170 | - | ||
| 1171 | - | let mut fnLow = fnLowerer(self, node, fnType, qualName); |
|
| 1172 | - | if requiresReturnParam(fnType) { |
|
| 1173 | - | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1174 | - | } |
|
| 1175 | - | let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName); |
|
| 1176 | - | let func = try! alloc::allocRaw(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn; |
|
| 1225 | + | registerSymbolName(self, sym, qualName); |
|
| 1226 | + | ||
| 1227 | + | let parent: 'function = &mut *self, arena = &mut *functionArena where 'phase: 'function in { |
|
| 1228 | + | let mut fnLow = fnLowerer(parent, node, fnType, qualName, arena); |
|
| 1229 | + | if requiresReturnParam(fnType) { |
|
| 1230 | + | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1231 | + | } |
|
| 1232 | + | let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName); |
|
| 1233 | + | let func = try! alloc::allocRaw(fnLow.arena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *unsafe mut il::Fn; |
|
| 1234 | + | ||
| 1235 | + | let returnType = il::Type::W64 if fnType.throwList.len > 0 |
|
| 1236 | + | else ilType(fnLow.low, *fnType.returnType); |
|
| 1237 | + | set *func = il::Fn { |
|
| 1238 | + | name: qualName, |
|
| 1239 | + | params: lowParams, |
|
| 1240 | + | returnType, |
|
| 1241 | + | isExtern: false, |
|
| 1242 | + | isLeaf: true, |
|
| 1243 | + | blocks: &[], |
|
| 1244 | + | }; |
|
| 1245 | + | set func.blocks = try lowerFnBody(&mut fnLow, body); |
|
| 1246 | + | set func.isLeaf = fnLow.isLeaf; |
|
| 1177 | 1247 | ||
| 1178 | - | set *func = il::Fn { |
|
| 1179 | - | name: qualName, |
|
| 1180 | - | params: lowParams, |
|
| 1181 | - | returnType: ilType(self, *fnType.returnType), |
|
| 1182 | - | isExtern: false, |
|
| 1183 | - | isLeaf: true, |
|
| 1184 | - | blocks: &[], |
|
| 1185 | - | }; |
|
| 1186 | - | if fnType.throwList.len > 0 { |
|
| 1187 | - | set func.returnType = il::Type::W64; |
|
| 1248 | + | return func; |
|
| 1188 | 1249 | } |
|
| 1189 | - | set func.blocks = try lowerFnBody(&mut fnLow, body); |
|
| 1190 | - | set func.isLeaf = fnLow.isLeaf; |
|
| 1191 | - | ||
| 1192 | - | return func; |
|
| 1193 | 1250 | } |
|
| 1194 | 1251 | ||
| 1195 | 1252 | /// Lower a standalone method declaration. |
|
| 1196 | 1253 | /// Produces a function with qualified name `Type::method`. |
|
| 1197 | - | unsafe fn lowerMethodDecl( |
|
| 1198 | - | self: &mut Lowerer, |
|
| 1254 | + | unsafe fn lowerMethodDecl 'arena 'phase ( |
|
| 1255 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1199 | 1256 | node: *ast::Node, |
|
| 1200 | 1257 | name: *ast::Node, |
|
| 1201 | 1258 | receiverName: *ast::Node, |
|
| 1202 | 1259 | sig: ast::FnSig, |
|
| 1203 | 1260 | body: *ast::Node, |
|
| 1204 | - | ) -> ?*unsafe il::Fn throws (LowerError) { |
|
| 1261 | + | functionArena: &mut alloc::Arena, |
|
| 1262 | + | ) -> ?*unsafe il::Fn throws (LowerError) where 'arena: 'phase { |
|
| 1205 | 1263 | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1206 | 1264 | else throw LowerError::MissingSymbol(node); |
|
| 1207 | 1265 | let case ast::NodeValue::Ident(mName) = name.value |
|
| 1208 | 1266 | else throw LowerError::ExpectedIdentifier; |
|
| 1209 | 1267 | let me = resolver::findMethodBySymbol(self.resolver, sym) |
|
| 1210 | 1268 | else throw LowerError::MissingMetadata; |
|
| 1211 | 1269 | let qualName = instanceMethodName(self, nil, me.concreteTypeName, mName); |
|
| 1212 | 1270 | ||
| 1213 | - | return try lowerMethod(self, node, qualName, receiverName, sig, body); |
|
| 1271 | + | return try lowerMethod(self, node, qualName, receiverName, sig, body, functionArena); |
|
| 1214 | 1272 | } |
|
| 1215 | 1273 | ||
| 1216 | 1274 | /// Check if a function should be lowered. |
|
| 1217 | 1275 | fn shouldLowerFn(decl: &ast::FnDecl, buildTest: bool) -> bool { |
|
| 1218 | 1276 | if checkAttr(decl.attrs, ast::Attribute::Test) { |
| 1229 | 1287 | return false; |
|
| 1230 | 1288 | } |
|
| 1231 | 1289 | ||
| 1232 | 1290 | /// Create a label with a numeric suffix, eg. `@base0`. |
|
| 1233 | 1291 | /// This ensures unique labels like `@then0`, `@then1`, etc. |
|
| 1234 | - | unsafe fn labelWithSuffix(self: &mut FnLowerer, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) { |
|
| 1292 | + | unsafe fn labelWithSuffix 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1235 | 1293 | let mut digits: [u8; fmt::U32_STR_LEN] = undefined; |
|
| 1236 | 1294 | let start = fmt::formatU32(suffix, &mut digits[..]); |
|
| 1237 | 1295 | let totalLen = base.len + digits.len - start; |
|
| 1238 | - | let buf = try! alloc::allocSlice(self.low.fnArena, 1, 1, totalLen) as *mut [u8]; |
|
| 1296 | + | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1239 | 1297 | ||
| 1240 | 1298 | try! mem::copy(&mut buf[..base.len], base); |
|
| 1241 | 1299 | try! mem::copy(&mut buf[base.len..totalLen], &digits[start..]); |
|
| 1242 | 1300 | ||
| 1243 | 1301 | return &buf[..totalLen]; |
|
| 1244 | 1302 | } |
|
| 1245 | 1303 | ||
| 1246 | 1304 | /// Generate a unique label by appending the global counter to the base. |
|
| 1247 | - | unsafe fn nextLabel(self: &mut FnLowerer, base: *[u8]) -> *[u8] throws (LowerError) { |
|
| 1305 | + | unsafe fn nextLabel 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: *[u8]) -> *[u8] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1248 | 1306 | let idx = self.labelCounter; |
|
| 1249 | 1307 | set self.labelCounter += 1; |
|
| 1250 | 1308 | ||
| 1251 | 1309 | return try labelWithSuffix(self, base, idx); |
|
| 1252 | 1310 | } |
| 1274 | 1332 | else => panic, |
|
| 1275 | 1333 | } |
|
| 1276 | 1334 | } |
|
| 1277 | 1335 | ||
| 1278 | 1336 | /// Convert a constant value to an IL value. |
|
| 1279 | - | unsafe fn constValueToVal(self: &mut FnLowerer, val: resolver::ConstValue, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 1337 | + | unsafe fn constValueToVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: resolver::ConstValue, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1280 | 1338 | if let case resolver::ConstValue::String(s) = val { |
|
| 1281 | 1339 | return try lowerStringLit(self, node, s); |
|
| 1282 | 1340 | } |
|
| 1283 | 1341 | return il::Val::Imm(constToScalar(val)); |
|
| 1284 | 1342 | } |
|
| 1285 | 1343 | ||
| 1286 | 1344 | /// Convert a resolver constant value to an IL data initializer item. |
|
| 1287 | - | unsafe fn constValueToDataItem(self: &mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem { |
|
| 1345 | + | unsafe fn constValueToDataItem 'arena 'phase (self: &mut Lowerer 'arena 'phase, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem where 'arena: 'phase { |
|
| 1288 | 1346 | if let case resolver::ConstValue::String(s) = val { |
|
| 1289 | 1347 | return il::DataItem::Str(s); |
|
| 1290 | 1348 | } |
|
| 1291 | 1349 | // Bool and char are byte-sized; integer uses the declared type. |
|
| 1292 | 1350 | let mut irTyp = il::Type::W8; |
| 1296 | 1354 | return il::DataItem::Val { typ: irTyp, val: constToScalar(val) }; |
|
| 1297 | 1355 | } |
|
| 1298 | 1356 | ||
| 1299 | 1357 | /// Lower scalar-like constant nodes into data values, including fallback handling |
|
| 1300 | 1358 | /// for void-variant tags and slice string initializers. |
|
| 1301 | - | unsafe fn lowerConstScalarDataInto( |
|
| 1302 | - | self: &mut Lowerer, |
|
| 1359 | + | unsafe fn lowerConstScalarDataInto 'arena 'phase ( |
|
| 1360 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1303 | 1361 | node: *ast::Node, |
|
| 1304 | 1362 | ty: resolver::Type, |
|
| 1305 | 1363 | dataPrefix: *[u8], |
|
| 1306 | 1364 | b: &mut DataValueBuilder |
|
| 1307 | - | ) throws (LowerError) { |
|
| 1365 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1308 | 1366 | let val = resolver::constValueEntry(self.resolver, node) else { |
|
| 1309 | 1367 | if let idx = voidVariantIndex(self.resolver, node) { |
|
| 1310 | 1368 | dataBuilderPush(b, il::DataValue { |
|
| 1311 | 1369 | item: il::DataItem::Val { typ: il::Type::W8, val: idx }, |
|
| 1312 | 1370 | count: 1 |
| 1328 | 1386 | count: 1 |
|
| 1329 | 1387 | }); |
|
| 1330 | 1388 | } |
|
| 1331 | 1389 | ||
| 1332 | 1390 | /// Lower a constant or static declaration to the data section. |
|
| 1333 | - | unsafe fn lowerDataDecl( |
|
| 1334 | - | self: &mut Lowerer, |
|
| 1391 | + | unsafe fn lowerDataDecl 'arena 'phase ( |
|
| 1392 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1335 | 1393 | node: *ast::Node, |
|
| 1336 | 1394 | value: *ast::Node, |
|
| 1337 | 1395 | readOnly: bool |
|
| 1338 | - | ) throws (LowerError) { |
|
| 1396 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1339 | 1397 | let data = resolver::nodeData(self.resolver, node); |
|
| 1340 | 1398 | let sym = data.sym else { |
|
| 1341 | 1399 | throw LowerError::MissingSymbol(node); |
|
| 1342 | 1400 | }; |
|
| 1343 | 1401 | if data.ty == resolver::Type::Unknown { |
|
| 1344 | 1402 | throw LowerError::MissingType(node); |
|
| 1345 | 1403 | } |
|
| 1346 | 1404 | let layout = resolver::getTypeLayout(data.ty); |
|
| 1347 | - | let qualName = qualifyName(self, nil, sym.name); |
|
| 1348 | - | let mut b = dataBuilder(self.allocator); |
|
| 1405 | + | let qualName = lookupSymbolName(self, sym) else qualifyName(self, nil, sym.name); |
|
| 1406 | + | let mut b = dataBuilder(alloc::arenaAllocator(self.arena)); |
|
| 1349 | 1407 | try lowerConstDataInto(self, value, data.ty, layout.size, qualName, &mut b); |
|
| 1350 | 1408 | let result = dataBuilderFinish(b); |
|
| 1351 | 1409 | ||
| 1352 | 1410 | self.data.append(il::Data { |
|
| 1353 | 1411 | name: qualName, |
|
| 1354 | 1412 | size: layout.size, |
|
| 1355 | 1413 | alignment: layout.alignment, |
|
| 1356 | 1414 | readOnly, |
|
| 1357 | 1415 | isZeroInit: not readOnly and result.zeroInit, |
|
| 1358 | 1416 | values: result.values, |
|
| 1359 | - | }, self.allocator); |
|
| 1417 | + | }, alloc::arenaAllocator(self.arena)); |
|
| 1360 | 1418 | } |
|
| 1361 | 1419 | ||
| 1362 | 1420 | /// Emit the in-memory representation of a slice header: `{ ptr, len, cap }`. |
|
| 1363 | 1421 | fn dataSliceHeader(b: &mut DataValueBuilder, dataSym: *[u8], len: u32) { |
|
| 1364 | 1422 | dataBuilderPush(b, il::DataValue { |
| 1380 | 1438 | count: 1 |
|
| 1381 | 1439 | }); |
|
| 1382 | 1440 | } |
|
| 1383 | 1441 | ||
| 1384 | 1442 | /// Lower a compile-time `&[...]` expression to a concrete slice header. |
|
| 1385 | - | unsafe fn lowerConstAddressSliceInto( |
|
| 1386 | - | self: &mut Lowerer, |
|
| 1443 | + | unsafe fn lowerConstAddressSliceInto 'arena 'phase ( |
|
| 1444 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1387 | 1445 | addr: ast::AddressOf, |
|
| 1388 | 1446 | ty: resolver::Type, |
|
| 1389 | 1447 | dataPrefix: *[u8], |
|
| 1390 | 1448 | b: &mut DataValueBuilder |
|
| 1391 | - | ) throws (LowerError) { |
|
| 1449 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1392 | 1450 | let case resolver::Type::Slice { mutable, .. } = ty |
|
| 1393 | 1451 | else throw LowerError::ExpectedSliceOrArray; |
|
| 1394 | 1452 | let targetTy = resolver::typeFor(self.resolver, addr.target) |
|
| 1395 | 1453 | else throw LowerError::MissingType(addr.target); |
|
| 1396 | 1454 | let case resolver::Type::Array(arrInfo) = targetTy |
|
| 1397 | 1455 | else throw LowerError::ExpectedArray; |
|
| 1398 | 1456 | ||
| 1399 | - | let mut nested = dataBuilder(self.allocator); |
|
| 1457 | + | let mut nested = dataBuilder(alloc::arenaAllocator(self.arena)); |
|
| 1400 | 1458 | let layout = resolver::getTypeLayout(targetTy); |
|
| 1401 | 1459 | try lowerConstDataInto(self, addr.target, targetTy, layout.size, dataPrefix, &mut nested); |
|
| 1402 | 1460 | ||
| 1403 | 1461 | let backing = dataBuilderFinish(nested); |
|
| 1404 | 1462 | let readOnly = not mutable; |
| 1414 | 1472 | } |
|
| 1415 | 1473 | dataSliceHeader(b, dataName, arrInfo.length); |
|
| 1416 | 1474 | } |
|
| 1417 | 1475 | ||
| 1418 | 1476 | /// Lower a constant expression payload into a builder without slot padding. |
|
| 1419 | - | /// Compute the type layout only when undefined data needs a byte count. |
|
| 1420 | - | unsafe fn lowerConstDataPayloadInto( |
|
| 1421 | - | self: &mut Lowerer, |
|
| 1477 | + | /// Compute the type layout when nil or undefined data needs a byte count. |
|
| 1478 | + | unsafe fn lowerConstDataPayloadInto 'arena 'phase ( |
|
| 1479 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1422 | 1480 | node: *ast::Node, |
|
| 1423 | 1481 | ty: resolver::Type, |
|
| 1424 | 1482 | dataPrefix: *[u8], |
|
| 1425 | 1483 | b: &mut DataValueBuilder |
|
| 1426 | - | ) throws (LowerError) { |
|
| 1484 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1485 | + | // Optional coercions define the tag and payload layout of constant values. |
|
| 1486 | + | if let case resolver::Type::Optional(inner) = ty { |
|
| 1487 | + | if let coercion = resolver::coercionFor(self.resolver, node) { |
|
| 1488 | + | if let case resolver::Coercion::OptionalLift(target) = coercion { |
|
| 1489 | + | if resolver::typesEqual(target, ty) { |
|
| 1490 | + | if resolver::isNullableType(*inner) { |
|
| 1491 | + | try lowerConstDataPayloadInto(self, node, *inner, dataPrefix, b); |
|
| 1492 | + | } else { |
|
| 1493 | + | let layout = resolver::getTypeLayout(ty); |
|
| 1494 | + | let offset = resolver::getOptionalValOffset(*inner); |
|
| 1495 | + | dataBuilderPush(b, il::DataValue { |
|
| 1496 | + | item: il::DataItem::Val { typ: il::Type::W8, val: 1 }, count: 1, |
|
| 1497 | + | }); |
|
| 1498 | + | if offset > 1 { |
|
| 1499 | + | dataBuilderPush(b, il::DataValue { item: il::DataItem::Undef, count: offset - 1 }); |
|
| 1500 | + | } |
|
| 1501 | + | try lowerConstDataInto(self, node, *inner, layout.size - offset, dataPrefix, b); |
|
| 1502 | + | } |
|
| 1503 | + | return; |
|
| 1504 | + | } |
|
| 1505 | + | } |
|
| 1506 | + | } |
|
| 1507 | + | } |
|
| 1427 | 1508 | // Function pointer references in constant data. |
|
| 1428 | 1509 | if let case resolver::Type::Fn(_) = ty { |
|
| 1429 | 1510 | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1430 | 1511 | else throw LowerError::MissingSymbol(node); |
|
| 1431 | 1512 | let modId = resolver::moduleIdForSymbol(self.resolver, sym); |
| 1444 | 1525 | return; |
|
| 1445 | 1526 | } |
|
| 1446 | 1527 | } |
|
| 1447 | 1528 | } |
|
| 1448 | 1529 | match node.value { |
|
| 1530 | + | case ast::NodeValue::Nil => { |
|
| 1531 | + | let case resolver::Type::Optional(_) = ty else throw LowerError::NilInNonOptional; |
|
| 1532 | + | let layout = resolver::getTypeLayout(ty); |
|
| 1533 | + | dataBuilderPush(b, il::DataValue { |
|
| 1534 | + | item: il::DataItem::Val { typ: il::Type::W8, val: 0 }, count: layout.size, |
|
| 1535 | + | }); |
|
| 1536 | + | }, |
|
| 1449 | 1537 | case ast::NodeValue::Undef => { |
|
| 1450 | 1538 | let layout = resolver::getTypeLayout(ty); |
|
| 1451 | 1539 | dataBuilderPush(b, il::DataValue { |
|
| 1452 | 1540 | item: il::DataItem::Undef, |
|
| 1453 | 1541 | count: layout.size |
| 1498 | 1586 | } |
|
| 1499 | 1587 | } |
|
| 1500 | 1588 | } |
|
| 1501 | 1589 | ||
| 1502 | 1590 | /// Lower a constant expression into a builder, padding to the given slot size. |
|
| 1503 | - | unsafe fn lowerConstDataInto( |
|
| 1504 | - | self: &mut Lowerer, |
|
| 1591 | + | unsafe fn lowerConstDataInto 'arena 'phase ( |
|
| 1592 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1505 | 1593 | node: *ast::Node, |
|
| 1506 | 1594 | ty: resolver::Type, |
|
| 1507 | 1595 | slotSize: u32, |
|
| 1508 | 1596 | dataPrefix: *[u8], |
|
| 1509 | 1597 | b: &mut DataValueBuilder |
|
| 1510 | - | ) throws (LowerError) { |
|
| 1598 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1511 | 1599 | let layout = resolver::getTypeLayout(ty); |
|
| 1512 | 1600 | try lowerConstDataPayloadInto(self, node, ty, dataPrefix, b); |
|
| 1513 | 1601 | // Pad to fill the enclosing slot. |
|
| 1514 | 1602 | let padding = slotSize - layout.size; |
|
| 1515 | 1603 | if padding > 0 { |
| 1517 | 1605 | } |
|
| 1518 | 1606 | } |
|
| 1519 | 1607 | ||
| 1520 | 1608 | /// Flatten a constant array literal `[a, b, c]` into a builder. |
|
| 1521 | 1609 | /// Each element payload fills its type size; no extra slot padding is needed. |
|
| 1522 | - | unsafe fn lowerConstArrayLitInto( |
|
| 1523 | - | self: &mut Lowerer, |
|
| 1610 | + | unsafe fn lowerConstArrayLitInto 'arena 'phase ( |
|
| 1611 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1524 | 1612 | elems: *[*ast::Node], |
|
| 1525 | 1613 | ty: resolver::Type, |
|
| 1526 | 1614 | dataPrefix: *[u8], |
|
| 1527 | 1615 | b: &mut DataValueBuilder |
|
| 1528 | - | ) throws (LowerError) { |
|
| 1616 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1529 | 1617 | let case resolver::Type::Array(arrInfo) = ty |
|
| 1530 | 1618 | else throw LowerError::ExpectedArray; |
|
| 1531 | 1619 | let elemTy = *arrInfo.item; |
|
| 1532 | 1620 | ||
| 1533 | 1621 | for elem in elems { |
| 1536 | 1624 | } |
|
| 1537 | 1625 | ||
| 1538 | 1626 | /// Build data values for a constant array repeat literal `[item; count]`. |
|
| 1539 | 1627 | /// Repeat element payloads without extra slot padding. Undefined data uses |
|
| 1540 | 1628 | /// the element layout to compute the total byte count. |
|
| 1541 | - | unsafe fn lowerConstArrayRepeatInto( |
|
| 1542 | - | self: &mut Lowerer, |
|
| 1629 | + | unsafe fn lowerConstArrayRepeatInto 'arena 'phase ( |
|
| 1630 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1543 | 1631 | repeat: ast::ArrayRepeatLit, |
|
| 1544 | 1632 | ty: resolver::Type, |
|
| 1545 | 1633 | dataPrefix: *[u8], |
|
| 1546 | 1634 | b: &mut DataValueBuilder |
|
| 1547 | - | ) throws (LowerError) { |
|
| 1635 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1548 | 1636 | let case resolver::Type::Array(arrInfo) = ty |
|
| 1549 | 1637 | else throw LowerError::ExpectedArray; |
|
| 1550 | 1638 | let length = arrInfo.length; |
|
| 1551 | 1639 | let elemTy = *arrInfo.item; |
|
| 1552 | 1640 | ||
| 1641 | + | if let case resolver::Type::Optional(_) = elemTy { |
|
| 1642 | + | for _ in 0..length { |
|
| 1643 | + | try lowerConstDataPayloadInto(self, repeat.item, elemTy, dataPrefix, b); |
|
| 1644 | + | } |
|
| 1645 | + | return; |
|
| 1646 | + | } |
|
| 1647 | + | ||
| 1553 | 1648 | if let case ast::NodeValue::Undef = repeat.item.value { |
|
| 1554 | 1649 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 1555 | 1650 | dataBuilderPush(b, il::DataValue { |
|
| 1556 | 1651 | item: il::DataItem::Undef, |
|
| 1557 | 1652 | count: elemLayout.size * length |
| 1576 | 1671 | } |
|
| 1577 | 1672 | } |
|
| 1578 | 1673 | ||
| 1579 | 1674 | /// Build data values for a constant record literal. |
|
| 1580 | 1675 | /// Each field is lowered with a slot size that includes trailing padding. |
|
| 1581 | - | unsafe fn lowerConstRecordLitInto( |
|
| 1582 | - | self: &mut Lowerer, |
|
| 1676 | + | unsafe fn lowerConstRecordLitInto 'arena 'phase ( |
|
| 1677 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1583 | 1678 | node: *ast::Node, |
|
| 1584 | 1679 | recLit: ast::RecordLit, |
|
| 1585 | 1680 | ty: resolver::Type, |
|
| 1586 | 1681 | dataPrefix: *[u8], |
|
| 1587 | 1682 | b: &mut DataValueBuilder |
|
| 1588 | - | ) throws (LowerError) { |
|
| 1683 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1589 | 1684 | match ty { |
|
| 1590 | 1685 | case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => { |
|
| 1591 | 1686 | try lowerConstRecordCtorInto(self, recLit.fields, recInfo, dataPrefix, b); |
|
| 1592 | 1687 | } |
|
| 1593 | 1688 | case resolver::Type::Nominal(resolver::NominalType::Union(_)) => { |
| 1602 | 1697 | else => throw LowerError::ExpectedRecord, |
|
| 1603 | 1698 | } |
|
| 1604 | 1699 | } |
|
| 1605 | 1700 | ||
| 1606 | 1701 | /// Build data values for record constants. |
|
| 1607 | - | unsafe fn lowerConstRecordCtorInto( |
|
| 1608 | - | self: &mut Lowerer, |
|
| 1702 | + | unsafe fn lowerConstRecordCtorInto 'arena 'phase ( |
|
| 1703 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1609 | 1704 | args: *[*ast::Node], |
|
| 1610 | 1705 | recInfo: resolver::RecordType, |
|
| 1611 | 1706 | dataPrefix: *[u8], |
|
| 1612 | 1707 | b: &mut DataValueBuilder |
|
| 1613 | - | ) throws (LowerError) { |
|
| 1614 | - | let layout = recInfo.layout; |
|
| 1708 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1709 | + | let layout = *recInfo.layout; |
|
| 1615 | 1710 | for argNode, i in args { |
|
| 1616 | 1711 | let mut valueNode = argNode; |
|
| 1617 | 1712 | if let case ast::NodeValue::RecordLitField(fieldLit) = argNode.value { |
|
| 1618 | 1713 | set valueNode = fieldLit.value; |
|
| 1619 | 1714 | } |
| 1628 | 1723 | try lowerConstDataInto(self, valueNode, fieldInfo.fieldType, slotSize, dataPrefix, b); |
|
| 1629 | 1724 | } |
|
| 1630 | 1725 | } |
|
| 1631 | 1726 | ||
| 1632 | 1727 | /// Build data values for a constant union variant value from payload fields/args. |
|
| 1633 | - | unsafe fn lowerConstUnionVariantInto( |
|
| 1634 | - | self: &mut Lowerer, |
|
| 1728 | + | unsafe fn lowerConstUnionVariantInto 'arena 'phase ( |
|
| 1729 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1635 | 1730 | node: *ast::Node, |
|
| 1636 | 1731 | variantSym: *unsafe mut resolver::Symbol, |
|
| 1637 | 1732 | ty: resolver::Type, |
|
| 1638 | 1733 | payloadArgs: *[*ast::Node], |
|
| 1639 | 1734 | dataPrefix: *[u8], |
|
| 1640 | 1735 | b: &mut DataValueBuilder |
|
| 1641 | - | ) throws (LowerError) { |
|
| 1642 | - | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = variantSym.data |
|
| 1736 | + | ) throws (LowerError) where 'arena: 'phase { |
|
| 1737 | + | let case resolver::SymbolData::Variant { ordinal, index, .. } = variantSym.data |
|
| 1643 | 1738 | else throw LowerError::UnexpectedNodeValue(node); |
|
| 1644 | 1739 | ||
| 1645 | 1740 | let unionInfo = unionInfoFromType(ty) else { |
|
| 1646 | 1741 | throw LowerError::MissingMetadata; |
|
| 1647 | 1742 | }; |
|
| 1743 | + | let payloadType = unionInfo.variants[ordinal].valueType; |
|
| 1648 | 1744 | let unionLayout = resolver::getTypeLayout(ty); |
|
| 1649 | 1745 | let payloadSlotSize = unionLayout.size - unionInfo.valOffset; |
|
| 1650 | 1746 | ||
| 1651 | 1747 | // Tag byte. |
|
| 1652 | 1748 | dataBuilderPush(b, il::DataValue { |
| 1674 | 1770 | } |
|
| 1675 | 1771 | ||
| 1676 | 1772 | let case resolver::Type::Nominal(resolver::NominalType::Record(payloadRec)) = payloadType else { |
|
| 1677 | 1773 | throw LowerError::ExpectedRecord; |
|
| 1678 | 1774 | }; |
|
| 1679 | - | let payloadLayout = payloadRec.layout; |
|
| 1775 | + | let payloadLayout = *payloadRec.layout; |
|
| 1680 | 1776 | try lowerConstRecordCtorInto(self, payloadArgs, payloadRec, dataPrefix, b); |
|
| 1681 | 1777 | ||
| 1682 | 1778 | // Unused bytes in the union payload slot for smaller variants. |
|
| 1683 | 1779 | if payloadSlotSize > payloadLayout.size { |
|
| 1684 | 1780 | dataBuilderPush(b, il::DataValue { |
| 1688 | 1784 | } |
|
| 1689 | 1785 | } |
|
| 1690 | 1786 | ||
| 1691 | 1787 | /// Find an existing string data entry with matching content. |
|
| 1692 | 1788 | // TODO: Optimize with hash table or remove? |
|
| 1693 | - | fn findStringData(self: &Lowerer, s: *[u8]) -> ?*[u8] { |
|
| 1694 | - | for d in &self.data[..] { |
|
| 1789 | + | fn findStringData 'arena 'phase (self: &Lowerer 'arena 'phase, s: *[u8]) -> ?*[u8] where 'arena: 'phase { |
|
| 1790 | + | for d in &self.data[self.packageDataStart..] { |
|
| 1695 | 1791 | if d.values.len == 1 { |
|
| 1696 | 1792 | if let case il::DataItem::Str(existing) = d.values[0].item { |
|
| 1697 | 1793 | if mem::eq(existing, s) { |
|
| 1698 | 1794 | return d.name; |
|
| 1699 | 1795 | } |
| 1703 | 1799 | return nil; |
|
| 1704 | 1800 | } |
|
| 1705 | 1801 | ||
| 1706 | 1802 | /// Compose a segmented symbol name from a list of path segments. |
|
| 1707 | 1803 | /// Example: `["func", "nominal", "VALUE"]` -> `func$nominal$VALUE`. |
|
| 1708 | - | unsafe fn buildSegmentedName( |
|
| 1709 | - | self: &mut Lowerer, |
|
| 1804 | + | unsafe fn buildSegmentedName 'arena 'phase ( |
|
| 1805 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1710 | 1806 | segments: &[*[u8]] |
|
| 1711 | - | ) -> *[u8] throws (LowerError) { |
|
| 1807 | + | ) -> *[u8] throws (LowerError) where 'arena: 'phase { |
|
| 1712 | 1808 | assert segments.len > 0; |
|
| 1713 | 1809 | ||
| 1714 | 1810 | let mut totalLen: u32 = 0; |
|
| 1715 | 1811 | for segment in segments { |
|
| 1716 | 1812 | set totalLen += segment.len; |
| 1732 | 1828 | ||
| 1733 | 1829 | return &buf[..totalLen]; |
|
| 1734 | 1830 | } |
|
| 1735 | 1831 | ||
| 1736 | 1832 | /// Generate a unique name for declaration-local backing data entries. |
|
| 1737 | - | unsafe fn nextDeclDataName( |
|
| 1738 | - | self: &mut Lowerer, |
|
| 1833 | + | unsafe fn nextDeclDataName 'arena 'phase ( |
|
| 1834 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1739 | 1835 | prefix: *[u8], |
|
| 1740 | 1836 | count: u32, |
|
| 1741 | 1837 | namespace: *[u8] |
|
| 1742 | - | ) -> *[u8] throws (LowerError) { |
|
| 1838 | + | ) -> *[u8] throws (LowerError) where 'arena: 'phase { |
|
| 1743 | 1839 | let mut digits: [u8; fmt::U32_STR_LEN] = undefined; |
|
| 1744 | 1840 | let start = fmt::formatU32(count, &mut digits[..]); |
|
| 1745 | 1841 | let suffix = try! alloc::allocSlice(self.arena, 1, 1, digits.len - start) as *mut [u8]; |
|
| 1746 | 1842 | try! mem::copy(suffix, &digits[start..]); |
|
| 1747 | 1843 | let segments = [prefix, namespace, suffix]; |
|
| 1748 | 1844 | ||
| 1749 | 1845 | return try buildSegmentedName(self, &segments[..]); |
|
| 1750 | 1846 | } |
|
| 1751 | 1847 | ||
| 1752 | 1848 | /// Append a data entry using a function-local literal namespace (`prefix$literal$N`). |
|
| 1753 | - | unsafe fn pushDeclData( |
|
| 1754 | - | self: &mut Lowerer, |
|
| 1849 | + | unsafe fn pushDeclData 'arena 'phase ( |
|
| 1850 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1755 | 1851 | size: u32, |
|
| 1756 | 1852 | alignment: u32, |
|
| 1757 | 1853 | readOnly: bool, |
|
| 1758 | 1854 | values: *[il::DataValue], |
|
| 1759 | 1855 | dataPrefix: *[u8] |
|
| 1760 | - | ) -> *[u8] throws (LowerError) { |
|
| 1856 | + | ) -> *[u8] throws (LowerError) where 'arena: 'phase { |
|
| 1761 | 1857 | let dataCount = self.data.len; |
|
| 1762 | 1858 | let name = try nextDeclDataName(self, dataPrefix, dataCount, "literal"); |
|
| 1763 | 1859 | self.data.append(il::Data { |
|
| 1764 | 1860 | name, |
|
| 1765 | 1861 | size, |
|
| 1766 | 1862 | alignment, |
|
| 1767 | 1863 | readOnly, |
|
| 1768 | 1864 | isZeroInit: not readOnly and dataValuesAreZeroInit(values), |
|
| 1769 | 1865 | values, |
|
| 1770 | - | }, self.allocator); |
|
| 1866 | + | }, alloc::arenaAllocator(self.arena)); |
|
| 1771 | 1867 | ||
| 1772 | 1868 | return name; |
|
| 1773 | 1869 | } |
|
| 1774 | 1870 | ||
| 1775 | 1871 | /// Find or create read-only string data and return its symbol name. |
|
| 1776 | - | unsafe fn getOrCreateStringData( |
|
| 1777 | - | self: &mut Lowerer, |
|
| 1872 | + | unsafe fn getOrCreateStringData 'arena 'phase ( |
|
| 1873 | + | self: &mut Lowerer 'arena 'phase, |
|
| 1778 | 1874 | s: *[u8], |
|
| 1779 | 1875 | dataPrefix: *[u8] |
|
| 1780 | - | ) -> *[u8] throws (LowerError) { |
|
| 1876 | + | ) -> *[u8] throws (LowerError) where 'arena: 'phase { |
|
| 1781 | 1877 | if let existing = findStringData(self, s) { |
|
| 1782 | 1878 | return existing; |
|
| 1783 | 1879 | } |
|
| 1784 | 1880 | let values = try! alloc::allocSlice( |
|
| 1785 | 1881 | self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1 |
| 1845 | 1941 | return true; |
|
| 1846 | 1942 | } |
|
| 1847 | 1943 | ||
| 1848 | 1944 | /// Find an existing read-only slice data entry with matching values. |
|
| 1849 | 1945 | // TODO: Optimize with hash table or remove? |
|
| 1850 | - | fn findSliceData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] { |
|
| 1851 | - | for d in &self.data[..] { |
|
| 1946 | + | fn findSliceData 'arena 'phase (self: &Lowerer 'arena 'phase, values: *[il::DataValue], alignment: u32) -> ?*[u8] where 'arena: 'phase { |
|
| 1947 | + | for d in &self.data[self.packageDataStart..] { |
|
| 1852 | 1948 | if d.alignment == alignment and d.readOnly and dataValuesEq(d.values, values) { |
|
| 1853 | 1949 | return d.name; |
|
| 1854 | 1950 | } |
|
| 1855 | 1951 | } |
|
| 1856 | 1952 | return nil; |
|
| 1857 | 1953 | } |
|
| 1858 | 1954 | ||
| 1859 | 1955 | /// Find existing constant data entry with matching content. |
|
| 1860 | 1956 | /// Handles both string data and slice data. |
|
| 1861 | - | fn findConstData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] { |
|
| 1957 | + | fn findConstData 'arena 'phase (self: &Lowerer 'arena 'phase, values: *[il::DataValue], alignment: u32) -> ?*[u8] where 'arena: 'phase { |
|
| 1862 | 1958 | // Fast path for strings. |
|
| 1863 | 1959 | if values.len == 1 and alignment == 1 { |
|
| 1864 | 1960 | if let case il::DataItem::Str(s) = values[0].item { |
|
| 1865 | 1961 | return findStringData(self, s); |
|
| 1866 | 1962 | } |
| 1869 | 1965 | return findSliceData(self, values, alignment); |
|
| 1870 | 1966 | } |
|
| 1871 | 1967 | ||
| 1872 | 1968 | /// Lower constant data to a slice value. |
|
| 1873 | 1969 | /// Creates or reuses a data section entry, then builds a slice header on the stack. |
|
| 1874 | - | unsafe fn lowerConstDataAsSlice( |
|
| 1875 | - | self: &mut FnLowerer, |
|
| 1970 | + | unsafe fn lowerConstDataAsSlice 'arena 'phase 'function ( |
|
| 1971 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 1876 | 1972 | result: &ConstDataResult, |
|
| 1877 | 1973 | alignment: u32, |
|
| 1878 | 1974 | readOnly: bool, |
|
| 1879 | 1975 | elemTy: *resolver::Type, |
|
| 1880 | 1976 | mutable: bool, |
|
| 1881 | 1977 | length: u32 |
|
| 1882 | - | ) -> il::Val throws (LowerError) { |
|
| 1978 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1883 | 1979 | let values = result.values; |
|
| 1884 | 1980 | let elemLayout = resolver::getTypeLayout(*elemTy); |
|
| 1885 | 1981 | let size = elemLayout.size * length; |
|
| 1886 | 1982 | let mut dataName: *[u8] = undefined; |
|
| 1887 | 1983 | let mut found: ?*[u8] = nil; |
| 1897 | 1993 | size, |
|
| 1898 | 1994 | alignment, |
|
| 1899 | 1995 | readOnly, |
|
| 1900 | 1996 | isZeroInit: not readOnly and result.zeroInit, |
|
| 1901 | 1997 | values, |
|
| 1902 | - | }, self.low.allocator); |
|
| 1998 | + | }, alloc::arenaAllocator(self.low.arena)); |
|
| 1903 | 1999 | } |
|
| 1904 | 2000 | ||
| 1905 | 2001 | // Get data address. |
|
| 1906 | 2002 | let ptrReg = nextReg(self); |
|
| 1907 | 2003 | emit(self, il::Instr::Copy { dst: ptrReg, val: il::Val::DataSym(dataName) }); |
| 1910 | 2006 | self, elemTy, mutable, il::Val::Reg(ptrReg), il::Val::Imm(length as i64), il::Val::Imm(length as i64) |
|
| 1911 | 2007 | ); |
|
| 1912 | 2008 | } |
|
| 1913 | 2009 | ||
| 1914 | 2010 | /// Generate a unique data name for inline literals, eg. `fnName$literal$N`. |
|
| 1915 | - | unsafe fn nextDataName(self: &mut FnLowerer) -> *[u8] throws (LowerError) { |
|
| 2011 | + | unsafe fn nextDataName 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> *[u8] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1916 | 2012 | let counter = self.dataCounter; |
|
| 1917 | 2013 | set self.dataCounter += 1; |
|
| 1918 | 2014 | let fnName = self.fnName; |
|
| 1919 | 2015 | return try nextDeclDataName(self.low, fnName, counter, "literal"); |
|
| 1920 | 2016 | } |
|
| 1921 | 2017 | ||
| 1922 | 2018 | /// Assign a unique function-local data symbol name. |
|
| 1923 | - | unsafe fn registerLocalDataDeclName(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 2019 | + | unsafe fn registerLocalDataDeclName 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1924 | 2020 | let sym = resolver::nodeData(self.low.resolver, node).sym |
|
| 1925 | 2021 | else throw LowerError::MissingSymbol(node); |
|
| 1926 | 2022 | ||
| 1927 | 2023 | let prefix = self.fnName; |
|
| 1928 | 2024 | let segments = [prefix, "nominal", sym.name]; |
|
| 1929 | 2025 | let name = try buildSegmentedName(self.low, &segments[..]); |
|
| 1930 | 2026 | ||
| 1931 | - | set sym.name = name; |
|
| 2027 | + | let qualified = qualifyName(self.low, nil, name); |
|
| 2028 | + | registerSymbolName(self.low, sym, qualified); |
|
| 1932 | 2029 | } |
|
| 1933 | 2030 | ||
| 1934 | 2031 | /// Get the next available SSA register. |
|
| 1935 | - | fn nextReg(self: &mut FnLowerer) -> il::Reg { |
|
| 2032 | + | fn nextReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 1936 | 2033 | let reg = il::Reg { n: self.regCounter }; |
|
| 1937 | 2034 | set self.regCounter += 1; |
|
| 1938 | 2035 | return reg; |
|
| 1939 | 2036 | } |
|
| 1940 | 2037 | ||
| 1941 | 2038 | /// Look up the resolved type of an AST node, or throw `MissingType`. |
|
| 1942 | - | unsafe fn typeOf(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) { |
|
| 2039 | + | unsafe fn typeOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> resolver::Type throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1943 | 2040 | let ty = resolver::typeFor(self.low.resolver, node) |
|
| 1944 | 2041 | else throw LowerError::MissingType(node); |
|
| 1945 | 2042 | return ty; |
|
| 1946 | 2043 | } |
|
| 1947 | 2044 | ||
| 1948 | 2045 | /// Look up the symbol for an AST node, or throw `MissingSymbol`. |
|
| 1949 | - | unsafe fn symOf(self: &mut FnLowerer, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) { |
|
| 2046 | + | unsafe fn symOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1950 | 2047 | let sym = resolver::nodeData(self.low.resolver, node).sym |
|
| 1951 | 2048 | else throw LowerError::MissingSymbol(node); |
|
| 1952 | 2049 | return sym; |
|
| 1953 | 2050 | } |
|
| 1954 | 2051 | ||
| 1955 | 2052 | /// Remove the last block parameter and its associated variable. |
|
| 1956 | 2053 | /// Used when detecting a trivial phi that can be eliminated. |
|
| 1957 | - | unsafe fn removeLastBlockParam(self: &mut FnLowerer, block: BlockId) { |
|
| 2054 | + | unsafe fn removeLastBlockParam 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) where 'arena: 'phase, 'phase: 'function { |
|
| 1958 | 2055 | let blk = getBlockMut(self, block); |
|
| 1959 | 2056 | if blk.params.len > 0 { |
|
| 1960 | 2057 | // TODO: Use `pop`? |
|
| 1961 | 2058 | set blk.params = @sliceOf(blk.params.ptr, blk.params.len - 1, blk.params.cap); |
|
| 1962 | 2059 | } |
| 1969 | 2066 | /// Rewrite cached SSA values for a variable across all blocks, and also |
|
| 1970 | 2067 | /// rewrite any terminator arguments that reference the provisional register. |
|
| 1971 | 2068 | /// The latter is necessary because recursive SSA resolution may have already |
|
| 1972 | 2069 | /// patched terminator arguments with the provisional value before it was |
|
| 1973 | 2070 | /// found to be trivial. |
|
| 1974 | - | unsafe fn rewriteCachedVarValue(self: &mut FnLowerer, v: Var, from: il::Val, to: il::Val) { |
|
| 2071 | + | unsafe fn rewriteCachedVarValue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var, from: il::Val, to: il::Val) where 'arena: 'phase, 'phase: 'function { |
|
| 1975 | 2072 | for i in 0..self.blockData.len { |
|
| 1976 | 2073 | let blk = getBlockMut(self, BlockId(i)); |
|
| 1977 | 2074 | if blk.vars[*v] == from { |
|
| 1978 | 2075 | set blk.vars[*v] = to; |
|
| 1979 | 2076 | } |
| 2026 | 2123 | /// Create a new basic block with the given label base. |
|
| 2027 | 2124 | /// |
|
| 2028 | 2125 | /// The block is initially unsealed (predecessors may be added later) and empty. |
|
| 2029 | 2126 | /// Returns a [`BlockId`] that can be used for jumps and branches. The block must |
|
| 2030 | 2127 | /// be switched to via [`switchToBlock`] before instructions can be emitted. |
|
| 2031 | - | unsafe fn createBlock(self: &mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) { |
|
| 2128 | + | unsafe fn createBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, labelBase: *[u8]) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2032 | 2129 | let label = try nextLabel(self, labelBase); |
|
| 2033 | 2130 | let id = BlockId(self.blockData.len); |
|
| 2034 | 2131 | let varCount = self.localCount; |
|
| 2035 | - | let vars = try! alloc::allocRawSlice(self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *unsafe mut [?il::Val]; |
|
| 2132 | + | let vars = try! alloc::allocRawSlice(self.arena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *unsafe mut [?il::Val]; |
|
| 2036 | 2133 | ||
| 2037 | 2134 | for i in 0..varCount { |
|
| 2038 | 2135 | set vars[i] = nil; |
|
| 2039 | 2136 | } |
|
| 2040 | 2137 | self.blockData.append(BlockData { |
| 2045 | 2142 | locs: &mut [], |
|
| 2046 | 2143 | preds: &mut [], |
|
| 2047 | 2144 | vars, |
|
| 2048 | 2145 | sealState: Sealed::No, |
|
| 2049 | 2146 | loopDepth: self.loopDepth, |
|
| 2050 | - | }, self.allocator); |
|
| 2147 | + | }, alloc::arenaAllocator(self.arena)); |
|
| 2051 | 2148 | ||
| 2052 | 2149 | return id; |
|
| 2053 | 2150 | } |
|
| 2054 | 2151 | ||
| 2055 | 2152 | /// Create a new block with a single parameter. |
|
| 2056 | - | unsafe fn createBlockWithParam( |
|
| 2057 | - | self: &mut FnLowerer, |
|
| 2153 | + | unsafe fn createBlockWithParam 'arena 'phase 'function ( |
|
| 2154 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2058 | 2155 | labelBase: *[u8], |
|
| 2059 | 2156 | param: il::Param |
|
| 2060 | - | ) -> BlockId throws (LowerError) { |
|
| 2157 | + | ) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2061 | 2158 | let block = try createBlock(self, labelBase); |
|
| 2062 | 2159 | let blk = getBlockMut(self, block); |
|
| 2063 | - | blk.params.append(param, self.allocator); |
|
| 2160 | + | blk.params.append(param, alloc::arenaAllocator(self.arena)); |
|
| 2064 | 2161 | ||
| 2065 | 2162 | return block; |
|
| 2066 | 2163 | } |
|
| 2067 | 2164 | ||
| 2068 | 2165 | /// Switch to building a different block. |
|
| 2069 | 2166 | /// All subsequent `emit` calls will add instructions to this block. |
|
| 2070 | - | fn switchToBlock(self: &mut FnLowerer, block: BlockId) { |
|
| 2167 | + | fn switchToBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) where 'arena: 'phase, 'phase: 'function { |
|
| 2071 | 2168 | set self.currentBlock = block; |
|
| 2072 | 2169 | } |
|
| 2073 | 2170 | ||
| 2074 | 2171 | /// Seal a block, indicating all predecessor edges are now known. |
|
| 2075 | 2172 | /// |
|
| 2076 | 2173 | /// Sealing enables SSA construction to resolve variable uses by looking up |
|
| 2077 | 2174 | /// values from predecessors and inserting block parameters as needed. It |
|
| 2078 | 2175 | /// does not prevent instructions from being added to the block. |
|
| 2079 | - | unsafe fn sealBlock(self: &mut FnLowerer, block: BlockId) throws (LowerError) { |
|
| 2176 | + | unsafe fn sealBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2080 | 2177 | let blk = getBlockMut(self, block); |
|
| 2081 | 2178 | let case Sealed::No = blk.sealState else { |
|
| 2082 | 2179 | return; // Already sealed. |
|
| 2083 | 2180 | }; |
|
| 2084 | 2181 | // Keep the current parameter list. Resolution can add more parameters. |
| 2090 | 2187 | try resolveBlockArgs(self, block, Var(varId), paramIdx); |
|
| 2091 | 2188 | } |
|
| 2092 | 2189 | } |
|
| 2093 | 2190 | ||
| 2094 | 2191 | /// Seal a block and switch to it. |
|
| 2095 | - | unsafe fn switchToAndSeal(self: &mut FnLowerer, block: BlockId) throws (LowerError) { |
|
| 2192 | + | unsafe fn switchToAndSeal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2096 | 2193 | try sealBlock(self, block); |
|
| 2097 | 2194 | switchToBlock(self, block); |
|
| 2098 | 2195 | } |
|
| 2099 | 2196 | ||
| 2100 | 2197 | /// Get block data by block id. |
|
| 2101 | - | unsafe fn getBlock(self: &FnLowerer, block: BlockId) -> *unsafe BlockData { |
|
| 2198 | + | unsafe fn getBlock 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function, block: BlockId) -> *unsafe BlockData where 'arena: 'phase, 'phase: 'function { |
|
| 2102 | 2199 | return &self.blockData[*block]; |
|
| 2103 | 2200 | } |
|
| 2104 | 2201 | ||
| 2105 | 2202 | /// Get mutable block data by block id. |
|
| 2106 | - | unsafe fn getBlockMut(self: &mut FnLowerer, block: BlockId) -> *unsafe mut BlockData { |
|
| 2203 | + | unsafe fn getBlockMut 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId) -> *unsafe mut BlockData where 'arena: 'phase, 'phase: 'function { |
|
| 2107 | 2204 | return &mut self.blockData[*block]; |
|
| 2108 | 2205 | } |
|
| 2109 | 2206 | ||
| 2110 | 2207 | /// Get the current block being built. |
|
| 2111 | - | fn currentBlock(self: &FnLowerer) -> BlockId { |
|
| 2208 | + | fn currentBlock 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function) -> BlockId where 'arena: 'phase, 'phase: 'function { |
|
| 2112 | 2209 | let block = self.currentBlock else { |
|
| 2113 | 2210 | panic "currentBlock: no current block"; |
|
| 2114 | 2211 | }; |
|
| 2115 | 2212 | return block; |
|
| 2116 | 2213 | } |
| 2118 | 2215 | ////////////////////////// |
|
| 2119 | 2216 | // Instruction Emission // |
|
| 2120 | 2217 | ////////////////////////// |
|
| 2121 | 2218 | ||
| 2122 | 2219 | /// Emit an instruction to the current block. |
|
| 2123 | - | unsafe fn emit(self: &mut FnLowerer, instr: il::Instr) { |
|
| 2220 | + | unsafe fn emit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, instr: il::Instr) where 'arena: 'phase, 'phase: 'function { |
|
| 2124 | 2221 | let blk = self.currentBlock else panic; |
|
| 2125 | 2222 | let mut block = getBlockMut(self, blk); |
|
| 2126 | 2223 | ||
| 2127 | 2224 | // Track whether this function is a leaf. |
|
| 2128 | - | if self.isLeaf { |
|
| 2129 | - | match instr { |
|
| 2130 | - | case il::Instr::Call { .. }, |
|
| 2131 | - | il::Instr::Ecall { .. } => set self.isLeaf = false, |
|
| 2132 | - | else => {}, |
|
| 2133 | - | } |
|
| 2225 | + | if il::isCall(instr) { |
|
| 2226 | + | set self.isLeaf = false; |
|
| 2134 | 2227 | } |
|
| 2135 | 2228 | // Record source location alongside instruction when enabled. |
|
| 2136 | 2229 | if self.low.options.debug { |
|
| 2137 | - | block.locs.append(self.srcLoc, self.allocator); |
|
| 2230 | + | block.locs.append(self.srcLoc, alloc::arenaAllocator(self.arena)); |
|
| 2138 | 2231 | } |
|
| 2139 | - | block.instrs.append(instr, self.allocator); |
|
| 2232 | + | block.instrs.append(instr, alloc::arenaAllocator(self.arena)); |
|
| 2140 | 2233 | } |
|
| 2141 | 2234 | ||
| 2142 | 2235 | /// Emit an unconditional jump to `target`. |
|
| 2143 | - | unsafe fn emitJmp(self: &mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2236 | + | unsafe fn emitJmp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2144 | 2237 | emit(self, il::Instr::Jmp { target: *target, args: &mut [] }); |
|
| 2145 | 2238 | addPredecessor(self, target, currentBlock(self)); |
|
| 2146 | 2239 | } |
|
| 2147 | 2240 | ||
| 2148 | 2241 | /// Emit an unconditional jump to `target` with a single argument. |
|
| 2149 | - | unsafe fn emitJmpWithArg(self: &mut FnLowerer, target: BlockId, arg: il::Val) throws (LowerError) { |
|
| 2242 | + | unsafe fn emitJmpWithArg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId, arg: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2150 | 2243 | let args = try allocVal(self, arg); |
|
| 2151 | 2244 | emit(self, il::Instr::Jmp { target: *target, args }); |
|
| 2152 | 2245 | addPredecessor(self, target, currentBlock(self)); |
|
| 2153 | 2246 | } |
|
| 2154 | 2247 | ||
| 2155 | 2248 | /// Emit an unconditional jump to `target` and switch to it. |
|
| 2156 | - | unsafe fn switchAndJumpTo(self: &mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2249 | + | unsafe fn switchAndJumpTo 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2157 | 2250 | try emitJmp(self, target); |
|
| 2158 | 2251 | switchToBlock(self, target); |
|
| 2159 | 2252 | } |
|
| 2160 | 2253 | ||
| 2161 | 2254 | /// Emit a conditional branch based on `cond`. |
|
| 2162 | - | unsafe fn emitBr(self: &mut FnLowerer, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) { |
|
| 2255 | + | unsafe fn emitBr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2163 | 2256 | assert thenBlock <> elseBlock; |
|
| 2164 | 2257 | emit(self, il::Instr::Br { |
|
| 2165 | 2258 | op: il::CmpOp::Ne, |
|
| 2166 | 2259 | typ: il::Type::W32, |
|
| 2167 | 2260 | a: il::Val::Reg(cond), |
| 2174 | 2267 | addPredecessor(self, thenBlock, currentBlock(self)); |
|
| 2175 | 2268 | addPredecessor(self, elseBlock, currentBlock(self)); |
|
| 2176 | 2269 | } |
|
| 2177 | 2270 | ||
| 2178 | 2271 | /// Emit a compare-and-branch instruction with the given comparison op. |
|
| 2179 | - | unsafe fn emitBrCmp( |
|
| 2180 | - | self: &mut FnLowerer, |
|
| 2272 | + | unsafe fn emitBrCmp 'arena 'phase 'function ( |
|
| 2273 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2181 | 2274 | op: il::CmpOp, |
|
| 2182 | 2275 | typ: il::Type, |
|
| 2183 | 2276 | a: il::Val, |
|
| 2184 | 2277 | b: il::Val, |
|
| 2185 | 2278 | thenBlock: BlockId, |
|
| 2186 | 2279 | elseBlock: BlockId |
|
| 2187 | - | ) throws (LowerError) { |
|
| 2280 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2188 | 2281 | assert thenBlock <> elseBlock; |
|
| 2189 | 2282 | emit(self, il::Instr::Br { |
|
| 2190 | 2283 | op, typ, a, b, |
|
| 2191 | 2284 | thenTarget: *thenBlock, thenArgs: &mut [], |
|
| 2192 | 2285 | elseTarget: *elseBlock, elseArgs: &mut [], |
| 2194 | 2287 | addPredecessor(self, thenBlock, currentBlock(self)); |
|
| 2195 | 2288 | addPredecessor(self, elseBlock, currentBlock(self)); |
|
| 2196 | 2289 | } |
|
| 2197 | 2290 | ||
| 2198 | 2291 | /// Emit a guard that traps with `ebreak` when a comparison is false. |
|
| 2199 | - | unsafe fn emitTrapUnlessCmp( |
|
| 2200 | - | self: &mut FnLowerer, |
|
| 2292 | + | unsafe fn emitTrapUnlessCmp 'arena 'phase 'function ( |
|
| 2293 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2201 | 2294 | op: il::CmpOp, |
|
| 2202 | 2295 | typ: il::Type, |
|
| 2203 | 2296 | a: il::Val, |
|
| 2204 | 2297 | b: il::Val |
|
| 2205 | - | ) throws (LowerError) { |
|
| 2298 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2206 | 2299 | let passBlock = try createBlock(self, "guard#pass"); |
|
| 2207 | 2300 | let trapBlock = try createBlock(self, "guard#trap"); |
|
| 2208 | 2301 | ||
| 2209 | 2302 | try emitBrCmp(self, op, typ, a, b, passBlock, trapBlock); |
|
| 2210 | 2303 | try switchToAndSeal(self, trapBlock); |
| 2234 | 2327 | } |
|
| 2235 | 2328 | return aa <= bb; |
|
| 2236 | 2329 | } |
|
| 2237 | 2330 | ||
| 2238 | 2331 | /// Emit an `ebreak` when `a < b` holds. |
|
| 2239 | - | unsafe fn emitTrapIfLt( |
|
| 2240 | - | self: &mut FnLowerer, |
|
| 2332 | + | unsafe fn emitTrapIfLt 'arena 'phase 'function ( |
|
| 2333 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2241 | 2334 | typ: il::Type, |
|
| 2242 | 2335 | a: il::Val, |
|
| 2243 | 2336 | b: il::Val |
|
| 2244 | - | ) throws (LowerError) { |
|
| 2337 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2245 | 2338 | let trapBlock = try createBlock(self, "guard#trap"); |
|
| 2246 | 2339 | let passBlock = try createBlock(self, "guard#pass"); |
|
| 2247 | 2340 | ||
| 2248 | 2341 | try emitBrCmp(self, il::CmpOp::Ult, typ, a, b, trapBlock, passBlock); |
|
| 2249 | 2342 | try switchToAndSeal(self, trapBlock); |
| 2254 | 2347 | try switchToAndSeal(self, passBlock); |
|
| 2255 | 2348 | } |
|
| 2256 | 2349 | ||
| 2257 | 2350 | /// Emit a conditional branch. Uses fused compare-and-branch for simple scalar |
|
| 2258 | 2351 | /// comparisons, falls back to separate comparison plus branch otherwise. |
|
| 2259 | - | unsafe fn emitCondBranch( |
|
| 2260 | - | self: &mut FnLowerer, |
|
| 2352 | + | unsafe fn emitCondBranch 'arena 'phase 'function ( |
|
| 2353 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2261 | 2354 | cond: *ast::Node, |
|
| 2262 | 2355 | thenBlock: BlockId, |
|
| 2263 | 2356 | elseBlock: BlockId |
|
| 2264 | - | ) throws (LowerError) { |
|
| 2357 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2265 | 2358 | // Try fused compare-and-branch for simple scalar comparisons. |
|
| 2266 | 2359 | if let case ast::NodeValue::BinOp(binop) = cond.value { |
|
| 2267 | 2360 | let leftTy = try typeOf(self, binop.left); |
|
| 2268 | 2361 | let rightTy = try typeOf(self, binop.right); |
|
| 2269 | 2362 | if not isAggregateType(leftTy) and not isAggregateType(rightTy) { |
| 2295 | 2388 | ||
| 2296 | 2389 | try emitBr(self, condReg, thenBlock, elseBlock); |
|
| 2297 | 2390 | } |
|
| 2298 | 2391 | ||
| 2299 | 2392 | /// Emit a 32-bit store instruction at the given offset. |
|
| 2300 | - | unsafe fn emitStoreW32At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2393 | + | unsafe fn emitStoreW32At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2301 | 2394 | emit(self, il::Instr::Store { typ: il::Type::W32, src, dst, offset }); |
|
| 2302 | 2395 | } |
|
| 2303 | 2396 | ||
| 2304 | 2397 | /// Emit a 32-bit load instruction at the given offset. |
|
| 2305 | - | unsafe fn emitLoadW32At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2398 | + | unsafe fn emitLoadW32At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2306 | 2399 | emit(self, il::Instr::Load { typ: il::Type::W32, dst, src, offset }); |
|
| 2307 | 2400 | } |
|
| 2308 | 2401 | ||
| 2309 | 2402 | /// Emit an 8-bit store instruction at the given offset. |
|
| 2310 | - | unsafe fn emitStoreW8At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2403 | + | unsafe fn emitStoreW8At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2311 | 2404 | emit(self, il::Instr::Store { typ: il::Type::W8, src, dst, offset }); |
|
| 2312 | 2405 | } |
|
| 2313 | 2406 | ||
| 2314 | 2407 | /// Emit an 8-bit load instruction at the given offset. |
|
| 2315 | - | unsafe fn emitLoadW8At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2408 | + | unsafe fn emitLoadW8At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2316 | 2409 | emit(self, il::Instr::Load { typ: il::Type::W8, dst, src, offset }); |
|
| 2317 | 2410 | } |
|
| 2318 | 2411 | ||
| 2319 | 2412 | /// Emit a 64-bit store instruction at the given offset. |
|
| 2320 | - | unsafe fn emitStoreW64At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2413 | + | unsafe fn emitStoreW64At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2321 | 2414 | emit(self, il::Instr::Store { typ: il::Type::W64, src, dst, offset }); |
|
| 2322 | 2415 | } |
|
| 2323 | 2416 | ||
| 2324 | 2417 | /// Emit a 64-bit load instruction at the given offset. |
|
| 2325 | - | unsafe fn emitLoadW64At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2418 | + | unsafe fn emitLoadW64At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2326 | 2419 | emit(self, il::Instr::Load { typ: il::Type::W64, dst, src, offset }); |
|
| 2327 | 2420 | } |
|
| 2328 | 2421 | ||
| 2329 | 2422 | /// Load a tag from memory at `src` plus `offset` with the given IL type. |
|
| 2330 | - | unsafe fn loadTag(self: &mut FnLowerer, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val { |
|
| 2423 | + | unsafe fn loadTag 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2331 | 2424 | let dst = nextReg(self); |
|
| 2332 | 2425 | emit(self, il::Instr::Load { typ: tagType, dst, src, offset }); |
|
| 2333 | 2426 | return il::Val::Reg(dst); |
|
| 2334 | 2427 | } |
|
| 2335 | 2428 | ||
| 2336 | 2429 | /// Load the data pointer from a slice value. |
|
| 2337 | - | unsafe fn loadSlicePtr(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Reg { |
|
| 2430 | + | unsafe fn loadSlicePtr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 2338 | 2431 | let ptrReg = nextReg(self); |
|
| 2339 | 2432 | emitLoadW64At(self, ptrReg, sliceReg, SLICE_PTR_OFFSET); |
|
| 2340 | 2433 | return ptrReg; |
|
| 2341 | 2434 | } |
|
| 2342 | 2435 | ||
| 2343 | 2436 | /// Load the length from a slice value. |
|
| 2344 | - | unsafe fn loadSliceLen(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Val { |
|
| 2437 | + | unsafe fn loadSliceLen 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2345 | 2438 | let lenReg = nextReg(self); |
|
| 2346 | 2439 | emitLoadW32At(self, lenReg, sliceReg, SLICE_LEN_OFFSET); |
|
| 2347 | 2440 | return il::Val::Reg(lenReg); |
|
| 2348 | 2441 | } |
|
| 2349 | 2442 | ||
| 2350 | 2443 | /// Load the capacity from a slice value. |
|
| 2351 | - | unsafe fn loadSliceCap(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Val { |
|
| 2444 | + | unsafe fn loadSliceCap 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2352 | 2445 | let capReg = nextReg(self); |
|
| 2353 | 2446 | emitLoadW32At(self, capReg, sliceReg, SLICE_CAP_OFFSET); |
|
| 2354 | 2447 | return il::Val::Reg(capReg); |
|
| 2355 | 2448 | } |
|
| 2356 | 2449 | ||
| 2357 | 2450 | /// Emit a load instruction for a scalar value at `src` plus `offset`. |
|
| 2358 | 2451 | /// For reading values that may be aggregates, use `emitRead` instead. |
|
| 2359 | - | unsafe fn emitLoad(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val { |
|
| 2452 | + | unsafe fn emitLoad 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2360 | 2453 | let dst = nextReg(self); |
|
| 2361 | 2454 | let ilTyp = ilType(self.low, typ); |
|
| 2362 | 2455 | ||
| 2363 | 2456 | if isSignedType(typ) { |
|
| 2364 | 2457 | emit(self, il::Instr::Sload { typ: ilTyp, dst, src, offset }); |
| 2368 | 2461 | return il::Val::Reg(dst); |
|
| 2369 | 2462 | } |
|
| 2370 | 2463 | ||
| 2371 | 2464 | /// Read a value from memory at `src` plus `offset`. Aggregates are represented |
|
| 2372 | 2465 | /// as pointers, so we return the address directly. Scalars are loaded via [`emitLoad`]. |
|
| 2373 | - | unsafe fn emitRead(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val { |
|
| 2466 | + | unsafe fn emitRead 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2374 | 2467 | if isAggregateType(typ) { |
|
| 2375 | 2468 | let ptr = emitPtrOffset(self, src, offset); |
|
| 2376 | 2469 | return il::Val::Reg(ptr); |
|
| 2377 | 2470 | } |
|
| 2378 | 2471 | return emitLoad(self, src, offset, typ); |
|
| 2379 | 2472 | } |
|
| 2380 | 2473 | ||
| 2381 | 2474 | /// Emit a copy instruction that loads a data symbol's address into a register. |
|
| 2382 | - | unsafe fn emitDataAddr(self: &mut FnLowerer, sym: *unsafe resolver::Symbol) -> il::Reg { |
|
| 2475 | + | unsafe fn emitDataAddr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sym: *unsafe resolver::Symbol) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 2383 | 2476 | let dst = nextReg(self); |
|
| 2384 | 2477 | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym); |
|
| 2385 | - | let qualName = qualifyName(self.low, modId, sym.name); |
|
| 2478 | + | let qualName = lookupSymbolName(self.low, sym) else qualifyName(self.low, modId, sym.name); |
|
| 2386 | 2479 | ||
| 2387 | 2480 | emit(self, il::Instr::Copy { dst, val: il::Val::DataSym(qualName) }); |
|
| 2388 | 2481 | ||
| 2389 | 2482 | return dst; |
|
| 2390 | 2483 | } |
|
| 2391 | 2484 | ||
| 2392 | 2485 | /// Emit a copy instruction that loads a function's address into a register. |
|
| 2393 | - | unsafe fn emitFnAddr(self: &mut FnLowerer, sym: *unsafe resolver::Symbol) -> il::Reg { |
|
| 2486 | + | unsafe fn emitFnAddr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sym: *unsafe resolver::Symbol) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 2394 | 2487 | let dst = nextReg(self); |
|
| 2395 | 2488 | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym); |
|
| 2396 | 2489 | let qualName = qualifyName(self.low, modId, sym.name); |
|
| 2397 | 2490 | ||
| 2398 | 2491 | emit(self, il::Instr::Copy { dst, val: il::Val::FnAddr(qualName) }); |
|
| 2399 | 2492 | ||
| 2400 | 2493 | return dst; |
|
| 2401 | 2494 | } |
|
| 2402 | 2495 | ||
| 2403 | 2496 | /// Emit pattern tests for a single pattern. |
|
| 2404 | - | unsafe fn emitPatternMatch( |
|
| 2405 | - | self: &mut FnLowerer, |
|
| 2497 | + | unsafe fn emitPatternMatch 'arena 'phase 'function ( |
|
| 2498 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2406 | 2499 | subject: &MatchSubject, |
|
| 2407 | 2500 | pattern: *ast::Node, |
|
| 2408 | 2501 | matchBlock: BlockId, |
|
| 2409 | 2502 | fallthrough: BlockId |
|
| 2410 | - | ) throws (LowerError) { |
|
| 2503 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2411 | 2504 | // Wildcards always match; array patterns are tested element-by-element |
|
| 2412 | 2505 | // during binding, so they also unconditionally enter the match block. |
|
| 2413 | 2506 | if isWildcardPattern(pattern) { |
|
| 2414 | 2507 | try emitJmp(self, matchBlock); |
|
| 2415 | 2508 | return; |
| 2417 | 2510 | if let case ast::NodeValue::ArrayLit(_) = pattern.value { |
|
| 2418 | 2511 | try emitJmp(self, matchBlock); |
|
| 2419 | 2512 | return; |
|
| 2420 | 2513 | } |
|
| 2421 | 2514 | if let case resolver::Type::Nominal(resolver::NominalType::Record(_)) = subject.type { |
|
| 2422 | - | if let case ast::NodeValue::RecordLit(_) = pattern.value { |
|
| 2423 | - | try emitJmp(self, matchBlock); |
|
| 2424 | - | return; |
|
| 2515 | + | match pattern.value { |
|
| 2516 | + | case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => { |
|
| 2517 | + | try emitJmp(self, matchBlock); |
|
| 2518 | + | return; |
|
| 2519 | + | } |
|
| 2520 | + | else => { |
|
| 2521 | + | }, |
|
| 2425 | 2522 | } |
|
| 2426 | 2523 | } |
|
| 2427 | 2524 | let isNil = pattern.value == ast::NodeValue::Nil; |
|
| 2428 | 2525 | ||
| 2429 | 2526 | match subject.kind { |
| 2497 | 2594 | } |
|
| 2498 | 2595 | ||
| 2499 | 2596 | /// Emit branches for multiple patterns. The first pattern that matches |
|
| 2500 | 2597 | /// causes a jump to the match block. If no patterns match, we jump to the |
|
| 2501 | 2598 | /// fallthrough block. |
|
| 2502 | - | unsafe fn emitPatternMatches( |
|
| 2503 | - | self: &mut FnLowerer, |
|
| 2599 | + | unsafe fn emitPatternMatches 'arena 'phase 'function ( |
|
| 2600 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2504 | 2601 | subject: &MatchSubject, |
|
| 2505 | 2602 | patterns: &[*ast::Node], |
|
| 2506 | 2603 | matchBlock: BlockId, |
|
| 2507 | 2604 | fallthrough: BlockId |
|
| 2508 | - | ) throws (LowerError) { |
|
| 2605 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2509 | 2606 | assert patterns.len > 0; |
|
| 2510 | 2607 | ||
| 2511 | 2608 | for i in 0..(patterns.len - 1) { |
|
| 2512 | 2609 | let pattern = patterns[i]; |
|
| 2513 | 2610 | let nextArm = try createBlock(self, "arm"); |
| 2526 | 2623 | ||
| 2527 | 2624 | /// Emit a match binding pattern. |
|
| 2528 | 2625 | /// Binding patterns always match for regular values, but for optionals they |
|
| 2529 | 2626 | /// check for the presence of a value. Jumps to `valuePresent` on success, |
|
| 2530 | 2627 | /// `valueAbsent` on failure. |
|
| 2531 | - | unsafe fn emitBindingTest( |
|
| 2532 | - | self: &mut FnLowerer, |
|
| 2628 | + | unsafe fn emitBindingTest 'arena 'phase 'function ( |
|
| 2629 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2533 | 2630 | subject: &MatchSubject, |
|
| 2534 | 2631 | valuePresent: BlockId, |
|
| 2535 | 2632 | valueAbsent: BlockId |
|
| 2536 | - | ) throws (LowerError) { |
|
| 2633 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2537 | 2634 | match subject.kind { |
|
| 2538 | 2635 | case MatchSubjectKind::OptionalPtr, MatchSubjectKind::OptionalAggregate => { |
|
| 2539 | 2636 | let nilReg = try optionalNilReg(self, subject.val, subject.type); |
|
| 2540 | 2637 | try emitBr(self, nilReg, valuePresent, valueAbsent); |
|
| 2541 | 2638 | } |
| 2545 | 2642 | } |
|
| 2546 | 2643 | } |
|
| 2547 | 2644 | } |
|
| 2548 | 2645 | ||
| 2549 | 2646 | /// Emit a jump to target if the current block hasn't terminated, then seal the target block. |
|
| 2550 | - | unsafe fn emitJmpAndSeal(self: &mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2647 | + | unsafe fn emitJmpAndSeal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2551 | 2648 | if not blockHasTerminator(self) { |
|
| 2552 | 2649 | try emitJmp(self, target); |
|
| 2553 | 2650 | } |
|
| 2554 | 2651 | try sealBlock(self, target); |
|
| 2555 | 2652 | } |
|
| 2556 | 2653 | ||
| 2557 | 2654 | /// Check if the current block already has a terminator instruction. |
|
| 2558 | - | unsafe fn blockHasTerminator(self: &FnLowerer) -> bool { |
|
| 2655 | + | unsafe fn blockHasTerminator 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function) -> bool where 'arena: 'phase, 'phase: 'function { |
|
| 2559 | 2656 | let blk = getBlock(self, currentBlock(self)); |
|
| 2560 | 2657 | if blk.instrs.len == 0 { |
|
| 2561 | 2658 | return false; |
|
| 2562 | 2659 | } |
|
| 2563 | 2660 | match blk.instrs[blk.instrs.len - 1] { |
| 2585 | 2682 | /// return 0; // @else diverges, no jump to merge. |
|
| 2586 | 2683 | /// } |
|
| 2587 | 2684 | /// |
|
| 2588 | 2685 | /// In the above example, the merge block stays `nil`, and no code is generated |
|
| 2589 | 2686 | /// after the `if`. The merge block is created on first use. |
|
| 2590 | - | unsafe fn emitMergeIfUnterminated(self: &mut FnLowerer, mergeBlock: &mut ?BlockId) throws (LowerError) { |
|
| 2687 | + | unsafe fn emitMergeIfUnterminated 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2591 | 2688 | if not blockHasTerminator(self) { |
|
| 2592 | 2689 | if *mergeBlock == nil { |
|
| 2593 | 2690 | set *mergeBlock = try createBlock(self, "merge"); |
|
| 2594 | 2691 | } |
|
| 2595 | 2692 | let target = *mergeBlock else { throw LowerError::MissingTarget; }; |
| 2601 | 2698 | // Control Flow Edge Management // |
|
| 2602 | 2699 | ////////////////////////////////// |
|
| 2603 | 2700 | ||
| 2604 | 2701 | /// Add a predecessor edge from `pred` to `target`. |
|
| 2605 | 2702 | /// Must be called before the target block is sealed. Duplicates are ignored. |
|
| 2606 | - | unsafe fn addPredecessor(self: &mut FnLowerer, target: BlockId, pred: BlockId) { |
|
| 2703 | + | unsafe fn addPredecessor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId, pred: BlockId) where 'arena: 'phase, 'phase: 'function { |
|
| 2607 | 2704 | let blk = getBlockMut(self, target); |
|
| 2608 | 2705 | assert blk.sealState <> Sealed::Yes, "addPredecessor: adding predecessor to sealed block"; |
|
| 2609 | 2706 | let preds = &mut blk.preds; |
|
| 2610 | 2707 | for i in 0..preds.len { |
|
| 2611 | 2708 | if preds[i] == *pred { // Avoid duplicate predecessor entries. |
|
| 2612 | 2709 | return; |
|
| 2613 | 2710 | } |
|
| 2614 | 2711 | } |
|
| 2615 | - | preds.append(*pred, self.allocator); |
|
| 2712 | + | preds.append(*pred, alloc::arenaAllocator(self.arena)); |
|
| 2616 | 2713 | } |
|
| 2617 | 2714 | ||
| 2618 | 2715 | /// Finalize all blocks and return the block array. |
|
| 2619 | - | unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *unsafe [il::Block] throws (LowerError) { |
|
| 2716 | + | unsafe fn finalizeBlocks 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> *unsafe [il::Block] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2620 | 2717 | let blockCount = self.blockData.len; |
|
| 2621 | 2718 | let blocks = try! alloc::allocRawSlice( |
|
| 2622 | - | self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), blockCount |
|
| 2719 | + | self.arena, @sizeOf(il::Block), @alignOf(il::Block), blockCount |
|
| 2623 | 2720 | ) as *unsafe mut [il::Block]; |
|
| 2624 | 2721 | ||
| 2625 | 2722 | for i in 0..self.blockData.len { |
|
| 2626 | 2723 | let data = &self.blockData[i]; |
|
| 2627 | 2724 |
| 2641 | 2738 | // Loop Management // |
|
| 2642 | 2739 | ///////////////////// |
|
| 2643 | 2740 | ||
| 2644 | 2741 | /// Enter a loop context for break/continue handling. |
|
| 2645 | 2742 | /// `continueBlock` is `nil` when the continue target is created lazily. |
|
| 2646 | - | unsafe fn enterLoop(self: &mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) { |
|
| 2743 | + | fn enterLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, breakBlock: BlockId, continueBlock: ?BlockId) where 'arena: 'phase, 'phase: 'function { |
|
| 2647 | 2744 | assert self.loopDepth < self.loopStack.len, "enterLoop: loop depth overflow"; |
|
| 2648 | 2745 | let slot = &mut self.loopStack[self.loopDepth]; |
|
| 2649 | 2746 | ||
| 2650 | 2747 | set slot.breakTarget = breakBlock; |
|
| 2651 | 2748 | set slot.continueTarget = continueBlock; |
|
| 2652 | 2749 | set self.loopDepth += 1; |
|
| 2653 | 2750 | } |
|
| 2654 | 2751 | ||
| 2655 | 2752 | /// Exit the current loop context. |
|
| 2656 | - | fn exitLoop(self: &mut FnLowerer) { |
|
| 2753 | + | fn exitLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) where 'arena: 'phase, 'phase: 'function { |
|
| 2657 | 2754 | assert self.loopDepth <> 0, "exitLoop: loopDepth is zero"; |
|
| 2658 | 2755 | set self.loopDepth -= 1; |
|
| 2659 | 2756 | } |
|
| 2660 | 2757 | ||
| 2661 | - | /// Get the current loop context. |
|
| 2662 | - | unsafe fn currentLoop(self: &mut FnLowerer) -> ?*unsafe mut LoopCtx { |
|
| 2758 | + | /// Copy the current loop targets. |
|
| 2759 | + | fn currentLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> ?LoopCtx where 'arena: 'phase, 'phase: 'function { |
|
| 2663 | 2760 | if self.loopDepth == 0 { |
|
| 2664 | 2761 | return nil; |
|
| 2665 | 2762 | } |
|
| 2666 | - | return &mut self.loopStack[self.loopDepth - 1]; |
|
| 2763 | + | return self.loopStack[self.loopDepth - 1]; |
|
| 2667 | 2764 | } |
|
| 2668 | 2765 | ||
| 2669 | 2766 | /// Get or lazily create the continue target block for the current loop. |
|
| 2670 | - | unsafe fn getOrCreateContinueBlock(self: &mut FnLowerer) -> BlockId throws (LowerError) { |
|
| 2767 | + | unsafe fn getOrCreateContinueBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2671 | 2768 | let ctx = currentLoop(self) else { |
|
| 2672 | 2769 | throw LowerError::OutsideOfLoop; |
|
| 2673 | 2770 | }; |
|
| 2674 | 2771 | if let block = ctx.continueTarget { |
|
| 2675 | 2772 | return block; |
|
| 2676 | 2773 | } |
|
| 2677 | 2774 | let block = try createBlock(self, "step"); |
|
| 2678 | - | set ctx.continueTarget = block; |
|
| 2775 | + | set self.loopStack[self.loopDepth - 1].continueTarget = block; |
|
| 2679 | 2776 | return block; |
|
| 2680 | 2777 | } |
|
| 2681 | 2778 | ||
| 2682 | 2779 | /// Allocate a slice of values in the lowering arena. |
|
| 2683 | - | unsafe fn allocVals(self: &mut FnLowerer, len: u32) -> *unsafe mut [il::Val] throws (LowerError) { |
|
| 2684 | - | return try! alloc::allocRawSlice(self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *unsafe mut [il::Val]; |
|
| 2780 | + | unsafe fn allocVals 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, len: u32) -> *unsafe mut [il::Val] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2781 | + | return try! alloc::allocRawSlice(self.arena, @sizeOf(il::Val), @alignOf(il::Val), len) as *unsafe mut [il::Val]; |
|
| 2685 | 2782 | } |
|
| 2686 | 2783 | ||
| 2687 | 2784 | /// Allocate a single-value slice in the lowering arena. |
|
| 2688 | - | unsafe fn allocVal(self: &mut FnLowerer, val: il::Val) -> *unsafe mut [il::Val] throws (LowerError) { |
|
| 2785 | + | unsafe fn allocVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) -> *unsafe mut [il::Val] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2689 | 2786 | let args = try allocVals(self, 1); |
|
| 2690 | 2787 | set args[0] = val; |
|
| 2691 | 2788 | return args; |
|
| 2692 | 2789 | } |
|
| 2693 | 2790 |
| 2779 | 2876 | // a block parameter, but defer filling in the terminator arguments. When the |
|
| 2780 | 2877 | // block is later sealed via [`sealBlock`], all incomplete block params are resolved. |
|
| 2781 | 2878 | ||
| 2782 | 2879 | /// Declare a new source-level variable and define its initial value. |
|
| 2783 | 2880 | /// If called before any block exists (e.g., for parameters), the definition is skipped. |
|
| 2784 | - | unsafe fn newVar( |
|
| 2785 | - | self: &mut FnLowerer, |
|
| 2881 | + | unsafe fn newVar 'arena 'phase 'function ( |
|
| 2882 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2786 | 2883 | name: ?*[u8], |
|
| 2787 | 2884 | type: il::Type, |
|
| 2788 | 2885 | mutable: bool, |
|
| 2789 | 2886 | val: il::Val |
|
| 2790 | - | ) -> Var { |
|
| 2887 | + | ) -> Var where 'arena: 'phase, 'phase: 'function { |
|
| 2791 | 2888 | let id = self.vars.len; |
|
| 2792 | - | self.vars.append(VarData { name, type, mutable, addressTaken: false }, self.allocator); |
|
| 2889 | + | self.vars.append( |
|
| 2890 | + | VarData { name, type, mutable, addressTaken: false }, |
|
| 2891 | + | alloc::arenaAllocator(self.arena) |
|
| 2892 | + | ); |
|
| 2793 | 2893 | ||
| 2794 | 2894 | let v = Var(id); |
|
| 2795 | 2895 | if self.currentBlock <> nil { |
|
| 2796 | 2896 | defVar(self, v, val); |
|
| 2797 | 2897 | } |
| 2800 | 2900 | ||
| 2801 | 2901 | /// Define (write) a variable. Record the SSA value of a variable in the |
|
| 2802 | 2902 | /// current block. Called when a variable is assigned or initialized (`let` |
|
| 2803 | 2903 | /// bindings, assignments, loop updates). When [`useVar`] is later called, |
|
| 2804 | 2904 | /// it will retrieve this value. |
|
| 2805 | - | unsafe fn defVar(self: &mut FnLowerer, v: Var, val: il::Val) { |
|
| 2905 | + | unsafe fn defVar 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var, val: il::Val) where 'arena: 'phase, 'phase: 'function { |
|
| 2806 | 2906 | assert *v < self.vars.len; |
|
| 2807 | 2907 | set getBlockMut(self, currentBlock(self)).vars[*v] = val; |
|
| 2808 | 2908 | } |
|
| 2809 | 2909 | ||
| 2810 | 2910 | /// Use (read) the current value of a variable in the current block. |
|
| 2811 | 2911 | /// May insert block parameters if the value must come from predecessors. |
|
| 2812 | - | unsafe fn useVar(self: &mut FnLowerer, v: Var) -> il::Val throws (LowerError) { |
|
| 2912 | + | unsafe fn useVar 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2813 | 2913 | return try useVarInBlock(self, currentBlock(self), v); |
|
| 2814 | 2914 | } |
|
| 2815 | 2915 | ||
| 2816 | 2916 | /// Resolve which SSA definition of a variable reaches a use point in a given block. |
|
| 2817 | 2917 | /// |
|
| 2818 | 2918 | /// Given a variable and a block where it's used, this function finds the |
|
| 2819 | 2919 | /// correct [`il::Val`] that holds the variable's value at that program point. |
|
| 2820 | 2920 | /// When control flow merges from multiple predecessors with different |
|
| 2821 | 2921 | /// definitions, it creates a block parameter to unify them. |
|
| 2822 | - | unsafe fn useVarInBlock(self: &mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) { |
|
| 2922 | + | unsafe fn useVarInBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId, v: Var) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2823 | 2923 | assert *v < self.vars.len; |
|
| 2824 | 2924 | ||
| 2825 | 2925 | let blk = getBlockMut(self, block); |
|
| 2826 | 2926 | if let val = blk.vars[*v] { |
|
| 2827 | 2927 | return val; |
| 2852 | 2952 | return try createBlockParam(self, block, v); |
|
| 2853 | 2953 | } |
|
| 2854 | 2954 | ||
| 2855 | 2955 | /// Look up a variable by name in the current scope. |
|
| 2856 | 2956 | /// Searches from most recently declared to first, enabling shadowing. |
|
| 2857 | - | unsafe fn lookupVarByName(self: &FnLowerer, name: *[u8]) -> ?Var { |
|
| 2957 | + | unsafe fn lookupVarByName 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function, name: *[u8]) -> ?Var where 'arena: 'phase, 'phase: 'function { |
|
| 2858 | 2958 | let mut id = self.vars.len; |
|
| 2859 | 2959 | while id > 0 { |
|
| 2860 | 2960 | set id -= 1; |
|
| 2861 | 2961 | if let varName = self.vars[id].name { |
|
| 2862 | 2962 | // Names are interned strings, so pointer comparison suffices. |
| 2867 | 2967 | } |
|
| 2868 | 2968 | return nil; |
|
| 2869 | 2969 | } |
|
| 2870 | 2970 | ||
| 2871 | 2971 | /// Look up a local variable bound to an identifier node. |
|
| 2872 | - | unsafe fn lookupLocalVar(self: &FnLowerer, node: *ast::Node) -> ?Var { |
|
| 2972 | + | unsafe fn lookupLocalVar 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function, node: *ast::Node) -> ?Var where 'arena: 'phase, 'phase: 'function { |
|
| 2873 | 2973 | let case ast::NodeValue::Ident(name) = node.value else { |
|
| 2874 | 2974 | return nil; |
|
| 2875 | 2975 | }; |
|
| 2876 | 2976 | return lookupVarByName(self, name); |
|
| 2877 | 2977 | } |
|
| 2878 | 2978 | ||
| 2879 | 2979 | /// Save current lexical variable scope depth. |
|
| 2880 | - | unsafe fn enterVarScope(self: &FnLowerer) -> u32 { |
|
| 2980 | + | unsafe fn enterVarScope 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function) -> u32 where 'arena: 'phase, 'phase: 'function { |
|
| 2881 | 2981 | return self.vars.len; |
|
| 2882 | 2982 | } |
|
| 2883 | 2983 | ||
| 2884 | 2984 | /// Restore lexical variable scope depth. |
|
| 2885 | - | unsafe fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) { |
|
| 2985 | + | unsafe fn exitVarScope 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, savedVarsLen: u32) where 'arena: 'phase, 'phase: 'function { |
|
| 2886 | 2986 | set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap); |
|
| 2887 | 2987 | } |
|
| 2888 | 2988 | ||
| 2889 | 2989 | /// Get the metadata for a variable. |
|
| 2890 | - | unsafe fn getVar(self: &FnLowerer, v: Var) -> *unsafe VarData { |
|
| 2990 | + | unsafe fn getVar 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function, v: Var) -> *unsafe VarData where 'arena: 'phase, 'phase: 'function { |
|
| 2891 | 2991 | assert *v < self.vars.len; |
|
| 2892 | 2992 | return &self.vars[*v]; |
|
| 2893 | 2993 | } |
|
| 2894 | 2994 | ||
| 2895 | 2995 | /// Create a block parameter to merge a variable's value from multiple |
| 2906 | 3006 | /// @end(w32 %1) // x = %1, merged from predecessors |
|
| 2907 | 3007 | /// ret %1; |
|
| 2908 | 3008 | /// |
|
| 2909 | 3009 | /// Create a register `%1` as a block parameter. In a sealed block, patch each |
|
| 2910 | 3010 | /// predecessor's jump to pass its value of `x`. Otherwise, defer until sealing. |
|
| 2911 | - | unsafe fn createBlockParam(self: &mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) { |
|
| 3011 | + | unsafe fn createBlockParam 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId, v: Var) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2912 | 3012 | // Entry block must not have block parameters. |
|
| 2913 | 3013 | assert block <> self.entryBlock, "createBlockParam: entry block must not have block parameters"; |
|
| 2914 | 3014 | // Allocate a register to hold the merged value. |
|
| 2915 | 3015 | let reg = nextReg(self); |
|
| 2916 | 3016 | let type = getVar(self, v).type; |
|
| 2917 | 3017 | ||
| 2918 | 3018 | // Create block parameter and add it to the block. |
|
| 2919 | 3019 | let param = il::Param { value: reg, type }; |
|
| 2920 | 3020 | let blk = getBlockMut(self, block); |
|
| 2921 | 3021 | let paramIdx = blk.paramVars.len; |
|
| 2922 | - | blk.params.append(param, self.allocator); |
|
| 2923 | - | blk.paramVars.append(*v, self.allocator); // Associate variable with parameter. |
|
| 3022 | + | blk.params.append(param, alloc::arenaAllocator(self.arena)); |
|
| 3023 | + | blk.paramVars.append(*v, alloc::arenaAllocator(self.arena)); // Associate variable with parameter. |
|
| 2924 | 3024 | ||
| 2925 | 3025 | // Record that this variable's value in this block is now the parameter register. |
|
| 2926 | 3026 | // This must happen before the predecessor loop to handle self-referential loops. |
|
| 2927 | 3027 | set blk.vars[*v] = il::Val::Reg(reg); |
|
| 2928 | 3028 |
| 2952 | 3052 | /// x3 = phi(x1, x2) |
|
| 2953 | 3053 | /// |
|
| 2954 | 3054 | /// This representation avoids the need for phi nodes to reference their |
|
| 2955 | 3055 | /// predecessor blocks explicitly, since the control flow edges already encode |
|
| 2956 | 3056 | /// that information. |
|
| 2957 | - | unsafe fn resolveBlockArgs(self: &mut FnLowerer, block: BlockId, v: Var, paramIdx: u32) throws (LowerError) { |
|
| 3057 | + | unsafe fn resolveBlockArgs 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId, v: Var, paramIdx: u32) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2958 | 3058 | let blk = getBlock(self, block); |
|
| 2959 | 3059 | ||
| 2960 | 3060 | // For each predecessor, recursively look up the variable's reaching definition |
|
| 2961 | 3061 | // in that block, then patch the predecessor's terminator to pass that value |
|
| 2962 | 3062 | // as an argument to this block's parameter. |
| 2970 | 3070 | } |
|
| 2971 | 3071 | } |
|
| 2972 | 3072 | ||
| 2973 | 3073 | /// Check if a block parameter is trivial, i.e. all predecessors provide |
|
| 2974 | 3074 | /// the same value. Returns the trivial value if so. |
|
| 2975 | - | unsafe fn getTrivialPhiVal(self: &mut FnLowerer, block: BlockId, v: Var) -> ?il::Val throws (LowerError) { |
|
| 3075 | + | unsafe fn getTrivialPhiVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, block: BlockId, v: Var) -> ?il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2976 | 3076 | let blk = getBlock(self, block); |
|
| 2977 | 3077 | // Get the block parameter register. |
|
| 2978 | 3078 | let paramReg = blk.vars[*v]; |
|
| 2979 | 3079 | // Check if all predecessors provide the same value. |
|
| 2980 | 3080 | let mut sameVal: ?il::Val = nil; |
| 3004 | 3104 | return sameVal; |
|
| 3005 | 3105 | } |
|
| 3006 | 3106 | ||
| 3007 | 3107 | /// Patch a single terminator argument for a specific edge. This is used during |
|
| 3008 | 3108 | /// SSA construction to pass variable values along control flow edges. |
|
| 3009 | - | unsafe fn patchTerminatorArg( |
|
| 3010 | - | self: &mut FnLowerer, |
|
| 3109 | + | unsafe fn patchTerminatorArg 'arena 'phase 'function ( |
|
| 3110 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3011 | 3111 | from: BlockId, // The predecessor block containing the terminator to patch. |
|
| 3012 | 3112 | target: u32, // The index of the target block we're passing the value to. |
|
| 3013 | 3113 | paramIdx: u32, // The index of the block parameter to set. |
|
| 3014 | 3114 | val: il::Val // The value to pass as the argument. |
|
| 3015 | - | ) { |
|
| 3115 | + | ) where 'arena: 'phase, 'phase: 'function { |
|
| 3016 | 3116 | let data = getBlockMut(self, from); |
|
| 3017 | 3117 | let ix = data.instrs.len - 1; // The terminator is always the last instruction. |
|
| 3018 | 3118 | ||
| 3019 | 3119 | // TODO: We shouldn't need to use a mutable subscript here, given that the |
|
| 3020 | 3120 | // fields are already mutable. |
| 3052 | 3152 | } |
|
| 3053 | 3153 | } |
|
| 3054 | 3154 | } |
|
| 3055 | 3155 | ||
| 3056 | 3156 | /// Grow an args array to hold at least the given capacity. |
|
| 3057 | - | unsafe fn growArgs(self: &mut FnLowerer, args: *unsafe mut [il::Val], capacity: u32) -> *unsafe mut [il::Val] { |
|
| 3157 | + | unsafe fn growArgs 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, args: *unsafe mut [il::Val], capacity: u32) -> *unsafe mut [il::Val] where 'arena: 'phase, 'phase: 'function { |
|
| 3058 | 3158 | if args.len >= capacity { |
|
| 3059 | 3159 | return args; |
|
| 3060 | 3160 | } |
|
| 3061 | 3161 | let newArgs = try! alloc::allocRawSlice( |
|
| 3062 | - | self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity |
|
| 3162 | + | self.arena, @sizeOf(il::Val), @alignOf(il::Val), capacity |
|
| 3063 | 3163 | ) as *unsafe mut [il::Val]; |
|
| 3064 | 3164 | ||
| 3065 | 3165 | for arg, i in args { |
|
| 3066 | 3166 | set newArgs[i] = arg; |
|
| 3067 | 3167 | } |
| 3082 | 3182 | return name; |
|
| 3083 | 3183 | } |
|
| 3084 | 3184 | ||
| 3085 | 3185 | /// Lower function parameters. Declares variables for each parameter. |
|
| 3086 | 3186 | /// When a receiver name is passed, we're handling a trait method. |
|
| 3087 | - | unsafe fn lowerParams( |
|
| 3088 | - | self: &mut FnLowerer, |
|
| 3187 | + | unsafe fn lowerParams 'arena 'phase 'function ( |
|
| 3188 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3089 | 3189 | fnType: resolver::FnType, |
|
| 3090 | 3190 | astParams: *[*ast::Node], |
|
| 3091 | 3191 | receiverName: ?*ast::Node |
|
| 3092 | - | ) -> *unsafe [il::Param] throws (LowerError) { |
|
| 3192 | + | ) -> *unsafe [il::Param] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3093 | 3193 | let offset: u32 = 1 if self.returnReg <> nil else 0; |
|
| 3094 | 3194 | let totalLen = fnType.paramTypes.len as u32 + offset; |
|
| 3095 | 3195 | if totalLen == 0 { |
|
| 3096 | 3196 | return &[]; |
|
| 3097 | 3197 | } |
|
| 3098 | 3198 | assert fnType.paramTypes.len as u32 <= resolver::MAX_FN_PARAMS; |
|
| 3099 | 3199 | ||
| 3100 | 3200 | let params = try! alloc::allocRawSlice( |
|
| 3101 | - | self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen |
|
| 3201 | + | self.arena, @sizeOf(il::Param), @alignOf(il::Param), totalLen |
|
| 3102 | 3202 | ) as *unsafe mut [il::Param]; |
|
| 3103 | 3203 | ||
| 3104 | 3204 | if let reg = self.returnReg { |
|
| 3105 | 3205 | set params[0] = il::Param { value: reg, type: il::Type::W64 }; |
|
| 3106 | 3206 | } |
| 3126 | 3226 | } else { |
|
| 3127 | 3227 | set name = try paramName(&astParams[i].value); |
|
| 3128 | 3228 | } |
|
| 3129 | 3229 | let v = newVar(self, name, type, false, il::Val::Undef); |
|
| 3130 | 3230 | ||
| 3131 | - | self.params.append(FnParamBinding { var: v, reg }, self.allocator); |
|
| 3231 | + | set self.params[i] = FnParamBinding { var: v, reg }; |
|
| 3232 | + | set self.paramsLen += 1; |
|
| 3132 | 3233 | } |
|
| 3133 | 3234 | return params; |
|
| 3134 | 3235 | } |
|
| 3135 | 3236 | ||
| 3136 | 3237 | /// Resolve match subject. |
|
| 3137 | - | unsafe fn lowerMatchSubject(self: &mut FnLowerer, subject: *ast::Node) -> MatchSubject throws (LowerError) { |
|
| 3238 | + | unsafe fn lowerMatchSubject 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, subject: *ast::Node) -> MatchSubject throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3138 | 3239 | let mut val = try lowerExpr(self, subject); |
|
| 3139 | 3240 | let subjectType = try typeOf(self, subject); |
|
| 3140 | 3241 | let unwrapped = resolver::unwrapMatchSubject(subjectType); |
|
| 3141 | 3242 | ||
| 3142 | 3243 | // When matching an aggregate by value, copy it to a fresh stack slot so |
| 3172 | 3273 | else => return false, |
|
| 3173 | 3274 | } |
|
| 3174 | 3275 | } |
|
| 3175 | 3276 | ||
| 3176 | 3277 | /// Load the tag byte from a tagged value aggregate (optionals and unions). |
|
| 3177 | - | unsafe fn tvalTagReg(self: &mut FnLowerer, base: il::Reg) -> il::Reg { |
|
| 3278 | + | unsafe fn tvalTagReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 3178 | 3279 | let tagReg = nextReg(self); |
|
| 3179 | 3280 | emitLoadW8At(self, tagReg, base, TVAL_TAG_OFFSET); |
|
| 3180 | 3281 | return tagReg; |
|
| 3181 | 3282 | } |
|
| 3182 | 3283 | ||
| 3183 | 3284 | /// Load the tag word from a result aggregate. |
|
| 3184 | - | unsafe fn resultTagReg(self: &mut FnLowerer, base: il::Reg) -> il::Reg { |
|
| 3285 | + | unsafe fn resultTagReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 3185 | 3286 | let tagReg = nextReg(self); |
|
| 3186 | 3287 | emitLoadW64At(self, tagReg, base, TVAL_TAG_OFFSET); |
|
| 3187 | 3288 | return tagReg; |
|
| 3188 | 3289 | } |
|
| 3189 | 3290 | ||
| 3190 | 3291 | /// Get the register to compare against `0` for optional `nil` checking. |
|
| 3191 | 3292 | /// For null-ptr-optimized types, loads the data pointer, or returns it |
|
| 3192 | 3293 | /// directly for scalar pointers. For aggregates, returns the tag register. |
|
| 3193 | - | unsafe fn optionalNilReg(self: &mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) { |
|
| 3294 | + | unsafe fn optionalNilReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3194 | 3295 | let reg = emitValToReg(self, val); |
|
| 3195 | 3296 | ||
| 3196 | 3297 | match typ { |
|
| 3197 | 3298 | case resolver::Type::Optional(resolver::Type::Slice { .. }) => { |
|
| 3198 | 3299 | let ptrReg = nextReg(self); |
| 3204 | 3305 | else => return reg, |
|
| 3205 | 3306 | } |
|
| 3206 | 3307 | } |
|
| 3207 | 3308 | ||
| 3208 | 3309 | /// Lower an optional nil check (`opt == nil` or `opt <> nil`). |
|
| 3209 | - | unsafe fn lowerNilCheck(self: &mut FnLowerer, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) { |
|
| 3310 | + | unsafe fn lowerNilCheck 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3210 | 3311 | let optTy = try typeOf(self, opt); |
|
| 3211 | 3312 | // Handle `nil == nil` or `nil <> nil`. |
|
| 3212 | 3313 | if optTy == resolver::Type::Nil { |
|
| 3213 | 3314 | return il::Val::Imm(1) if isEq else il::Val::Imm(0); |
|
| 3214 | 3315 | } |
| 3222 | 3323 | let op = il::BinOp::Eq if isEq else il::BinOp::Ne; |
|
| 3223 | 3324 | return emitTypedBinOp(self, op, cmpType, il::Val::Reg(cmpReg), il::Val::Imm(0)); |
|
| 3224 | 3325 | } |
|
| 3225 | 3326 | ||
| 3226 | 3327 | /// Load the payload value from a tagged value aggregate at the given offset. |
|
| 3227 | - | unsafe fn tvalPayloadVal(self: &mut FnLowerer, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val { |
|
| 3328 | + | unsafe fn tvalPayloadVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 3228 | 3329 | if payload == resolver::Type::Void { |
|
| 3229 | 3330 | return il::Val::Undef; |
|
| 3230 | 3331 | } |
|
| 3231 | 3332 | return emitRead(self, base, valOffset, payload); |
|
| 3232 | 3333 | } |
|
| 3233 | 3334 | ||
| 3234 | 3335 | /// Compute the address of the payload in a tagged value aggregate. |
|
| 3235 | - | unsafe fn tvalPayloadAddr(self: &mut FnLowerer, base: il::Reg, valOffset: i32) -> il::Val { |
|
| 3336 | + | unsafe fn tvalPayloadAddr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, valOffset: i32) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 3236 | 3337 | return il::Val::Reg(emitPtrOffset(self, base, valOffset)); |
|
| 3237 | 3338 | } |
|
| 3238 | 3339 | ||
| 3239 | 3340 | /// Bind a variable to a tagged value's payload. |
|
| 3240 | - | unsafe fn bindPayloadVariable( |
|
| 3241 | - | self: &mut FnLowerer, |
|
| 3341 | + | unsafe fn bindPayloadVariable 'arena 'phase 'function ( |
|
| 3342 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3242 | 3343 | name: *[u8], |
|
| 3243 | 3344 | subjectVal: il::Val, |
|
| 3244 | 3345 | bindType: resolver::Type, |
|
| 3245 | 3346 | matchBy: resolver::MatchBy, |
|
| 3246 | 3347 | valOffset: i32, |
|
| 3247 | 3348 | mutable: bool |
|
| 3248 | - | ) -> Var throws (LowerError) { |
|
| 3349 | + | ) -> Var throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3249 | 3350 | let base = emitValToReg(self, subjectVal); |
|
| 3250 | 3351 | let mut payload: il::Val = undefined; |
|
| 3251 | 3352 | ||
| 3252 | 3353 | match matchBy { |
|
| 3253 | 3354 | case resolver::MatchBy::Value => |
| 3257 | 3358 | }; |
|
| 3258 | 3359 | return newVar(self, name, ilType(self.low, bindType), mutable, payload); |
|
| 3259 | 3360 | } |
|
| 3260 | 3361 | ||
| 3261 | 3362 | /// Bind an identifier from a matched subject. |
|
| 3262 | - | unsafe fn bindMatchVariable( |
|
| 3263 | - | self: &mut FnLowerer, |
|
| 3363 | + | unsafe fn bindMatchVariable 'arena 'phase 'function ( |
|
| 3364 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3264 | 3365 | subject: &MatchSubject, |
|
| 3265 | 3366 | binding: *ast::Node, |
|
| 3266 | 3367 | mutable: bool |
|
| 3267 | - | ) -> ?Var throws (LowerError) { |
|
| 3368 | + | ) -> ?Var throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3268 | 3369 | // Only bind if the pattern is an identifier. |
|
| 3269 | 3370 | let case ast::NodeValue::Ident(name) = binding.value else { |
|
| 3270 | 3371 | return nil; |
|
| 3271 | 3372 | }; |
|
| 3272 | 3373 | // For optional aggregates, extract the payload from the tagged value. |
| 3280 | 3381 | } |
|
| 3281 | 3382 | ||
| 3282 | 3383 | /// Bind variables from inside case patterns (union variants, records, slices). |
|
| 3283 | 3384 | /// `failBlock` is passed when nested patterns may require additional tests |
|
| 3284 | 3385 | /// that branch on mismatch (e.g. nested union variant tests). |
|
| 3285 | - | unsafe fn bindPatternVariables(self: &mut FnLowerer, subject: &MatchSubject, patterns: &[*ast::Node], failBlock: BlockId) throws (LowerError) { |
|
| 3386 | + | unsafe fn bindPatternVariables 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, subject: &MatchSubject, patterns: &[*ast::Node], failBlock: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3286 | 3387 | for pattern in patterns { |
|
| 3287 | 3388 | ||
| 3288 | 3389 | // Handle simple variant patterns like `Variant(x)`. |
|
| 3289 | 3390 | if let arg = resolver::variantPatternBinding(self.low.resolver, pattern) { |
|
| 3290 | 3391 | let case MatchSubjectKind::Union(unionInfo) = subject.kind |
| 3319 | 3420 | try bindFieldVariable(self, arg, payloadBase, fieldInfo, subject.by, failBlock); |
|
| 3320 | 3421 | } |
|
| 3321 | 3422 | } |
|
| 3322 | 3423 | } |
|
| 3323 | 3424 | match pattern.value { |
|
| 3425 | + | case ast::NodeValue::Call(call) => { |
|
| 3426 | + | if let body = resolver::getRecord(subject.type) { |
|
| 3427 | + | let base = emitValToReg(self, subject.val); |
|
| 3428 | + | for binding, i in call.args { |
|
| 3429 | + | try bindFieldVariable(self, binding, base, body.fields[i], subject.by, failBlock); |
|
| 3430 | + | } |
|
| 3431 | + | } |
|
| 3432 | + | } |
|
| 3324 | 3433 | // Compound variant patterns like `Variant { a, b }`. |
|
| 3325 | 3434 | case ast::NodeValue::RecordLit(lit) => |
|
| 3326 | 3435 | try bindRecordPatternFields(self, subject, pattern, lit, failBlock), |
|
| 3327 | 3436 | // Array patterns like `[a, b, c]`. |
|
| 3328 | 3437 | case ast::NodeValue::ArrayLit(items) => |
| 3334 | 3443 | } |
|
| 3335 | 3444 | ||
| 3336 | 3445 | /// Bind variables from an array literal pattern (e.g., `[a, 1, c]`). |
|
| 3337 | 3446 | /// Each element is either bound as a variable, skipped (placeholder), or |
|
| 3338 | 3447 | /// tested against the subject element, branching to `failBlock` on mismatch. |
|
| 3339 | - | unsafe fn bindArrayPatternElements( |
|
| 3340 | - | self: &mut FnLowerer, |
|
| 3448 | + | unsafe fn bindArrayPatternElements 'arena 'phase 'function ( |
|
| 3449 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3341 | 3450 | subject: &MatchSubject, |
|
| 3342 | 3451 | items: *[*ast::Node], |
|
| 3343 | 3452 | failBlock: BlockId |
|
| 3344 | - | ) throws (LowerError) { |
|
| 3453 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3345 | 3454 | let case resolver::Type::Array(arrInfo) = subject.type |
|
| 3346 | 3455 | else throw LowerError::ExpectedSliceOrArray; |
|
| 3347 | 3456 | ||
| 3348 | 3457 | let elemTy = *arrInfo.item; |
|
| 3349 | 3458 | let elemLayout = resolver::getTypeLayout(elemTy); |
| 3359 | 3468 | try bindFieldVariable(self, elem, base, fieldInfo, subject.by, failBlock); |
|
| 3360 | 3469 | } |
|
| 3361 | 3470 | } |
|
| 3362 | 3471 | ||
| 3363 | 3472 | /// Bind fields from a record pattern. |
|
| 3364 | - | unsafe fn bindRecordPatternFields(self: &mut FnLowerer, subject: &MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) { |
|
| 3473 | + | unsafe fn bindRecordPatternFields 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, subject: &MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3365 | 3474 | // No fields to bind (e.g., `{ .. }`). |
|
| 3366 | 3475 | if lit.fields.len == 0 { |
|
| 3367 | 3476 | return; |
|
| 3368 | 3477 | } |
|
| 3369 | 3478 | // Optional value patterns were already compared structurally by |
| 3399 | 3508 | try bindNestedRecordFields(self, payloadBase, lit, recInfo, subject.by, failBlock); |
|
| 3400 | 3509 | } |
|
| 3401 | 3510 | ||
| 3402 | 3511 | /// Bind a single record field to a pattern variable, with support for nested |
|
| 3403 | 3512 | /// pattern tests that branch to `failBlock` on mismatch. |
|
| 3404 | - | unsafe fn bindFieldVariable( |
|
| 3405 | - | self: &mut FnLowerer, |
|
| 3513 | + | unsafe fn bindFieldVariable 'arena 'phase 'function ( |
|
| 3514 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3406 | 3515 | binding: *ast::Node, |
|
| 3407 | 3516 | base: il::Reg, |
|
| 3408 | 3517 | fieldInfo: resolver::RecordField, |
|
| 3409 | 3518 | matchBy: resolver::MatchBy, |
|
| 3410 | 3519 | failBlock: BlockId |
|
| 3411 | - | ) throws (LowerError) { |
|
| 3520 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3412 | 3521 | match binding.value { |
|
| 3413 | 3522 | case ast::NodeValue::Ident(name) => { |
|
| 3414 | 3523 | let val = emitRead(self, base, fieldInfo.offset, fieldInfo.fieldType) |
|
| 3415 | 3524 | if matchBy == resolver::MatchBy::Value |
|
| 3416 | 3525 | else il::Val::Reg(emitPtrOffset(self, base, fieldInfo.offset)); |
| 3448 | 3557 | } |
|
| 3449 | 3558 | ||
| 3450 | 3559 | /// Emit a nested pattern test for a record field value, branching to |
|
| 3451 | 3560 | /// `failBlock` if the pattern does not match. On success, continues in |
|
| 3452 | 3561 | /// a fresh block and binds any nested variables. |
|
| 3453 | - | unsafe fn emitNestedFieldTest( |
|
| 3454 | - | self: &mut FnLowerer, |
|
| 3562 | + | unsafe fn emitNestedFieldTest 'arena 'phase 'function ( |
|
| 3563 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3455 | 3564 | pattern: *ast::Node, |
|
| 3456 | 3565 | base: il::Reg, |
|
| 3457 | 3566 | fieldInfo: resolver::RecordField, |
|
| 3458 | 3567 | matchBy: resolver::MatchBy, |
|
| 3459 | 3568 | failBlock: BlockId |
|
| 3460 | - | ) throws (LowerError) { |
|
| 3569 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3461 | 3570 | let mut fieldType = fieldInfo.fieldType; |
|
| 3462 | 3571 | let fieldPtr = emitPtrOffset(self, base, fieldInfo.offset); |
|
| 3463 | 3572 | ||
| 3464 | 3573 | // Auto-deref: when the field is a pointer and the pattern destructures |
|
| 3465 | 3574 | // the pointed-to value, load the pointer and use the target type. |
| 3507 | 3616 | let patterns = [pattern]; |
|
| 3508 | 3617 | try bindPatternVariables(self, &nestedSubject, &patterns[..], failBlock); |
|
| 3509 | 3618 | } |
|
| 3510 | 3619 | ||
| 3511 | 3620 | /// Bind variables from a nested record literal pattern. |
|
| 3512 | - | unsafe fn bindNestedRecordFields( |
|
| 3513 | - | self: &mut FnLowerer, |
|
| 3621 | + | unsafe fn bindNestedRecordFields 'arena 'phase 'function ( |
|
| 3622 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3514 | 3623 | base: il::Reg, |
|
| 3515 | 3624 | lit: ast::RecordLit, |
|
| 3516 | 3625 | recInfo: resolver::RecordType, |
|
| 3517 | 3626 | matchBy: resolver::MatchBy, |
|
| 3518 | 3627 | failBlock: BlockId |
|
| 3519 | - | ) throws (LowerError) { |
|
| 3628 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3520 | 3629 | for fieldNode in lit.fields { |
|
| 3521 | 3630 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value else { |
|
| 3522 | 3631 | throw LowerError::UnexpectedNodeValue(fieldNode); |
|
| 3523 | 3632 | }; |
|
| 3524 | 3633 | let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode) |
| 3531 | 3640 | try bindFieldVariable(self, field.value, base, fieldInfo, matchBy, failBlock); |
|
| 3532 | 3641 | } |
|
| 3533 | 3642 | } |
|
| 3534 | 3643 | ||
| 3535 | 3644 | /// Lower function body to a list of basic blocks. |
|
| 3536 | - | unsafe fn lowerFnBody(self: &mut FnLowerer, body: *ast::Node) -> *unsafe [il::Block] throws (LowerError) { |
|
| 3645 | + | unsafe fn lowerFnBody 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, body: *ast::Node) -> *unsafe [il::Block] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3537 | 3646 | // Create and switch to entry block. |
|
| 3538 | 3647 | let entry = try createBlock(self, "entry"); |
|
| 3539 | 3648 | set self.entryBlock = entry; |
|
| 3540 | 3649 | switchToBlock(self, entry); |
|
| 3541 | 3650 | ||
| 3542 | 3651 | /// Bind parameter registers to variables in the entry block. |
|
| 3543 | - | for def in self.params { |
|
| 3652 | + | for i in 0..self.paramsLen { |
|
| 3653 | + | let def = self.params[i]; |
|
| 3544 | 3654 | defVar(self, def.var, il::Val::Reg(def.reg)); |
|
| 3545 | 3655 | } |
|
| 3546 | 3656 | /// Lower function body. |
|
| 3547 | 3657 | try lowerBlock(self, body); |
|
| 3548 | 3658 |
| 3566 | 3676 | } |
|
| 3567 | 3677 | return try finalizeBlocks(self); |
|
| 3568 | 3678 | } |
|
| 3569 | 3679 | ||
| 3570 | 3680 | /// Lower a scalar match as a switch instruction. |
|
| 3571 | - | unsafe fn lowerMatchSwitch(self: &mut FnLowerer, prongs: *[*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) { |
|
| 3681 | + | unsafe fn lowerMatchSwitch 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, prongs: *[*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3572 | 3682 | let mut blocks: [BlockId; 32] = undefined; |
|
| 3573 | 3683 | let mut cases: *unsafe mut [il::SwitchCase] = &mut []; |
|
| 3574 | 3684 | let mut defaultIdx: u32 = 0; |
|
| 3575 | 3685 | let entry = currentBlock(self); |
|
| 3576 | 3686 |
| 3591 | 3701 | ||
| 3592 | 3702 | cases.append(il::SwitchCase { |
|
| 3593 | 3703 | value: constToScalar(cv), |
|
| 3594 | 3704 | target: *blocks[i], |
|
| 3595 | 3705 | args: &mut [] |
|
| 3596 | - | }, self.allocator); |
|
| 3706 | + | }, alloc::arenaAllocator(self.arena)); |
|
| 3597 | 3707 | } |
|
| 3598 | 3708 | } |
|
| 3599 | 3709 | } |
|
| 3600 | 3710 | addPredecessor(self, blocks[i], entry); |
|
| 3601 | 3711 | } |
| 3669 | 3779 | /// arm#1: |
|
| 3670 | 3780 | /// jmp else#0; // guard failed, fallthrough to `else` |
|
| 3671 | 3781 | /// else#0: |
|
| 3672 | 3782 | /// ret 0; // `else` body |
|
| 3673 | 3783 | /// |
|
| 3674 | - | unsafe fn lowerMatch(self: &mut FnLowerer, node: *ast::Node, m: ast::Match) throws (LowerError) { |
|
| 3784 | + | unsafe fn lowerMatch 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, m: ast::Match) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3675 | 3785 | assert m.prongs.len > 0; |
|
| 3676 | 3786 | ||
| 3677 | 3787 | let prongs = m.prongs; |
|
| 3678 | 3788 | // Lower the subject expression once; reused across all arms. |
|
| 3679 | 3789 | let subject = try lowerMatchSubject(self, m.subject); |
| 3773 | 3883 | try switchToAndSeal(self, blk); |
|
| 3774 | 3884 | } |
|
| 3775 | 3885 | } |
|
| 3776 | 3886 | ||
| 3777 | 3887 | /// Lower an `if let` statement. |
|
| 3778 | - | unsafe fn lowerIfLet(self: &mut FnLowerer, cond: ast::IfLet) throws (LowerError) { |
|
| 3888 | + | unsafe fn lowerIfLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, cond: ast::IfLet) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3779 | 3889 | let savedVarsLen = enterVarScope(self); |
|
| 3780 | 3890 | let subject = try lowerMatchSubject(self, cond.pattern.scrutinee); |
|
| 3781 | 3891 | let mut thenBlock: BlockId = undefined; |
|
| 3782 | 3892 | if cond.pattern.guard == nil { |
|
| 3783 | 3893 | set thenBlock = try createBlock(self, "then"); |
| 3811 | 3921 | /// Emit pattern match branch with optional guard, and bind variables. |
|
| 3812 | 3922 | /// Used by `if-let`, `let-else`, and `while-let` lowering. |
|
| 3813 | 3923 | /// |
|
| 3814 | 3924 | /// When a guard is present, the guard block is created before `successBlock` |
|
| 3815 | 3925 | /// to ensure block indices are in RPO. |
|
| 3816 | - | unsafe fn lowerPatternMatch( |
|
| 3817 | - | self: &mut FnLowerer, |
|
| 3926 | + | unsafe fn lowerPatternMatch 'arena 'phase 'function ( |
|
| 3927 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3818 | 3928 | subject: &MatchSubject, |
|
| 3819 | 3929 | pat: &ast::PatternMatch, |
|
| 3820 | 3930 | successBlock: &mut BlockId, |
|
| 3821 | 3931 | successLabel: *[u8], |
|
| 3822 | 3932 | failBlock: BlockId |
|
| 3823 | - | ) throws (LowerError) { |
|
| 3933 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3824 | 3934 | // If guard present, pattern match jumps to @guard, then guard evaluation |
|
| 3825 | 3935 | // jumps to `successBlock` or `failBlock`. Otherwise, jump directly to |
|
| 3826 | 3936 | // `successBlock`. |
|
| 3827 | 3937 | let mut targetBlock: BlockId = undefined; |
|
| 3828 | 3938 | if pat.guard <> nil { |
| 3863 | 3973 | try switchToAndSeal(self, *successBlock); |
|
| 3864 | 3974 | } |
|
| 3865 | 3975 | } |
|
| 3866 | 3976 | ||
| 3867 | 3977 | /// Lower a `let-else` statement. |
|
| 3868 | - | unsafe fn lowerLetElse(self: &mut FnLowerer, letElse: ast::LetElse) throws (LowerError) { |
|
| 3978 | + | unsafe fn lowerLetElse 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, letElse: ast::LetElse) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3869 | 3979 | let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee); |
|
| 3870 | 3980 | let mut successBlock: BlockId = undefined; |
|
| 3871 | 3981 | if letElse.pattern.guard == nil { |
|
| 3872 | 3982 | set successBlock = try createBlock(self, "success"); |
|
| 3873 | 3983 | } |
| 3904 | 4014 | // Continue at @merge after a successful match or value-producing fallback. |
|
| 3905 | 4015 | try switchToAndSeal(self, mergeBlock); |
|
| 3906 | 4016 | } |
|
| 3907 | 4017 | ||
| 3908 | 4018 | /// Lower a `while let` loop as a match-driven loop. |
|
| 3909 | - | unsafe fn lowerWhileLet(self: &mut FnLowerer, w: ast::WhileLet) throws (LowerError) { |
|
| 4019 | + | unsafe fn lowerWhileLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, w: ast::WhileLet) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3910 | 4020 | let savedVarsLen = enterVarScope(self); |
|
| 3911 | 4021 | // Create control flow blocks: loop header, body (created lazily when |
|
| 3912 | 4022 | // there's a guard), and exit. |
|
| 3913 | 4023 | let whileBlock = try createBlock(self, "while"); |
|
| 3914 | 4024 | let mut bodyBlock: BlockId = undefined; |
| 3937 | 4047 | /////////////////// |
|
| 3938 | 4048 | // Node Lowering // |
|
| 3939 | 4049 | /////////////////// |
|
| 3940 | 4050 | ||
| 3941 | 4051 | /// Lower an AST node. |
|
| 3942 | - | unsafe fn lowerNode(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 4052 | + | unsafe fn lowerNode 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3943 | 4053 | if self.low.options.debug { |
|
| 3944 | 4054 | set self.srcLoc.offset = node.span.offset; |
|
| 3945 | 4055 | } |
|
| 3946 | 4056 | match node.value { |
|
| 4057 | + | case ast::NodeValue::RegionBlock { bindings, body, .. } => { |
|
| 4058 | + | let savedVarsLen = enterVarScope(self); |
|
| 4059 | + | let values = try allocVals(self, bindings.len); |
|
| 4060 | + | for bindingNode, i in bindings { |
|
| 4061 | + | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
|
| 4062 | + | else throw LowerError::ExpectedIdentifier; |
|
| 4063 | + | set values[i] = try lowerExpr(self, binding.value); |
|
| 4064 | + | if blockHasTerminator(self) { |
|
| 4065 | + | exitVarScope(self, savedVarsLen); |
|
| 4066 | + | return; |
|
| 4067 | + | } |
|
| 4068 | + | } |
|
| 4069 | + | for bindingNode, i in bindings { |
|
| 4070 | + | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
|
| 4071 | + | else throw LowerError::ExpectedIdentifier; |
|
| 4072 | + | try bindLetValue(self, bindingNode, ast::borrowBinding(binding), values[i]); |
|
| 4073 | + | } |
|
| 4074 | + | try lowerBlock(self, body); |
|
| 4075 | + | exitVarScope(self, savedVarsLen); |
|
| 4076 | + | } |
|
| 3947 | 4077 | case ast::NodeValue::Block(_) => { |
|
| 3948 | 4078 | try lowerBlock(self, node); |
|
| 3949 | 4079 | } |
|
| 3950 | 4080 | case ast::NodeValue::Return { value } => { |
|
| 3951 | 4081 | try lowerReturnStmt(self, node, value); |
| 4027 | 4157 | } |
|
| 4028 | 4158 | } |
|
| 4029 | 4159 | } |
|
| 4030 | 4160 | ||
| 4031 | 4161 | /// Lower a code block. |
|
| 4032 | - | unsafe fn lowerBlock(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 4162 | + | unsafe fn lowerBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4033 | 4163 | let case ast::NodeValue::Block(blk) = node.value else { |
|
| 4034 | 4164 | throw LowerError::ExpectedBlock(node); |
|
| 4035 | 4165 | }; |
|
| 4036 | 4166 | let savedVarsLen = enterVarScope(self); |
|
| 4037 | 4167 | for stmt in blk.statements { |
| 4068 | 4198 | ||
| 4069 | 4199 | /// Return the effective type of a node after any coercion applied by |
|
| 4070 | 4200 | /// the resolver. `lowerExpr` already materializes the coercion in the |
|
| 4071 | 4201 | /// IL value, so the lowerer must use the post-coercion type when |
|
| 4072 | 4202 | /// choosing how to compare or store that value. |
|
| 4073 | - | unsafe fn effectiveType(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) { |
|
| 4203 | + | unsafe fn effectiveType 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> resolver::Type throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4074 | 4204 | let ty = try typeOf(self, node); |
|
| 4075 | 4205 | if let coerce = resolver::coercionFor(self.low.resolver, node) { |
|
| 4076 | 4206 | if let case resolver::Coercion::OptionalLift(optTy) = coerce { |
|
| 4077 | 4207 | return optTy; |
|
| 4078 | 4208 | } |
| 4082 | 4212 | ||
| 4083 | 4213 | /// Check if a resolver type lowers to an aggregate in memory. |
|
| 4084 | 4214 | unsafe fn isAggregateType(typ: resolver::Type) -> bool { |
|
| 4085 | 4215 | match typ { |
|
| 4086 | 4216 | case resolver::Type::Slice { .. }, |
|
| 4087 | - | resolver::Type::TraitObject { .. } => return true, |
|
| 4217 | + | resolver::Type::TraitObject { .. }, |
|
| 4218 | + | resolver::Type::Session(_) => return true, |
|
| 4088 | 4219 | case resolver::Type::Optional(resolver::Type::Pointer { .. }) => { |
|
| 4089 | 4220 | // Optional pointers are scalar due to NPO. |
|
| 4090 | 4221 | return false; |
|
| 4091 | 4222 | } |
|
| 4092 | 4223 | case resolver::Type::Optional(_) => { |
| 4130 | 4261 | } |
|
| 4131 | 4262 | ||
| 4132 | 4263 | /// Check if a node is a void union variant literal (e.g. `Color::Red`). |
|
| 4133 | 4264 | /// If so, returns the variant's tag index. This enables optimized comparisons |
|
| 4134 | 4265 | /// that only check the tag instead of doing full aggregate comparison. |
|
| 4135 | - | unsafe fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 { |
|
| 4266 | + | unsafe fn voidVariantIndex 'arena (res: &resolver::Resolver 'arena, node: *ast::Node) -> ?i64 { |
|
| 4136 | 4267 | let data = resolver::nodeData(res, node); |
|
| 4137 | 4268 | // Optional equality checks both the presence tag and the union value. |
|
| 4138 | 4269 | if let case resolver::Coercion::OptionalLift(_) = data.coercion { |
|
| 4139 | 4270 | return nil; |
|
| 4140 | 4271 | } |
| 4150 | 4281 | } |
|
| 4151 | 4282 | return index as i64; |
|
| 4152 | 4283 | } |
|
| 4153 | 4284 | ||
| 4154 | 4285 | /// Reserve stack storage for a value of the given type. |
|
| 4155 | - | unsafe fn emitReserve(self: &mut FnLowerer, typ: resolver::Type) -> il::Reg throws (LowerError) { |
|
| 4286 | + | unsafe fn emitReserve 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, typ: resolver::Type) -> il::Reg throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4156 | 4287 | let layout = resolver::getTypeLayout(typ); |
|
| 4157 | 4288 | return emitReserveLayout(self, layout); |
|
| 4158 | 4289 | } |
|
| 4159 | 4290 | ||
| 4160 | 4291 | /// Reserve stack storage with an explicit layout. |
|
| 4161 | - | unsafe fn emitReserveLayout(self: &mut FnLowerer, layout: resolver::Layout) -> il::Reg { |
|
| 4292 | + | unsafe fn emitReserveLayout 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, layout: resolver::Layout) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4162 | 4293 | let dst = nextReg(self); |
|
| 4163 | 4294 | ||
| 4164 | 4295 | emit(self, il::Instr::Reserve { |
|
| 4165 | 4296 | dst, |
|
| 4166 | 4297 | size: il::Val::Imm(layout.size as i64), |
| 4168 | 4299 | }); |
|
| 4169 | 4300 | return dst; |
|
| 4170 | 4301 | } |
|
| 4171 | 4302 | ||
| 4172 | 4303 | /// Store a value into an address. |
|
| 4173 | - | unsafe fn emitStore(self: &mut FnLowerer, base: il::Reg, offset: i32, typ: resolver::Type, src: il::Val) throws (LowerError) { |
|
| 4304 | + | unsafe fn emitStore 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, offset: i32, typ: resolver::Type, src: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4174 | 4305 | // `undefined` values need no store. |
|
| 4175 | 4306 | if let case il::Val::Undef = src { |
|
| 4176 | 4307 | return; |
|
| 4177 | 4308 | } |
|
| 4178 | 4309 | if isAggregateType(typ) { |
| 4190 | 4321 | }); |
|
| 4191 | 4322 | } |
|
| 4192 | 4323 | } |
|
| 4193 | 4324 | ||
| 4194 | 4325 | /// Allocate stack space for a value and store it. Returns a pointer to the value. |
|
| 4195 | - | unsafe fn emitStackVal(self: &mut FnLowerer, typ: resolver::Type, val: il::Val) -> il::Val throws (LowerError) { |
|
| 4326 | + | unsafe fn emitStackVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, typ: resolver::Type, val: il::Val) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4196 | 4327 | let ptr = try emitReserve(self, typ); |
|
| 4197 | 4328 | try emitStore(self, ptr, 0, typ, val); |
|
| 4198 | 4329 | return il::Val::Reg(ptr); |
|
| 4199 | 4330 | } |
|
| 4200 | 4331 | ||
| 4201 | 4332 | /// Generic helper to build any tagged aggregate. |
|
| 4202 | 4333 | /// Reserves space based on the provided layout, stores the tag, and optionally |
|
| 4203 | 4334 | /// stores the payload value at `valOffset`. |
|
| 4204 | - | unsafe fn buildTagged( |
|
| 4205 | - | self: &mut FnLowerer, |
|
| 4335 | + | unsafe fn buildTagged 'arena 'phase 'function ( |
|
| 4336 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4206 | 4337 | layout: resolver::Layout, |
|
| 4207 | 4338 | tag: i64, |
|
| 4208 | 4339 | payload: ?il::Val, |
|
| 4209 | 4340 | payloadType: resolver::Type, |
|
| 4210 | 4341 | tagSize: u32, |
|
| 4211 | 4342 | valOffset: i32 |
|
| 4212 | - | ) -> il::Val throws (LowerError) { |
|
| 4343 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4213 | 4344 | let dst = nextReg(self); |
|
| 4214 | 4345 | emit(self, il::Instr::Reserve { |
|
| 4215 | 4346 | dst, |
|
| 4216 | 4347 | size: il::Val::Imm(layout.size as i64), |
|
| 4217 | 4348 | alignment: layout.alignment, |
| 4233 | 4364 | /// Wrap a value in an optional type. |
|
| 4234 | 4365 | /// |
|
| 4235 | 4366 | /// For optional pointers (`?*T`), the value is returned as-is since pointers |
|
| 4236 | 4367 | /// use zero to represent `nil`. For other optionals, builds a tagged aggregate. |
|
| 4237 | 4368 | /// with the tag set to `1`, and the value as payload. |
|
| 4238 | - | unsafe fn wrapInOptional(self: &mut FnLowerer, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4369 | + | unsafe fn wrapInOptional 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4239 | 4370 | let case resolver::Type::Optional(inner) = optType else { |
|
| 4240 | 4371 | throw LowerError::ExpectedOptional; |
|
| 4241 | 4372 | }; |
|
| 4242 | 4373 | // Null-pointer-optimized (NPO) types are used as-is -- valid values are never null. |
|
| 4243 | 4374 | if resolver::isNullableType(*inner) { |
| 4251 | 4382 | ||
| 4252 | 4383 | /// Build a `nil` value for an optional type. |
|
| 4253 | 4384 | /// |
|
| 4254 | 4385 | /// For optional pointers (`?*T`), returns an immediate `0` (null pointer). |
|
| 4255 | 4386 | /// For other optionals, builds a tagged aggregate with tag set to `0` (absent). |
|
| 4256 | - | unsafe fn buildNilOptional(self: &mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4387 | + | unsafe fn buildNilOptional 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, optType: resolver::Type) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4257 | 4388 | let case resolver::Type::Optional(inner) = optType |
|
| 4258 | 4389 | else throw LowerError::ExpectedOptional; |
|
| 4259 | 4390 | if let case resolver::Type::Pointer { .. } = *inner { |
|
| 4260 | 4391 | return il::Val::Imm(0); |
|
| 4261 | 4392 | } |
| 4267 | 4398 | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4268 | 4399 | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4269 | 4400 | } |
|
| 4270 | 4401 | ||
| 4271 | 4402 | /// Build a result value for throwing functions. |
|
| 4272 | - | unsafe fn buildResult( |
|
| 4273 | - | self: &mut FnLowerer, |
|
| 4403 | + | unsafe fn buildResult 'arena 'phase 'function ( |
|
| 4404 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4274 | 4405 | tag: i64, |
|
| 4275 | 4406 | payload: ?il::Val, |
|
| 4276 | 4407 | payloadType: resolver::Type |
|
| 4277 | - | ) -> il::Val throws (LowerError) { |
|
| 4408 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4278 | 4409 | let successType = *self.fnType.returnType; |
|
| 4279 | 4410 | let layout = resolver::getResultLayout( |
|
| 4280 | 4411 | successType, self.fnType.throwList |
|
| 4281 | 4412 | ); |
|
| 4282 | 4413 | return try buildTagged(self, layout, tag, payload, payloadType, resolver::PTR_SIZE as i32, RESULT_VAL_OFFSET); |
|
| 4283 | 4414 | } |
|
| 4284 | 4415 | ||
| 4285 | 4416 | /// Build a slice aggregate from a data pointer, length and capacity. |
|
| 4286 | - | unsafe fn buildSliceValue( |
|
| 4287 | - | self: &mut FnLowerer, |
|
| 4417 | + | unsafe fn buildSliceValue 'arena 'phase 'function ( |
|
| 4418 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4288 | 4419 | elemTy: *resolver::Type, |
|
| 4289 | 4420 | mutable: bool, |
|
| 4290 | 4421 | ptrVal: il::Val, |
|
| 4291 | 4422 | lenVal: il::Val, |
|
| 4292 | 4423 | capVal: il::Val |
|
| 4293 | - | ) -> il::Val throws (LowerError) { |
|
| 4424 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4294 | 4425 | let sliceType = resolver::Type::Slice { |
|
| 4295 | 4426 | class: types::PointerClass::Unsafe, |
|
| 4296 | 4427 | item: elemTy, |
|
| 4297 | 4428 | mutable, |
|
| 4298 | 4429 | }; |
| 4309 | 4440 | ||
| 4310 | 4441 | return il::Val::Reg(dst); |
|
| 4311 | 4442 | } |
|
| 4312 | 4443 | ||
| 4313 | 4444 | /// Build a trait object fat pointer from a data pointer and a v-table. |
|
| 4314 | - | unsafe fn buildTraitObject( |
|
| 4315 | - | self: &mut FnLowerer, |
|
| 4445 | + | unsafe fn buildTraitObject 'arena 'phase 'function ( |
|
| 4446 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4316 | 4447 | dataVal: il::Val, |
|
| 4317 | 4448 | traitInfo: *unsafe resolver::TraitType, |
|
| 4318 | 4449 | inst: &resolver::InstanceEntry |
|
| 4319 | - | ) -> il::Val throws (LowerError) { |
|
| 4450 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4320 | 4451 | let vName = vtableName(self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name); |
|
| 4321 | 4452 | ||
| 4322 | 4453 | // Reserve space for the trait object on the stack. |
|
| 4323 | 4454 | let slot = emitReserveLayout(self, resolver::Layout { |
|
| 4324 | 4455 | size: resolver::PTR_SIZE * 2, |
| 4342 | 4473 | }); |
|
| 4343 | 4474 | return il::Val::Reg(slot); |
|
| 4344 | 4475 | } |
|
| 4345 | 4476 | ||
| 4346 | 4477 | /// Compute a field pointer by adding a byte offset to a base address. |
|
| 4347 | - | unsafe fn emitPtrOffset(self: &mut FnLowerer, base: il::Reg, offset: i32) -> il::Reg { |
|
| 4478 | + | unsafe fn emitPtrOffset 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, offset: i32) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4348 | 4479 | if offset == 0 { |
|
| 4349 | 4480 | return base; |
|
| 4350 | 4481 | } |
|
| 4351 | 4482 | let dst = nextReg(self); |
|
| 4352 | 4483 |
| 4360 | 4491 | return dst; |
|
| 4361 | 4492 | } |
|
| 4362 | 4493 | ||
| 4363 | 4494 | /// Emit an element address computation for array/slice indexing. |
|
| 4364 | 4495 | /// Computes: `base + idx * stride`. |
|
| 4365 | - | unsafe fn emitElem(self: &mut FnLowerer, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg { |
|
| 4496 | + | unsafe fn emitElem 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4366 | 4497 | // If index is zero, return base directly. |
|
| 4367 | 4498 | if idx == il::Val::Imm(0) { |
|
| 4368 | 4499 | return base; |
|
| 4369 | 4500 | } |
|
| 4370 | 4501 | // If stride is `1`, skip the multiply. |
| 4401 | 4532 | }); |
|
| 4402 | 4533 | return dst; |
|
| 4403 | 4534 | } |
|
| 4404 | 4535 | ||
| 4405 | 4536 | /// Emit a typed binary operation, returning the result as a value. |
|
| 4406 | - | unsafe fn emitTypedBinOp(self: &mut FnLowerer, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val { |
|
| 4537 | + | unsafe fn emitTypedBinOp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 4407 | 4538 | let dst = nextReg(self); |
|
| 4408 | 4539 | emit(self, il::Instr::BinOp { op, typ, dst, a, b }); |
|
| 4409 | 4540 | return il::Val::Reg(dst); |
|
| 4410 | 4541 | } |
|
| 4411 | 4542 | ||
| 4412 | 4543 | /// Emit a tag comparison for void variant equality/inequality. |
|
| 4413 | - | unsafe fn emitTagCmp(self: &mut FnLowerer, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val |
|
| 4414 | - | throws (LowerError) |
|
| 4544 | + | unsafe fn emitTagCmp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val |
|
| 4545 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 4415 | 4546 | { |
|
| 4416 | 4547 | let reg = emitValToReg(self, val); |
|
| 4417 | 4548 | ||
| 4418 | 4549 | // For all-void unions, the value *is* the tag, not a pointer. |
|
| 4419 | 4550 | let mut tag: il::Val = undefined; |
| 4425 | 4556 | let binOp = il::BinOp::Eq if op == ast::BinaryOp::Eq else il::BinOp::Ne; |
|
| 4426 | 4557 | return emitTypedBinOp(self, binOp, il::Type::W8, tag, il::Val::Imm(tagIdx)); |
|
| 4427 | 4558 | } |
|
| 4428 | 4559 | ||
| 4429 | 4560 | /// Logical "and" between two values. Returns the result in a register. |
|
| 4430 | - | unsafe fn emitLogicalAnd(self: &mut FnLowerer, left: ?il::Val, right: il::Val) -> il::Val { |
|
| 4561 | + | unsafe fn emitLogicalAnd 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, left: ?il::Val, right: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 4431 | 4562 | let prev = left else { |
|
| 4432 | 4563 | return right; |
|
| 4433 | 4564 | }; |
|
| 4434 | 4565 | return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, prev, right); |
|
| 4435 | 4566 | } |
| 4437 | 4568 | ////////////////////////// |
|
| 4438 | 4569 | // Aggregate Comparison // |
|
| 4439 | 4570 | ////////////////////////// |
|
| 4440 | 4571 | ||
| 4441 | 4572 | /// Emit an equality test for values at an offset of the given base registers. |
|
| 4442 | - | unsafe fn emitEqAtOffset( |
|
| 4443 | - | self: &mut FnLowerer, |
|
| 4573 | + | unsafe fn emitEqAtOffset 'arena 'phase 'function ( |
|
| 4574 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4444 | 4575 | left: il::Reg, |
|
| 4445 | 4576 | right: il::Reg, |
|
| 4446 | 4577 | offset: i32, |
|
| 4447 | 4578 | fieldType: resolver::Type |
|
| 4448 | - | ) -> il::Val throws (LowerError) { |
|
| 4579 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4449 | 4580 | // For aggregate types, pass offset through and compare recursively. |
|
| 4450 | 4581 | if isAggregateType(fieldType) { |
|
| 4451 | 4582 | return try lowerAggregateEq(self, fieldType, left, right, offset); |
|
| 4452 | 4583 | } |
|
| 4453 | 4584 | // For scalar types, load and compare directly. |
| 4458 | 4589 | ||
| 4459 | 4590 | return il::Val::Reg(dst); |
|
| 4460 | 4591 | } |
|
| 4461 | 4592 | ||
| 4462 | 4593 | /// Compare two record values for equality. |
|
| 4463 | - | unsafe fn lowerRecordEq( |
|
| 4464 | - | self: &mut FnLowerer, |
|
| 4594 | + | unsafe fn lowerRecordEq 'arena 'phase 'function ( |
|
| 4595 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4465 | 4596 | recInfo: resolver::RecordType, |
|
| 4466 | 4597 | a: il::Reg, |
|
| 4467 | 4598 | b: il::Reg, |
|
| 4468 | 4599 | offset: i32 |
|
| 4469 | - | ) -> il::Val throws (LowerError) { |
|
| 4600 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4470 | 4601 | let mut result: ?il::Val = nil; |
|
| 4471 | 4602 | ||
| 4472 | 4603 | for field in recInfo.fields { |
|
| 4473 | 4604 | let cmp = try emitEqAtOffset(self, a, b, offset + field.offset, field.fieldType); |
|
| 4474 | 4605 |
| 4479 | 4610 | } |
|
| 4480 | 4611 | return il::Val::Imm(1); |
|
| 4481 | 4612 | } |
|
| 4482 | 4613 | ||
| 4483 | 4614 | /// Compare two slice values for equality. |
|
| 4484 | - | unsafe fn lowerSliceEq( |
|
| 4485 | - | self: &mut FnLowerer, |
|
| 4615 | + | unsafe fn lowerSliceEq 'arena 'phase 'function ( |
|
| 4616 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4486 | 4617 | elemTy: *resolver::Type, |
|
| 4487 | 4618 | mutable: bool, |
|
| 4488 | 4619 | a: il::Reg, |
|
| 4489 | 4620 | b: il::Reg, |
|
| 4490 | 4621 | offset: i32 |
|
| 4491 | - | ) -> il::Val throws (LowerError) { |
|
| 4622 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4492 | 4623 | let ptrTy = resolver::Type::Pointer { |
|
| 4493 | 4624 | class: types::PointerClass::Unsafe, |
|
| 4494 | 4625 | target: elemTy, |
|
| 4495 | 4626 | mutable, |
|
| 4496 | 4627 | }; |
| 4509 | 4640 | /// branchless formulation: `tagEq AND (tagNil OR payloadEq)` |
|
| 4510 | 4641 | /// |
|
| 4511 | 4642 | /// For inner types that may contain uninitialized data when `nil` (unions, |
|
| 4512 | 4643 | /// nested optionals), the payload comparison is guarded behind a branch |
|
| 4513 | 4644 | /// so that `nil` payloads are never inspected. |
|
| 4514 | - | unsafe fn lowerOptionalEq( |
|
| 4515 | - | self: &mut FnLowerer, |
|
| 4645 | + | unsafe fn lowerOptionalEq 'arena 'phase 'function ( |
|
| 4646 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4516 | 4647 | inner: resolver::Type, |
|
| 4517 | 4648 | a: il::Reg, |
|
| 4518 | 4649 | b: il::Reg, |
|
| 4519 | 4650 | offset: i32 |
|
| 4520 | - | ) -> il::Val throws (LowerError) { |
|
| 4651 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4521 | 4652 | let valOffset = resolver::getOptionalValOffset(inner) as i32; |
|
| 4522 | 4653 | ||
| 4523 | 4654 | // Load tags. |
|
| 4524 | 4655 | let tagA = loadTag(self, a, offset + TVAL_TAG_OFFSET, il::Type::W8); |
|
| 4525 | 4656 | let tagB = loadTag(self, b, offset + TVAL_TAG_OFFSET, il::Type::W8); |
| 4601 | 4732 | /// For all-void unions, we skip the control flow entirely and just compare |
|
| 4602 | 4733 | /// the tags directly. |
|
| 4603 | 4734 | /// |
|
| 4604 | 4735 | /// TODO: Could be optimized to branchless when all non-void variants share |
|
| 4605 | 4736 | /// the same payload type: `tagEq AND (isVoidVariant OR payloadEq)`. |
|
| 4606 | - | unsafe fn lowerUnionEq( |
|
| 4607 | - | self: &mut FnLowerer, |
|
| 4737 | + | unsafe fn lowerUnionEq 'arena 'phase 'function ( |
|
| 4738 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4608 | 4739 | unionInfo: resolver::UnionType, |
|
| 4609 | 4740 | a: il::Reg, |
|
| 4610 | 4741 | b: il::Reg, |
|
| 4611 | 4742 | offset: i32 |
|
| 4612 | - | ) -> il::Val throws (LowerError) { |
|
| 4743 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4613 | 4744 | // Compare tags. |
|
| 4614 | 4745 | let tagA = loadTag(self, a, offset + TVAL_TAG_OFFSET, il::Type::W8); |
|
| 4615 | 4746 | let tagB = loadTag(self, b, offset + TVAL_TAG_OFFSET, il::Type::W8); |
|
| 4616 | 4747 | ||
| 4617 | 4748 | // Fast path: all-void union just needs tag comparison. |
| 4645 | 4776 | ||
| 4646 | 4777 | // Create comparison blocks for each non-void variant and build switch cases. |
|
| 4647 | 4778 | // Void variants jump directly to merge with `true`. |
|
| 4648 | 4779 | let trueArgs = try allocVal(self, il::Val::Imm(1)); |
|
| 4649 | 4780 | let cases = try! alloc::allocRawSlice( |
|
| 4650 | - | self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32 |
|
| 4781 | + | self.arena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32 |
|
| 4651 | 4782 | ) as *unsafe mut [il::SwitchCase]; |
|
| 4652 | 4783 | ||
| 4653 | 4784 | let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined; |
|
| 4654 | 4785 | for variant, i in unionInfo.variants { |
|
| 4655 | 4786 | if variant.valueType == resolver::Type::Void { |
| 4708 | 4839 | try switchToAndSeal(self, mergeBlock); |
|
| 4709 | 4840 | return il::Val::Reg(resultReg); |
|
| 4710 | 4841 | } |
|
| 4711 | 4842 | ||
| 4712 | 4843 | /// Compare two array values for equality, element by element. |
|
| 4713 | - | unsafe fn lowerArrayEq( |
|
| 4714 | - | self: &mut FnLowerer, |
|
| 4844 | + | unsafe fn lowerArrayEq 'arena 'phase 'function ( |
|
| 4845 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4715 | 4846 | arr: resolver::ArrayType, |
|
| 4716 | 4847 | a: il::Reg, |
|
| 4717 | 4848 | b: il::Reg, |
|
| 4718 | 4849 | offset: i32 |
|
| 4719 | - | ) -> il::Val throws (LowerError) { |
|
| 4850 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4720 | 4851 | let elemLayout = resolver::getTypeLayout(*arr.item); |
|
| 4721 | 4852 | let stride = elemLayout.size as i32; |
|
| 4722 | 4853 | let mut result: ?il::Val = nil; |
|
| 4723 | 4854 | ||
| 4724 | 4855 | for i in 0..arr.length { |
| 4732 | 4863 | // Empty arrays are always equal. |
|
| 4733 | 4864 | return il::Val::Imm(1); |
|
| 4734 | 4865 | } |
|
| 4735 | 4866 | ||
| 4736 | 4867 | /// Compare two aggregate values for equality. |
|
| 4737 | - | unsafe fn lowerAggregateEq( |
|
| 4738 | - | self: &mut FnLowerer, |
|
| 4868 | + | unsafe fn lowerAggregateEq 'arena 'phase 'function ( |
|
| 4869 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4739 | 4870 | typ: resolver::Type, |
|
| 4740 | 4871 | a: il::Reg, |
|
| 4741 | 4872 | b: il::Reg, |
|
| 4742 | 4873 | offset: i32 |
|
| 4743 | - | ) -> il::Val throws (LowerError) { |
|
| 4874 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4744 | 4875 | match typ { |
|
| 4745 | 4876 | case resolver::Type::Slice { item, mutable, .. } => |
|
| 4746 | 4877 | return try lowerSliceEq(self, item, mutable, a, b, offset), |
|
| 4747 | 4878 | case resolver::Type::Optional(inner) => { |
|
| 4748 | 4879 | if let case resolver::Type::Slice { item, mutable, .. } = *inner { |
| 4766 | 4897 | } |
|
| 4767 | 4898 | } |
|
| 4768 | 4899 | ||
| 4769 | 4900 | /// Lower a record literal expression. Handles both plain records and union variant |
|
| 4770 | 4901 | /// record literals like `Union::Variant { field: value }`. |
|
| 4771 | - | unsafe fn lowerRecordLit(self: &mut FnLowerer, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) { |
|
| 4902 | + | unsafe fn lowerRecordLit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4772 | 4903 | let typ = try typeOf(self, node); |
|
| 4773 | 4904 | match typ { |
|
| 4774 | 4905 | case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => { |
|
| 4775 | 4906 | let dst = try emitReserve(self, typ); |
|
| 4776 | 4907 | try lowerRecordFields(self, dst, &recInfo, lit.fields, 0); |
| 4803 | 4934 | } |
|
| 4804 | 4935 | } |
|
| 4805 | 4936 | ||
| 4806 | 4937 | /// Lower fields of a record literal into a destination register. |
|
| 4807 | 4938 | /// The `offset` is added to each field's offset when storing. |
|
| 4808 | - | unsafe fn lowerRecordFields( |
|
| 4809 | - | self: &mut FnLowerer, |
|
| 4939 | + | unsafe fn lowerRecordFields 'arena 'phase 'function ( |
|
| 4940 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4810 | 4941 | dst: il::Reg, |
|
| 4811 | 4942 | recInfo: &resolver::RecordType, |
|
| 4812 | 4943 | fields: *[*ast::Node], |
|
| 4813 | 4944 | offset: i32 |
|
| 4814 | - | ) throws (LowerError) { |
|
| 4945 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4815 | 4946 | for fieldNode, i in fields { |
|
| 4816 | 4947 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value else { |
|
| 4817 | 4948 | throw LowerError::UnexpectedNodeValue(fieldNode); |
|
| 4818 | 4949 | }; |
|
| 4819 | 4950 | let mut fieldIdx: u32 = i; |
| 4833 | 4964 | } |
|
| 4834 | 4965 | } |
|
| 4835 | 4966 | } |
|
| 4836 | 4967 | ||
| 4837 | 4968 | /// Lower an unlabeled record constructor call. |
|
| 4838 | - | unsafe fn lowerRecordCtor(self: &mut FnLowerer, nominal: *unsafe resolver::NominalType, args: *[*ast::Node]) -> il::Val throws (LowerError) { |
|
| 4969 | + | unsafe fn lowerRecordCtor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, nominal: *unsafe resolver::NominalType, args: *[*ast::Node]) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4839 | 4970 | let case resolver::NominalType::Record(recInfo) = *nominal else { |
|
| 4840 | 4971 | throw LowerError::ExpectedRecord; |
|
| 4841 | 4972 | }; |
|
| 4842 | 4973 | let typ = resolver::Type::Nominal(nominal); |
|
| 4843 | 4974 | let dst = try emitReserve(self, typ); |
| 4852 | 4983 | } |
|
| 4853 | 4984 | return il::Val::Reg(dst); |
|
| 4854 | 4985 | } |
|
| 4855 | 4986 | ||
| 4856 | 4987 | /// Lower an array literal expression like `[1, 2, 3]`. |
|
| 4857 | - | unsafe fn lowerArrayLit(self: &mut FnLowerer, node: *ast::Node, elements: *[*ast::Node]) -> il::Val |
|
| 4858 | - | throws (LowerError) |
|
| 4988 | + | unsafe fn lowerArrayLit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, elements: *[*ast::Node]) -> il::Val |
|
| 4989 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 4859 | 4990 | { |
|
| 4860 | 4991 | let typ = try typeOf(self, node); |
|
| 4861 | 4992 | let case resolver::Type::Array(arrInfo) = typ else { |
|
| 4862 | 4993 | throw LowerError::ExpectedArray; |
|
| 4863 | 4994 | }; |
| 4875 | 5006 | } |
|
| 4876 | 5007 | ||
| 4877 | 5008 | /// Lower an array repeat literal expression like `[42; 3]`. |
|
| 4878 | 5009 | /// Unrolls the initialization at compile time. |
|
| 4879 | 5010 | // TODO: Beyond a certain length, lower this to a loop. |
|
| 4880 | - | unsafe fn lowerArrayRepeatLit(self: &mut FnLowerer, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val |
|
| 4881 | - | throws (LowerError) |
|
| 5011 | + | unsafe fn lowerArrayRepeatLit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val |
|
| 5012 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 4882 | 5013 | { |
|
| 4883 | 5014 | let typ = try typeOf(self, node); |
|
| 4884 | 5015 | let case resolver::Type::Array(arrInfo) = typ else { |
|
| 4885 | 5016 | throw LowerError::ExpectedArray; |
|
| 4886 | 5017 | }; |
| 4899 | 5030 | } |
|
| 4900 | 5031 | return il::Val::Reg(dst); |
|
| 4901 | 5032 | } |
|
| 4902 | 5033 | ||
| 4903 | 5034 | /// Lower a union constructor call like `Union::Variant(...)`. |
|
| 4904 | - | unsafe fn lowerUnionCtor(self: &mut FnLowerer, node: *ast::Node, sym: *unsafe mut resolver::Symbol, call: ast::Call) -> il::Val |
|
| 4905 | - | throws (LowerError) |
|
| 5035 | + | unsafe fn lowerUnionCtor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, sym: *unsafe mut resolver::Symbol, call: ast::Call) -> il::Val |
|
| 5036 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 4906 | 5037 | { |
|
| 4907 | 5038 | let unionTy = try typeOf(self, node); |
|
| 4908 | 5039 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else { |
|
| 4909 | 5040 | throw LowerError::ExpectedVariant; |
|
| 4910 | 5041 | }; |
| 4921 | 5052 | } |
|
| 4922 | 5053 | return try buildTagged(self, resolver::getTypeLayout(unionTy), index as i64, payloadVal, payloadType, 1, valOffset); |
|
| 4923 | 5054 | } |
|
| 4924 | 5055 | ||
| 4925 | 5056 | /// Lower a field access into a pointer to the field. |
|
| 4926 | - | unsafe fn lowerFieldRef(self: &mut FnLowerer, access: ast::Access) -> FieldRef throws (LowerError) { |
|
| 5057 | + | unsafe fn lowerFieldRef 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, access: ast::Access) -> FieldRef throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4927 | 5058 | let parentTy = try typeOf(self, access.parent); |
|
| 4928 | 5059 | let subjectTy = resolver::autoDeref(parentTy); |
|
| 4929 | 5060 | let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, access.child) else { |
|
| 4930 | 5061 | throw LowerError::MissingMetadata; |
|
| 4931 | 5062 | }; |
| 4941 | 5072 | fieldType: fieldInfo.fieldType, |
|
| 4942 | 5073 | }; |
|
| 4943 | 5074 | } |
|
| 4944 | 5075 | ||
| 4945 | 5076 | /// Lower a field access expression. |
|
| 4946 | - | unsafe fn lowerFieldAccess(self: &mut FnLowerer, access: ast::Access) -> il::Val throws (LowerError) { |
|
| 5077 | + | unsafe fn lowerFieldAccess 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, access: ast::Access) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4947 | 5078 | let fieldRef = try lowerFieldRef(self, access); |
|
| 4948 | 5079 | return emitRead(self, fieldRef.base, fieldRef.offset, fieldRef.fieldType); |
|
| 4949 | 5080 | } |
|
| 4950 | 5081 | ||
| 4951 | 5082 | /// Compute data pointer and element count for a range into a container. |
|
| 4952 | 5083 | /// Used by both slice range expressions (`&a[start..end]`) and slice |
|
| 4953 | 5084 | /// assignments (`a[start..end] = value`). |
|
| 4954 | - | unsafe fn resolveSliceRangePtr( |
|
| 4955 | - | self: &mut FnLowerer, |
|
| 5085 | + | unsafe fn resolveSliceRangePtr 'arena 'phase 'function ( |
|
| 5086 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 4956 | 5087 | container: *ast::Node, |
|
| 4957 | 5088 | range: ast::Range, |
|
| 4958 | 5089 | info: resolver::SliceRangeInfo |
|
| 4959 | - | ) -> SliceRangeResult throws (LowerError) { |
|
| 5090 | + | ) -> SliceRangeResult throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4960 | 5091 | let baseVal = try lowerExpr(self, container); |
|
| 4961 | 5092 | let baseReg = emitValToReg(self, baseVal); |
|
| 4962 | 5093 | ||
| 4963 | 5094 | // Extract data pointer and container length. |
|
| 4964 | 5095 | let mut dataReg = baseReg; |
| 5015 | 5146 | } |
|
| 5016 | 5147 | return SliceRangeResult { dataReg, count }; |
|
| 5017 | 5148 | } |
|
| 5018 | 5149 | ||
| 5019 | 5150 | /// Lower a slice range expression into a slice header value. |
|
| 5020 | - | unsafe fn lowerSliceRange( |
|
| 5021 | - | self: &mut FnLowerer, |
|
| 5151 | + | unsafe fn lowerSliceRange 'arena 'phase 'function ( |
|
| 5152 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 5022 | 5153 | container: *ast::Node, |
|
| 5023 | 5154 | range: ast::Range, |
|
| 5024 | 5155 | sliceNode: *ast::Node |
|
| 5025 | - | ) -> il::Val throws (LowerError) { |
|
| 5156 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5026 | 5157 | let info = resolver::sliceRangeInfoFor(self.low.resolver, sliceNode) else { |
|
| 5027 | 5158 | throw LowerError::MissingMetadata; |
|
| 5028 | 5159 | }; |
|
| 5029 | 5160 | let r = try resolveSliceRangePtr(self, container, range, info); |
|
| 5030 | 5161 | return try buildSliceValue( |
|
| 5031 | 5162 | self, info.itemType, info.mutable, il::Val::Reg(r.dataReg), r.count, r.count |
|
| 5032 | 5163 | ); |
|
| 5033 | 5164 | } |
|
| 5034 | 5165 | ||
| 5035 | 5166 | /// Lower an address-of (`&x`) expression. |
|
| 5036 | - | unsafe fn lowerAddressOf(self: &mut FnLowerer, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) { |
|
| 5167 | + | unsafe fn lowerAddressOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5037 | 5168 | // Handle subscript: `&ary[i]` or `&ary[start..end]`. |
|
| 5038 | 5169 | if let case ast::NodeValue::Subscript { container, index } = addr.target.value { |
|
| 5039 | 5170 | if let case ast::NodeValue::Range(range) = index.value { |
|
| 5040 | 5171 | return try lowerSliceRange(self, container, range, node); |
|
| 5041 | 5172 | } |
| 5048 | 5179 | let fieldRef = try lowerFieldRef(self, access); |
|
| 5049 | 5180 | let ptr = emitPtrOffset(self, fieldRef.base, fieldRef.offset); |
|
| 5050 | 5181 | ||
| 5051 | 5182 | return il::Val::Reg(ptr); |
|
| 5052 | 5183 | } |
|
| 5184 | + | // A qualified constant or static uses its resolved owner's data symbol. |
|
| 5185 | + | if let case ast::NodeValue::ScopeAccess(_) = addr.target.value { |
|
| 5186 | + | let sym = resolver::nodeData(self.low.resolver, addr.target).sym else { |
|
| 5187 | + | throw LowerError::MissingSymbol(addr.target); |
|
| 5188 | + | }; |
|
| 5189 | + | return il::Val::Reg(emitDataAddr(self, sym)); |
|
| 5190 | + | } |
|
| 5053 | 5191 | // Handle variable address: `&x` |
|
| 5054 | 5192 | if let case ast::NodeValue::Ident(_) = addr.target.value { |
|
| 5055 | 5193 | if let v = lookupLocalVar(self, addr.target) { |
|
| 5056 | 5194 | let val = try useVar(self, v); |
|
| 5057 | 5195 | let typ = try typeOf(self, addr.target); |
| 5099 | 5237 | } |
|
| 5100 | 5238 | throw LowerError::UnexpectedNodeValue(addr.target); |
|
| 5101 | 5239 | } |
|
| 5102 | 5240 | ||
| 5103 | 5241 | /// Lower an addressed array literal as a slice. |
|
| 5104 | - | unsafe fn lowerArrayLiteralSlice( |
|
| 5105 | - | self: &mut FnLowerer, |
|
| 5242 | + | unsafe fn lowerArrayLiteralSlice 'arena 'phase 'function ( |
|
| 5243 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 5106 | 5244 | sliceNode: *ast::Node, |
|
| 5107 | 5245 | arrayNode: *ast::Node |
|
| 5108 | - | ) -> il::Val throws (LowerError) { |
|
| 5246 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5109 | 5247 | let sliceTy = try typeOf(self, sliceNode); |
|
| 5110 | 5248 | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 5111 | 5249 | throw LowerError::UnexpectedType(sliceTy); |
|
| 5112 | 5250 | }; |
|
| 5113 | 5251 | let arrayTy = try typeOf(self, arrayNode); |
| 5120 | 5258 | self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0) |
|
| 5121 | 5259 | ); |
|
| 5122 | 5260 | } |
|
| 5123 | 5261 | if resolver::isConstExpr(self.low.resolver, arrayNode) { |
|
| 5124 | 5262 | let fnName = self.fnName; |
|
| 5125 | - | let mut b = dataBuilder(self.low.allocator); |
|
| 5263 | + | let mut b = dataBuilder(alloc::arenaAllocator(self.low.arena)); |
|
| 5126 | 5264 | match arrayNode.value { |
|
| 5127 | 5265 | case ast::NodeValue::ArrayLit(elements) => |
|
| 5128 | 5266 | try lowerConstArrayLitInto(self.low, elements, arrayTy, fnName, &mut b), |
|
| 5129 | 5267 | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 5130 | 5268 | try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, fnName, &mut b), |
| 5144 | 5282 | ||
| 5145 | 5283 | /// Lower the common element pointer computation for subscript operations. |
|
| 5146 | 5284 | /// Handles both arrays and slices by resolving the container type, extracting |
|
| 5147 | 5285 | /// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute |
|
| 5148 | 5286 | /// the element address. |
|
| 5149 | - | unsafe fn lowerElemPtr( |
|
| 5150 | - | self: &mut FnLowerer, container: *ast::Node, index: *ast::Node |
|
| 5151 | - | ) -> ElemPtrResult throws (LowerError) { |
|
| 5287 | + | unsafe fn lowerElemPtr 'arena 'phase 'function ( |
|
| 5288 | + | self: &mut FnLowerer 'arena 'phase 'function, container: *ast::Node, index: *ast::Node |
|
| 5289 | + | ) -> ElemPtrResult throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5152 | 5290 | let containerTy = try typeOf(self, container); |
|
| 5153 | 5291 | let subjectTy = resolver::autoDeref(containerTy); |
|
| 5154 | 5292 | let baseVal = try lowerExpr(self, container); |
|
| 5155 | 5293 | let indexVal = try lowerExpr(self, index); |
|
| 5156 | 5294 | let baseReg = emitValToReg(self, baseVal); |
| 5186 | 5324 | } |
|
| 5187 | 5325 | ||
| 5188 | 5326 | /// Lower a dereference expression. |
|
| 5189 | 5327 | /// Handles both pointer deref (`*ptr`) and record deref (`*r` on single-field |
|
| 5190 | 5328 | /// unlabeled record). Both read at offset 0 using the resolver-assigned type. |
|
| 5191 | - | unsafe fn lowerDeref(self: &mut FnLowerer, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) { |
|
| 5329 | + | unsafe fn lowerDeref 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5192 | 5330 | let type = try typeOf(self, node); |
|
| 5193 | 5331 | let ptrVal = try lowerExpr(self, target); |
|
| 5194 | 5332 | let ptrReg = emitValToReg(self, ptrVal); |
|
| 5195 | 5333 | ||
| 5196 | - | return emitRead(self, ptrReg, 0, type); |
|
| 5334 | + | let value = emitRead(self, ptrReg, 0, type); |
|
| 5335 | + | if let case resolver::Type::Cell { .. } = try typeOf(self, target); isAggregateType(type) { |
|
| 5336 | + | return try emitStackVal(self, type, value); |
|
| 5337 | + | } |
|
| 5338 | + | return value; |
|
| 5197 | 5339 | } |
|
| 5198 | 5340 | ||
| 5199 | 5341 | /// Lower a subscript expression. |
|
| 5200 | - | unsafe fn lowerSubscript(self: &mut FnLowerer, node: *ast::Node, container: *ast::Node, index: *ast::Node) -> il::Val |
|
| 5201 | - | throws (LowerError) |
|
| 5342 | + | unsafe fn lowerSubscript 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, container: *ast::Node, index: *ast::Node) -> il::Val |
|
| 5343 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 5202 | 5344 | { |
|
| 5203 | 5345 | if let case ast::NodeValue::Range(_) = index.value { |
|
| 5204 | 5346 | panic "lowerSubscript: range subscript must use address-of (&)"; |
|
| 5205 | 5347 | } |
|
| 5206 | 5348 | let result = try lowerElemPtr(self, container, index); |
|
| 5207 | 5349 | ||
| 5208 | 5350 | return emitRead(self, result.elemReg, 0, result.elemType); |
|
| 5209 | 5351 | } |
|
| 5210 | 5352 | ||
| 5211 | 5353 | /// Lower a let binding. |
|
| 5212 | - | unsafe fn lowerLet(self: &mut FnLowerer, node: *ast::Node, l: ast::Let) throws (LowerError) { |
|
| 5354 | + | unsafe fn lowerLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, l: ast::Let) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5213 | 5355 | // Evaluate value. |
|
| 5214 | 5356 | let val = try lowerExpr(self, l.value); |
|
| 5215 | 5357 | if blockHasTerminator(self) { |
|
| 5216 | 5358 | return; |
|
| 5217 | 5359 | } |
|
| 5360 | + | try bindLetValue(self, node, l, val); |
|
| 5361 | + | } |
|
| 5362 | + | ||
| 5363 | + | /// Bind an evaluated initializer in the current variable scope. |
|
| 5364 | + | unsafe fn bindLetValue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, l: ast::Let, val: il::Val) |
|
| 5365 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 5366 | + | { |
|
| 5218 | 5367 | // Handle placeholder pattern: `let _ = expr;` |
|
| 5219 | 5368 | if let case ast::NodeValue::Placeholder = l.ident.value { |
|
| 5220 | 5369 | return; |
|
| 5221 | 5370 | } |
|
| 5222 | 5371 | let case ast::NodeValue::Ident(name) = l.ident.value else { |
|
| 5223 | 5372 | throw LowerError::ExpectedIdentifier; |
|
| 5224 | 5373 | }; |
|
| 5225 | - | let typ = try typeOf(self, l.value); |
|
| 5374 | + | let typ = try typeOf(self, l.ident); |
|
| 5226 | 5375 | let ilType = ilType(self.low, typ); |
|
| 5227 | 5376 | let mut varVal = val; |
|
| 5228 | 5377 | ||
| 5229 | 5378 | // Aggregates with persistent storage need a local copy to avoid aliasing. |
|
| 5230 | 5379 | // Temporaries such as literals or call results can be adopted directly. |
| 5235 | 5384 | // Void variant literals (e.g. `Option::None`) use scope access syntax and |
|
| 5236 | 5385 | // are flagged as place expressions, but they are freshly constructed |
|
| 5237 | 5386 | // temporaries with no persistent storage. |
|
| 5238 | 5387 | if isAggregateType(typ) and |
|
| 5239 | 5388 | ast::isPlaceExpr(l.value) and |
|
| 5389 | + | not resolver::isCellDeref(self.low.resolver, l.value) and |
|
| 5240 | 5390 | voidVariantIndex(self.low.resolver, l.value) == nil { |
|
| 5241 | 5391 | set varVal = try emitStackVal(self, typ, val); |
|
| 5242 | 5392 | } |
|
| 5243 | 5393 | ||
| 5244 | 5394 | // If the resolver determined that this variable's address is taken |
| 5274 | 5424 | /// |
|
| 5275 | 5425 | /// @entry -> (true) @then ---> @end <--. |
|
| 5276 | 5426 | /// | ) |
|
| 5277 | 5427 | /// `---- (false) -------------------' |
|
| 5278 | 5428 | /// |
|
| 5279 | - | unsafe fn lowerIf(self: &mut FnLowerer, i: ast::If) throws (LowerError) { |
|
| 5429 | + | unsafe fn lowerIf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, i: ast::If) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5280 | 5430 | let thenBlock = try createBlock(self, "then"); |
|
| 5281 | 5431 | ||
| 5282 | 5432 | if let elseNode = i.elseBranch { // If-else case. |
|
| 5283 | 5433 | let elseBlock = try createBlock(self, "else"); |
|
| 5284 | 5434 | try emitCondBranch(self, i.condition, thenBlock, elseBlock); |
| 5338 | 5488 | } |
|
| 5339 | 5489 | ||
| 5340 | 5490 | /// Lower an assignment target that designates a memory location, and return |
|
| 5341 | 5491 | /// the address to store through. Returns `nil` without emitting anything for |
|
| 5342 | 5492 | /// targets that aren't memory-backed, such as locals tracked in SSA. |
|
| 5343 | - | unsafe fn lowerPlace(self: &mut FnLowerer, target: *ast::Node) -> ?FieldRef throws (LowerError) { |
|
| 5493 | + | unsafe fn lowerPlace 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: *ast::Node) -> ?FieldRef throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5344 | 5494 | match target.value { |
|
| 5345 | 5495 | case ast::NodeValue::ScopeAccess(_) => { |
|
| 5346 | 5496 | let sym = try symOf(self, target); |
|
| 5347 | 5497 | let case resolver::SymbolData::Value { type, .. } = sym.data else { |
|
| 5348 | 5498 | throw LowerError::ImmutableAssignment; |
| 5371 | 5521 | /// whether it was handled. Nothing is emitted when it isn't. |
|
| 5372 | 5522 | /// |
|
| 5373 | 5523 | /// Compound assignments share their target node with the left operand of the |
|
| 5374 | 5524 | /// desugared binary expression. Resolve that place once so side effects in a |
|
| 5375 | 5525 | /// dereference, field parent, or subscript index are not repeated for the store. |
|
| 5376 | - | unsafe fn lowerCompoundAssign( |
|
| 5377 | - | self: &mut FnLowerer, expr: *ast::Node, binop: ast::BinOp |
|
| 5378 | - | ) -> bool throws (LowerError) { |
|
| 5526 | + | unsafe fn lowerCompoundAssign 'arena 'phase 'function ( |
|
| 5527 | + | self: &mut FnLowerer 'arena 'phase 'function, expr: *ast::Node, binop: ast::BinOp |
|
| 5528 | + | ) -> bool throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5379 | 5529 | let place = try lowerPlace(self, binop.left) else return false; |
|
| 5380 | 5530 | let current = emitRead(self, place.base, place.offset, place.fieldType); |
|
| 5381 | 5531 | let left = try applyCoercion(self, binop.left, current); |
|
| 5382 | 5532 | let right = try lowerExpr(self, binop.right); |
|
| 5383 | 5533 | let exprType = try typeOf(self, expr); |
| 5389 | 5539 | ||
| 5390 | 5540 | return true; |
|
| 5391 | 5541 | } |
|
| 5392 | 5542 | ||
| 5393 | 5543 | /// Lower an assignment statement. |
|
| 5394 | - | unsafe fn lowerAssign(self: &mut FnLowerer, node: *ast::Node, a: ast::Assign) throws (LowerError) { |
|
| 5544 | + | unsafe fn lowerAssign 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, a: ast::Assign) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5395 | 5545 | // Slice assignment: `slice[range] = value`. |
|
| 5396 | 5546 | if let info = resolver::sliceRangeInfoFor(self.low.resolver, node) { |
|
| 5397 | 5547 | let case ast::NodeValue::Subscript { container, index } = a.left.value |
|
| 5398 | 5548 | else panic "lowerAssign: slice assign without subscript"; |
|
| 5399 | 5549 | let case ast::NodeValue::Range(range) = index.value |
| 5446 | 5596 | } |
|
| 5447 | 5597 | } |
|
| 5448 | 5598 | } |
|
| 5449 | 5599 | ||
| 5450 | 5600 | /// Lower `slice[range] = value`. |
|
| 5451 | - | unsafe fn lowerSliceAssign( |
|
| 5452 | - | self: &mut FnLowerer, |
|
| 5601 | + | unsafe fn lowerSliceAssign 'arena 'phase 'function ( |
|
| 5602 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 5453 | 5603 | rhs: *ast::Node, |
|
| 5454 | 5604 | container: *ast::Node, |
|
| 5455 | 5605 | range: ast::Range, |
|
| 5456 | 5606 | info: resolver::SliceRangeInfo |
|
| 5457 | - | ) throws (LowerError) { |
|
| 5607 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5458 | 5608 | let r = try resolveSliceRangePtr(self, container, range, info); |
|
| 5459 | 5609 | let elemSize = resolver::getTypeLayout(*info.itemType).size; |
|
| 5460 | 5610 | let rhsTy = try typeOf(self, rhs); |
|
| 5461 | 5611 | ||
| 5462 | 5612 | if let case resolver::Type::Slice { .. } = rhsTy { |
| 5478 | 5628 | try emitFillLoop(self, r.dataReg, fillVal, r.count, *info.itemType, elemSize); |
|
| 5479 | 5629 | } |
|
| 5480 | 5630 | } |
|
| 5481 | 5631 | ||
| 5482 | 5632 | /// Emit a typed fill loop: `for i in 0..count { dst[i * stride] = value; }`. |
|
| 5483 | - | unsafe fn emitFillLoop( |
|
| 5484 | - | self: &mut FnLowerer, |
|
| 5633 | + | unsafe fn emitFillLoop 'arena 'phase 'function ( |
|
| 5634 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 5485 | 5635 | dst: il::Reg, |
|
| 5486 | 5636 | value: il::Val, |
|
| 5487 | 5637 | count: il::Val, |
|
| 5488 | 5638 | elemType: resolver::Type, |
|
| 5489 | 5639 | elemSize: u32 |
|
| 5490 | - | ) throws (LowerError) { |
|
| 5640 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5491 | 5641 | let iReg = nextReg(self); |
|
| 5492 | 5642 | let header = try createBlockWithParam( |
|
| 5493 | 5643 | self, "fill", il::Param { value: iReg, type: il::Type::W32 } |
|
| 5494 | 5644 | ); |
|
| 5495 | 5645 | let body = try createBlock(self, "fill"); |
| 5528 | 5678 | /// |
|
| 5529 | 5679 | /// @entry -> @loop -> @loop |
|
| 5530 | 5680 | /// | |
|
| 5531 | 5681 | /// `----> @end |
|
| 5532 | 5682 | /// |
|
| 5533 | - | unsafe fn lowerLoop(self: &mut FnLowerer, body: *ast::Node) throws (LowerError) { |
|
| 5683 | + | unsafe fn lowerLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, body: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5534 | 5684 | let loopBlock = try createBlock(self, "loop"); |
|
| 5535 | 5685 | let endBlock = try createBlock(self, "merge"); |
|
| 5536 | 5686 | ||
| 5537 | 5687 | // Enter the loop with the given break and continue targets. |
|
| 5538 | 5688 | // `break` jumps to `endBlock`, |
| 5561 | 5711 | /// |
|
| 5562 | 5712 | /// @entry -> @loop -> (true) @body -> @loop |
|
| 5563 | 5713 | /// | |
|
| 5564 | 5714 | /// `----> (false) @end |
|
| 5565 | 5715 | /// |
|
| 5566 | - | unsafe fn lowerWhile(self: &mut FnLowerer, w: ast::While) throws (LowerError) { |
|
| 5716 | + | unsafe fn lowerWhile 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, w: ast::While) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5567 | 5717 | let whileBlock = try createBlock(self, "while"); |
|
| 5568 | 5718 | let bodyBlock = try createBlock(self, "body"); |
|
| 5569 | 5719 | let endBlock = try createBlock(self, "merge"); |
|
| 5570 | 5720 | ||
| 5571 | 5721 | enterLoop(self, endBlock, whileBlock); |
| 5585 | 5735 | try switchToAndSeal(self, endBlock); |
|
| 5586 | 5736 | exitLoop(self); |
|
| 5587 | 5737 | } |
|
| 5588 | 5738 | ||
| 5589 | 5739 | /// Emit an increment of a variable by `1`. |
|
| 5590 | - | unsafe fn emitIncrement(self: &mut FnLowerer, v: Var, typ: il::Type) throws (LowerError) { |
|
| 5740 | + | unsafe fn emitIncrement 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var, typ: il::Type) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5591 | 5741 | let cur = try useVar(self, v); |
|
| 5592 | 5742 | let next = nextReg(self); |
|
| 5593 | 5743 | ||
| 5594 | 5744 | emit(self, il::Instr::BinOp { op: il::BinOp::Add, typ, dst: next, a: cur, b: il::Val::Imm(1) }); |
|
| 5595 | 5745 | defVar(self, v, il::Val::Reg(next)); |
| 5600 | 5750 | /// The step block is created lazily (after the loop body) so that it gets |
|
| 5601 | 5751 | /// a block index higher than all body blocks. This ensures the register |
|
| 5602 | 5752 | /// allocator processes definitions before uses in forward block order, |
|
| 5603 | 5753 | /// avoiding stale assignments when a value defined deep in the body flows |
|
| 5604 | 5754 | /// through the step block as a block argument. |
|
| 5605 | - | unsafe fn lowerForLoop(self: &mut FnLowerer, iter: &ForIter, body: *ast::Node) throws (LowerError) { |
|
| 5755 | + | unsafe fn lowerForLoop 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, iter: &ForIter, body: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5606 | 5756 | let loopBlock = try createBlock(self, "loop"); |
|
| 5607 | 5757 | let bodyBlock = try createBlock(self, "body"); |
|
| 5608 | 5758 | let endBlock = try createBlock(self, "merge"); |
|
| 5609 | 5759 | ||
| 5610 | 5760 | enterLoop(self, endBlock, nil); |
| 5669 | 5819 | ||
| 5670 | 5820 | switchToBlock(self, endBlock); |
|
| 5671 | 5821 | } |
|
| 5672 | 5822 | ||
| 5673 | 5823 | /// Lower a `for` loop over a range, array, or slice. |
|
| 5674 | - | unsafe fn lowerFor(self: &mut FnLowerer, node: *ast::Node, f: ast::For) throws (LowerError) { |
|
| 5824 | + | unsafe fn lowerFor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, f: ast::For) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5675 | 5825 | let savedVarsLen = enterVarScope(self); |
|
| 5676 | 5826 | let info = resolver::forLoopInfoFor(self.low.resolver, node) else { |
|
| 5677 | 5827 | throw LowerError::MissingMetadata; |
|
| 5678 | 5828 | }; |
|
| 5679 | 5829 | match info { |
| 5733 | 5883 | } |
|
| 5734 | 5884 | exitVarScope(self, savedVarsLen); |
|
| 5735 | 5885 | } |
|
| 5736 | 5886 | ||
| 5737 | 5887 | /// Lower a break statement. |
|
| 5738 | - | unsafe fn lowerBreak(self: &mut FnLowerer) throws (LowerError) { |
|
| 5888 | + | unsafe fn lowerBreak 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5739 | 5889 | let ctx = currentLoop(self) else { |
|
| 5740 | 5890 | throw LowerError::OutsideOfLoop; |
|
| 5741 | 5891 | }; |
|
| 5742 | 5892 | try emitJmp(self, ctx.breakTarget); |
|
| 5743 | 5893 | } |
|
| 5744 | 5894 | ||
| 5745 | 5895 | /// Lower a continue statement. |
|
| 5746 | - | unsafe fn lowerContinue(self: &mut FnLowerer) throws (LowerError) { |
|
| 5896 | + | unsafe fn lowerContinue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5747 | 5897 | let block = try getOrCreateContinueBlock(self); |
|
| 5748 | 5898 | try emitJmp(self, block); |
|
| 5749 | 5899 | } |
|
| 5750 | 5900 | ||
| 5751 | 5901 | /// Emit a return, blitting into the caller's return buffer if needed. |
|
| 5752 | 5902 | /// |
|
| 5753 | 5903 | /// When the function has a return buffer parameter, the value is blitted |
|
| 5754 | 5904 | /// into the buffer and the buffer pointer is returned. Otherwise, the value is |
|
| 5755 | 5905 | /// returned directly. |
|
| 5756 | - | unsafe fn emitRetVal(self: &mut FnLowerer, val: il::Val) throws (LowerError) { |
|
| 5906 | + | unsafe fn emitRetVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5757 | 5907 | if let retReg = self.returnReg { |
|
| 5758 | 5908 | let src = emitValToReg(self, val); |
|
| 5759 | 5909 | let size = resolver::getResultLayout(*self.fnType.returnType, self.fnType.throwList).size |
|
| 5760 | 5910 | if self.fnType.throwList.len > 0 |
|
| 5761 | 5911 | else resolver::getTypeLayout(*self.fnType.returnType).size; |
|
| 5762 | 5912 | ||
| 5763 | 5913 | emit(self, il::Instr::Blit { dst: retReg, src, size: il::Val::Imm(size as i64) }); |
|
| 5764 | 5914 | emit(self, il::Instr::Ret { val: il::Val::Reg(retReg) }); |
|
| 5765 | 5915 | } else if isSmallAggregate(*self.fnType.returnType) { |
|
| 5766 | - | let src = emitValToReg(self, val); |
|
| 5916 | + | let mut src = emitValToReg(self, val); |
|
| 5917 | + | let layout = resolver::getTypeLayout(*self.fnType.returnType); |
|
| 5918 | + | if layout.alignment < resolver::PTR_SIZE or layout.size < resolver::PTR_SIZE { |
|
| 5919 | + | // The return word must not read beyond the value or its alignment. |
|
| 5920 | + | let word = emitReserveLayout(self, resolver::Layout { |
|
| 5921 | + | size: resolver::PTR_SIZE, alignment: resolver::PTR_SIZE, |
|
| 5922 | + | }); |
|
| 5923 | + | emitStoreW64At(self, il::Val::Imm(0), word, 0); |
|
| 5924 | + | emit(self, il::Instr::Blit { dst: word, src, size: il::Val::Imm(layout.size as i64) }); |
|
| 5925 | + | set src = word; |
|
| 5926 | + | } |
|
| 5767 | 5927 | let dst = nextReg(self); |
|
| 5768 | 5928 | ||
| 5769 | 5929 | emit(self, il::Instr::Load { typ: il::Type::W64, dst, src, offset: 0 }); |
|
| 5770 | 5930 | emit(self, il::Instr::Ret { val: il::Val::Reg(dst) }); |
|
| 5771 | 5931 | } else { |
|
| 5772 | 5932 | emit(self, il::Instr::Ret { val }); |
|
| 5773 | 5933 | } |
|
| 5774 | 5934 | } |
|
| 5775 | 5935 | ||
| 5776 | 5936 | /// Lower a return statement. |
|
| 5777 | - | unsafe fn lowerReturnStmt(self: &mut FnLowerer, node: *ast::Node, value: ?*ast::Node) throws (LowerError) { |
|
| 5937 | + | unsafe fn lowerReturnStmt 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, value: ?*ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5778 | 5938 | let mut val = il::Val::Undef; |
|
| 5779 | 5939 | if let expr = value { |
|
| 5780 | 5940 | set val = try lowerExpr(self, expr); |
|
| 5781 | 5941 | } |
|
| 5782 | 5942 | if blockHasTerminator(self) { |
| 5785 | 5945 | set val = try applyCoercion(self, node, val); |
|
| 5786 | 5946 | try emitRetVal(self, val); |
|
| 5787 | 5947 | } |
|
| 5788 | 5948 | ||
| 5789 | 5949 | /// Lower a throw statement. |
|
| 5790 | - | unsafe fn lowerThrowStmt(self: &mut FnLowerer, expr: *ast::Node) throws (LowerError) { |
|
| 5950 | + | unsafe fn lowerThrowStmt 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, expr: *ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5791 | 5951 | assert self.fnType.throwList.len > 0; |
|
| 5792 | 5952 | ||
| 5793 | 5953 | let errType = *self.fnType.throwList[0] if self.fnType.throwList.len == 1 |
|
| 5794 | 5954 | else try typeOf(self, expr); |
|
| 5795 | 5955 | let tag = getOrAssignErrorTag(self.low, errType) as i64; |
| 5798 | 5958 | ||
| 5799 | 5959 | try emitRetVal(self, resultVal); |
|
| 5800 | 5960 | } |
|
| 5801 | 5961 | ||
| 5802 | 5962 | /// Ensure a value is in a register (eg. for branch conditions). |
|
| 5803 | - | unsafe fn emitValToReg(self: &mut FnLowerer, val: il::Val) -> il::Reg { |
|
| 5963 | + | unsafe fn emitValToReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 5804 | 5964 | match val { |
|
| 5805 | 5965 | case il::Val::Reg(r) => return r, |
|
| 5806 | 5966 | case il::Val::Imm(_), il::Val::DataSym(_), il::Val::FnAddr(_) => { |
|
| 5807 | 5967 | let dst = nextReg(self); |
|
| 5808 | 5968 | emit(self, il::Instr::Copy { dst, val }); |
| 5850 | 6010 | /// // ... |
|
| 5851 | 6011 | /// jmp @end(%b); |
|
| 5852 | 6012 | /// @end(w8 %result) |
|
| 5853 | 6013 | /// ret %result; |
|
| 5854 | 6014 | /// |
|
| 5855 | - | unsafe fn lowerLogicalOp( |
|
| 5856 | - | self: &mut FnLowerer, |
|
| 6015 | + | unsafe fn lowerLogicalOp 'arena 'phase 'function ( |
|
| 6016 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 5857 | 6017 | binop: ast::BinOp, |
|
| 5858 | 6018 | thenLabel: *[u8], |
|
| 5859 | 6019 | elseLabel: *[u8], |
|
| 5860 | 6020 | mergeLabel: *[u8], |
|
| 5861 | 6021 | op: LogicalOp |
|
| 5862 | - | ) -> il::Val throws (LowerError) { |
|
| 6022 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5863 | 6023 | let thenBlock = try createBlock(self, thenLabel); |
|
| 5864 | 6024 | let elseBlock = try createBlock(self, elseLabel); |
|
| 5865 | 6025 | ||
| 5866 | 6026 | let resultReg = nextReg(self); |
|
| 5867 | 6027 | let mergeBlock = try createBlockWithParam( |
| 5900 | 6060 | try switchToAndSeal(self, mergeBlock); |
|
| 5901 | 6061 | return il::Val::Reg(resultReg); |
|
| 5902 | 6062 | } |
|
| 5903 | 6063 | ||
| 5904 | 6064 | /// Lower a conditional expression (`thenExpr if condition else elseExpr`). |
|
| 5905 | - | unsafe fn lowerCondExpr(self: &mut FnLowerer, node: *ast::Node, cond: ast::CondExpr) -> il::Val |
|
| 5906 | - | throws (LowerError) |
|
| 6065 | + | unsafe fn lowerCondExpr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, cond: ast::CondExpr) -> il::Val |
|
| 6066 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 5907 | 6067 | { |
|
| 5908 | 6068 | let typ = try typeOf(self, node); |
|
| 5909 | 6069 | let thenBlock = try createBlock(self, "cond#then"); |
|
| 5910 | 6070 | let elseBlock = try createBlock(self, "cond#else"); |
|
| 5911 | 6071 |
| 5967 | 6127 | else => return nil, |
|
| 5968 | 6128 | } |
|
| 5969 | 6129 | } |
|
| 5970 | 6130 | ||
| 5971 | 6131 | /// Lower a binary operation. |
|
| 5972 | - | unsafe fn lowerBinOp(self: &mut FnLowerer, node: *ast::Node, binop: ast::BinOp) -> il::Val throws (LowerError) { |
|
| 6132 | + | unsafe fn lowerBinOp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, binop: ast::BinOp) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5973 | 6133 | // Short-circuit logical operators don't evaluate both operands eagerly. |
|
| 5974 | 6134 | if binop.op == ast::BinaryOp::And { |
|
| 5975 | 6135 | return try lowerLogicalOp(self, binop, "and#then", "and#else", "and#end", LogicalOp::And); |
|
| 5976 | 6136 | } else if binop.op == ast::BinaryOp::Or { |
|
| 5977 | 6137 | return try lowerLogicalOp(self, binop, "or#then", "or#else", "or#end", LogicalOp::Or); |
| 6026 | 6186 | } |
|
| 6027 | 6187 | return emitScalarBinOp(self, binop.op, ilType(self.low, resultTy), a, b, isUnsignedType(resultTy)); |
|
| 6028 | 6188 | } |
|
| 6029 | 6189 | ||
| 6030 | 6190 | /// Emit an aggregate equality or inequality comparison. |
|
| 6031 | - | unsafe fn emitAggregateEqOp( |
|
| 6032 | - | self: &mut FnLowerer, |
|
| 6191 | + | unsafe fn emitAggregateEqOp 'arena 'phase 'function ( |
|
| 6192 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6033 | 6193 | op: ast::BinaryOp, |
|
| 6034 | 6194 | typ: resolver::Type, |
|
| 6035 | 6195 | a: il::Val, |
|
| 6036 | 6196 | b: il::Val |
|
| 6037 | - | ) -> il::Val throws (LowerError) { |
|
| 6197 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6038 | 6198 | let regA = emitValToReg(self, a); |
|
| 6039 | 6199 | let regB = emitValToReg(self, b); |
|
| 6040 | 6200 | let result = try lowerAggregateEq(self, typ, regA, regB, 0); |
|
| 6041 | 6201 | ||
| 6042 | 6202 | if op == ast::BinaryOp::Ne { |
| 6044 | 6204 | } |
|
| 6045 | 6205 | return result; |
|
| 6046 | 6206 | } |
|
| 6047 | 6207 | ||
| 6048 | 6208 | /// Emit a scalar binary operation instruction. |
|
| 6049 | - | unsafe fn emitScalarBinOp( |
|
| 6050 | - | self: &mut FnLowerer, |
|
| 6209 | + | unsafe fn emitScalarBinOp 'arena 'phase 'function ( |
|
| 6210 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6051 | 6211 | op: ast::BinaryOp, |
|
| 6052 | 6212 | typ: il::Type, |
|
| 6053 | 6213 | a: il::Val, |
|
| 6054 | 6214 | b: il::Val, |
|
| 6055 | 6215 | unsigned: bool |
|
| 6056 | - | ) -> il::Val { |
|
| 6216 | + | ) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 6057 | 6217 | let dst = nextReg(self); |
|
| 6058 | 6218 | let mut needsExt: bool = false; |
|
| 6059 | 6219 | match op { |
|
| 6060 | 6220 | case ast::BinaryOp::Add => { |
|
| 6061 | 6221 | emit(self, il::Instr::BinOp { op: il::BinOp::Add, typ, dst, a, b }); |
| 6121 | 6281 | } |
|
| 6122 | 6282 | return il::Val::Reg(dst); |
|
| 6123 | 6283 | } |
|
| 6124 | 6284 | ||
| 6125 | 6285 | /// Normalize sub-word values to well-defined high bits. |
|
| 6126 | - | unsafe fn normalizeSubword(self: &mut FnLowerer, typ: il::Type, unsigned: bool, val: il::Val) -> il::Val { |
|
| 6286 | + | unsafe fn normalizeSubword 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, typ: il::Type, unsigned: bool, val: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 6127 | 6287 | if typ == il::Type::W8 or typ == il::Type::W16 { |
|
| 6128 | 6288 | let extDst: il::Reg = nextReg(self); |
|
| 6129 | 6289 | if unsigned { |
|
| 6130 | 6290 | emit(self, il::Instr::Zext { typ, dst: extDst, val }); |
|
| 6131 | 6291 | } else { |
| 6135 | 6295 | } |
|
| 6136 | 6296 | return val; |
|
| 6137 | 6297 | } |
|
| 6138 | 6298 | ||
| 6139 | 6299 | /// Lower a unary operation. |
|
| 6140 | - | unsafe fn lowerUnOp(self: &mut FnLowerer, node: *ast::Node, unop: ast::UnOp) -> il::Val throws (LowerError) { |
|
| 6300 | + | unsafe fn lowerUnOp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, unop: ast::UnOp) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6141 | 6301 | if unop.op == ast::UnaryOp::Neg { |
|
| 6142 | 6302 | if let case ast::NodeValue::Number(lit) = unop.value.value { |
|
| 6143 | 6303 | return il::Val::Imm((0 - lit.magnitude) as i64); |
|
| 6144 | 6304 | } |
|
| 6145 | 6305 | } |
| 6167 | 6327 | } |
|
| 6168 | 6328 | return il::Val::Reg(dst); |
|
| 6169 | 6329 | } |
|
| 6170 | 6330 | ||
| 6171 | 6331 | /// Lower a cast expression (`x as T`). |
|
| 6172 | - | unsafe fn lowerCast(self: &mut FnLowerer, node: *ast::Node, cast: ast::As) -> il::Val throws (LowerError) { |
|
| 6332 | + | unsafe fn lowerCast 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, cast: ast::As) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6173 | 6333 | let val = try lowerExpr(self, cast.value); |
|
| 6174 | 6334 | ||
| 6175 | 6335 | let srcType = try typeOf(self, cast.value); |
|
| 6176 | 6336 | let dstType = try typeOf(self, node); |
|
| 6177 | 6337 | if resolver::typesEqual(srcType, dstType) { |
| 6199 | 6359 | ||
| 6200 | 6360 | /// Lower a string literal to a slice value. |
|
| 6201 | 6361 | /// |
|
| 6202 | 6362 | /// String literals are stored as global data and the result is a slice |
|
| 6203 | 6363 | /// pointing to the data with the appropriate length. |
|
| 6204 | - | unsafe fn lowerStringLit(self: &mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) { |
|
| 6364 | + | unsafe fn lowerStringLit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6205 | 6365 | // Get the slice type from the node. |
|
| 6206 | 6366 | let sliceTy = try typeOf(self, node); |
|
| 6207 | 6367 | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
|
| 6208 | 6368 | else throw LowerError::ExpectedSliceOrArray; |
|
| 6209 | 6369 | // Build the string data value. |
| 6218 | 6378 | self, &result, 1, true, item, mutable, s.len |
|
| 6219 | 6379 | ); |
|
| 6220 | 6380 | } |
|
| 6221 | 6381 | ||
| 6222 | 6382 | /// Lower a builtin call expression. |
|
| 6223 | - | unsafe fn lowerBuiltinCall(self: &mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *[*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6383 | + | unsafe fn lowerBuiltinCall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, kind: ast::Builtin, args: *[*ast::Node]) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6224 | 6384 | match kind { |
|
| 6225 | 6385 | case ast::Builtin::SliceOf => return try lowerSliceOf(self, node, args), |
|
| 6226 | 6386 | case ast::Builtin::SizeOf, ast::Builtin::AlignOf => { |
|
| 6227 | 6387 | let constVal = resolver::constValueEntry(self.low.resolver, node) else { |
|
| 6228 | 6388 | throw LowerError::MissingConst(node); |
| 6231 | 6391 | } |
|
| 6232 | 6392 | } |
|
| 6233 | 6393 | } |
|
| 6234 | 6394 | ||
| 6235 | 6395 | /// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call. |
|
| 6236 | - | unsafe fn lowerSliceOf(self: &mut FnLowerer, node: *ast::Node, args: *[*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6396 | + | unsafe fn lowerSliceOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, args: *[*ast::Node]) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6237 | 6397 | if args.len <> 2 and args.len <> 3 { |
|
| 6238 | 6398 | throw LowerError::InvalidArgCount; |
|
| 6239 | 6399 | } |
|
| 6240 | 6400 | let sliceTy = try typeOf(self, node); |
|
| 6241 | 6401 | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
| 6251 | 6411 | } |
|
| 6252 | 6412 | return try buildSliceValue(self, item, mutable, ptrVal, lenVal, capVal); |
|
| 6253 | 6413 | } |
|
| 6254 | 6414 | ||
| 6255 | 6415 | /// Lower a `try` expression. |
|
| 6256 | - | unsafe fn lowerTry(self: &mut FnLowerer, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) { |
|
| 6416 | + | unsafe fn lowerTry 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6257 | 6417 | let case ast::NodeValue::Call(callExpr) = t.expr.value else { |
|
| 6258 | 6418 | throw LowerError::ExpectedCall; |
|
| 6259 | 6419 | }; |
|
| 6260 | 6420 | let calleeTy = try typeOf(self, callExpr.callee); |
|
| 6261 | 6421 | let case resolver::Type::Fn(calleeInfo) = calleeTy else { |
| 6266 | 6426 | // Type of the try expression, which is either the return type of the function |
|
| 6267 | 6427 | // if successful, or an optional of it, if using `try?`. |
|
| 6268 | 6428 | let tryExprTy = try typeOf(self, node); |
|
| 6269 | 6429 | // Check for trait method dispatch or standalone method call. |
|
| 6270 | 6430 | let mut resVal: il::Val = undefined; |
|
| 6431 | + | let mut initialization: ?SessionInitialization = nil; |
|
| 6271 | 6432 | let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra; |
|
| 6272 | - | if let case resolver::NodeExtra::TraitMethodCall { |
|
| 6433 | + | if let case resolver::NodeExtra::SessionAllocation(allocation) = callNodeExtra { |
|
| 6434 | + | let prepared = try prepareSessionAllocation(self, callExpr, allocation); |
|
| 6435 | + | set initialization = prepared; |
|
| 6436 | + | set resVal = prepared.result; |
|
| 6437 | + | } else if let case resolver::NodeExtra::TraitMethodCall { |
|
| 6273 | 6438 | traitInfo, methodIndex |
|
| 6274 | 6439 | } = callNodeExtra { |
|
| 6275 | 6440 | set resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex); |
|
| 6276 | 6441 | } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra { |
|
| 6277 | 6442 | set resVal = try lowerMethodCall(self, t.expr, callExpr, method); |
|
| 6278 | 6443 | } else { |
|
| 6279 | 6444 | set resVal = try lowerCall(self, t.expr, callExpr); |
|
| 6280 | 6445 | } |
|
| 6446 | + | if blockHasTerminator(self) { |
|
| 6447 | + | return il::Val::Undef; |
|
| 6448 | + | } |
|
| 6281 | 6449 | let base = emitValToReg(self, resVal); // The result value. |
|
| 6282 | 6450 | let tagReg = resultTagReg(self, base); // The result tag. |
|
| 6283 | 6451 | ||
| 6284 | 6452 | let okBlock = try createBlock(self, "ok"); // Block if success. |
|
| 6285 | 6453 | let errBlock = try createBlock(self, "err"); // Block if failure. |
| 6301 | 6469 | try sealBlock(self, errBlock); |
|
| 6302 | 6470 | ||
| 6303 | 6471 | // Success path: extract the successful value from the result and store it |
|
| 6304 | 6472 | // in the result slot for later use after the merge point. |
|
| 6305 | 6473 | switchToBlock(self, okBlock); |
|
| 6474 | + | if let prepared = initialization { |
|
| 6475 | + | try initializeSessionAllocation(self, prepared); |
|
| 6476 | + | } |
|
| 6306 | 6477 | ||
| 6307 | 6478 | if okValueTy == resolver::Type::Never { |
|
| 6308 | 6479 | emit(self, il::Instr::Unreachable); |
|
| 6309 | 6480 | } else if let slot = resultSlot { |
|
| 6310 | 6481 | // Extract the success payload. If the result type differs from the payload |
| 6397 | 6568 | /// Lower typed multi-catch clauses. |
|
| 6398 | 6569 | /// |
|
| 6399 | 6570 | /// Emits a switch on the global error tag to dispatch to the correct catch |
|
| 6400 | 6571 | /// clause. Each typed clause extracts the error payload for its specific type |
|
| 6401 | 6572 | /// and binds it to the clause's identifier. |
|
| 6402 | - | unsafe fn lowerMultiCatch( |
|
| 6403 | - | self: &mut FnLowerer, |
|
| 6573 | + | unsafe fn lowerMultiCatch 'arena 'phase 'function ( |
|
| 6574 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6404 | 6575 | catches: *[*ast::Node], |
|
| 6405 | 6576 | calleeInfo: *resolver::FnType, |
|
| 6406 | 6577 | base: il::Reg, |
|
| 6407 | 6578 | tagReg: il::Reg, |
|
| 6408 | 6579 | mergeBlock: &mut ?BlockId |
|
| 6409 | - | ) throws (LowerError) { |
|
| 6580 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6410 | 6581 | let entry = currentBlock(self); |
|
| 6411 | 6582 | ||
| 6412 | 6583 | // First pass: create blocks, resolve error types, and build switch cases. |
|
| 6413 | 6584 | let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined; |
|
| 6414 | 6585 | let mut errTypes: [?resolver::Type; MAX_CATCH_CLAUSES] = undefined; |
| 6428 | 6599 | ||
| 6429 | 6600 | cases.append(il::SwitchCase { |
|
| 6430 | 6601 | value: getOrAssignErrorTag(self.low, errTy) as i64, |
|
| 6431 | 6602 | target: *blocks[i], |
|
| 6432 | 6603 | args: &mut [] |
|
| 6433 | - | }, self.allocator); |
|
| 6604 | + | }, alloc::arenaAllocator(self.arena)); |
|
| 6434 | 6605 | } else { |
|
| 6435 | 6606 | set errTypes[i] = nil; |
|
| 6436 | 6607 | set defaultIdx = i; |
|
| 6437 | 6608 | } |
|
| 6438 | 6609 | } |
| 6485 | 6656 | /// Emit a byte-copy loop: `for i in 0..size { dst[i] = src[i]; }`. |
|
| 6486 | 6657 | /// |
|
| 6487 | 6658 | /// Used when `blit` cannot be used because the copy size is dynamic. |
|
| 6488 | 6659 | /// Terminates the current block and leaves the builder positioned |
|
| 6489 | 6660 | /// after the loop. |
|
| 6490 | - | unsafe fn emitByteCopyLoop( |
|
| 6491 | - | self: &mut FnLowerer, |
|
| 6661 | + | unsafe fn emitByteCopyLoop 'arena 'phase 'function ( |
|
| 6662 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6492 | 6663 | dst: il::Reg, |
|
| 6493 | 6664 | src: il::Reg, |
|
| 6494 | 6665 | size: il::Val, |
|
| 6495 | 6666 | label: *[u8] |
|
| 6496 | - | ) throws (LowerError) { |
|
| 6667 | + | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6497 | 6668 | let iReg = nextReg(self); |
|
| 6498 | 6669 | let header = try createBlockWithParam( |
|
| 6499 | 6670 | self, label, il::Param { value: iReg, type: il::Type::W32 } |
|
| 6500 | 6671 | ); |
|
| 6501 | 6672 | let body = try createBlock(self, label); |
| 6544 | 6715 | /// |
|
| 6545 | 6716 | /// @store: |
|
| 6546 | 6717 | /// store element at ptr + len * stride |
|
| 6547 | 6718 | /// increment len |
|
| 6548 | 6719 | /// |
|
| 6549 | - | unsafe fn lowerSliceAppend(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) { |
|
| 6720 | + | unsafe fn lowerSliceAppend 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6550 | 6721 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6551 | 6722 | else throw LowerError::MissingMetadata; |
|
| 6552 | 6723 | ||
| 6553 | 6724 | // Get the address of the slice header. |
|
| 6554 | 6725 | let sliceVal = try lowerExpr(self, access.parent); |
| 6630 | 6801 | ||
| 6631 | 6802 | /// Lower `slice.delete(index)`. |
|
| 6632 | 6803 | /// |
|
| 6633 | 6804 | /// Bounds-check the index, shift elements after it by one stride |
|
| 6634 | 6805 | /// via a byte-copy loop, and decrement `len`. |
|
| 6635 | - | unsafe fn lowerSliceDelete(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) throws (LowerError) { |
|
| 6806 | + | unsafe fn lowerSliceDelete 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call, elemType: *resolver::Type) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6636 | 6807 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6637 | 6808 | else throw LowerError::MissingMetadata; |
|
| 6638 | 6809 | ||
| 6639 | 6810 | let elemLayout = resolver::getTypeLayout(*elemType); |
|
| 6640 | 6811 | let stride = elemLayout.size; |
| 6669 | 6840 | let newLen = emitTypedBinOp(self, il::BinOp::Sub, il::Type::W32, lenVal, il::Val::Imm(1)); |
|
| 6670 | 6841 | ||
| 6671 | 6842 | emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET); |
|
| 6672 | 6843 | } |
|
| 6673 | 6844 | ||
| 6845 | + | /// Initializer values retained across a fallible session reservation. |
|
| 6846 | + | record SessionInitialization: Copy { |
|
| 6847 | + | /// Resolved allocation operation and element type. |
|
| 6848 | + | allocation: resolver::SessionAllocation, |
|
| 6849 | + | /// Runtime reservation result. |
|
| 6850 | + | result: il::Val, |
|
| 6851 | + | /// Evaluated object, fill value, or source slice. |
|
| 6852 | + | value: il::Val, |
|
| 6853 | + | /// Evaluated number of elements. |
|
| 6854 | + | count: il::Val, |
|
| 6855 | + | } |
|
| 6856 | + | ||
| 6857 | + | /// Evaluate initializer values and reserve their session storage. |
|
| 6858 | + | unsafe fn prepareSessionAllocation 'arena 'phase 'function ( |
|
| 6859 | + | self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call, allocation: resolver::SessionAllocation |
|
| 6860 | + | ) -> SessionInitialization throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6861 | + | let case ast::NodeValue::FieldAccess(access) = call.callee.value else throw LowerError::ExpectedCall; |
|
| 6862 | + | let receiver = try lowerExpr(self, access.parent); |
|
| 6863 | + | let sessionReg = emitValToReg(self, receiver); |
|
| 6864 | + | let dataReg = nextReg(self); |
|
| 6865 | + | emitLoadW64At(self, dataReg, sessionReg, TRAIT_OBJ_DATA_OFFSET); |
|
| 6866 | + | let vtableReg = nextReg(self); |
|
| 6867 | + | emitLoadW64At(self, vtableReg, sessionReg, TRAIT_OBJ_VTABLE_OFFSET); |
|
| 6868 | + | let functionReg = nextReg(self); |
|
| 6869 | + | emitLoadW64At( |
|
| 6870 | + | self, functionReg, vtableReg, |
|
| 6871 | + | (allocation.methodIndex * resolver::PTR_SIZE) as i32, |
|
| 6872 | + | ); |
|
| 6873 | + | let value = try lowerCallArg(self, call.args[0], true); |
|
| 6874 | + | if blockHasTerminator(self) { |
|
| 6875 | + | return SessionInitialization { allocation, result: il::Val::Undef, value, count: il::Val::Undef }; |
|
| 6876 | + | } |
|
| 6877 | + | let slice = allocation.kind <> resolver::SessionAllocationKind::New; |
|
| 6878 | + | let mut count = il::Val::Imm(1); |
|
| 6879 | + | if allocation.kind == resolver::SessionAllocationKind::Copy { |
|
| 6880 | + | set count = loadSliceLen(self, emitValToReg(self, value)); |
|
| 6881 | + | } else if allocation.kind == resolver::SessionAllocationKind::Fill { |
|
| 6882 | + | set count = try lowerExpr(self, call.args[1]); |
|
| 6883 | + | if blockHasTerminator(self) { |
|
| 6884 | + | return SessionInitialization { allocation, result: il::Val::Undef, value, count: il::Val::Undef }; |
|
| 6885 | + | } |
|
| 6886 | + | } |
|
| 6887 | + | let layout = resolver::getTypeLayout(*allocation.item); |
|
| 6888 | + | let size = layout.size if layout.size > 0 else 1; |
|
| 6889 | + | let alignment = layout.alignment if layout.alignment > 0 else 1; |
|
| 6890 | + | let runtime = allocation.traitInfo.methods[allocation.methodIndex].fnType; |
|
| 6891 | + | let argOffset: u32 = 1 if requiresReturnParam(runtime) else 0; |
|
| 6892 | + | let args = try allocVals(self, (4 if slice else 3) + argOffset); |
|
| 6893 | + | set args[argOffset] = il::Val::Reg(dataReg); |
|
| 6894 | + | set args[argOffset + 1] = il::Val::Imm(size as i64); |
|
| 6895 | + | set args[argOffset + 2] = il::Val::Imm(alignment as i64); |
|
| 6896 | + | if slice { |
|
| 6897 | + | set args[argOffset + 3] = count; |
|
| 6898 | + | } |
|
| 6899 | + | let result = try emitCallValue(self, il::Val::Reg(functionReg), runtime, args); |
|
| 6900 | + | return SessionInitialization { allocation, result, value, count }; |
|
| 6901 | + | } |
|
| 6902 | + | ||
| 6903 | + | /// Initialize successful reservation storage before its reference is published. |
|
| 6904 | + | unsafe fn initializeSessionAllocation 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, prepared: SessionInitialization) |
|
| 6905 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 6906 | + | { |
|
| 6907 | + | let allocation = prepared.allocation; |
|
| 6908 | + | let base = emitValToReg(self, prepared.result); |
|
| 6909 | + | let value = prepared.value; |
|
| 6910 | + | let count = prepared.count; |
|
| 6911 | + | let layout = resolver::getTypeLayout(*allocation.item); |
|
| 6912 | + | let destination = nextReg(self); |
|
| 6913 | + | emitLoadW64At(self, destination, base, RESULT_VAL_OFFSET); |
|
| 6914 | + | match allocation.kind { |
|
| 6915 | + | case resolver::SessionAllocationKind::New => |
|
| 6916 | + | try emitStore(self, destination, 0, *allocation.item, value), |
|
| 6917 | + | case resolver::SessionAllocationKind::Copy => { |
|
| 6918 | + | let source = loadSlicePtr(self, emitValToReg(self, value)); |
|
| 6919 | + | let bytes = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, count, il::Val::Imm(layout.size as i64)); |
|
| 6920 | + | try emitByteCopyLoop(self, destination, source, bytes, "allocate.copy"); |
|
| 6921 | + | } |
|
| 6922 | + | case resolver::SessionAllocationKind::Fill => |
|
| 6923 | + | try emitFillLoop(self, destination, value, count, *allocation.item, layout.size), |
|
| 6924 | + | } |
|
| 6925 | + | } |
|
| 6926 | + | ||
| 6674 | 6927 | /// Lower a call expression, which may be a function call or type constructor. |
|
| 6675 | - | unsafe fn lowerCallOrCtor(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6928 | + | unsafe fn lowerCallOrCtor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6676 | 6929 | let nodeData = resolver::nodeData(self.low.resolver, node).extra; |
|
| 6677 | 6930 | ||
| 6678 | 6931 | // Check for slice method dispatch. |
|
| 6679 | 6932 | if let case resolver::NodeExtra::SliceAppend { elemType } = nodeData { |
|
| 6680 | 6933 | return try lowerSliceAppend(self, call, elemType); |
| 6690 | 6943 | // Check for standalone method call. |
|
| 6691 | 6944 | if let case resolver::NodeExtra::MethodCall { method } = nodeData { |
|
| 6692 | 6945 | return try lowerMethodCall(self, node, call, method); |
|
| 6693 | 6946 | } |
|
| 6694 | 6947 | if let sym = resolver::nodeData(self.low.resolver, call.callee).sym { |
|
| 6695 | - | if let case resolver::SymbolData::Type(nominal) = sym.data { |
|
| 6696 | - | let case resolver::NominalType::Record(_) = *nominal else { |
|
| 6697 | - | throw LowerError::ExpectedRecord; |
|
| 6698 | - | }; |
|
| 6948 | + | if let case resolver::SymbolData::Type(_) = sym.data { |
|
| 6949 | + | let ty = try typeOf(self, node); |
|
| 6950 | + | let case resolver::Type::Nominal(nominal) = ty else throw LowerError::ExpectedRecord; |
|
| 6699 | 6951 | return try lowerRecordCtor(self, nominal, call.args); |
|
| 6700 | 6952 | } |
|
| 6701 | 6953 | if let case resolver::SymbolData::Variant { .. } = sym.data { |
|
| 6702 | 6954 | return try lowerUnionCtor(self, node, sym, call); |
|
| 6703 | 6955 | } |
| 6712 | 6964 | /// load w64 %data %obj 0 // data pointer |
|
| 6713 | 6965 | /// load w64 %vtable %obj 8 // v-table pointer |
|
| 6714 | 6966 | /// load w64 %fn %vtable <slot> // function pointer |
|
| 6715 | 6967 | /// call <retTy> %ret %fn(%data, args...) |
|
| 6716 | 6968 | /// |
|
| 6717 | - | unsafe fn lowerTraitMethodCall( |
|
| 6718 | - | self: &mut FnLowerer, |
|
| 6969 | + | unsafe fn lowerTraitMethodCall 'arena 'phase 'function ( |
|
| 6970 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6719 | 6971 | node: *ast::Node, |
|
| 6720 | 6972 | call: ast::Call, |
|
| 6721 | 6973 | traitInfo: *unsafe resolver::TraitType, |
|
| 6722 | 6974 | methodIndex: u32 |
|
| 6723 | - | ) -> il::Val throws (LowerError) { |
|
| 6975 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6724 | 6976 | // Method calls look like field accesses. |
|
| 6725 | 6977 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6726 | 6978 | else throw LowerError::MissingMetadata; |
|
| 6727 | 6979 | ||
| 6728 | 6980 | // Lower the trait object expression. |
| 6774 | 7026 | ||
| 6775 | 7027 | /// Lower a call argument, snapshotting aggregate place expressions before |
|
| 6776 | 7028 | /// evaluating later arguments. Aggregates are represented by addresses in the |
|
| 6777 | 7029 | /// IL, so retaining the original address would let a later argument mutation |
|
| 6778 | 7030 | /// change the value already supplied for this argument. |
|
| 6779 | - | unsafe fn lowerCallArg(self: &mut FnLowerer, arg: *ast::Node, hasLater: bool) -> il::Val |
|
| 6780 | - | throws (LowerError) |
|
| 7031 | + | unsafe fn lowerCallArg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, arg: *ast::Node, hasLater: bool) -> il::Val |
|
| 7032 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 6781 | 7033 | { |
|
| 6782 | 7034 | let val = try lowerExpr(self, arg); |
|
| 6783 | 7035 | ||
| 6784 | 7036 | if hasLater and ast::isPlaceExpr(arg) { |
|
| 6785 | 7037 | let argType = try effectiveType(self, arg); |
| 6794 | 7046 | /// |
|
| 6795 | 7047 | /// All call lowering paths (regular, trait method, standalone method) converge |
|
| 6796 | 7048 | /// here after preparing the callee value, function type, and argument array. |
|
| 6797 | 7049 | /// The `args` slice must already include a slot at index zero for the hidden |
|
| 6798 | 7050 | /// return parameter; that slot is filled by this function. |
|
| 6799 | - | unsafe fn emitCallValue( |
|
| 6800 | - | self: &mut FnLowerer, |
|
| 7051 | + | unsafe fn emitCallValue 'arena 'phase 'function ( |
|
| 7052 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6801 | 7053 | callee: il::Val, |
|
| 6802 | 7054 | fnInfo: *resolver::FnType, |
|
| 6803 | 7055 | args: *unsafe mut [il::Val], |
|
| 6804 | - | ) -> il::Val throws (LowerError) { |
|
| 7056 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6805 | 7057 | let retTy = *fnInfo.returnType; |
|
| 6806 | 7058 | ||
| 6807 | 7059 | if requiresReturnParam(fnInfo) { |
|
| 6808 | 7060 | if fnInfo.throwList.len > 0 { |
|
| 6809 | 7061 | let layout = resolver::getResultLayout(retTy, fnInfo.throwList); |
| 6856 | 7108 | ||
| 6857 | 7109 | /// Lower a method receiver expression to a pointer value. |
|
| 6858 | 7110 | /// |
|
| 6859 | 7111 | /// If the parent is already a pointer type, the value is used directly. |
|
| 6860 | 7112 | /// If the parent is a value type (eg. a local record), its address is taken. |
|
| 6861 | - | unsafe fn lowerReceiver(self: &mut FnLowerer, parent: *ast::Node, parentTy: resolver::Type) -> il::Val |
|
| 6862 | - | throws (LowerError) |
|
| 7113 | + | unsafe fn lowerReceiver 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, parent: *ast::Node, parentTy: resolver::Type) -> il::Val |
|
| 7114 | + | throws (LowerError) where 'arena: 'phase, 'phase: 'function |
|
| 6863 | 7115 | { |
|
| 6864 | 7116 | if let case resolver::Type::Pointer { .. } = parentTy { |
|
| 6865 | 7117 | // Already a pointer: lower and use directly. |
|
| 6866 | 7118 | return try lowerExpr(self, parent); |
|
| 6867 | 7119 | } |
| 6884 | 7136 | /// Given `obj.method(args)` where `method` is a standalone method on a concrete type, |
|
| 6885 | 7137 | /// emits a direct call with the receiver address as the first argument: |
|
| 6886 | 7138 | /// |
|
| 6887 | 7139 | /// call <retTy> %ret @Type::method(&obj, args...) |
|
| 6888 | 7140 | /// |
|
| 6889 | - | unsafe fn lowerMethodCall( |
|
| 6890 | - | self: &mut FnLowerer, |
|
| 7141 | + | unsafe fn lowerMethodCall 'arena 'phase 'function ( |
|
| 7142 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6891 | 7143 | node: *ast::Node, |
|
| 6892 | 7144 | call: ast::Call, |
|
| 6893 | 7145 | method: &resolver::MethodEntry, |
|
| 6894 | - | ) -> il::Val throws (LowerError) { |
|
| 7146 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6895 | 7147 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6896 | 7148 | else throw LowerError::MissingMetadata; |
|
| 6897 | 7149 | ||
| 6898 | 7150 | // Get the receiver as a pointer. |
|
| 6899 | 7151 | let parentTy = try typeOf(self, access.parent); |
| 6914 | 7166 | } |
|
| 6915 | 7167 | return try emitCallValue(self, il::Val::FnAddr(qualName), fnInfo, args); |
|
| 6916 | 7168 | } |
|
| 6917 | 7169 | ||
| 6918 | 7170 | /// Check if a call is to a compiler intrinsic and lower it directly. |
|
| 6919 | - | unsafe fn lowerIntrinsicCall(self: &mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) { |
|
| 7171 | + | unsafe fn lowerIntrinsicCall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> ?il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6920 | 7172 | // Get the callee symbol and check if it's marked as an intrinsic. |
|
| 6921 | 7173 | let sym = resolver::nodeData(self.low.resolver, call.callee).sym else { |
|
| 6922 | 7174 | // Expressions or function pointers may not have an associated symbol. |
|
| 6923 | 7175 | return nil; |
|
| 6924 | 7176 | }; |
| 6926 | 7178 | return nil; |
|
| 6927 | 7179 | } |
|
| 6928 | 7180 | // Check for known intrinsic names. |
|
| 6929 | 7181 | if mem::eq(sym.name, "ecall") { |
|
| 6930 | 7182 | return try lowerEcall(self, call); |
|
| 7183 | + | } else if mem::eq(sym.name, "deviceRead8") { |
|
| 7184 | + | return try lowerDevice(self, call, il::Type::W8, false); |
|
| 7185 | + | } else if mem::eq(sym.name, "deviceWrite8") { |
|
| 7186 | + | return try lowerDevice(self, call, il::Type::W8, true); |
|
| 7187 | + | } else if mem::eq(sym.name, "deviceRead16") { |
|
| 7188 | + | return try lowerDevice(self, call, il::Type::W16, false); |
|
| 7189 | + | } else if mem::eq(sym.name, "deviceWrite16") { |
|
| 7190 | + | return try lowerDevice(self, call, il::Type::W16, true); |
|
| 7191 | + | } else if mem::eq(sym.name, "deviceRead32") { |
|
| 7192 | + | return try lowerDevice(self, call, il::Type::W32, false); |
|
| 7193 | + | } else if mem::eq(sym.name, "deviceWrite32") { |
|
| 7194 | + | return try lowerDevice(self, call, il::Type::W32, true); |
|
| 7195 | + | } else if mem::eq(sym.name, "deviceRead64") { |
|
| 7196 | + | return try lowerDevice(self, call, il::Type::W64, false); |
|
| 7197 | + | } else if mem::eq(sym.name, "deviceWrite64") { |
|
| 7198 | + | return try lowerDevice(self, call, il::Type::W64, true); |
|
| 6931 | 7199 | } else if mem::eq(sym.name, "ebreak") { |
|
| 6932 | 7200 | return try lowerEbreak(self, call); |
|
| 6933 | 7201 | } else if mem::eq(sym.name, "memoryFence") { |
|
| 6934 | 7202 | return try lowerMemoryFence(self, call); |
|
| 6935 | 7203 | } else { |
|
| 6936 | 7204 | throw LowerError::UnknownIntrinsic; |
|
| 6937 | 7205 | } |
|
| 6938 | 7206 | } |
|
| 6939 | 7207 | ||
| 6940 | 7208 | /// Lower an ecall intrinsic: `ecall(num, a0, a1, a2, a3) -> i32`. |
|
| 6941 | - | unsafe fn lowerEcall(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 7209 | + | unsafe fn lowerEcall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6942 | 7210 | if call.args.len <> 5 { |
|
| 6943 | 7211 | throw LowerError::InvalidArgCount; |
|
| 6944 | 7212 | } |
|
| 6945 | 7213 | let num = try lowerExpr(self, call.args[0]); |
|
| 6946 | 7214 | let a0 = try lowerExpr(self, call.args[1]); |
| 6953 | 7221 | ||
| 6954 | 7222 | return il::Val::Reg(dst); |
|
| 6955 | 7223 | } |
|
| 6956 | 7224 | ||
| 6957 | 7225 | /// Lower an ebreak intrinsic: `ebreak()`. |
|
| 6958 | - | unsafe fn lowerEbreak(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 7226 | + | unsafe fn lowerEbreak 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6959 | 7227 | if call.args.len <> 0 { |
|
| 6960 | 7228 | throw LowerError::InvalidArgCount; |
|
| 6961 | 7229 | } |
|
| 6962 | 7230 | emit(self, il::Instr::Ebreak); |
|
| 6963 | 7231 | ||
| 6964 | 7232 | return il::Val::Undef; |
|
| 6965 | 7233 | } |
|
| 6966 | 7234 | ||
| 6967 | 7235 | /// Lower `memoryFence()`. |
|
| 6968 | - | unsafe fn lowerMemoryFence(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 7236 | + | unsafe fn lowerMemoryFence 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6969 | 7237 | if call.args.len <> 0 { |
|
| 6970 | 7238 | throw LowerError::InvalidArgCount; |
|
| 6971 | 7239 | } |
|
| 6972 | 7240 | emit(self, il::Instr::MemoryFence); |
|
| 6973 | 7241 | return il::Val::Undef; |
|
| 6974 | 7242 | } |
|
| 6975 | 7243 | ||
| 6976 | 7244 | /// Resolve callee to an IL value. For direct function calls, use the symbol name. |
|
| 6977 | 7245 | /// For variables holding function pointers or complex expressions (eg. `array[i]()`), |
|
| 6978 | 7246 | /// lower the callee expression. |
|
| 6979 | - | unsafe fn lowerCallee(self: &mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7247 | + | unsafe fn lowerCallee 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, callee: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6980 | 7248 | if let sym = resolver::nodeData(self.low.resolver, callee).sym { |
|
| 6981 | 7249 | if let case ast::NodeValue::FnDecl(_) = sym.node.value { |
|
| 6982 | 7250 | // First try to look up the symbol in our registered functions. |
|
| 6983 | 7251 | // This handles cross-package calls correctly, since packages are |
|
| 6984 | 7252 | // lowered in dependency order. |
|
| 6985 | - | if let qualName = lookupFnSym(self.low, sym) { |
|
| 7253 | + | if let qualName = lookupSymbolName(self.low, sym) { |
|
| 6986 | 7254 | return il::Val::FnAddr(qualName); |
|
| 6987 | 7255 | } |
|
| 6988 | 7256 | // Fall back to computing the qualified name from the module graph. |
|
| 6989 | 7257 | // This works for functions in the current package. |
|
| 6990 | - | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym) else { |
|
| 6991 | - | throw LowerError::MissingMetadata; |
|
| 6992 | - | }; |
|
| 7258 | + | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym); |
|
| 6993 | 7259 | return il::Val::FnAddr(qualifyName(self.low, modId, sym.name)); |
|
| 6994 | 7260 | } |
|
| 6995 | 7261 | } |
|
| 6996 | 7262 | return try lowerExpr(self, callee); |
|
| 6997 | 7263 | } |
|
| 6998 | 7264 | ||
| 6999 | 7265 | /// Lower a function call expression. |
|
| 7000 | - | unsafe fn lowerCall(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 7266 | + | unsafe fn lowerCall 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7001 | 7267 | // Check for intrinsic calls before normal call lowering. |
|
| 7002 | 7268 | if let intrinsicVal = try lowerIntrinsicCall(self, call) { |
|
| 7003 | 7269 | return intrinsicVal; |
|
| 7004 | 7270 | } |
|
| 7005 | 7271 | let calleeTy = try typeOf(self, call.callee); |
| 7017 | 7283 | ||
| 7018 | 7284 | return try emitCallValue(self, callee, fnInfo, args); |
|
| 7019 | 7285 | } |
|
| 7020 | 7286 | ||
| 7021 | 7287 | /// Apply coercions requested by the resolver. |
|
| 7022 | - | unsafe fn applyCoercion(self: &mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) { |
|
| 7288 | + | unsafe fn applyCoercion 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7023 | 7289 | let coerce = resolver::coercionFor(self.low.resolver, node) else { |
|
| 7024 | 7290 | return val; |
|
| 7025 | 7291 | }; |
|
| 7026 | 7292 | match coerce { |
|
| 7027 | 7293 | case resolver::Coercion::OptionalLift(optType) => { |
| 7046 | 7312 | ||
| 7047 | 7313 | /// Lower an implicit numeric cast coercion. |
|
| 7048 | 7314 | /// |
|
| 7049 | 7315 | /// Handles widening conversions between integer types. Uses sign-extension |
|
| 7050 | 7316 | /// for signed source types and zero-extension for unsigned source types. |
|
| 7051 | - | unsafe fn lowerNumericCast(self: &mut FnLowerer, val: il::Val, srcType: resolver::Type, dstType: resolver::Type) -> il::Val { |
|
| 7317 | + | unsafe fn lowerNumericCast 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val, srcType: resolver::Type, dstType: resolver::Type) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 7052 | 7318 | let srcLayout = resolver::getTypeLayout(srcType); |
|
| 7053 | 7319 | let dstLayout = resolver::getTypeLayout(dstType); |
|
| 7054 | 7320 | ||
| 7055 | 7321 | if srcLayout.size == dstLayout.size { |
|
| 7056 | 7322 | // Same size: bit pattern is unchanged, value is returned as-is. |
| 7070 | 7336 | } |
|
| 7071 | 7337 | return il::Val::Reg(dst); |
|
| 7072 | 7338 | } |
|
| 7073 | 7339 | ||
| 7074 | 7340 | /// Lower a global value symbol. |
|
| 7075 | - | unsafe fn lowerGlobalValue(self: &mut FnLowerer, sym: *unsafe resolver::Symbol, ty: resolver::Type) -> il::Val { |
|
| 7341 | + | unsafe fn lowerGlobalValue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sym: *unsafe resolver::Symbol, ty: resolver::Type) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 7076 | 7342 | // Function pointer reference: return the function's address directly. |
|
| 7077 | 7343 | // Functions have no separate storage cell in the data section. |
|
| 7078 | 7344 | if let case resolver::Type::Fn(_) = ty { |
|
| 7079 | 7345 | return il::Val::Reg(emitFnAddr(self, sym)); |
|
| 7080 | 7346 | } |
| 7082 | 7348 | ||
| 7083 | 7349 | return emitRead(self, src, 0, ty); |
|
| 7084 | 7350 | } |
|
| 7085 | 7351 | ||
| 7086 | 7352 | /// Lower an identifier that refers to a global symbol. |
|
| 7087 | - | unsafe fn lowerGlobalSymbol(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7353 | + | unsafe fn lowerGlobalSymbol 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7088 | 7354 | // First try to get a compile-time constant value. |
|
| 7089 | 7355 | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7090 | 7356 | return try constValueToVal(self, constVal, node); |
|
| 7091 | 7357 | } |
|
| 7092 | 7358 | // Otherwise get the symbol. |
| 7103 | 7369 | else => throw LowerError::UnexpectedNodeValue(node), |
|
| 7104 | 7370 | } |
|
| 7105 | 7371 | } |
|
| 7106 | 7372 | ||
| 7107 | 7373 | /// Lower an assignment to a static variable. |
|
| 7108 | - | unsafe fn lowerStaticAssign(self: &mut FnLowerer, target: *ast::Node, val: il::Val) throws (LowerError) { |
|
| 7374 | + | unsafe fn lowerStaticAssign 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: *ast::Node, val: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7109 | 7375 | let sym = try symOf(self, target); |
|
| 7110 | 7376 | let case resolver::SymbolData::Value { type, .. } = sym.data else { |
|
| 7111 | 7377 | throw LowerError::ImmutableAssignment; |
|
| 7112 | 7378 | }; |
|
| 7113 | 7379 | let dst = emitDataAddr(self, sym); |
| 7115 | 7381 | try emitStore(self, dst, 0, type, val); |
|
| 7116 | 7382 | } |
|
| 7117 | 7383 | ||
| 7118 | 7384 | /// Lower a scope access expression like `Module::Const` or `Union::Variant`. |
|
| 7119 | 7385 | /// This doesn't handle record literal variants. |
|
| 7120 | - | unsafe fn lowerScopeAccess(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7386 | + | unsafe fn lowerScopeAccess 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7121 | 7387 | // First try to get a compile-time constant value. |
|
| 7122 | 7388 | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7123 | 7389 | return try constValueToVal(self, constVal, node); |
|
| 7124 | 7390 | } |
|
| 7125 | 7391 | // Otherwise get the associated symbol. |
| 7172 | 7438 | } |
|
| 7173 | 7439 | } |
|
| 7174 | 7440 | ||
| 7175 | 7441 | /// Lower an expression AST node to an IL value. |
|
| 7176 | 7442 | /// This is the main expression dispatch, all expression nodes go through here. |
|
| 7177 | - | unsafe fn lowerExpr(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7443 | + | unsafe fn lowerExpr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7178 | 7444 | if self.low.options.debug { |
|
| 7179 | 7445 | set self.srcLoc.offset = node.span.offset; |
|
| 7180 | 7446 | } |
|
| 7181 | 7447 | let mut val: il::Val = undefined; |
|
| 7182 | 7448 |
| 7263 | 7529 | set val = try lowerArrayLit(self, node, elements); |
|
| 7264 | 7530 | } |
|
| 7265 | 7531 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
|
| 7266 | 7532 | set val = try lowerArrayRepeatLit(self, node, repeat); |
|
| 7267 | 7533 | } |
|
| 7534 | + | case ast::NodeValue::RegionApply { value, .. } => { |
|
| 7535 | + | set val = try lowerExpr(self, value); |
|
| 7536 | + | } |
|
| 7268 | 7537 | case ast::NodeValue::As(cast) => { |
|
| 7269 | 7538 | set val = try lowerCast(self, node, cast); |
|
| 7270 | 7539 | } |
|
| 7271 | 7540 | case ast::NodeValue::CondExpr(cond) => { |
|
| 7272 | 7541 | set val = try lowerCondExpr(self, node, cond); |
| 7335 | 7604 | /// are used. If a Radiance type doesn't fit in a machine word, it is passed |
|
| 7336 | 7605 | /// by reference. |
|
| 7337 | 7606 | /// |
|
| 7338 | 7607 | /// The IL doesn't track signedness - that's encoded in the instructions |
|
| 7339 | 7608 | /// (e.g., Slt vs Ult). |
|
| 7340 | - | unsafe fn ilType(self: &mut Lowerer, typ: resolver::Type) -> il::Type { |
|
| 7609 | + | unsafe fn ilType 'arena 'phase (self: &mut Lowerer 'arena 'phase, typ: resolver::Type) -> il::Type where 'arena: 'phase { |
|
| 7341 | 7610 | match typ { |
|
| 7342 | 7611 | case resolver::Type::Bool, |
|
| 7343 | 7612 | resolver::Type::I8, |
|
| 7344 | 7613 | resolver::Type::U8 => return il::Type::W8, |
|
| 7345 | 7614 | case resolver::Type::I16, |
| 7351 | 7620 | resolver::Type::Pointer { .. }, |
|
| 7352 | 7621 | resolver::Type::Slice { .. }, |
|
| 7353 | 7622 | resolver::Type::TraitObject { .. }, |
|
| 7354 | 7623 | resolver::Type::Array(_), |
|
| 7355 | 7624 | resolver::Type::Optional(_), |
|
| 7356 | - | resolver::Type::Fn(_) => return il::Type::W64, |
|
| 7625 | + | resolver::Type::Fn(_), |
|
| 7626 | + | resolver::Type::Session(_), |
|
| 7627 | + | resolver::Type::Cell { .. } => return il::Type::W64, |
|
| 7357 | 7628 | case resolver::Type::Nominal(_) => { |
|
| 7358 | 7629 | if resolver::isVoidUnion(typ) { |
|
| 7359 | 7630 | return il::Type::W8; |
|
| 7360 | 7631 | } |
|
| 7361 | 7632 | return il::Type::W64; |
| 7370 | 7641 | case resolver::Type::Int => return il::Type::W64, |
|
| 7371 | 7642 | case resolver::Type::Opaque => panic "ilType: opaque type must be behind a pointer", |
|
| 7372 | 7643 | else => panic "ilType: type cannot be lowered", |
|
| 7373 | 7644 | } |
|
| 7374 | 7645 | } |
|
| 7646 | + | ||
| 7647 | + | /// Lower one fixed-width MMIO operation without exposing a pointer value. |
|
| 7648 | + | unsafe fn lowerDevice 'arena 'phase 'function ( |
|
| 7649 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 7650 | + | call: ast::Call, |
|
| 7651 | + | typ: il::Type, |
|
| 7652 | + | writing: bool |
|
| 7653 | + | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 7654 | + | let count: u32 = 3 if writing else 2; |
|
| 7655 | + | if call.args.len <> count { |
|
| 7656 | + | throw LowerError::InvalidArgCount; |
|
| 7657 | + | } |
|
| 7658 | + | let handle = try lowerExpr(self, call.args[0]); |
|
| 7659 | + | let offset = try lowerExpr(self, call.args[1]); |
|
| 7660 | + | if writing { |
|
| 7661 | + | let value = try lowerExpr(self, call.args[2]); |
|
| 7662 | + | emit(self, il::Instr::DeviceWrite { typ, handle, offset, value }); |
|
| 7663 | + | return il::Val::Undef; |
|
| 7664 | + | } |
|
| 7665 | + | let dst = nextReg(self); |
|
| 7666 | + | emit(self, il::Instr::DeviceRead { typ, dst, handle, offset }); |
|
| 7667 | + | return il::Val::Reg(dst); |
|
| 7668 | + | } |
lib/std/lang/module.rad
+90 -53
| 12 | 12 | use std::lang::alloc; |
|
| 13 | 13 | use std::lang::ast; |
|
| 14 | 14 | use std::lang::strings; |
|
| 15 | 15 | ||
| 16 | 16 | /// Maximum number of modules tracked in a single compilation graph. |
|
| 17 | - | export constant MAX_MODULES: u32 = 128; |
|
| 17 | + | export constant MAX_MODULES: u32 = 192; |
|
| 18 | 18 | /// Maximum number of characters for a module path. |
|
| 19 | 19 | constant MAX_PATH_LEN: u32 = 256; |
|
| 20 | 20 | /// Maximum number of components that make up a logical module path. |
|
| 21 | 21 | constant MAX_MODULE_PATH_DEPTH: u32 = 16; |
|
| 22 | 22 | /// Filesystem separator used when constructing child paths. |
| 70 | 70 | name: *[u8], |
|
| 71 | 71 | /// Logical path from the root module to this module. |
|
| 72 | 72 | path: [*[u8]; MAX_MODULE_PATH_DEPTH], |
|
| 73 | 73 | /// Number of segments inside `path`. |
|
| 74 | 74 | pathDepth: u32, |
|
| 75 | + | /// Mutable module state shared by all entry views. |
|
| 76 | + | updates: *cell ModuleUpdates, |
|
| 77 | + | } |
|
| 78 | + | ||
| 79 | + | /// Copyable state stored in a module entry's cell. |
|
| 80 | + | export record ModuleUpdates: Copy { |
|
| 75 | 81 | /// Current lifecycle state. |
|
| 76 | 82 | state: ModuleState, |
|
| 77 | 83 | /// Child module identifiers declared directly inside this module. |
|
| 78 | 84 | children: [u16; MAX_MODULES], |
|
| 79 | 85 | /// Number of entries stored in `children`. |
| 84 | 90 | source: ?*[u8], |
|
| 85 | 91 | } |
|
| 86 | 92 | ||
| 87 | 93 | /// Dense storage for all modules referenced by the compilation unit. |
|
| 88 | 94 | export record ModuleGraph { |
|
| 89 | - | /// Permanent storage for module entries. |
|
| 90 | - | entries: *mut [ModuleEntry], |
|
| 95 | + | /// Entry identities indexed by module identifier. |
|
| 96 | + | entries: *mut [?*ModuleEntry], |
|
| 91 | 97 | /// Number of initialized entries. |
|
| 92 | 98 | entriesLen: u32, |
|
| 93 | - | /// AST arena. It must outlive the graph. |
|
| 94 | - | arena: ?*unsafe ast::NodeArena, |
|
| 99 | + | /// Arena for AST nodes and stable entry allocations. |
|
| 100 | + | arena: ?*unsafe mut ast::NodeArena, |
|
| 95 | 101 | } |
|
| 96 | 102 | ||
| 97 | 103 | /// Initialize an empty module graph backed by the provided storage. |
|
| 98 | - | /// The AST arena must outlive the returned graph. |
|
| 104 | + | /// The AST arena must outlive the graph and all retained entry views. |
|
| 105 | + | /// Entry allocation must occur outside speculative parser allocations. |
|
| 99 | 106 | export unsafe fn moduleGraph( |
|
| 100 | - | storage: *mut [ModuleEntry], |
|
| 107 | + | storage: *mut [?*ModuleEntry], |
|
| 101 | 108 | arena: &mut ast::NodeArena |
|
| 102 | 109 | ) -> ModuleGraph { |
|
| 110 | + | for i in 0..storage.len { |
|
| 111 | + | set storage[i] = nil; |
|
| 112 | + | } |
|
| 103 | 113 | return ModuleGraph { |
|
| 104 | 114 | entries: storage, |
|
| 105 | 115 | entriesLen: 0, |
|
| 106 | - | arena: arena as *unsafe ast::NodeArena, |
|
| 116 | + | arena: arena as *unsafe mut ast::NodeArena, |
|
| 107 | 117 | }; |
|
| 108 | 118 | } |
|
| 109 | 119 | ||
| 110 | 120 | /// Register a root module residing at `path` for a package. |
|
| 111 | - | export fn registerRoot(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 121 | + | export unsafe fn registerRoot(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 112 | 122 | let name = try basenameSlice(filePath); |
|
| 113 | 123 | return try registerRootWithName(graph, pool, packageId, name, filePath); |
|
| 114 | 124 | } |
|
| 115 | 125 | ||
| 116 | 126 | /// Register a root module with an explicit name and file path for a package. |
|
| 117 | - | export fn registerRootWithName( |
|
| 127 | + | export unsafe fn registerRootWithName( |
|
| 118 | 128 | graph: &mut ModuleGraph, pool: &mut strings::Pool, |
|
| 119 | 129 | packageId: u16, |
|
| 120 | 130 | name: *[u8], |
|
| 121 | 131 | filePath: *[u8] |
|
| 122 | 132 | ) -> u16 throws (ModuleError) { |
|
| 123 | 133 | let m = try allocModule(graph, pool, packageId, name, filePath); |
|
| 124 | 134 | try appendPathSegment(m, name); |
|
| 125 | 135 | ||
| 126 | - | set m.state = ModuleState::Registered; |
|
| 127 | - | ||
| 128 | - | return m.id; |
|
| 136 | + | let id = m.id; |
|
| 137 | + | publish(graph, m); |
|
| 138 | + | return id; |
|
| 129 | 139 | } |
|
| 130 | 140 | ||
| 131 | 141 | /// Register a child module. |
|
| 132 | 142 | /// Returns the module identifier. |
|
| 133 | - | export fn registerChild( |
|
| 143 | + | export unsafe fn registerChild( |
|
| 134 | 144 | graph: &mut ModuleGraph, pool: &mut strings::Pool, |
|
| 135 | 145 | parentId: u16, |
|
| 136 | 146 | name: *[u8], |
|
| 137 | 147 | filePath: *[u8] |
|
| 138 | 148 | ) -> u16 throws (ModuleError) { |
|
| 139 | 149 | assert name.len > 0, "registerChild: name must not be empty"; |
|
| 140 | 150 | assert filePath.len > 0, "registerChild: file path must not be empty"; |
|
| 141 | 151 | ||
| 142 | - | let parent = getMut(graph, parentId) |
|
| 152 | + | let parent = get(graph, parentId) |
|
| 143 | 153 | else throw ModuleError::NotFound(parentId); |
|
| 144 | 154 | ||
| 145 | 155 | // If the child already exists under this parent, return it. |
|
| 146 | 156 | if let child = findChild(graph, name, parentId) { |
|
| 147 | 157 | return child.id; |
|
| 148 | 158 | } |
|
| 149 | 159 | // Inherit packageId from parent. |
|
| 150 | 160 | let m = try allocModule(graph, pool, parent.packageId, name, filePath); |
|
| 151 | 161 | ||
| 152 | - | set m.state = ModuleState::Registered; |
|
| 153 | 162 | set m.parent = parentId; |
|
| 154 | 163 | ||
| 155 | 164 | // Inherit path prefix from parent. |
|
| 156 | 165 | for p in moduleQualifiedPath(parent) { |
|
| 157 | 166 | try appendPathSegment(m, p); |
|
| 158 | 167 | } |
|
| 159 | 168 | try appendPathSegment(m, name); |
|
| 160 | 169 | ||
| 161 | - | return try addChild(parent, m.id); |
|
| 170 | + | let id = try addChild(parent, m.id); |
|
| 171 | + | publish(graph, m); |
|
| 172 | + | return id; |
|
| 162 | 173 | } |
|
| 163 | 174 | ||
| 164 | 175 | /// Fetch a read-only view of the module identified by `id`. |
|
| 165 | 176 | export fn get(graph: &ModuleGraph, id: u16) -> ?*ModuleEntry { |
|
| 166 | 177 | if not isValidId(graph, id) { |
|
| 167 | 178 | return nil; |
|
| 168 | 179 | } |
|
| 169 | - | return &graph.entries[id as u32]; |
|
| 170 | - | } |
|
| 171 | - | ||
| 172 | - | /// Access a mutable entry by identifier. |
|
| 173 | - | fn getMut(graph: &mut ModuleGraph, id: u16) -> ?*mut ModuleEntry { |
|
| 174 | - | if not isValidId(graph, id) { |
|
| 175 | - | return nil; |
|
| 176 | - | } |
|
| 177 | - | return &mut graph.entries[id as u32]; |
|
| 180 | + | return graph.entries[id as u32]; |
|
| 178 | 181 | } |
|
| 179 | 182 | ||
| 180 | 183 | /// Return the identifier of the child stored at `index`. |
|
| 181 | 184 | export fn childAt(m: *ModuleEntry, index: u32) -> u16 { |
|
| 182 | - | assert index < m.childrenLen, "childAt: index must be valid"; |
|
| 183 | - | return m.children[index]; |
|
| 185 | + | let updates = *m.updates; |
|
| 186 | + | assert index < updates.childrenLen, "childAt: index must be valid"; |
|
| 187 | + | return updates.children[index]; |
|
| 184 | 188 | } |
|
| 185 | 189 | ||
| 186 | 190 | /// Public accessor for a module's directory prefix. |
|
| 187 | 191 | export fn moduleDir(m: *ModuleEntry) -> *[u8] { |
|
| 188 | 192 | return &m.filePath[..m.dirLen]; |
| 197 | 201 | /// Retrieve the lifecycle state for `id`. |
|
| 198 | 202 | export fn state(graph: &ModuleGraph, id: u16) -> ModuleState throws (ModuleError) { |
|
| 199 | 203 | let m = get(graph, id) else { |
|
| 200 | 204 | throw ModuleError::NotFound(id); |
|
| 201 | 205 | }; |
|
| 202 | - | return m.state; |
|
| 206 | + | return (*m.updates).state; |
|
| 203 | 207 | } |
|
| 204 | 208 | ||
| 205 | 209 | /// Record the parsed AST root for `id`. |
|
| 206 | 210 | export fn setAst(graph: &mut ModuleGraph, id: u16, root: *ast::Node) throws (ModuleError) { |
|
| 207 | - | let m = getMut(graph, id) else throw ModuleError::NotFound(id); |
|
| 208 | - | set m.ast = root; |
|
| 209 | - | set m.state = ModuleState::Parsed; |
|
| 211 | + | let m = get(graph, id) else throw ModuleError::NotFound(id); |
|
| 212 | + | let mut updates = *m.updates; |
|
| 213 | + | set updates.ast = root; |
|
| 214 | + | set updates.state = ModuleState::Parsed; |
|
| 215 | + | set *m.updates = updates; |
|
| 210 | 216 | } |
|
| 211 | 217 | ||
| 212 | 218 | /// Set the source text for a module. |
|
| 213 | 219 | export fn setSource(graph: &mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) { |
|
| 214 | - | let m = getMut(graph, id) else throw ModuleError::NotFound(id); |
|
| 215 | - | set m.source = source; |
|
| 220 | + | let m = get(graph, id) else throw ModuleError::NotFound(id); |
|
| 221 | + | let mut updates = *m.updates; |
|
| 222 | + | set updates.source = source; |
|
| 223 | + | set *m.updates = updates; |
|
| 216 | 224 | } |
|
| 217 | 225 | ||
| 218 | 226 | /// Look up a child module by name under the given parent. |
|
| 219 | 227 | export fn findChild(graph: &ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry { |
|
| 220 | 228 | assert isValidId(graph, parentId), "findChild: parent identifier is valid"; |
|
| 221 | 229 | ||
| 222 | - | let parent = &graph.entries[parentId as u32]; |
|
| 223 | - | for i in 0..parent.childrenLen { |
|
| 224 | - | let childId = parent.children[i]; |
|
| 225 | - | let child = &graph.entries[childId as u32]; |
|
| 230 | + | let parent = get(graph, parentId) else panic; |
|
| 231 | + | let updates = *parent.updates; |
|
| 232 | + | for i in 0..updates.childrenLen { |
|
| 233 | + | let childId = updates.children[i]; |
|
| 234 | + | let child = get(graph, childId) else panic; |
|
| 226 | 235 | if mem::eq(child.name, name) { |
|
| 227 | 236 | return child; |
|
| 228 | 237 | } |
|
| 229 | 238 | } |
|
| 230 | 239 | return nil; |
| 266 | 275 | ||
| 267 | 276 | /// Register a module from a file path, creating the full hierarchy as needed. |
|
| 268 | 277 | /// The path is split into components and the module hierarchy is built accordingly. |
|
| 269 | 278 | /// If `rootId` is `nil`, registers a new root for the given package. |
|
| 270 | 279 | /// Returns the module ID of the last component. |
|
| 271 | - | export fn registerFromPath(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 280 | + | export unsafe fn registerFromPath(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 272 | 281 | let root = rootId else { |
|
| 273 | 282 | return try registerRoot(graph, pool, packageId, filePath); |
|
| 274 | 283 | }; |
|
| 275 | 284 | let rootEntry = get(graph, root) else { |
|
| 276 | 285 | panic "registerFromPath: root is missing from storage"; |
| 311 | 320 | } |
|
| 312 | 321 | return try registerChild(graph, pool, parentId, childName, filePath); |
|
| 313 | 322 | } |
|
| 314 | 323 | ||
| 315 | 324 | /// Allocate a fresh entry in the graph. |
|
| 316 | - | fn allocModule(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) { |
|
| 325 | + | unsafe fn allocModule(graph: &mut ModuleGraph, pool: &mut strings::Pool, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) { |
|
| 317 | 326 | if graph.entriesLen >= graph.entries.len { |
|
| 318 | 327 | throw ModuleError::CapacityExceeded; |
|
| 319 | 328 | } |
|
| 320 | 329 | let idx = graph.entriesLen; |
|
| 321 | - | set graph.entriesLen += 1; |
|
| 330 | + | let arena = graph.arena else panic; |
|
| 331 | + | let state = try! alloc::alloc(&mut arena.arena, @sizeOf(ModuleUpdates), @alignOf(ModuleUpdates)) as *mut ModuleUpdates; |
|
| 332 | + | set *state = ModuleUpdates { |
|
| 333 | + | state: ModuleState::Registered, |
|
| 334 | + | children: [0; MAX_MODULES], |
|
| 335 | + | childrenLen: 0, |
|
| 336 | + | ast: nil, |
|
| 337 | + | source: nil, |
|
| 338 | + | }; |
|
| 339 | + | let updates = state as *cell ModuleUpdates; |
|
| 322 | 340 | ||
| 323 | 341 | // TODO: This is a common pattern that needs better syntax. |
|
| 324 | - | let m = &mut graph.entries[idx]; |
|
| 342 | + | let m = try! alloc::alloc(&mut arena.arena, @sizeOf(ModuleEntry), @alignOf(ModuleEntry)) as *mut ModuleEntry; |
|
| 325 | 343 | set *m = ModuleEntry { |
|
| 326 | 344 | id: idx as u16, |
|
| 327 | 345 | packageId, |
|
| 328 | 346 | parent: nil, |
|
| 329 | 347 | filePath, |
|
| 330 | - | dirLen: 0, |
|
| 348 | + | dirLen: dirLength(filePath), |
|
| 331 | 349 | name: strings::intern(pool, name), |
|
| 332 | 350 | path: [""; MAX_MODULE_PATH_DEPTH], |
|
| 333 | 351 | pathDepth: 0, |
|
| 334 | - | state: ModuleState::Vacant, |
|
| 335 | - | children: [0; MAX_MODULES], |
|
| 336 | - | childrenLen: 0, |
|
| 337 | - | ast: nil, |
|
| 338 | - | source: nil, |
|
| 352 | + | updates, |
|
| 339 | 353 | }; |
|
| 340 | - | set m.dirLen = dirLength(m.filePath); |
|
| 341 | 354 | ||
| 342 | 355 | return m; |
|
| 343 | 356 | } |
|
| 344 | 357 | ||
| 345 | 358 | /// Append a logical path segment (module identifier) to the entry. |
| 350 | 363 | set entry.path[entry.pathDepth] = segment; |
|
| 351 | 364 | set entry.pathDepth += 1; |
|
| 352 | 365 | } |
|
| 353 | 366 | ||
| 354 | 367 | /// Append a child identifier to the parent's child list. |
|
| 355 | - | fn addChild(parent: &mut ModuleEntry, childId: u16) -> u16 throws (ModuleError) { |
|
| 356 | - | if parent.childrenLen >= parent.children.len { |
|
| 368 | + | fn addChild(parent: *ModuleEntry, childId: u16) -> u16 throws (ModuleError) { |
|
| 369 | + | let mut updates = *parent.updates; |
|
| 370 | + | if updates.childrenLen >= updates.children.len { |
|
| 357 | 371 | throw ModuleError::CapacityExceeded; |
|
| 358 | 372 | } |
|
| 359 | - | set parent.children[parent.childrenLen] = childId; |
|
| 360 | - | set parent.childrenLen += 1; |
|
| 373 | + | set updates.children[updates.childrenLen] = childId; |
|
| 374 | + | set updates.childrenLen += 1; |
|
| 375 | + | set *parent.updates = updates; |
|
| 361 | 376 | ||
| 362 | 377 | return childId; |
|
| 363 | 378 | } |
|
| 364 | 379 | ||
| 380 | + | /// Publish a fully initialized module entry in identifier order. |
|
| 381 | + | fn publish(graph: &mut ModuleGraph, entry: *ModuleEntry) { |
|
| 382 | + | assert entry.id as u32 == graph.entriesLen; |
|
| 383 | + | set graph.entries[graph.entriesLen] = entry; |
|
| 384 | + | set graph.entriesLen += 1; |
|
| 385 | + | } |
|
| 386 | + | ||
| 387 | + | /// Return the number of registered children. |
|
| 388 | + | export fn childCount(entry: *ModuleEntry) -> u32 { |
|
| 389 | + | return (*entry.updates).childrenLen; |
|
| 390 | + | } |
|
| 391 | + | ||
| 392 | + | /// Return the current parsed root for an entry. |
|
| 393 | + | export fn astFor(entry: *ModuleEntry) -> ?*ast::Node { |
|
| 394 | + | return (*entry.updates).ast; |
|
| 395 | + | } |
|
| 396 | + | ||
| 397 | + | /// Return the current source text for an entry. |
|
| 398 | + | export fn sourceFor(entry: *ModuleEntry) -> ?*[u8] { |
|
| 399 | + | return (*entry.updates).source; |
|
| 400 | + | } |
|
| 401 | + | ||
| 365 | 402 | /// Check if `id` points at an allocated entry. |
|
| 366 | 403 | fn isValidId(graph: &ModuleGraph, id: u16) -> bool { |
|
| 367 | 404 | return (id as u32) < graph.entriesLen; |
|
| 368 | 405 | } |
|
| 369 | 406 |
lib/std/lang/module/printer.rad
+7 -6
| 42 | 42 | for seg, i in path { set buf[i] = sexpr::sym(seg); } |
|
| 43 | 43 | set pathBuf = buf; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | let mut childBuf: *[sexpr::Expr] = &[]; |
|
| 47 | - | if entry.childrenLen > 0 { |
|
| 48 | - | let buf = try! sexpr::allocExprs(a, entry.childrenLen); |
|
| 49 | - | for i in 0..entry.childrenLen { |
|
| 50 | - | if let child = super::get(graph, entry.children[i]) { |
|
| 47 | + | let childCount = super::childCount(entry); |
|
| 48 | + | if childCount > 0 { |
|
| 49 | + | let buf = try! sexpr::allocExprs(a, childCount); |
|
| 50 | + | for i in 0..childCount { |
|
| 51 | + | if let child = super::get(graph, super::childAt(entry, i)) { |
|
| 51 | 52 | set buf[i] = subtreeToExpr(a, graph, child); |
|
| 52 | 53 | } |
|
| 53 | 54 | } |
|
| 54 | 55 | set childBuf = buf; |
|
| 55 | 56 | } |
|
| 56 | 57 | ||
| 57 | 58 | return sexpr::block(a, "module", &[ |
|
| 58 | 59 | sexpr::sym(entry.name), |
|
| 59 | 60 | sexpr::list(a, "id", &[sexpr::sym(idText)]), |
|
| 60 | - | sexpr::list(a, "state", &[stateToExpr(entry.state)]), |
|
| 61 | + | sexpr::list(a, "state", &[stateToExpr((*entry.updates).state)]), |
|
| 61 | 62 | sexpr::Expr::Str(entry.filePath), |
|
| 62 | 63 | sexpr::Expr::List { head: "::", tail: pathBuf, multiline: false } |
|
| 63 | 64 | ], childBuf); |
|
| 64 | 65 | } |
|
| 65 | 66 | ||
| 66 | 67 | /// Print the entire module graph in S-expression format. |
|
| 67 | 68 | export unsafe fn printGraph(graph: &super::ModuleGraph, arena: &mut alloc::Arena) { |
|
| 68 | 69 | // Print all root modules. |
|
| 69 | 70 | for i in 0..graph.entriesLen { |
|
| 70 | - | let entry = &graph.entries[i]; |
|
| 71 | + | let entry = super::get(graph, i as u16) else panic; |
|
| 71 | 72 | if entry.parent == nil { |
|
| 72 | 73 | sexpr::print(subtreeToExpr(arena, graph, entry), 0); |
|
| 73 | 74 | io::print("\n"); |
|
| 74 | 75 | } |
|
| 75 | 76 | } |
lib/std/lang/module/tests.rad
+58 -13
| 1 | 1 | //! Tests for the module loader. |
|
| 2 | 2 | ||
| 3 | 3 | use std::mem; |
|
| 4 | 4 | use std::testing; |
|
| 5 | 5 | use std::lang::ast; |
|
| 6 | + | use std::lang::alloc; |
|
| 7 | + | use std::lang::parser; |
|
| 8 | + | use std::lang::scanner; |
|
| 6 | 9 | use std::lang::strings; |
|
| 7 | 10 | ||
| 8 | 11 | /// Test arena backing storage. |
|
| 9 | - | static TEST_ARENA: [u8; 4096] = [0; 4096]; |
|
| 12 | + | static TEST_ARENA: [u8; 16384] = [0; 16384]; |
|
| 10 | 13 | /// Interned string pool. |
|
| 11 | 14 | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 12 | 15 | ||
| 13 | 16 | fn expectSliceEq(actual: *[u8], expected: *[u8]) |
|
| 14 | 17 | throws (testing::TestError) |
| 27 | 30 | try expectSliceEq(actual[i], expected[i]); |
|
| 28 | 31 | } |
|
| 29 | 32 | } |
|
| 30 | 33 | ||
| 31 | 34 | @test unsafe fn testRegisterChildren() throws (testing::TestError) { |
|
| 32 | - | static storage: [super::ModuleEntry; 4] = undefined; |
|
| 35 | + | static storage: [?*super::ModuleEntry; 4] = [nil; 4]; |
|
| 33 | 36 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 34 | 37 | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 35 | 38 | ||
| 36 | 39 | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/root.rad") catch { |
|
| 37 | 40 | throw testing::TestError::Failed; |
| 65 | 68 | ||
| 66 | 69 | let parent = super::get(&graph, rootId) else { |
|
| 67 | 70 | throw testing::TestError::Failed; |
|
| 68 | 71 | }; |
|
| 69 | 72 | try testing::expect(graph.entriesLen == 3); |
|
| 70 | - | try testing::expect(parent.childrenLen == 2); |
|
| 73 | + | try testing::expect(super::childCount(parent) == 2); |
|
| 71 | 74 | try testing::expect(super::childAt(parent, 0) == firstId); |
|
| 72 | 75 | try testing::expect(super::childAt(parent, 1) == secondId); |
|
| 73 | 76 | } |
|
| 74 | 77 | ||
| 75 | 78 | @test unsafe fn testRegisterChildReusesExisting() throws (testing::TestError) { |
|
| 76 | - | static storage: [super::ModuleEntry; 4] = undefined; |
|
| 79 | + | static storage: [?*super::ModuleEntry; 4] = [nil; 4]; |
|
| 77 | 80 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 78 | 81 | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 79 | 82 | ||
| 80 | 83 | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/main.rad") catch { |
|
| 81 | 84 | throw testing::TestError::Failed; |
| 90 | 93 | ||
| 91 | 94 | let parent = super::get(&graph, rootId) else { |
|
| 92 | 95 | throw testing::TestError::Failed; |
|
| 93 | 96 | }; |
|
| 94 | 97 | try testing::expect(graph.entriesLen == 2); |
|
| 95 | - | try testing::expect(parent.childrenLen == 1); |
|
| 98 | + | try testing::expect(super::childCount(parent) == 1); |
|
| 96 | 99 | } |
|
| 97 | 100 | ||
| 98 | 101 | @test fn testTrimExtensionWithRadExtension() throws (testing::TestError) { |
|
| 99 | 102 | let input = "parser.rad"; |
|
| 100 | 103 | let result = super::trimExtension(input) |
| 142 | 145 | let result = super::parsePath(path, &mut components[..]); |
|
| 143 | 146 | try testing::expect(result == nil); |
|
| 144 | 147 | } |
|
| 145 | 148 | ||
| 146 | 149 | @test unsafe fn testRegisterFromPathHierarchy() throws (testing::TestError) { |
|
| 147 | - | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 150 | + | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 148 | 151 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 149 | 152 | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 150 | 153 | ||
| 151 | 154 | // Register root module. |
|
| 152 | 155 | let stdId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch { |
| 183 | 186 | try expectSliceEq(super::moduleDir(parser), "lib/std/lang/"); |
|
| 184 | 187 | try expectPathSegments(parser, &["std", "lang", "parser"]); |
|
| 185 | 188 | ||
| 186 | 189 | // Verify parent-child relationships. |
|
| 187 | 190 | try testing::expect(graph.entriesLen == 3); |
|
| 188 | - | try testing::expect(std.childrenLen == 1); |
|
| 191 | + | try testing::expect(super::childCount(std) == 1); |
|
| 189 | 192 | try testing::expect(super::childAt(std, 0) == langId); |
|
| 190 | - | try testing::expect(lang.childrenLen == 1); |
|
| 193 | + | try testing::expect(super::childCount(lang) == 1); |
|
| 191 | 194 | try testing::expect(super::childAt(lang, 0) == parserId); |
|
| 192 | - | try testing::expect(parser.childrenLen == 0); |
|
| 195 | + | try testing::expect(super::childCount(parser) == 0); |
|
| 193 | 196 | } |
|
| 194 | 197 | ||
| 195 | 198 | @test unsafe fn testRegisterFromPathMissingParent() throws (testing::TestError) { |
|
| 196 | - | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 199 | + | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 197 | 200 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 198 | 201 | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 199 | 202 | ||
| 200 | 203 | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch { |
|
| 201 | 204 | throw testing::TestError::Failed; |
| 206 | 209 | }; |
|
| 207 | 210 | throw testing::TestError::Failed; |
|
| 208 | 211 | } |
|
| 209 | 212 | ||
| 210 | 213 | @test unsafe fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) { |
|
| 211 | - | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 214 | + | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 212 | 215 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 213 | 216 | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 214 | 217 | ||
| 215 | 218 | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "std.rad") catch { |
|
| 216 | 219 | throw testing::TestError::Failed; |
| 221 | 224 | }; |
|
| 222 | 225 | throw testing::TestError::Failed; |
|
| 223 | 226 | } |
|
| 224 | 227 | ||
| 225 | 228 | @test unsafe fn testRegisterFromPathRegistersRoot() throws (testing::TestError) { |
|
| 226 | - | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 229 | + | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 227 | 230 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 228 | 231 | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 229 | 232 | ||
| 230 | 233 | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "lib/std.rad") catch { |
|
| 231 | 234 | throw testing::TestError::Failed; |
| 240 | 243 | try expectSliceEq(root.filePath, "lib/std.rad"); |
|
| 241 | 244 | try expectPathSegments(root, &["std"]); |
|
| 242 | 245 | } |
|
| 243 | 246 | ||
| 244 | 247 | @test unsafe fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) { |
|
| 245 | - | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 248 | + | static storage: [?*super::ModuleEntry; 8] = [nil; 8]; |
|
| 246 | 249 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 247 | 250 | let mut graph = super::moduleGraph(&mut storage[..], &mut arena); |
|
| 248 | 251 | ||
| 249 | 252 | let rootId = try super::registerFromPath(&mut graph, &mut STRING_POOL, 0, nil, "src/pkg/root.rad") catch { |
|
| 250 | 253 | throw testing::TestError::Failed; |
| 268 | 271 | throw testing::TestError::Failed; |
|
| 269 | 272 | }; |
|
| 270 | 273 | try expectSliceEq(parser.name, "parser"); |
|
| 271 | 274 | try expectPathSegments(parser, &["root", "lang", "parser"]); |
|
| 272 | 275 | } |
|
| 276 | + | /// Move the graph owner while its published entries retain their identities. |
|
| 277 | + | fn relocate(graph: super::ModuleGraph) -> super::ModuleGraph { |
|
| 278 | + | return graph; |
|
| 279 | + | } |
|
| 280 | + | @test unsafe fn testGraphRelocation() throws (testing::TestError) { |
|
| 281 | + | static DATA: [u8; 16384] = [0; 16384]; |
|
| 282 | + | static ENTRIES: [?*super::ModuleEntry; 2] = [nil; 2]; |
|
| 283 | + | static POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 284 | + | let mut arena = ast::nodeArena(&mut DATA[..]); |
|
| 285 | + | let mut original = super::moduleGraph(&mut ENTRIES[..], &mut arena); |
|
| 286 | + | let rootId = try! super::registerRoot(&mut original, &mut POOL, 0, "root.rad"); |
|
| 287 | + | let root = super::get(&original, rootId) else panic; |
|
| 288 | + | let alias = root; |
|
| 289 | + | let mut graph = relocate(original); |
|
| 290 | + | let child = try! super::registerChild(&mut graph, &mut POOL, rootId, "child", "root/child.rad"); |
|
| 291 | + | try testing::expect(super::childCount(root) == 1); |
|
| 292 | + | try testing::expect(super::childAt(alias, 0) == child); |
|
| 293 | + | let published = super::get(&graph, rootId) else panic; |
|
| 294 | + | try testing::expect(published as u64 == root as u64); |
|
| 295 | + | let saved = alloc::used(&arena.arena); |
|
| 296 | + | let reused = try! super::registerChild(&mut graph, &mut POOL, rootId, "child", "root/child.rad"); |
|
| 297 | + | try testing::expect(reused == child); |
|
| 298 | + | try testing::expect(alloc::used(&arena.arena) == saved); |
|
| 299 | + | let mut failed = false; |
|
| 300 | + | try super::registerChild(&mut graph, &mut POOL, rootId, "full", "root/full.rad") catch { |
|
| 301 | + | set failed = true; |
|
| 302 | + | }; |
|
| 303 | + | try testing::expect(failed); |
|
| 304 | + | try testing::expect(graph.entriesLen == 2); |
|
| 305 | + | try testing::expect(super::childCount(alias) == 1); |
|
| 306 | + | try testing::expect(alloc::used(&arena.arena) == saved); |
|
| 307 | + | let source = "fn answer() -> u32 { return 42; }"; |
|
| 308 | + | let parsed: *ast::Node = try! parser::parse(scanner::SourceLoc::String, source, &mut arena, &mut POOL); |
|
| 309 | + | try! super::setAst(&mut graph, rootId, parsed); |
|
| 310 | + | try! super::setSource(&mut graph, rootId, source); |
|
| 311 | + | let retainedAst = super::astFor(alias) else panic; |
|
| 312 | + | try testing::expect(retainedAst as u64 == parsed as u64); |
|
| 313 | + | let retainedSource = super::sourceFor(root) else panic; |
|
| 314 | + | try testing::expect(mem::eq(retainedSource, source)); |
|
| 315 | + | try testing::expect(try! super::state(&graph, rootId) == super::ModuleState::Parsed); |
|
| 316 | + | ||
| 317 | + | } |
lib/std/lang/package.rad
+1 -1
| 31 | 31 | set pkg.rootModuleId = nil; |
|
| 32 | 32 | ||
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Register a module described by the file path. |
|
| 36 | - | export fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, pool: &mut strings::Pool, filePath: *[u8]) -> u16 |
|
| 36 | + | export unsafe fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, pool: &mut strings::Pool, filePath: *[u8]) -> u16 |
|
| 37 | 37 | throws (module::ModuleError) |
|
| 38 | 38 | { |
|
| 39 | 39 | let modId = try module::registerFromPath(graph, pool, pkg.id, pkg.rootModuleId, filePath); |
|
| 40 | 40 | // First registered module becomes the root. |
|
| 41 | 41 | if pkg.rootModuleId == nil { |
lib/std/lang/parser.rad
+246 -25
| 3 | 3 | ||
| 4 | 4 | use std::mem; |
|
| 5 | 5 | use std::io; |
|
| 6 | 6 | use std::fmt; |
|
| 7 | 7 | use std::lang::alloc; |
|
| 8 | - | use std::lang::types; |
|
| 9 | 8 | use std::lang::ast; |
|
| 10 | 9 | use std::lang::strings; |
|
| 11 | 10 | use std::lang::scanner; |
|
| 12 | 11 | ||
| 13 | 12 | /// Maximum `u32` value. |
| 85 | 84 | count: u32, |
|
| 86 | 85 | } |
|
| 87 | 86 | ||
| 88 | 87 | /// Snapshot of parser state for speculative parsing. |
|
| 89 | 88 | record SavedState: Copy { |
|
| 89 | + | /// Scanner position, tokens, diagnostics, and expression context. |
|
| 90 | 90 | parser: Parser, |
|
| 91 | + | /// First byte available for tentative allocations. |
|
| 91 | 92 | arena: u32, |
|
| 93 | + | /// First node identifier available to the tentative parse. |
|
| 92 | 94 | nextId: u32, |
|
| 93 | 95 | } |
|
| 94 | 96 | ||
| 95 | 97 | /// Operator metadata for precedence climbing. |
|
| 96 | 98 | record OpInfo: Copy { |
| 383 | 385 | loop { |
|
| 384 | 386 | match p.current.kind { |
|
| 385 | 387 | case scanner::TokenKind::Dot => { |
|
| 386 | 388 | advance(p); |
|
| 387 | 389 | ||
| 388 | - | let field = try parseIdent(p, "expected field name after `.`"); |
|
| 390 | + | let mut field: *ast::Node = undefined; |
|
| 391 | + | if consume(p, scanner::TokenKind::Set) { |
|
| 392 | + | set field = node(p, ast::NodeValue::Ident(p.previous.source)); |
|
| 393 | + | } else { |
|
| 394 | + | set field = try parseIdent(p, "expected field name after `.`"); |
|
| 395 | + | } |
|
| 389 | 396 | set result = node(p, ast::NodeValue::FieldAccess( |
|
| 390 | 397 | ast::Access { parent: result, child: field } |
|
| 391 | 398 | )); |
|
| 392 | 399 | } |
|
| 393 | 400 | case scanner::TokenKind::ColonColon => { |
| 396 | 403 | let ident = try parseIdent(p, "expected identifier after `::`"); |
|
| 397 | 404 | set result = node(p, ast::NodeValue::ScopeAccess( |
|
| 398 | 405 | ast::Access { parent: result, child: ident } |
|
| 399 | 406 | )); |
|
| 400 | 407 | } |
|
| 408 | + | case scanner::TokenKind::Region => { |
|
| 409 | + | let regions = try parseRegions(p); |
|
| 410 | + | set result = node(p, ast::NodeValue::RegionApply { value: result, regions }); |
|
| 411 | + | } |
|
| 401 | 412 | case scanner::TokenKind::LBracket => { |
|
| 402 | 413 | set result = try parseSubscriptOrSlice(p, result); |
|
| 403 | 414 | } |
|
| 404 | 415 | case scanner::TokenKind::LParen => { |
|
| 405 | 416 | set result = try parseCall(p, result); |
| 569 | 580 | ast::NodeValue::WhileLet(_), |
|
| 570 | 581 | ast::NodeValue::For(_), |
|
| 571 | 582 | ast::NodeValue::Loop { .. }, |
|
| 572 | 583 | ast::NodeValue::Match(_), |
|
| 573 | 584 | ast::NodeValue::Block(_), |
|
| 585 | + | ast::NodeValue::RegionBlock { .. }, |
|
| 574 | 586 | ast::NodeValue::FnDecl(_), |
|
| 575 | 587 | ast::NodeValue::RecordDecl(_), |
|
| 576 | 588 | ast::NodeValue::UnionDecl(_), |
|
| 577 | 589 | ast::NodeValue::TraitDecl { .. }, |
|
| 578 | 590 | ast::NodeValue::InstanceDecl { .. }, |
| 906 | 918 | } |
|
| 907 | 919 | case scanner::TokenKind::Match => { |
|
| 908 | 920 | return try parseMatch(p); |
|
| 909 | 921 | } |
|
| 910 | 922 | case scanner::TokenKind::Let => { |
|
| 923 | + | if isRegionBlock(p) { |
|
| 924 | + | return try parseRegionBlock(p); |
|
| 925 | + | } |
|
| 911 | 926 | advance(p); |
|
| 912 | 927 | if consume(p, scanner::TokenKind::Case) { |
|
| 913 | 928 | return try parseLetCase(p); |
|
| 914 | 929 | } |
|
| 915 | 930 | if consume(p, scanner::TokenKind::Mut) { |
| 935 | 950 | } |
|
| 936 | 951 | case scanner::TokenKind::Record => { |
|
| 937 | 952 | return try parseRecordDecl(p, attrs); |
|
| 938 | 953 | } |
|
| 939 | 954 | case scanner::TokenKind::Use => { |
|
| 955 | + | if isSessionBlock(p) { |
|
| 956 | + | return try parseSessionBlock(p); |
|
| 957 | + | } |
|
| 940 | 958 | return try parseUse(p, attrs); |
|
| 941 | 959 | } |
|
| 942 | 960 | case scanner::TokenKind::Mod => { |
|
| 943 | 961 | return try parseMod(p, attrs); |
|
| 944 | 962 | } |
| 952 | 970 | return try parseExprStmt(p); |
|
| 953 | 971 | } |
|
| 954 | 972 | } |
|
| 955 | 973 | } |
|
| 956 | 974 | ||
| 975 | + | /// Return whether the current `let` statement starts a regional block. |
|
| 976 | + | unsafe fn isRegionBlock(p: &Parser) -> bool { |
|
| 977 | + | let mut lookahead = p.scanner; |
|
| 978 | + | return scanner::next(&mut lookahead).kind == scanner::TokenKind::Ident |
|
| 979 | + | and scanner::next(&mut lookahead).kind == scanner::TokenKind::Colon |
|
| 980 | + | and scanner::next(&mut lookahead).kind == scanner::TokenKind::Region; |
|
| 981 | + | } |
|
| 982 | + | ||
| 983 | + | /// Return whether the current `use` statement has an allocation-session header. |
|
| 984 | + | unsafe fn isSessionBlock(p: &mut Parser) -> bool { |
|
| 985 | + | let saved = saveState(p); |
|
| 986 | + | advance(p); |
|
| 987 | + | let source: ?*ast::Node = try? parseUnaryExpr(p); |
|
| 988 | + | let result = source <> nil |
|
| 989 | + | and consume(p, scanner::TokenKind::As) |
|
| 990 | + | and consume(p, scanner::TokenKind::Ident) |
|
| 991 | + | and check(p, scanner::TokenKind::In); |
|
| 992 | + | restoreState(p, &saved); |
|
| 993 | + | return result; |
|
| 994 | + | } |
|
| 995 | + | ||
| 957 | 996 | /// Parse statements until the specified ending token is encountered. |
|
| 958 | 997 | /// |
|
| 959 | 998 | /// Returns the completed immutable statement list. |
|
| 960 | 999 | export unsafe fn parseStmtsUntil(p: &mut Parser, end: scanner::TokenKind, capacity: u32) -> *[*ast::Node] |
|
| 961 | 1000 | throws (ParseError) |
| 1056 | 1095 | arena: alloc::save(&p.arena.arena), |
|
| 1057 | 1096 | nextId: p.arena.nextId, |
|
| 1058 | 1097 | }; |
|
| 1059 | 1098 | } |
|
| 1060 | 1099 | ||
| 1061 | - | /// Restore parser state from a snapshot, fully undoing any |
|
| 1062 | - | /// side effects of a failed speculative parse. |
|
| 1100 | + | /// Restore scanner state, diagnostics, arena storage, and node identifiers. |
|
| 1101 | + | /// Tentative nodes must be unreachable from all state retained by the caller. |
|
| 1102 | + | /// Interned tokens retain source storage, which must outlive the string pool. |
|
| 1063 | 1103 | unsafe fn restoreState(p: &mut Parser, s: &SavedState) { |
|
| 1064 | 1104 | set *p = s.parser; |
|
| 1065 | 1105 | alloc::restore(&mut p.arena.arena, s.arena); |
|
| 1066 | 1106 | set p.arena.nextId = s.nextId; |
|
| 1067 | 1107 | } |
| 1537 | 1577 | set p.context = saved; |
|
| 1538 | 1578 | ||
| 1539 | 1579 | return pattern; |
|
| 1540 | 1580 | } |
|
| 1541 | 1581 | ||
| 1582 | + | /// Parse a region name. |
|
| 1583 | + | unsafe fn parseRegion(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 1584 | + | let name = try expect(p, scanner::TokenKind::Region, "expected region name"); |
|
| 1585 | + | return node(p, ast::NodeValue::Region { name, parent: nil }); |
|
| 1586 | + | } |
|
| 1587 | + | ||
| 1588 | + | /// Parse consecutive region names. |
|
| 1589 | + | unsafe fn parseRegions(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) { |
|
| 1590 | + | let mut regions = ast::nodeSlice(p.arena, 4); |
|
| 1591 | + | while check(p, scanner::TokenKind::Region) { |
|
| 1592 | + | regions.append(try parseRegion(p), p.allocator); |
|
| 1593 | + | } |
|
| 1594 | + | return regions; |
|
| 1595 | + | } |
|
| 1596 | + | ||
| 1597 | + | /// Parse region bounds for a declaration. |
|
| 1598 | + | unsafe fn parseRegionBounds(p: &mut Parser, regions: &mut [*ast::Node]) |
|
| 1599 | + | throws (ParseError) |
|
| 1600 | + | { |
|
| 1601 | + | if not consume(p, scanner::TokenKind::Where) { |
|
| 1602 | + | return; |
|
| 1603 | + | } |
|
| 1604 | + | loop { |
|
| 1605 | + | let parent = try parseRegion(p); |
|
| 1606 | + | try expect(p, scanner::TokenKind::Colon, "expected `:` in region bound"); |
|
| 1607 | + | let childName = try expect(p, scanner::TokenKind::Region, "expected region name"); |
|
| 1608 | + | let mut matched = false; |
|
| 1609 | + | for i in 0..regions.len { |
|
| 1610 | + | let region = regions[i]; |
|
| 1611 | + | if let case ast::NodeValue::Region { name, parent: declaredParent } = region.value; |
|
| 1612 | + | mem::eq(name, childName) |
|
| 1613 | + | { |
|
| 1614 | + | if declaredParent <> nil { |
|
| 1615 | + | throw failParsing(p, "region parameter already has a bound"); |
|
| 1616 | + | } |
|
| 1617 | + | set regions[i] = node(p, ast::NodeValue::Region { name, parent }); |
|
| 1618 | + | set matched = true; |
|
| 1619 | + | break; |
|
| 1620 | + | } |
|
| 1621 | + | } |
|
| 1622 | + | if not matched { |
|
| 1623 | + | throw failParsing(p, "expected declared region parameter in bound"); |
|
| 1624 | + | } |
|
| 1625 | + | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1626 | + | break; |
|
| 1627 | + | } |
|
| 1628 | + | } |
|
| 1629 | + | } |
|
| 1630 | + | ||
| 1631 | + | /// Parse scoped borrow bindings. |
|
| 1632 | + | unsafe fn parseRegionBlock(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 1633 | + | advance(p); |
|
| 1634 | + | let mut binding = try parseIdent(p, "expected region binding name"); |
|
| 1635 | + | try expect(p, scanner::TokenKind::Colon, "expected `:` after region binding"); |
|
| 1636 | + | let regionName = try expect(p, scanner::TokenKind::Region, "expected region name after `:`"); |
|
| 1637 | + | let mut region = node(p, ast::NodeValue::Region { name: regionName, parent: nil }); |
|
| 1638 | + | let mut bindings = ast::nodeSlice(p.arena, 4); |
|
| 1639 | + | loop { |
|
| 1640 | + | try expect(p, scanner::TokenKind::Equal, "expected `=` after region binding"); |
|
| 1641 | + | try expect(p, scanner::TokenKind::Amp, "expected `&` before borrowed place"); |
|
| 1642 | + | let mutable = consume(p, scanner::TokenKind::Mut); |
|
| 1643 | + | let target = try parseUnaryExpr(p); |
|
| 1644 | + | let value = node(p, ast::NodeValue::AddressOf({ target, mutable })); |
|
| 1645 | + | bindings.append(node(p, ast::NodeValue::RegionBinding({ label: binding, value })), p.allocator); |
|
| 1646 | + | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1647 | + | break; |
|
| 1648 | + | } |
|
| 1649 | + | set binding = try parseIdent(p, "expected region binding name"); |
|
| 1650 | + | } |
|
| 1651 | + | if consume(p, scanner::TokenKind::Where) { |
|
| 1652 | + | let parent = try parseRegion(p); |
|
| 1653 | + | try expect(p, scanner::TokenKind::Colon, "expected `:` in region bound"); |
|
| 1654 | + | let childName = try expect(p, scanner::TokenKind::Region, "expected child region in bound"); |
|
| 1655 | + | if not mem::eq(regionName, childName) { |
|
| 1656 | + | throw failParsing(p, "region bound must name the declared region"); |
|
| 1657 | + | } |
|
| 1658 | + | set region = node(p, ast::NodeValue::Region { name: regionName, parent }); |
|
| 1659 | + | } |
|
| 1660 | + | try expect(p, scanner::TokenKind::In, "expected `in` after region bindings"); |
|
| 1661 | + | let body = try parseBlock(p); |
|
| 1662 | + | return node(p, ast::NodeValue::RegionBlock { region, bindings, body, isSession: false }); |
|
| 1663 | + | } |
|
| 1664 | + | ||
| 1665 | + | /// Create a region name from an allocation-session binding name. |
|
| 1666 | + | unsafe fn sessionRegion(p: &mut Parser, binding: *ast::Node) -> *ast::Node { |
|
| 1667 | + | let case ast::NodeValue::Ident(name) = binding.value |
|
| 1668 | + | else panic "sessionRegion: invalid binding"; |
|
| 1669 | + | let len = name.len + 1; |
|
| 1670 | + | let buf = alloc::remainingBuf(&mut p.arena.arena); |
|
| 1671 | + | assert buf.len >= len, "sessionRegion: node arena is full"; |
|
| 1672 | + | set buf[0] = 39; |
|
| 1673 | + | try! mem::copy(&mut buf[1..len], name); |
|
| 1674 | + | alloc::commit(&mut p.arena.arena, len); |
|
| 1675 | + | return node(p, ast::NodeValue::Region { name: &buf[..len], parent: nil }); |
|
| 1676 | + | } |
|
| 1677 | + | ||
| 1678 | + | /// Parse an allocation session with an implicit exclusive source borrow. |
|
| 1679 | + | unsafe fn parseSessionBlock(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 1680 | + | try expect(p, scanner::TokenKind::Use, "expected `use`"); |
|
| 1681 | + | let target = try parseUnaryExpr(p); |
|
| 1682 | + | let value = node(p, ast::NodeValue::AddressOf({ target, mutable: true })); |
|
| 1683 | + | try expect(p, scanner::TokenKind::As, "expected `as` after allocation source"); |
|
| 1684 | + | let binding = try parseIdent(p, "expected allocation binding after `as`"); |
|
| 1685 | + | let region = sessionRegion(p, binding); |
|
| 1686 | + | try expect(p, scanner::TokenKind::In, "expected `in` after allocation binding"); |
|
| 1687 | + | let bindings = ast::nodeSlice(p.arena, 1).append( |
|
| 1688 | + | node(p, ast::NodeValue::RegionBinding({ label: binding, value })), |
|
| 1689 | + | p.allocator, |
|
| 1690 | + | ); |
|
| 1691 | + | let body = try parseBlock(p); |
|
| 1692 | + | return node(p, ast::NodeValue::RegionBlock { region, bindings, body, isSession: true }); |
|
| 1693 | + | } |
|
| 1694 | + | ||
| 1542 | 1695 | /// Parse an identifier. |
|
| 1543 | 1696 | unsafe fn parseIdent(p: &mut Parser, err: *[u8]) -> *ast::Node |
|
| 1544 | 1697 | throws (ParseError) |
|
| 1545 | 1698 | { |
|
| 1546 | 1699 | let source = try expect(p, scanner::TokenKind::Ident, err); |
| 1618 | 1771 | try expect(p, terminator, "expected closing delimiter after record fields"); |
|
| 1619 | 1772 | ||
| 1620 | 1773 | return fields; |
|
| 1621 | 1774 | } |
|
| 1622 | 1775 | ||
| 1623 | - | /// Parse an optional derives list (`: Trait + Trait`). |
|
| 1624 | - | unsafe fn parseDerives(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) { |
|
| 1625 | - | let mut derives = ast::nodeSlice(p.arena, 4); |
|
| 1626 | - | ||
| 1776 | + | /// Parse an optional declaration list and separate regions from derives. |
|
| 1777 | + | unsafe fn parseNominalClauses( |
|
| 1778 | + | p: &mut Parser, |
|
| 1779 | + | regions: &mut *mut [*ast::Node], |
|
| 1780 | + | derives: &mut *mut [*ast::Node], |
|
| 1781 | + | ) throws (ParseError) { |
|
| 1627 | 1782 | if not consume(p, scanner::TokenKind::Colon) { |
|
| 1628 | - | return derives; |
|
| 1783 | + | return; |
|
| 1629 | 1784 | } |
|
| 1785 | + | set *regions = ast::nodeSlice(p.arena, 4); |
|
| 1786 | + | set *derives = ast::nodeSlice(p.arena, 4); |
|
| 1630 | 1787 | loop { |
|
| 1631 | - | let t = try parseIdent(p, "expected trait name in derive list"); |
|
| 1632 | - | derives.append(t, p.allocator); |
|
| 1788 | + | if check(p, scanner::TokenKind::Region) { |
|
| 1789 | + | regions.append(try parseRegion(p), p.allocator); |
|
| 1790 | + | } else { |
|
| 1791 | + | derives.append( |
|
| 1792 | + | try parseIdent(p, "expected region or trait name in declaration list"), |
|
| 1793 | + | p.allocator, |
|
| 1794 | + | ); |
|
| 1795 | + | } |
|
| 1633 | 1796 | ||
| 1634 | 1797 | if not consume(p, scanner::TokenKind::Plus) { |
|
| 1635 | 1798 | break; |
|
| 1636 | 1799 | } |
|
| 1637 | 1800 | } |
|
| 1801 | + | } |
|
| 1802 | + | ||
| 1803 | + | /// Parse an optional list of trait names. |
|
| 1804 | + | unsafe fn parseDerives(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) { |
|
| 1805 | + | if not consume(p, scanner::TokenKind::Colon) { |
|
| 1806 | + | return &mut []; |
|
| 1807 | + | } |
|
| 1808 | + | let mut derives = ast::nodeSlice(p.arena, 4); |
|
| 1809 | + | loop { |
|
| 1810 | + | derives.append(try parseIdent(p, "expected trait name"), p.allocator); |
|
| 1811 | + | if not consume(p, scanner::TokenKind::Plus) { |
|
| 1812 | + | break; |
|
| 1813 | + | } |
|
| 1814 | + | } |
|
| 1638 | 1815 | return derives; |
|
| 1639 | 1816 | } |
|
| 1640 | 1817 | ||
| 1641 | 1818 | /// Parse a single record literal field. |
|
| 1642 | 1819 | /// Can be either labeled, or shorthand. |
| 1693 | 1870 | throws (ParseError) |
|
| 1694 | 1871 | { |
|
| 1695 | 1872 | try expect(p, scanner::TokenKind::Record, "expected `record`"); |
|
| 1696 | 1873 | ||
| 1697 | 1874 | let name = try parseIdent(p, "expected record name"); |
|
| 1698 | - | let derives = try parseDerives(p); |
|
| 1875 | + | let mut regions: *mut [*ast::Node] = &mut []; |
|
| 1876 | + | let mut derives: *mut [*ast::Node] = &mut []; |
|
| 1877 | + | try parseNominalClauses(p, &mut regions, &mut derives); |
|
| 1878 | + | try parseRegionBounds(p, &mut regions[..]); |
|
| 1699 | 1879 | ||
| 1700 | 1880 | if consume(p, scanner::TokenKind::LParen) { |
|
| 1701 | 1881 | let fields = try parseRecordFields(p, RecordFieldMode::Unlabeled); |
|
| 1702 | 1882 | try expect(p, scanner::TokenKind::Semicolon, "expected `;` after record"); |
|
| 1703 | 1883 | return node(p, ast::NodeValue::RecordDecl( |
|
| 1704 | - | ast::RecordDecl { name, fields, attrs, derives, labeled: false } |
|
| 1884 | + | ast::RecordDecl { |
|
| 1885 | + | name, fields, attrs, regions, derives, labeled: false, |
|
| 1886 | + | } |
|
| 1705 | 1887 | )); |
|
| 1706 | 1888 | } else { |
|
| 1707 | 1889 | try expect(p, scanner::TokenKind::LBrace, "expected `{` before record body"); |
|
| 1708 | 1890 | let fields = try parseRecordFields(p, RecordFieldMode::Labeled); |
|
| 1709 | 1891 | return node(p, ast::NodeValue::RecordDecl( |
|
| 1710 | - | ast::RecordDecl { name, fields, attrs, derives, labeled: true } |
|
| 1892 | + | ast::RecordDecl { |
|
| 1893 | + | name, fields, attrs, regions, derives, labeled: true, |
|
| 1894 | + | } |
|
| 1711 | 1895 | )); |
|
| 1712 | 1896 | } |
|
| 1713 | 1897 | } |
|
| 1714 | 1898 | ||
| 1715 | 1899 | /// Parse a union declaration. |
| 1718 | 1902 | throws (ParseError) |
|
| 1719 | 1903 | { |
|
| 1720 | 1904 | try expect(p, scanner::TokenKind::Union, "expected `union`"); |
|
| 1721 | 1905 | ||
| 1722 | 1906 | let name = try parseIdent(p, "expected union name"); |
|
| 1723 | - | let derives = try parseDerives(p); |
|
| 1907 | + | let mut regions: *mut [*ast::Node] = &mut []; |
|
| 1908 | + | let mut derives: *mut [*ast::Node] = &mut []; |
|
| 1909 | + | try parseNominalClauses(p, &mut regions, &mut derives); |
|
| 1910 | + | try parseRegionBounds(p, &mut regions[..]); |
|
| 1724 | 1911 | ||
| 1725 | 1912 | try expect(p, scanner::TokenKind::LBrace, "expected `{` before union body"); |
|
| 1726 | 1913 | ||
| 1727 | 1914 | let mut variants = ast::nodeSlice(p.arena, 128); |
|
| 1728 | 1915 | while not check(p, scanner::TokenKind::RBrace) { |
| 1763 | 1950 | } |
|
| 1764 | 1951 | } |
|
| 1765 | 1952 | try expect(p, scanner::TokenKind::RBrace, "expected `}`"); |
|
| 1766 | 1953 | ||
| 1767 | 1954 | return node(p, ast::NodeValue::UnionDecl( |
|
| 1768 | - | ast::UnionDecl { name, variants, attrs, derives } |
|
| 1955 | + | ast::UnionDecl { |
|
| 1956 | + | name, variants, attrs, regions, derives, |
|
| 1957 | + | } |
|
| 1769 | 1958 | )); |
|
| 1770 | 1959 | } |
|
| 1771 | 1960 | ||
| 1772 | 1961 | /// Parse a function parameter. |
|
| 1773 | 1962 | unsafe fn parseFnParam(p: &mut Parser) -> *ast::Node |
| 1864 | 2053 | // Method syntax: `fn (recv: *Type) name(params) { body }`. |
|
| 1865 | 2054 | if check(p, scanner::TokenKind::LParen) { |
|
| 1866 | 2055 | return try parseMethodDecl(p, attrs); |
|
| 1867 | 2056 | } |
|
| 1868 | 2057 | let name = try parseIdent(p, "expected function name"); |
|
| 2058 | + | let regions = try parseRegions(p); |
|
| 1869 | 2059 | let sig = try parseFnTypeSig(p); |
|
| 2060 | + | try parseRegionBounds(p, &mut regions[..]); |
|
| 1870 | 2061 | let mut body: ?*ast::Node = nil; |
|
| 1871 | 2062 | let mut fnAttrs = attrs; |
|
| 1872 | 2063 | ||
| 1873 | 2064 | if consume(p, scanner::TokenKind::Semicolon) { |
|
| 1874 | 2065 | if let a = attrs; ast::attributesContains(&a, ast::Attribute::Extern) { |
| 1886 | 2077 | } |
|
| 1887 | 2078 | } else { |
|
| 1888 | 2079 | set body = try parseBlock(p); |
|
| 1889 | 2080 | } |
|
| 1890 | 2081 | return node(p, ast::NodeValue::FnDecl( |
|
| 1891 | - | ast::FnDecl { name, sig, body, attrs: fnAttrs } |
|
| 2082 | + | ast::FnDecl { name, regions, sig, body, attrs: fnAttrs } |
|
| 1892 | 2083 | )); |
|
| 1893 | 2084 | } |
|
| 1894 | 2085 | ||
| 1895 | 2086 | /// Parse a pointer-like type after its ownership prefix. |
|
| 1896 | 2087 | unsafe fn parsePointerLikeType( |
|
| 1897 | 2088 | p: &mut Parser, |
|
| 1898 | - | class: types::PointerClass, |
|
| 2089 | + | class: ast::PointerClass, |
|
| 1899 | 2090 | ) -> *ast::Node throws (ParseError) { |
|
| 2091 | + | if check(p, scanner::TokenKind::Ident) and mem::eq(p.current.source, "cell") { |
|
| 2092 | + | advance(p); |
|
| 2093 | + | let payload = try parseType(p); |
|
| 2094 | + | return node(p, ast::NodeValue::TypeSig(ast::TypeSig::Cell { class, payload })); |
|
| 2095 | + | } |
|
| 1900 | 2096 | let mutable = consume(p, scanner::TokenKind::Mut); |
|
| 1901 | 2097 | ||
| 1902 | 2098 | if consume(p, scanner::TokenKind::LBracket) { |
|
| 1903 | 2099 | let itemType = try parseType(p); |
|
| 1904 | 2100 | try expect(p, scanner::TokenKind::RBracket, "expected `]` after slice element type"); |
| 1978 | 2174 | ast::TypeSig::Optional { valueType } |
|
| 1979 | 2175 | )); |
|
| 1980 | 2176 | } |
|
| 1981 | 2177 | case scanner::TokenKind::Star => { |
|
| 1982 | 2178 | advance(p); |
|
| 1983 | - | let class = types::PointerClass::Unsafe |
|
| 2179 | + | let class = ast::PointerClass::Unsafe |
|
| 1984 | 2180 | if consume(p, scanner::TokenKind::Unsafe) |
|
| 1985 | - | else types::PointerClass::Owned; |
|
| 2181 | + | else ast::PointerClass::Owned; |
|
| 1986 | 2182 | return try parsePointerLikeType(p, class); |
|
| 1987 | 2183 | } |
|
| 1988 | 2184 | case scanner::TokenKind::Amp => { |
|
| 1989 | 2185 | advance(p); |
|
| 1990 | - | return try parsePointerLikeType(p, types::PointerClass::Ref); |
|
| 2186 | + | let mut region: ?*ast::Node = nil; |
|
| 2187 | + | if check(p, scanner::TokenKind::Region) { |
|
| 2188 | + | set region = try parseRegion(p); |
|
| 2189 | + | } |
|
| 2190 | + | let type = try parsePointerLikeType(p, ast::PointerClass::Ref); |
|
| 2191 | + | if let r = region { |
|
| 2192 | + | return node(p, ast::NodeValue::TypeSig(ast::TypeSig::RegionRef { region: r, type })); |
|
| 2193 | + | } |
|
| 2194 | + | return type; |
|
| 1991 | 2195 | } |
|
| 1992 | 2196 | case scanner::TokenKind::LBracket => { |
|
| 1993 | 2197 | return try parseArrayType(p); |
|
| 1994 | 2198 | } |
|
| 1995 | 2199 | case scanner::TokenKind::Super, scanner::TokenKind::Ident => { |
|
| 1996 | 2200 | let path = try parseTypePath(p); |
|
| 1997 | - | ||
| 2201 | + | if check(p, scanner::TokenKind::Region) { |
|
| 2202 | + | let regions = try parseRegions(p); |
|
| 2203 | + | return node(p, ast::NodeValue::TypeSig(ast::TypeSig::Applied { name: path, regions })); |
|
| 2204 | + | } |
|
| 1998 | 2205 | return node(p, ast::NodeValue::TypeSig( |
|
| 1999 | 2206 | ast::TypeSig::Nominal(path) |
|
| 2000 | 2207 | )); |
|
| 2001 | 2208 | } |
|
| 2002 | 2209 | case scanner::TokenKind::U8 => { |
| 2302 | 2509 | let receiver = try parseType(p); |
|
| 2303 | 2510 | ||
| 2304 | 2511 | try expect(p, scanner::TokenKind::RParen, "expected `)` after receiver"); |
|
| 2305 | 2512 | ||
| 2306 | 2513 | let name = try parseIdent(p, "expected method name"); |
|
| 2514 | + | let mut regions = try parseRegions(p); |
|
| 2307 | 2515 | let sig = try parseFnTypeSig(p); |
|
| 2516 | + | try parseRegionBounds(p, &mut regions[..]); |
|
| 2308 | 2517 | try expect(p, scanner::TokenKind::Semicolon, "expected `;` after method signature"); |
|
| 2309 | 2518 | ||
| 2310 | - | return node(p, ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs }); |
|
| 2519 | + | let modifiers = try! alloc::alloc( |
|
| 2520 | + | &mut p.arena.arena, @sizeOf(ast::MethodModifiers), @alignOf(ast::MethodModifiers) |
|
| 2521 | + | ) as *mut ast::MethodModifiers; |
|
| 2522 | + | set *modifiers = ast::MethodModifiers { regions, attrs }; |
|
| 2523 | + | return node(p, ast::NodeValue::TraitMethodSig { name, modifiers, receiver, sig }); |
|
| 2311 | 2524 | } |
|
| 2312 | 2525 | ||
| 2313 | 2526 | /// Parse an instance block. |
|
| 2314 | 2527 | /// Syntax: `instance Trait for Type { fn (t: *mut Type) fnord(..) {..} }` |
|
| 2315 | 2528 | /// |
| 2320 | 2533 | { |
|
| 2321 | 2534 | try expect(p, scanner::TokenKind::Instance, "expected `instance`"); |
|
| 2322 | 2535 | let traitName = try parseTypePath(p); |
|
| 2323 | 2536 | try expect(p, scanner::TokenKind::For, "expected `for` after trait name"); |
|
| 2324 | 2537 | let targetType = try parseTypePath(p); |
|
| 2538 | + | let mut regions = try parseRegions(p); |
|
| 2539 | + | try parseRegionBounds(p, &mut regions[..]); |
|
| 2325 | 2540 | try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type"); |
|
| 2326 | 2541 | ||
| 2327 | 2542 | let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2328 | 2543 | ||
| 2329 | 2544 | while not check(p, scanner::TokenKind::RBrace) and |
| 2335 | 2550 | ||
| 2336 | 2551 | methods.append(method, p.allocator); |
|
| 2337 | 2552 | } |
|
| 2338 | 2553 | try expect(p, scanner::TokenKind::RBrace, "expected `}` after instance methods"); |
|
| 2339 | 2554 | ||
| 2340 | - | return node(p, ast::NodeValue::InstanceDecl { traitName, targetType, methods }); |
|
| 2555 | + | return node(p, ast::NodeValue::InstanceDecl { traitName, targetType, regions, methods }); |
|
| 2341 | 2556 | } |
|
| 2342 | 2557 | ||
| 2343 | 2558 | /// Parse a method declaration with a receiver. |
|
| 2344 | 2559 | /// Syntax: `fn (t: *mut Type) fnord(<params>) -> ReturnType { body }` |
|
| 2345 | 2560 | /// |
| 2355 | 2570 | let receiverType = try parseType(p); |
|
| 2356 | 2571 | ||
| 2357 | 2572 | try expect(p, scanner::TokenKind::RParen, "expected `)` after receiver type"); |
|
| 2358 | 2573 | ||
| 2359 | 2574 | let name = try parseIdent(p, "expected method name"); |
|
| 2575 | + | let mut regions = try parseRegions(p); |
|
| 2360 | 2576 | let sig = try parseFnTypeSig(p); |
|
| 2577 | + | try parseRegionBounds(p, &mut regions[..]); |
|
| 2361 | 2578 | let body = try parseBlock(p); |
|
| 2362 | 2579 | ||
| 2580 | + | let modifiers = try! alloc::alloc( |
|
| 2581 | + | &mut p.arena.arena, @sizeOf(ast::MethodModifiers), @alignOf(ast::MethodModifiers) |
|
| 2582 | + | ) as *mut ast::MethodModifiers; |
|
| 2583 | + | set *modifiers = ast::MethodModifiers { regions, attrs }; |
|
| 2363 | 2584 | return node(p, ast::NodeValue::MethodDecl { |
|
| 2364 | - | name, receiverName, receiverType, sig, body, attrs, |
|
| 2585 | + | name, modifiers, receiverName, receiverType, sig, body, |
|
| 2365 | 2586 | }); |
|
| 2366 | 2587 | } |
|
| 2367 | 2588 | ||
| 2368 | 2589 | /// Parse a comma-separated list enclosed by the given delimiters. |
|
| 2369 | 2590 | unsafe fn parseList( |
lib/std/lang/parser/tests.rad
+303 -17
| 2 | 2 | ||
| 3 | 3 | use std::mem; |
|
| 4 | 4 | use std::fmt; |
|
| 5 | 5 | use std::testing; |
|
| 6 | 6 | use std::lang::ast; |
|
| 7 | - | use std::lang::types; |
|
| 8 | 7 | use std::lang::alloc; |
|
| 9 | 8 | use std::lang::sexpr; |
|
| 10 | 9 | use std::lang::ast::printer; |
|
| 11 | 10 | use std::lang::scanner; |
|
| 12 | 11 | use std::lang::strings; |
| 790 | 789 | } |
|
| 791 | 790 | ||
| 792 | 791 | /// Parse an `i32` pointer type and verify its class and mutability. |
|
| 793 | 792 | unsafe fn expectI32Pointer( |
|
| 794 | 793 | source: *[u8], |
|
| 795 | - | class: types::PointerClass, |
|
| 794 | + | class: ast::PointerClass, |
|
| 796 | 795 | mutable: bool, |
|
| 797 | 796 | ) throws (testing::TestError) { |
|
| 798 | 797 | let node = try! parseTypeStr(source); |
|
| 799 | 798 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 800 | 799 | else throw testing::TestError::Failed; |
| 807 | 806 | assert actualMutable == mutable; |
|
| 808 | 807 | } |
|
| 809 | 808 | ||
| 810 | 809 | /// Test parsing a mutable owned pointer. |
|
| 811 | 810 | @test unsafe fn testParseTypePointer() throws (testing::TestError) { |
|
| 812 | - | try expectI32Pointer("*mut i32", types::PointerClass::Owned, true); |
|
| 811 | + | try expectI32Pointer("*mut i32", ast::PointerClass::Owned, true); |
|
| 813 | 812 | } |
|
| 814 | 813 | ||
| 815 | 814 | /// Test parsing an immutable owned pointer. |
|
| 816 | 815 | @test unsafe fn testParseTypePointerImmutable() throws (testing::TestError) { |
|
| 817 | - | try expectI32Pointer("*i32", types::PointerClass::Owned, false); |
|
| 816 | + | try expectI32Pointer("*i32", ast::PointerClass::Owned, false); |
|
| 818 | 817 | } |
|
| 819 | 818 | ||
| 820 | 819 | /// Test parsing immutable and mutable references. |
|
| 821 | 820 | @test unsafe fn testParseTypeRef() throws (testing::TestError) { |
|
| 822 | - | try expectI32Pointer("&i32", types::PointerClass::Ref, false); |
|
| 823 | - | try expectI32Pointer("&mut i32", types::PointerClass::Ref, true); |
|
| 821 | + | try expectI32Pointer("&i32", ast::PointerClass::Ref, false); |
|
| 822 | + | try expectI32Pointer("&mut i32", ast::PointerClass::Ref, true); |
|
| 824 | 823 | } |
|
| 825 | 824 | ||
| 826 | 825 | /// Test parsing immutable and mutable unsafe pointers. |
|
| 827 | 826 | @test unsafe fn testParseTypeUnsafePointer() throws (testing::TestError) { |
|
| 828 | - | try expectI32Pointer("*unsafe i32", types::PointerClass::Unsafe, false); |
|
| 829 | - | try expectI32Pointer("*unsafe mut i32", types::PointerClass::Unsafe, true); |
|
| 827 | + | try expectI32Pointer("*unsafe i32", ast::PointerClass::Unsafe, false); |
|
| 828 | + | try expectI32Pointer("*unsafe mut i32", ast::PointerClass::Unsafe, true); |
|
| 830 | 829 | } |
|
| 831 | 830 | ||
| 832 | 831 | /// Test parsing a slice type. |
|
| 833 | 832 | @test unsafe fn testParseTypeSlice() throws (testing::TestError) { |
|
| 834 | 833 | let node = try! parseTypeStr("*[u8]"); |
|
| 835 | 834 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 836 | 835 | else throw testing::TestError::Failed; |
|
| 837 | 836 | let case ast::TypeSig::Slice { class, itemType, mutable } = sig |
|
| 838 | 837 | else throw testing::TestError::Failed; |
|
| 839 | 838 | ||
| 840 | - | assert class == types::PointerClass::Owned; |
|
| 839 | + | assert class == ast::PointerClass::Owned; |
|
| 841 | 840 | try expectIntType(itemType, 1, ast::Signedness::Unsigned); |
|
| 842 | 841 | assert not mutable; |
|
| 843 | 842 | } |
|
| 844 | 843 | ||
| 845 | 844 | /// Test parsing a mutable slice type. |
| 848 | 847 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 849 | 848 | else throw testing::TestError::Failed; |
|
| 850 | 849 | let case ast::TypeSig::Slice { class, itemType, mutable } = sig |
|
| 851 | 850 | else throw testing::TestError::Failed; |
|
| 852 | 851 | ||
| 853 | - | assert class == types::PointerClass::Owned; |
|
| 852 | + | assert class == ast::PointerClass::Owned; |
|
| 854 | 853 | try expectIntType(itemType, 1, ast::Signedness::Unsigned); |
|
| 855 | 854 | assert mutable; |
|
| 856 | 855 | } |
|
| 857 | 856 | ||
| 858 | 857 | /// Test parsing reference and unsafe slice classes. |
|
| 859 | 858 | @test unsafe fn testParseTypeSliceClasses() throws (testing::TestError) { |
|
| 860 | 859 | let refNode = try! parseTypeStr("&[u8]"); |
|
| 861 | 860 | let case ast::NodeValue::TypeSig(ast::TypeSig::Slice { |
|
| 862 | 861 | class: refClass, .. |
|
| 863 | 862 | }) = refNode.value else throw testing::TestError::Failed; |
|
| 864 | - | assert refClass == types::PointerClass::Ref; |
|
| 863 | + | assert refClass == ast::PointerClass::Ref; |
|
| 865 | 864 | ||
| 866 | 865 | let unsafeNode = try! parseTypeStr("*unsafe [u8]"); |
|
| 867 | 866 | let case ast::NodeValue::TypeSig(ast::TypeSig::Slice { |
|
| 868 | 867 | class: unsafeClass, .. |
|
| 869 | 868 | }) = unsafeNode.value else throw testing::TestError::Failed; |
|
| 870 | - | assert unsafeClass == types::PointerClass::Unsafe; |
|
| 869 | + | assert unsafeClass == ast::PointerClass::Unsafe; |
|
| 871 | 870 | } |
|
| 872 | 871 | ||
| 873 | 872 | /// Test parsing trait object pointer classes. |
|
| 874 | 873 | @test unsafe fn testParseTypeTraitObjectClasses() throws (testing::TestError) { |
|
| 875 | 874 | let ownedNode = try! parseTypeStr("*opaque Read"); |
|
| 876 | 875 | let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject { |
|
| 877 | 876 | class: ownedClass, .. |
|
| 878 | 877 | }) = ownedNode.value else throw testing::TestError::Failed; |
|
| 879 | - | assert ownedClass == types::PointerClass::Owned; |
|
| 878 | + | assert ownedClass == ast::PointerClass::Owned; |
|
| 880 | 879 | ||
| 881 | 880 | let refNode = try! parseTypeStr("&opaque Read"); |
|
| 882 | 881 | let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject { |
|
| 883 | 882 | class: refClass, .. |
|
| 884 | 883 | }) = refNode.value else throw testing::TestError::Failed; |
|
| 885 | - | assert refClass == types::PointerClass::Ref; |
|
| 884 | + | assert refClass == ast::PointerClass::Ref; |
|
| 886 | 885 | ||
| 887 | 886 | let unsafeNode = try! parseTypeStr("*unsafe opaque Read"); |
|
| 888 | 887 | let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject { |
|
| 889 | 888 | class: unsafeClass, .. |
|
| 890 | 889 | }) = unsafeNode.value else throw testing::TestError::Failed; |
|
| 891 | - | assert unsafeClass == types::PointerClass::Unsafe; |
|
| 890 | + | assert unsafeClass == ast::PointerClass::Unsafe; |
|
| 892 | 891 | } |
|
| 893 | 892 | ||
| 894 | 893 | /// Test parsing an array type. |
|
| 895 | 894 | @test unsafe fn testParseTypeArray() throws (testing::TestError) { |
|
| 896 | 895 | let node = try! parseTypeStr("[i32; 4]"); |
| 1157 | 1156 | "instance Read for Value { unsafe fn (value: &Value) get() {} }" |
|
| 1158 | 1157 | ); |
|
| 1159 | 1158 | let case ast::NodeValue::InstanceDecl { methods, .. } = instanceNode.value |
|
| 1160 | 1159 | else throw testing::TestError::Failed; |
|
| 1161 | 1160 | try testing::expect(methods.len == 1); |
|
| 1162 | - | let case ast::NodeValue::MethodDecl { attrs, .. } = methods[0].value |
|
| 1161 | + | let case ast::NodeValue::MethodDecl { modifiers, .. } = methods[0].value |
|
| 1163 | 1162 | else throw testing::TestError::Failed; |
|
| 1163 | + | let attrs = modifiers.attrs; |
|
| 1164 | 1164 | let methodAttrs = attrs else throw testing::TestError::Failed; |
|
| 1165 | 1165 | assert ast::attributesContains(&methodAttrs, ast::Attribute::Unsafe); |
|
| 1166 | 1166 | ||
| 1167 | 1167 | static printStorage: [u8; 4096] = undefined; |
|
| 1168 | 1168 | let mut printArena = alloc::new(&mut printStorage[..]); |
| 1181 | 1181 | "trait Read { unsafe fn (&Read) get(); }" |
|
| 1182 | 1182 | ); |
|
| 1183 | 1183 | let case ast::NodeValue::TraitDecl { methods: traitMethods, .. } = traitNode.value |
|
| 1184 | 1184 | else throw testing::TestError::Failed; |
|
| 1185 | 1185 | let case ast::NodeValue::TraitMethodSig { |
|
| 1186 | - | attrs: traitAttrs, .. |
|
| 1186 | + | modifiers: traitModifiers, .. |
|
| 1187 | 1187 | } = traitMethods[0].value else throw testing::TestError::Failed; |
|
| 1188 | + | let traitAttrs = traitModifiers.attrs; |
|
| 1188 | 1189 | let traitMethodAttrs = traitAttrs else throw testing::TestError::Failed; |
|
| 1189 | 1190 | assert ast::attributesContains(&traitMethodAttrs, ast::Attribute::Unsafe); |
|
| 1190 | 1191 | ||
| 1191 | 1192 | let traitMethodExpr = printer::toExpr(&mut printArena, traitMethods[0]); |
|
| 1192 | 1193 | let case sexpr::Expr::List { tail: traitMethodItems, .. } = traitMethodExpr |
| 1198 | 1199 | let case sexpr::Expr::Sym(traitAttr) = printedTraitAttrs[0] |
|
| 1199 | 1200 | else throw testing::TestError::Failed; |
|
| 1200 | 1201 | assert mem::eq(traitAttr, "@unsafe"); |
|
| 1201 | 1202 | } |
|
| 1202 | 1203 | ||
| 1204 | + | /// Test region parameters on methods, trait methods, and instances. |
|
| 1205 | + | @test unsafe fn testParseRegionalMethods() throws (testing::TestError) { |
|
| 1206 | + | let instanceNode = try! parseStmtStr( |
|
| 1207 | + | "instance Read for View 'view { fn (view: &View 'view) get 'method () -> &'method u32 { panic; } }" |
|
| 1208 | + | ); |
|
| 1209 | + | let case ast::NodeValue::InstanceDecl { regions, methods, .. } = instanceNode.value |
|
| 1210 | + | else throw testing::TestError::Failed; |
|
| 1211 | + | try testing::expect(regions.len == 1 and methods.len == 1); |
|
| 1212 | + | let case ast::NodeValue::MethodDecl { modifiers, .. } = methods[0].value |
|
| 1213 | + | else throw testing::TestError::Failed; |
|
| 1214 | + | try testing::expect(modifiers.regions.len == 1); |
|
| 1215 | + | ||
| 1216 | + | let traitNode = try! parseStmtStr( |
|
| 1217 | + | "trait Read { fn (&Read) get 'method () -> &'method u32; }" |
|
| 1218 | + | ); |
|
| 1219 | + | let case ast::NodeValue::TraitDecl { methods: traitMethods, .. } = traitNode.value |
|
| 1220 | + | else throw testing::TestError::Failed; |
|
| 1221 | + | let case ast::NodeValue::TraitMethodSig { modifiers: traitModifiers, .. } = traitMethods[0].value |
|
| 1222 | + | else throw testing::TestError::Failed; |
|
| 1223 | + | try testing::expect(traitModifiers.regions.len == 1); |
|
| 1224 | + | } |
|
| 1225 | + | ||
| 1203 | 1226 | /// Test parsing a function declaration with attributes. |
|
| 1204 | 1227 | @test unsafe fn testParseFnDeclAttributes() throws (testing::TestError) { |
|
| 1205 | 1228 | let node = try! parseStmtStr("export fn run();"); |
|
| 1206 | 1229 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1207 | 1230 | else throw testing::TestError::Failed; |
| 2985 | 3008 | assert isUnsafe; |
|
| 2986 | 3009 | assert sig.params.len == 1; |
|
| 2987 | 3010 | assert sig.returnType <> nil; |
|
| 2988 | 3011 | assert sig.throwList.len == 1; |
|
| 2989 | 3012 | } |
|
| 3013 | + | ||
| 3014 | + | /// Region arguments on an inner type remain distinct from a reference region. |
|
| 3015 | + | @test unsafe fn testRegionReferenceTypes() throws (testing::TestError) { |
|
| 3016 | + | let root = try! parseTypeStr("&'short mut Node 'arena"); |
|
| 3017 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::RegionRef { region, type }) = root.value |
|
| 3018 | + | else throw testing::TestError::Failed; |
|
| 3019 | + | let case ast::NodeValue::Region { name, parent: nil } = region.value |
|
| 3020 | + | else throw testing::TestError::Failed; |
|
| 3021 | + | try testing::expect(mem::eq(name, "'short")); |
|
| 3022 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { class, valueType, mutable }) = type.value |
|
| 3023 | + | else throw testing::TestError::Failed; |
|
| 3024 | + | try testing::expect(class == ast::PointerClass::Ref and mutable); |
|
| 3025 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Applied { name: nominal, regions }) = valueType.value |
|
| 3026 | + | else throw testing::TestError::Failed; |
|
| 3027 | + | try expectIdent(nominal, "Node"); |
|
| 3028 | + | try testing::expect(regions.len == 1); |
|
| 3029 | + | let case ast::NodeValue::Region { name: argument, .. } = regions[0].value |
|
| 3030 | + | else throw testing::TestError::Failed; |
|
| 3031 | + | try testing::expect(mem::eq(argument, "'arena")); |
|
| 3032 | + | let slice = try! parseTypeStr("&'r [View 'a 'b]"); |
|
| 3033 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::RegionRef { type: sliceType, .. }) = slice.value |
|
| 3034 | + | else throw testing::TestError::Failed; |
|
| 3035 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Slice { .. }) = sliceType.value |
|
| 3036 | + | else throw testing::TestError::Failed; |
|
| 3037 | + | } |
|
| 3038 | + | ||
| 3039 | + | /// Declaration lists accept regions, parent relations, and ownership derives. |
|
| 3040 | + | @test unsafe fn testRegionDeclarations() throws (testing::TestError) { |
|
| 3041 | + | let parsedRecord = try! parseStmtStr("record View: 'a + 'b + Copy where 'a: 'b { item: &'b u8 }"); |
|
| 3042 | + | let case ast::NodeValue::RecordDecl(decl) = parsedRecord.value |
|
| 3043 | + | else throw testing::TestError::Failed; |
|
| 3044 | + | try testing::expect(decl.regions.len == 2 and decl.derives.len == 1); |
|
| 3045 | + | let case ast::NodeValue::Region { parent: parent, .. } = decl.regions[1].value |
|
| 3046 | + | else throw testing::TestError::Failed; |
|
| 3047 | + | try testing::expect(parent <> nil); |
|
| 3048 | + | let func = try! parseStmtStr( |
|
| 3049 | + | "fn read 'long 'short (input: &'short u8) -> &'short u8 throws (ReadError) where 'long: 'short { return input; }" |
|
| 3050 | + | ); |
|
| 3051 | + | let case ast::NodeValue::FnDecl(f) = func.value else throw testing::TestError::Failed; |
|
| 3052 | + | try testing::expect(f.regions.len == 2 and f.sig.throwList.len == 1); |
|
| 3053 | + | let case ast::NodeValue::Region { name: shortName, parent: shortParent } = f.regions[1].value |
|
| 3054 | + | else throw testing::TestError::Failed; |
|
| 3055 | + | try testing::expect(mem::eq(shortName, "'short") and shortParent <> nil); |
|
| 3056 | + | let parentNode = shortParent else throw testing::TestError::Failed; |
|
| 3057 | + | let case ast::NodeValue::Region { name: parentName, parent: nil } = parentNode.value |
|
| 3058 | + | else throw testing::TestError::Failed; |
|
| 3059 | + | try testing::expect(mem::eq(parentName, "'long")); |
|
| 3060 | + | let parsedUnion = try! parseStmtStr("union Result: 'x { Value(&'x u8), Empty }"); |
|
| 3061 | + | let case ast::NodeValue::UnionDecl(u) = parsedUnion.value else throw testing::TestError::Failed; |
|
| 3062 | + | try testing::expect(u.regions.len == 1 and u.derives.len == 0); |
|
| 3063 | + | } |
|
| 3064 | + | ||
| 3065 | + | /// Regional blocks retain their source bindings and derived session region. |
|
| 3066 | + | @test unsafe fn testRegionBlocks() throws (testing::TestError) { |
|
| 3067 | + | let root = try! parseStmtStr("let left: 'r = &mut state.left, right = &state.right in { useView(left); }"); |
|
| 3068 | + | let case ast::NodeValue::RegionBlock { bindings, isSession, .. } = root.value |
|
| 3069 | + | else throw testing::TestError::Failed; |
|
| 3070 | + | try testing::expect(not isSession and bindings.len == 2); |
|
| 3071 | + | let nested = try! parseStmtStr("let view: 'inner = &value where 'outer: 'inner in {}"); |
|
| 3072 | + | let case ast::NodeValue::RegionBlock { region: nestedRegion, .. } = nested.value |
|
| 3073 | + | else throw testing::TestError::Failed; |
|
| 3074 | + | let case ast::NodeValue::Region { name: nestedName, parent: nestedParent } = nestedRegion.value |
|
| 3075 | + | else throw testing::TestError::Failed; |
|
| 3076 | + | let parent = nestedParent else throw testing::TestError::Failed; |
|
| 3077 | + | let case ast::NodeValue::Region { name: parentName, parent: nil } = parent.value |
|
| 3078 | + | else throw testing::TestError::Failed; |
|
| 3079 | + | try testing::expect(mem::eq(nestedName, "'inner") and mem::eq(parentName, "'outer")); |
|
| 3080 | + | let arena = try! parseStmtStr("use arena as objects in { make(objects); }"); |
|
| 3081 | + | let case ast::NodeValue::RegionBlock { |
|
| 3082 | + | region: arenaRegion, bindings: arenaBindings, isSession: arenaSession, .. |
|
| 3083 | + | } = arena.value |
|
| 3084 | + | else throw testing::TestError::Failed; |
|
| 3085 | + | let case ast::NodeValue::Region { name: arenaRegionName, parent: nil } = arenaRegion.value |
|
| 3086 | + | else throw testing::TestError::Failed; |
|
| 3087 | + | let case ast::NodeValue::RegionBinding(arenaBinding) = arenaBindings[0].value |
|
| 3088 | + | else throw testing::TestError::Failed; |
|
| 3089 | + | let arenaLabel = arenaBinding.label else throw testing::TestError::Failed; |
|
| 3090 | + | let case ast::NodeValue::Ident(arenaLabelName) = arenaLabel.value |
|
| 3091 | + | else throw testing::TestError::Failed; |
|
| 3092 | + | let case ast::NodeValue::AddressOf({ target: arenaTarget, mutable: true }) = arenaBinding.value.value |
|
| 3093 | + | else throw testing::TestError::Failed; |
|
| 3094 | + | let case ast::NodeValue::Ident(arenaTargetName) = arenaTarget.value |
|
| 3095 | + | else throw testing::TestError::Failed; |
|
| 3096 | + | try testing::expect( |
|
| 3097 | + | arenaSession and arenaBindings.len == 1 |
|
| 3098 | + | and mem::eq(arenaRegionName, "'objects") |
|
| 3099 | + | and mem::eq(arenaLabelName, "objects") |
|
| 3100 | + | and mem::eq(arenaTargetName, "arena") |
|
| 3101 | + | ); |
|
| 3102 | + | } |
|
| 3103 | + | ||
| 3104 | + | /// Region grammar rejects incomplete lists and invalid allocation headers. |
|
| 3105 | + | @test unsafe fn testMalformedRegionSyntax() throws (testing::TestError) { |
|
| 3106 | + | for source in [ |
|
| 3107 | + | "fn bad: Copy () {}", |
|
| 3108 | + | "record Bad: 'r + {}", |
|
| 3109 | + | "let 'r in {}", |
|
| 3110 | + | "let p: 'r = value in {}", |
|
| 3111 | + | "let 'r p = &value in {}", |
|
| 3112 | + | "let 'child < 'parent p = &value in {}", |
|
| 3113 | + | "let 'parent as p = &value in {}", |
|
| 3114 | + | "let value: 'child = &source where 'parent: 'other in {}", |
|
| 3115 | + | "use arena in {}", |
|
| 3116 | + | "use arena as in {}", |
|
| 3117 | + | "use arena as first, second in {}", |
|
| 3118 | + | "use 'r a = &mut arena in {}", |
|
| 3119 | + | "use 'r a as &mut arena in {}", |
|
| 3120 | + | ] { |
|
| 3121 | + | let parsed = try? parseStmtStr(source); |
|
| 3122 | + | try testing::expect(parsed == nil); |
|
| 3123 | + | } |
|
| 3124 | + | for source in ["&'x' u8", "&mut 'x u8", "View '", "&'r"] { |
|
| 3125 | + | let parsed = try? parseTypeStr(source); |
|
| 3126 | + | try testing::expect(parsed == nil); |
|
| 3127 | + | } |
|
| 3128 | + | } |
|
| 3129 | + | ||
| 3130 | + | /// Ordinary identifiers remain available beside regional block syntax. |
|
| 3131 | + | @test unsafe fn testRegionContextualKeywords() throws (testing::TestError) { |
|
| 3132 | + | for source in [ |
|
| 3133 | + | "let borrow = 1;", |
|
| 3134 | + | "let session = 2;", |
|
| 3135 | + | "borrow(value);", |
|
| 3136 | + | "session(value);", |
|
| 3137 | + | "record Context { borrow: u32, session: u32 }", |
|
| 3138 | + | ] { |
|
| 3139 | + | let parsed = try? parseStmtStr(source); |
|
| 3140 | + | try testing::expect(parsed <> nil); |
|
| 3141 | + | } |
|
| 3142 | + | } |
|
| 3143 | + | ||
| 3144 | + | /// The AST printer retains region parameters and qualified reference types. |
|
| 3145 | + | @test unsafe fn testPrintRegionSignature() throws (testing::TestError) { |
|
| 3146 | + | let root = try! parseStmtStr("fn read 'r (input: &'r u8) -> &'r u8 { return input; }"); |
|
| 3147 | + | static PRINT_STORAGE: [u8; 4096] = [0; 4096]; |
|
| 3148 | + | let mut arena = alloc::new(&mut PRINT_STORAGE[..]); |
|
| 3149 | + | let printed = printer::toExpr(&mut arena, root); |
|
| 3150 | + | let case sexpr::Expr::Block { items, .. } = printed |
|
| 3151 | + | else throw testing::TestError::Failed; |
|
| 3152 | + | try testing::expect(items.len == 4); |
|
| 3153 | + | let case sexpr::Expr::List { head, tail, .. } = items[1] |
|
| 3154 | + | else throw testing::TestError::Failed; |
|
| 3155 | + | try testing::expect(mem::eq(head, "regions") and tail.len == 1); |
|
| 3156 | + | let case sexpr::Expr::Sym(name) = tail[0] else throw testing::TestError::Failed; |
|
| 3157 | + | try testing::expect(mem::eq(name, "'r")); |
|
| 3158 | + | let case sexpr::Expr::List { head: refHead, tail: refTail, .. } = items[3] |
|
| 3159 | + | else throw testing::TestError::Failed; |
|
| 3160 | + | try testing::expect(mem::eq(refHead, "region-ref") and refTail.len == 2); |
|
| 3161 | + | } |
|
| 3162 | + | ||
| 3163 | + | /// Explicit function region arguments precede ordinary call arguments. |
|
| 3164 | + | @test unsafe fn testFunctionRegionApplication() throws (testing::TestError) { |
|
| 3165 | + | let expr = try! parseExprStr("read 'a 'b (p)"); |
|
| 3166 | + | let case ast::NodeValue::Call(call) = expr.value else throw testing::TestError::Failed; |
|
| 3167 | + | let case ast::NodeValue::RegionApply { value, regions } = call.callee.value |
|
| 3168 | + | else throw testing::TestError::Failed; |
|
| 3169 | + | try expectIdent(value, "read"); |
|
| 3170 | + | try testing::expect(regions.len == 2 and call.args.len == 1); |
|
| 3171 | + | } |
|
| 3172 | + | ||
| 3173 | + | /// Concrete region headers can name a parent and end without a semicolon. |
|
| 3174 | + | @test unsafe fn testRegionParentSyntax() throws (testing::TestError) { |
|
| 3175 | + | let parsed = try? parseStmtsStr("fn f 'a (p: &'a u32) { let q: 'b = &*p where 'a: 'b in {} return; }"); |
|
| 3176 | + | try testing::expect(parsed <> nil); |
|
| 3177 | + | } |
|
| 3178 | + | ||
| 3179 | + | /// Cell qualifiers retain pointer ownership and payload syntax. |
|
| 3180 | + | @test unsafe fn testCellPointerType() throws (testing::TestError) { |
|
| 3181 | + | let root = try! parseTypeStr("*cell u32"); |
|
| 3182 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Cell { class, payload }) = root.value |
|
| 3183 | + | else throw testing::TestError::Failed; |
|
| 3184 | + | try testing::expect(class == ast::PointerClass::Owned); |
|
| 3185 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Integer { width: 4, .. }) = payload.value |
|
| 3186 | + | else throw testing::TestError::Failed; |
|
| 3187 | + | let borrowed = try! parseTypeStr("&'r cell Pair 's"); |
|
| 3188 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::RegionRef { type, .. }) = borrowed.value |
|
| 3189 | + | else throw testing::TestError::Failed; |
|
| 3190 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Cell { class: refClass, payload: inner }) = type.value |
|
| 3191 | + | else throw testing::TestError::Failed; |
|
| 3192 | + | try testing::expect(refClass == ast::PointerClass::Ref); |
|
| 3193 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Applied { regions, .. }) = inner.value |
|
| 3194 | + | else throw testing::TestError::Failed; |
|
| 3195 | + | try testing::expect(regions.len == 1); |
|
| 3196 | + | } |
|
| 3197 | + | ||
| 3198 | + | ||
| 3199 | + | /// Measure committed storage for a statement after an existing node. |
|
| 3200 | + | unsafe fn statementStorageUsed(source: *[u8]) -> u32 { |
|
| 3201 | + | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 3202 | + | ast::allocNode(&mut arena, ast::Span { offset: 0, length: 0 }, ast::NodeValue::Bool(true)); |
|
| 3203 | + | let start = alloc::used(&arena.arena); |
|
| 3204 | + | let mut parser = super::mkParser(scanner::SourceLoc::String, source, &mut arena, &mut STRING_POOL); |
|
| 3205 | + | super::advance(&mut parser); |
|
| 3206 | + | try! super::parseStmt(&mut parser); |
|
| 3207 | + | return alloc::used(&arena.arena) - start; |
|
| 3208 | + | } |
|
| 3209 | + | ||
| 3210 | + | /// Rewinding a failed expression preserves published nodes and source tokens. |
|
| 3211 | + | @test unsafe fn testSpeculativeRestoreStorage() throws (testing::TestError) { |
|
| 3212 | + | let expectedBytes = statementStorageUsed("return"); |
|
| 3213 | + | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 3214 | + | let retained = ast::allocNode(&mut arena, ast::Span { offset: 3, length: 1 }, ast::NodeValue::Bool(true)); |
|
| 3215 | + | let mut parser = super::mkParser(scanner::SourceLoc::String, |
|
| 3216 | + | "return [speculativeRestoreIdentifier, 1 +", &mut arena, &mut STRING_POOL); |
|
| 3217 | + | super::advance(&mut parser); |
|
| 3218 | + | try super::expect(&mut parser, scanner::TokenKind::Eof, "retained diagnostic") catch { |
|
| 3219 | + | }; |
|
| 3220 | + | let mut expected = parser; |
|
| 3221 | + | super::advance(&mut expected); |
|
| 3222 | + | let startOffset = alloc::used(&arena.arena); |
|
| 3223 | + | let startId = arena.nextId; |
|
| 3224 | + | let statement = try! super::parseStmt(&mut parser); |
|
| 3225 | + | let case ast::NodeValue::Return { value } = statement.value else throw testing::TestError::Failed; |
|
| 3226 | + | try testing::expect(value == nil); |
|
| 3227 | + | try testing::expect(statement.id == startId); |
|
| 3228 | + | try testing::expect(alloc::used(&arena.arena) == startOffset + expectedBytes); |
|
| 3229 | + | try testing::expect(arena.nextId == startId + 1); |
|
| 3230 | + | try testing::expect(parser.scanner.cursor == expected.scanner.cursor); |
|
| 3231 | + | try testing::expect(parser.scanner.token == expected.scanner.token); |
|
| 3232 | + | try testing::expect(parser.current.kind == expected.current.kind); |
|
| 3233 | + | try testing::expect(parser.current.offset == expected.current.offset); |
|
| 3234 | + | try testing::expect(parser.previous.kind == expected.previous.kind); |
|
| 3235 | + | try testing::expect(parser.previous.offset == expected.previous.offset); |
|
| 3236 | + | try testing::expect(parser.context == expected.context); |
|
| 3237 | + | try testing::expect(parser.errors.count == 1); |
|
| 3238 | + | for i in alloc::used(&arena.arena)..ARENA_STORAGE.len { |
|
| 3239 | + | set ARENA_STORAGE[i] = 0xA5; |
|
| 3240 | + | } |
|
| 3241 | + | let case ast::NodeValue::Bool(true) = retained.value else throw testing::TestError::Failed; |
|
| 3242 | + | try testing::expect(retained.span.offset == 3); |
|
| 3243 | + | try testing::expect(retained.span.length == 1); |
|
| 3244 | + | try testing::expect(mem::eq(parser.errors.list[0].message, "retained diagnostic")); |
|
| 3245 | + | try testing::expect(mem::eq(parser.errors.list[0].token.source, "return")); |
|
| 3246 | + | let interned = strings::find(&STRING_POOL, "speculativeRestoreIdentifier") else throw testing::TestError::Failed; |
|
| 3247 | + | try testing::expect(mem::eq(interned, "speculativeRestoreIdentifier")); |
|
| 3248 | + | let replacement = ast::allocNode(&mut arena, ast::Span { offset: 0, length: 0 }, ast::NodeValue::Bool(false)); |
|
| 3249 | + | try testing::expect(replacement.id == startId + 1); |
|
| 3250 | + | let case ast::NodeValue::Bool(true) = retained.value else throw testing::TestError::Failed; |
|
| 3251 | + | } |
|
| 3252 | + | ||
| 3253 | + | /// Optional return and panic expressions publish only their wrapper after failure. |
|
| 3254 | + | @test unsafe fn testSpeculativeStatementPublication() throws (testing::TestError) { |
|
| 3255 | + | let expectedBytes = statementStorageUsed("return"); |
|
| 3256 | + | for source in ["return (speculativeReturnIdentifier +", "panic (speculativePanicIdentifier +"] { |
|
| 3257 | + | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 3258 | + | ast::allocNode(&mut arena, ast::Span { offset: 0, length: 0 }, ast::NodeValue::Bool(true)); |
|
| 3259 | + | let startOffset = alloc::used(&arena.arena); |
|
| 3260 | + | let mut parser = super::mkParser(scanner::SourceLoc::String, source, &mut arena, &mut STRING_POOL); |
|
| 3261 | + | super::advance(&mut parser); |
|
| 3262 | + | let startId = arena.nextId; |
|
| 3263 | + | let statement = try! super::parseStmt(&mut parser); |
|
| 3264 | + | try testing::expect(statement.id == startId); |
|
| 3265 | + | try testing::expect(arena.nextId == startId + 1); |
|
| 3266 | + | try testing::expect(alloc::used(&arena.arena) == startOffset + expectedBytes); |
|
| 3267 | + | try testing::expect(parser.errors.count == 0); |
|
| 3268 | + | try testing::expect(parser.current.kind == scanner::TokenKind::LParen); |
|
| 3269 | + | match statement.value { |
|
| 3270 | + | case ast::NodeValue::Return { value } => try testing::expect(value == nil), |
|
| 3271 | + | case ast::NodeValue::Panic { message } => try testing::expect(message == nil), |
|
| 3272 | + | else => throw testing::TestError::Failed, |
|
| 3273 | + | } |
|
| 3274 | + | } |
|
| 3275 | + | } |
lib/std/lang/resolver.rad
+3240 -824
| 47 | 47 | export constant MAX_INSTANCES: u32 = 128; |
|
| 48 | 48 | /// Maximum standalone methods (across all types). |
|
| 49 | 49 | export constant MAX_METHODS: u32 = 256; |
|
| 50 | 50 | /// Maximum number of linear bindings active in one function. |
|
| 51 | 51 | constant MAX_LINEAR_BINDINGS: u32 = 32; |
|
| 52 | + | /// Maximum full-region projections active in nested lexical regions. |
|
| 53 | + | constant MAX_REGIONAL_LOANS: u32 = 32; |
|
| 52 | 54 | /// Maximum inline field depth used to prove borrow separation. |
|
| 53 | 55 | constant MAX_BORROW_FIELDS: u32 = 16; |
|
| 54 | 56 | /// Maximum nesting depth tracked for loops. |
|
| 55 | 57 | constant MAX_LINEAR_LOOP_DEPTH: u32 = 16; |
|
| 56 | 58 | ||
| 57 | 59 | /// Trait definition stored in the resolver. |
|
| 58 | 60 | export record TraitType: Copy { |
|
| 59 | 61 | /// Trait name. |
|
| 60 | 62 | name: *[u8], |
|
| 63 | + | /// Module that declares the trait. |
|
| 64 | + | moduleId: u16, |
|
| 61 | 65 | /// Method signatures, including from supertraits. |
|
| 62 | 66 | methods: *unsafe mut [TraitMethod], |
|
| 63 | 67 | /// Supertraits that must also be implemented. |
|
| 64 | 68 | supertraits: *unsafe mut [*unsafe TraitType], |
|
| 65 | 69 | } |
| 166 | 170 | length: u32, |
|
| 167 | 171 | } |
|
| 168 | 172 | ||
| 169 | 173 | /// Record nominal type. |
|
| 170 | 174 | export record RecordType: Copy { |
|
| 175 | + | /// Region parameters of the source declaration. |
|
| 176 | + | regions: ?*RegionScope, |
|
| 177 | + | /// Exact region arguments, if this is an applied type. |
|
| 178 | + | application: ?*unsafe NominalApplication, |
|
| 171 | 179 | fields: *unsafe [RecordField], |
|
| 172 | 180 | labeled: bool, |
|
| 173 | - | /// Cached layout. |
|
| 174 | - | layout: Layout, |
|
| 181 | + | /// Shared layout of the source declaration. |
|
| 182 | + | layout: *Layout, |
|
| 175 | 183 | /// Whether the declaration explicitly carries the `Once` marker. |
|
| 176 | 184 | declaredLinear: bool, |
|
| 177 | 185 | /// Whether the declaration explicitly carries the `Copy` marker. |
|
| 178 | 186 | declaredCopy: bool, |
|
| 179 | 187 | } |
|
| 180 | 188 | ||
| 181 | 189 | /// Union nominal type. |
|
| 182 | 190 | export record UnionType: Copy { |
|
| 191 | + | /// Region parameters of the source declaration. |
|
| 192 | + | regions: ?*RegionScope, |
|
| 193 | + | /// Exact region arguments, if this is an applied type. |
|
| 194 | + | application: ?*unsafe NominalApplication, |
|
| 183 | 195 | variants: *unsafe [UnionVariant], |
|
| 184 | - | /// Cached layout. |
|
| 185 | - | layout: Layout, |
|
| 196 | + | /// Shared layout of the source declaration. |
|
| 197 | + | layout: *Layout, |
|
| 186 | 198 | /// Cached payload offset within the union aggregate. |
|
| 187 | 199 | valOffset: u32, |
|
| 188 | 200 | /// If all variants have void payloads. |
|
| 189 | 201 | isAllVoid: bool, |
|
| 190 | 202 | /// Whether the declaration explicitly carries the `Once` marker. |
| 196 | 208 | /// Metadata for user-defined types. |
|
| 197 | 209 | export union NominalType: Copy { |
|
| 198 | 210 | /// Placeholder for a type that hasn't been fully resolved yet. |
|
| 199 | 211 | /// Stores the declaration node for lazy resolution. |
|
| 200 | 212 | Placeholder(*ast::Node), |
|
| 213 | + | /// Declaration whose value layout is under analysis. |
|
| 214 | + | Resolving(*ast::Node), |
|
| 215 | + | /// Applied type whose field or variant view is not yet resolved. |
|
| 216 | + | Application(*unsafe NominalApplication), |
|
| 201 | 217 | Record(RecordType), |
|
| 202 | 218 | Union(UnionType), |
|
| 203 | 219 | } |
|
| 204 | 220 | ||
| 205 | 221 | /// Coercion plan, when coercion from one type to another. |
| 274 | 290 | }, |
|
| 275 | 291 | } |
|
| 276 | 292 | ||
| 277 | 293 | /// Resolved function signature details. |
|
| 278 | 294 | export record FnType: Copy { |
|
| 295 | + | /// Symbolic regions declared by the source function. |
|
| 296 | + | regions: ?*RegionScope, |
|
| 279 | 297 | /// Parameter types in call order. |
|
| 280 | 298 | paramTypes: *[*Type], |
|
| 281 | 299 | /// Return value type. |
|
| 282 | 300 | returnType: *Type, |
|
| 283 | 301 | /// Error types that the function can throw. |
| 294 | 312 | Nil, Undefined, Int, |
|
| 295 | 313 | /// Primitive types. |
|
| 296 | 314 | Void, Opaque, Never, Bool, |
|
| 297 | 315 | /// Integer types. |
|
| 298 | 316 | U8, U16, U32, U64, I8, I16, I32, I64, |
|
| 317 | + | /// Shared cell pointer with value access to a Copy payload. |
|
| 318 | + | Cell { |
|
| 319 | + | /// Storage lifetime and ownership class. |
|
| 320 | + | class: types::PointerClass, |
|
| 321 | + | /// Payload type, preserved by all writes. |
|
| 322 | + | payload: *Type, |
|
| 323 | + | }, |
|
| 324 | + | /// Affine allocation interface retained by a lexical region. |
|
| 325 | + | Session(*unsafe types::Region), |
|
| 299 | 326 | /// Range types, eg. `start..end`. |
|
| 300 | 327 | Range { |
|
| 301 | 328 | start: ?*Type, |
|
| 302 | 329 | end: ?*Type, |
|
| 303 | 330 | }, |
| 545 | 572 | TryNonThrowing, |
|
| 546 | 573 | /// Inferred catch binding used with multi-error callee. |
|
| 547 | 574 | TryCatchMultiError, |
|
| 548 | 575 | /// Duplicate error type in typed catch clauses. |
|
| 549 | 576 | TryCatchDuplicateType, |
|
| 577 | + | /// Distinct error types have the same tag after region erasure. |
|
| 578 | + | AmbiguousRegionalError, |
|
| 550 | 579 | /// Typed catch clauses do not cover all error types. |
|
| 551 | 580 | TryCatchNonExhaustive, |
|
| 552 | 581 | /// Called a fallible function without using `try`. |
|
| 553 | 582 | MissingTry, |
|
| 554 | 583 | /// Cannot use opaque type in this context. |
| 625 | 654 | LinearUndefined, |
|
| 626 | 655 | /// A `Copy` declaration contains a non-copy field or variant. |
|
| 627 | 656 | CopyContainsNonCopy, |
|
| 628 | 657 | /// A declaration carries both `Copy` and `Once`. |
|
| 629 | 658 | ConflictingOwnershipMarkers, |
|
| 659 | + | /// A region name is not visible in this declaration or block. |
|
| 660 | + | UnknownRegion(*[u8]), |
|
| 661 | + | /// A region application has the wrong argument count. |
|
| 662 | + | RegionArgumentCount(CountMismatch), |
|
| 663 | + | /// A region parameter has no consistent argument from checked references. |
|
| 664 | + | RegionInference(*[u8]), |
|
| 665 | + | /// A region argument does not satisfy its declared parent relation. |
|
| 666 | + | RegionParent(*[u8]), |
|
| 667 | + | /// A region parent relation contains a cycle. |
|
| 668 | + | RegionCycle(*[u8]), |
|
| 669 | + | /// A value retains a region that has left lexical scope. |
|
| 670 | + | RegionEscape(*[u8]), |
|
| 671 | + | /// A session requires one exclusive borrow of an allocation trait implementer. |
|
| 672 | + | InvalidSessionSource, |
|
| 673 | + | /// Allocation requires a value that can be discarded without destruction. |
|
| 674 | + | InvalidAllocationValue, |
|
| 675 | + | /// Cell payload is not a storable plain Copy value. |
|
| 676 | + | InvalidCellPayload, |
|
| 677 | + | /// The allocated value has an invalid or overflowing layout. |
|
| 678 | + | InvalidAllocationLayout, |
|
| 679 | + | /// A compiler-known allocation trait method has an invalid signature. |
|
| 680 | + | InvalidAllocationRuntime, |
|
| 681 | + | /// The function has too many distinct full-region projections. |
|
| 682 | + | RegionalLoanOverflow, |
|
| 683 | + | /// A nominal value layout contains itself. |
|
| 684 | + | RecursiveType, |
|
| 630 | 685 | /// A reference appears in a storable or escaping position. |
|
| 631 | 686 | InvalidRefPosition, |
|
| 632 | 687 | /// A reference local requires a fixed binding to existing storage. |
|
| 633 | 688 | RefBinding, |
|
| 634 | 689 | /// Call arguments contain overlapping incompatible loans. |
| 675 | 730 | record SuperAccessResult: Copy { |
|
| 676 | 731 | scope: *unsafe mut Scope, |
|
| 677 | 732 | child: *ast::Node, |
|
| 678 | 733 | } |
|
| 679 | 734 | ||
| 735 | + | /// Initialization operation performed after session storage reservation. |
|
| 736 | + | export union SessionAllocationKind: Copy { |
|
| 737 | + | /// Initialize one object from a value. |
|
| 738 | + | New, |
|
| 739 | + | /// Copy plain Copy elements from a slice. |
|
| 740 | + | Copy, |
|
| 741 | + | /// Fill a slice with a plain Copy value. |
|
| 742 | + | Fill, |
|
| 743 | + | } |
|
| 744 | + | ||
| 745 | + | /// Typed session allocation and its checked runtime reservation function. |
|
| 746 | + | export record SessionAllocation: Copy { |
|
| 747 | + | /// Initialization operation. |
|
| 748 | + | kind: SessionAllocationKind, |
|
| 749 | + | /// Initialized element type. |
|
| 750 | + | item: *Type, |
|
| 751 | + | /// Allocation trait used by the session source. |
|
| 752 | + | traitInfo: *unsafe TraitType, |
|
| 753 | + | /// Reservation method slot in the allocation trait. |
|
| 754 | + | methodIndex: u32, |
|
| 755 | + | } |
|
| 756 | + | ||
| 680 | 757 | /// Node-specific resolver metadata. |
|
| 681 | 758 | export union NodeExtra: Copy { |
|
| 682 | 759 | /// No extra data for this node. |
|
| 683 | 760 | None, |
|
| 761 | + | /// Region identities owned by a source declaration. |
|
| 762 | + | Regions(*RegionScope), |
|
| 684 | 763 | /// Resolved field index for record literal fields. |
|
| 685 | 764 | RecordField { index: u32 }, |
|
| 686 | 765 | /// Slice range metadata for subscript expressions with ranges. |
|
| 687 | 766 | SliceRange(SliceRangeInfo), |
|
| 688 | 767 | /// Cached union variant metadata for patterns/constructors. |
| 700 | 779 | /// Method index in the v-table. |
|
| 701 | 780 | methodIndex: u32, |
|
| 702 | 781 | }, |
|
| 703 | 782 | /// Standalone method call metadata. |
|
| 704 | 783 | MethodCall { method: *unsafe MethodEntry }, |
|
| 784 | + | /// Typed allocation through a session interface. |
|
| 785 | + | SessionAllocation(SessionAllocation), |
|
| 705 | 786 | /// Slice `.append(val, allocator)` method call. |
|
| 706 | 787 | SliceAppend { elemType: *Type }, |
|
| 707 | 788 | /// Slice `.delete(index)` method call. |
|
| 708 | 789 | SliceDelete { elemType: *Type }, |
|
| 709 | 790 | } |
| 817 | 898 | } |
|
| 818 | 899 | ||
| 819 | 900 | /// Per-control-flow-path ownership state. |
|
| 820 | 901 | /// Read only the initialized symbol prefix below `len`. |
|
| 821 | 902 | record LinearEnv: Copy { |
|
| 903 | + | /// Active full-region loans, indexed by the checker's regional loan table. |
|
| 904 | + | regionalLoans: u64, |
|
| 822 | 905 | /// Symbol pointers. Entries below `len` are initialized and not optional. |
|
| 823 | 906 | symbols: [*unsafe mut Symbol; MAX_LINEAR_BINDINGS], |
|
| 824 | 907 | /// Bit set for each binding that remains available. |
|
| 825 | 908 | available: u64, |
|
| 826 | 909 | /// Number of initialized entries in `symbols`. |
| 852 | 935 | } |
|
| 853 | 936 | ||
| 854 | 937 | /// Function-local exact-use checker state. |
|
| 855 | 938 | /// Read loop arrays only at indices below `loopDepth`. |
|
| 856 | 939 | /// `enterLinearLoop` initializes each slot before it increases `loopDepth`. |
|
| 857 | - | record LinearChecker: Copy { |
|
| 940 | + | record LinearChecker: 'arena + 'checking where 'arena: 'checking { |
|
| 858 | 941 | /// Resolver that owns the symbols and diagnostics. |
|
| 859 | - | resolver: *unsafe mut Resolver, |
|
| 942 | + | resolver: &'checking mut Resolver 'arena, |
|
| 943 | + | /// Regional projections discovered in this function. |
|
| 944 | + | regional: [RegionalLoan; MAX_REGIONAL_LOANS], |
|
| 945 | + | /// Number of initialized regional loan entries. |
|
| 946 | + | regionalLen: u32, |
|
| 947 | + | /// Named regions active at the current source location. |
|
| 948 | + | regions: ?*RegionScope, |
|
| 949 | + | /// Regional loans carried to each loop's next iteration. |
|
| 950 | + | loopBackLoans: [u64; MAX_LINEAR_LOOP_DEPTH], |
|
| 951 | + | /// Regional loans carried to each loop's exits. |
|
| 952 | + | loopExitLoans: [u64; MAX_LINEAR_LOOP_DEPTH], |
|
| 953 | + | /// Regions active at each loop's entry and exit. |
|
| 954 | + | loopRegions: [?*RegionScope; MAX_LINEAR_LOOP_DEPTH], |
|
| 860 | 955 | /// Source places protected by active pattern references. |
|
| 861 | 956 | loans: [BorrowPlace; MAX_LINEAR_BINDINGS], |
|
| 862 | 957 | /// Number of initialized entries in `loans`. |
|
| 863 | 958 | loanLen: u32, |
|
| 864 | 959 | /// Reference locals in active lexical scopes. |
| 886 | 981 | return MatchSubject { effectiveTy: *target, by }; |
|
| 887 | 982 | } |
|
| 888 | 983 | return MatchSubject { effectiveTy: ty, by: MatchBy::Value }; |
|
| 889 | 984 | } |
|
| 890 | 985 | ||
| 986 | + | /// Region names introduced by a declaration or lexical block. |
|
| 987 | + | export record RegionScope: Copy { |
|
| 988 | + | /// Entries in declaration order. |
|
| 989 | + | entries: *unsafe [*unsafe mut types::Region], |
|
| 990 | + | /// Enclosing lexical region environment. |
|
| 991 | + | parent: ?*RegionScope, |
|
| 992 | + | } |
|
| 993 | + | ||
| 994 | + | /// Region arguments for one source declaration. |
|
| 995 | + | record RegionSubstitution: Copy { |
|
| 996 | + | /// Declared parameters in source order. |
|
| 997 | + | parameters: *RegionScope, |
|
| 998 | + | /// Inferred or explicit arguments. Every entry must be set before substitution. |
|
| 999 | + | arguments: *unsafe mut [?*unsafe types::Region], |
|
| 1000 | + | } |
|
| 1001 | + | ||
| 1002 | + | /// One interned application of a nominal declaration to exact region arguments. |
|
| 1003 | + | export record NominalApplication: Copy { |
|
| 1004 | + | /// Canonical source declaration identity. |
|
| 1005 | + | base: *unsafe NominalType, |
|
| 1006 | + | /// Source parameters in declaration order. |
|
| 1007 | + | parameters: *RegionScope, |
|
| 1008 | + | /// Region arguments in parameter order. |
|
| 1009 | + | arguments: *unsafe [*unsafe types::Region], |
|
| 1010 | + | /// Stable descriptor for the substituted field or variant view. |
|
| 1011 | + | view: *unsafe mut NominalType, |
|
| 1012 | + | /// Next application in the resolver cache. |
|
| 1013 | + | next: ?*unsafe NominalApplication, |
|
| 1014 | + | } |
|
| 1015 | + | ||
| 891 | 1016 | /// Global resolver state. |
|
| 892 | - | export record Resolver { |
|
| 1017 | + | export record Resolver: 'arena { |
|
| 1018 | + | /// Active region names for source type checking. |
|
| 1019 | + | regionScope: ?*RegionScope, |
|
| 1020 | + | /// Interned applications of nominal region parameters. |
|
| 1021 | + | applications: ?*unsafe NominalApplication, |
|
| 893 | 1022 | /// Current scope. |
|
| 894 | 1023 | scope: *unsafe mut Scope, |
|
| 895 | 1024 | /// Package scope containing package roots and top-level symbols. |
|
| 896 | 1025 | pkgScope: *unsafe mut Scope, |
|
| 897 | 1026 | /// Stack of loop contexts for nested loops. |
| 906 | 1035 | currentMod: u16, |
|
| 907 | 1036 | /// Whether the current lexical context permits unsafe operations. |
|
| 908 | 1037 | inUnsafeContext: bool, |
|
| 909 | 1038 | /// Configuration for semantic analysis. |
|
| 910 | 1039 | config: Config, |
|
| 911 | - | /// Unified arena for symbols, scopes, and nominal type. |
|
| 912 | - | arena: alloc::Arena, |
|
| 1040 | + | /// Caller-owned arena, valid for this resolver and all emitted metadata. |
|
| 1041 | + | arena: &'arena mut alloc::Arena, |
|
| 913 | 1042 | /// Combined semantic metadata table indexed by node ID. |
|
| 914 | 1043 | nodeData: NodeDataTable, |
|
| 915 | 1044 | /// Linked list of interned types. |
|
| 916 | 1045 | types: ?*TypeNode, |
|
| 917 | 1046 | /// Diagnostics recorded so far. |
| 939 | 1068 | record TypeNode: Copy { |
|
| 940 | 1069 | ty: Type, |
|
| 941 | 1070 | next: ?*TypeNode, |
|
| 942 | 1071 | } |
|
| 943 | 1072 | ||
| 1073 | + | /// Look up a region name in a lexical environment. |
|
| 1074 | + | unsafe fn findRegion(scope: ?*RegionScope, name: *[u8]) -> ?*unsafe mut types::Region { |
|
| 1075 | + | let mut current = scope; |
|
| 1076 | + | while let env = current { |
|
| 1077 | + | for region in env.entries { |
|
| 1078 | + | if mem::eq(region.name, name) { |
|
| 1079 | + | return region; |
|
| 1080 | + | } |
|
| 1081 | + | } |
|
| 1082 | + | set current = env.parent; |
|
| 1083 | + | } |
|
| 1084 | + | return nil; |
|
| 1085 | + | } |
|
| 1086 | + | ||
| 1087 | + | /// Resolve a source region name without using its spelling as an identity. |
|
| 1088 | + | unsafe fn resolveRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe types::Region |
|
| 1089 | + | throws (ResolveError) |
|
| 1090 | + | { |
|
| 1091 | + | let case ast::NodeValue::Region { name, .. } = node.value |
|
| 1092 | + | else panic "resolveRegion: invalid region node"; |
|
| 1093 | + | let region = findRegion(self.regionScope, name) |
|
| 1094 | + | else throw emitError(self, node, ErrorKind::UnknownRegion(name)); |
|
| 1095 | + | return region; |
|
| 1096 | + | } |
|
| 1097 | + | ||
| 1098 | + | /// Bind all region parameters before resolving their parent relations. |
|
| 1099 | + | unsafe fn bindRegions 'arena (self: &mut Resolver 'arena, owner: *ast::Node, nodes: *[*ast::Node]) -> ?*RegionScope |
|
| 1100 | + | throws (ResolveError) |
|
| 1101 | + | { |
|
| 1102 | + | if let case NodeExtra::Regions(scope) = self.nodeData.entries[owner.id].extra { |
|
| 1103 | + | return scope; |
|
| 1104 | + | } |
|
| 1105 | + | let mut count: u32 = 0; |
|
| 1106 | + | for node in nodes { |
|
| 1107 | + | if let case ast::NodeValue::Region { .. } = node.value { |
|
| 1108 | + | set count += 1; |
|
| 1109 | + | } |
|
| 1110 | + | } |
|
| 1111 | + | if count == 0 { |
|
| 1112 | + | return nil; |
|
| 1113 | + | } |
|
| 1114 | + | let entries = try! alloc::allocRawSlice( |
|
| 1115 | + | self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), count |
|
| 1116 | + | ) as *unsafe mut [*unsafe mut types::Region]; |
|
| 1117 | + | let mut index: u32 = 0; |
|
| 1118 | + | for node in nodes { |
|
| 1119 | + | let case ast::NodeValue::Region { name, .. } = node.value else continue; |
|
| 1120 | + | for i in 0..index { |
|
| 1121 | + | if mem::eq(entries[i].name, name) { |
|
| 1122 | + | throw emitError(self, node, ErrorKind::DuplicateBinding(name)); |
|
| 1123 | + | } |
|
| 1124 | + | } |
|
| 1125 | + | let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region)) |
|
| 1126 | + | as *unsafe mut types::Region; |
|
| 1127 | + | set *region = types::Region { id: node.id, origin: types::RegionOrigin::Parameter, name, parent: nil }; |
|
| 1128 | + | set entries[index] = region; |
|
| 1129 | + | set index += 1; |
|
| 1130 | + | } |
|
| 1131 | + | let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope; |
|
| 1132 | + | set *scope = RegionScope { entries, parent: nil }; |
|
| 1133 | + | let frozen: *RegionScope = scope; |
|
| 1134 | + | set index = 0; |
|
| 1135 | + | for node in nodes { |
|
| 1136 | + | let case ast::NodeValue::Region { parent, .. } = node.value else continue; |
|
| 1137 | + | if let parentNode = parent { |
|
| 1138 | + | let case ast::NodeValue::Region { name, .. } = parentNode.value |
|
| 1139 | + | else panic "bindRegions: invalid parent node"; |
|
| 1140 | + | let target = findRegion(frozen, name) |
|
| 1141 | + | else throw emitError(self, parentNode, ErrorKind::UnknownRegion(name)); |
|
| 1142 | + | if types::regionContains(entries[index], target) { |
|
| 1143 | + | throw emitError(self, parentNode, ErrorKind::RegionCycle(entries[index].name)); |
|
| 1144 | + | } |
|
| 1145 | + | set entries[index].parent = target; |
|
| 1146 | + | } |
|
| 1147 | + | set index += 1; |
|
| 1148 | + | } |
|
| 1149 | + | set self.nodeData.entries[owner.id].extra = NodeExtra::Regions(frozen); |
|
| 1150 | + | return frozen; |
|
| 1151 | + | } |
|
| 1152 | + | ||
| 944 | 1153 | /// Allocate and intern a type in the arena, returning a pointer for deduplication. |
|
| 945 | - | export unsafe fn allocType(self: &mut Resolver, ty: Type) -> *Type { |
|
| 1154 | + | export unsafe fn allocType 'arena (self: &mut Resolver 'arena, ty: Type) -> *Type { |
|
| 946 | 1155 | // Search existing types for a match. |
|
| 947 | 1156 | let mut cursor = self.types; |
|
| 948 | 1157 | while let node = cursor { |
|
| 949 | 1158 | if node.ty == ty { |
|
| 950 | 1159 | return &node.ty; |
|
| 951 | 1160 | } |
|
| 952 | 1161 | set cursor = node.next; |
|
| 953 | 1162 | } |
|
| 954 | 1163 | // Allocate a new type node from the arena. |
|
| 955 | 1164 | let node = try! alloc::alloc( |
|
| 956 | - | &mut self.arena, @sizeOf(TypeNode), @alignOf(TypeNode) |
|
| 1165 | + | &mut *self.arena, @sizeOf(TypeNode), @alignOf(TypeNode) |
|
| 957 | 1166 | ) as *mut TypeNode; |
|
| 958 | 1167 | ||
| 959 | 1168 | set *node = TypeNode { ty, next: self.types }; |
|
| 960 | 1169 | let frozen: *TypeNode = node; |
|
| 961 | 1170 | set self.types = frozen; |
|
| 962 | 1171 | ||
| 963 | 1172 | return &frozen.ty; |
|
| 964 | 1173 | } |
|
| 965 | 1174 | ||
| 966 | 1175 | /// Allocate a nominal type descriptor and return a pointer to it. |
|
| 967 | - | unsafe fn allocNominalType(self: &mut Resolver, info: NominalType) -> *unsafe mut NominalType { |
|
| 1176 | + | unsafe fn allocNominalType 'arena (self: &mut Resolver 'arena, info: NominalType) -> *unsafe mut NominalType { |
|
| 968 | 1177 | // Nb. We don't attempt to de-duplicate nominal type entries, |
|
| 969 | 1178 | // since they don't carry node information and we create |
|
| 970 | 1179 | // placeholder entries when binding symbols. |
|
| 971 | 1180 | let entry = try! alloc::allocRaw( |
|
| 972 | - | &mut self.arena, @sizeOf(NominalType), @alignOf(NominalType) |
|
| 1181 | + | self.arena, @sizeOf(NominalType), @alignOf(NominalType) |
|
| 973 | 1182 | ) as *unsafe mut NominalType; |
|
| 974 | 1183 | ||
| 975 | 1184 | set *entry = info; |
|
| 976 | 1185 | ||
| 977 | 1186 | return entry; |
|
| 978 | 1187 | } |
|
| 979 | 1188 | ||
| 1189 | + | /// Allocate the single runtime layout for a nominal declaration. |
|
| 1190 | + | unsafe fn allocLayout 'arena (self: &mut Resolver 'arena, value: Layout) -> *Layout { |
|
| 1191 | + | let layout = try! alloc::alloc(&mut *self.arena, @sizeOf(Layout), @alignOf(Layout)) as *mut Layout; |
|
| 1192 | + | set *layout = value; |
|
| 1193 | + | return layout; |
|
| 1194 | + | } |
|
| 1195 | + | ||
| 1196 | + | /// Get the exact arguments of an applied nominal descriptor. |
|
| 1197 | + | export unsafe fn nominalApplication(info: *unsafe NominalType) -> ?*unsafe NominalApplication { |
|
| 1198 | + | match *info { |
|
| 1199 | + | case NominalType::Application(applied) => return applied, |
|
| 1200 | + | case NominalType::Record(body) => return body.application, |
|
| 1201 | + | case NominalType::Union(body) => return body.application, |
|
| 1202 | + | else => return nil, |
|
| 1203 | + | } |
|
| 1204 | + | } |
|
| 1205 | + | ||
| 1206 | + | /// Get source region parameters without forcing a recursive type's layout. |
|
| 1207 | + | unsafe fn nominalParameters 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType) -> ?*RegionScope |
|
| 1208 | + | throws (ResolveError) |
|
| 1209 | + | { |
|
| 1210 | + | match *info { |
|
| 1211 | + | case NominalType::Placeholder(node) => return try declarationRegions(self, node), |
|
| 1212 | + | case NominalType::Resolving(node) => return try declarationRegions(self, node), |
|
| 1213 | + | case NominalType::Application(applied) => return applied.parameters, |
|
| 1214 | + | case NominalType::Record(body) => return body.regions, |
|
| 1215 | + | case NominalType::Union(body) => return body.regions, |
|
| 1216 | + | } |
|
| 1217 | + | } |
|
| 1218 | + | ||
| 1219 | + | /// Bind the regions declared by a nominal source node. |
|
| 1220 | + | unsafe fn declarationRegions 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*RegionScope |
|
| 1221 | + | throws (ResolveError) |
|
| 1222 | + | { |
|
| 1223 | + | match node.value { |
|
| 1224 | + | case ast::NodeValue::RecordDecl(decl) => return try bindRegions(self, node, decl.regions), |
|
| 1225 | + | case ast::NodeValue::UnionDecl(decl) => return try bindRegions(self, node, decl.regions), |
|
| 1226 | + | else => panic "declarationRegions: expected nominal declaration", |
|
| 1227 | + | } |
|
| 1228 | + | } |
|
| 1229 | + | ||
| 1230 | + | /// Use a hinted application only for the same unapplied nominal declaration. |
|
| 1231 | + | unsafe fn hintedNominal(info: *unsafe NominalType, hint: Type) -> *unsafe NominalType { |
|
| 1232 | + | if nominalApplication(info) <> nil { |
|
| 1233 | + | return info; |
|
| 1234 | + | } |
|
| 1235 | + | let mut target = hint; |
|
| 1236 | + | if let case Type::Optional(inner) = target { |
|
| 1237 | + | set target = *inner; |
|
| 1238 | + | } |
|
| 1239 | + | if let case Type::Nominal(other) = target { |
|
| 1240 | + | if let applied = nominalApplication(other); applied.base == info { |
|
| 1241 | + | return other; |
|
| 1242 | + | } |
|
| 1243 | + | } |
|
| 1244 | + | return info; |
|
| 1245 | + | } |
|
| 1246 | + | ||
| 1247 | + | /// Require explicit arguments for a parameterized nominal type. |
|
| 1248 | + | unsafe fn requireNominalArguments 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType, site: *ast::Node) |
|
| 1249 | + | throws (ResolveError) |
|
| 1250 | + | { |
|
| 1251 | + | if nominalApplication(info) <> nil { |
|
| 1252 | + | return; |
|
| 1253 | + | } |
|
| 1254 | + | if let parameters = try nominalParameters(self, info) { |
|
| 1255 | + | throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { |
|
| 1256 | + | expected: parameters.entries.len, actual: 0, |
|
| 1257 | + | })); |
|
| 1258 | + | } |
|
| 1259 | + | } |
|
| 1260 | + | ||
| 1261 | + | /// Intern an exact nominal application before resolving its recursive members. |
|
| 1262 | + | unsafe fn internNominalApplication 'arena ( |
|
| 1263 | + | self: &mut Resolver 'arena, base: *unsafe NominalType, map: &RegionSubstitution |
|
| 1264 | + | ) -> *unsafe mut NominalType { |
|
| 1265 | + | let mut cursor = self.applications; |
|
| 1266 | + | while let applied = cursor { |
|
| 1267 | + | if applied.base == base { |
|
| 1268 | + | let mut same = true; |
|
| 1269 | + | for argument, i in applied.arguments { |
|
| 1270 | + | let other = map.arguments[i] else panic "internNominalApplication: missing argument"; |
|
| 1271 | + | if argument.id <> other.id { |
|
| 1272 | + | set same = false; |
|
| 1273 | + | break; |
|
| 1274 | + | } |
|
| 1275 | + | } |
|
| 1276 | + | if same { |
|
| 1277 | + | return applied.view; |
|
| 1278 | + | } |
|
| 1279 | + | } |
|
| 1280 | + | set cursor = applied.next; |
|
| 1281 | + | } |
|
| 1282 | + | let arguments = try! alloc::allocRawSlice( |
|
| 1283 | + | self.arena, @sizeOf(*unsafe types::Region), @alignOf(*unsafe types::Region), map.arguments.len |
|
| 1284 | + | ) as *unsafe mut [*unsafe types::Region]; |
|
| 1285 | + | for argument, i in map.arguments { |
|
| 1286 | + | let region = argument else panic "internNominalApplication: incomplete map"; |
|
| 1287 | + | set arguments[i] = region; |
|
| 1288 | + | } |
|
| 1289 | + | let entry = try! alloc::allocRaw( |
|
| 1290 | + | self.arena, @sizeOf(NominalApplication), @alignOf(NominalApplication) |
|
| 1291 | + | ) as *unsafe mut NominalApplication; |
|
| 1292 | + | let view = allocNominalType(self, NominalType::Application(entry)); |
|
| 1293 | + | set *entry = NominalApplication { base, parameters: map.parameters, arguments, view, next: self.applications }; |
|
| 1294 | + | set self.applications = entry; |
|
| 1295 | + | return view; |
|
| 1296 | + | } |
|
| 1297 | + | ||
| 1298 | + | /// Check explicit region arguments and intern the applied nominal type. |
|
| 1299 | + | unsafe fn applyNominalRegions 'arena ( |
|
| 1300 | + | self: &mut Resolver 'arena, base: *unsafe NominalType, regions: *[*ast::Node], site: *ast::Node |
|
| 1301 | + | ) -> *unsafe mut NominalType throws (ResolveError) { |
|
| 1302 | + | if nominalApplication(base) <> nil { |
|
| 1303 | + | throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: 0, actual: regions.len })); |
|
| 1304 | + | } |
|
| 1305 | + | let parameters = try nominalParameters(self, base); |
|
| 1306 | + | let mut count: u32 = 0; |
|
| 1307 | + | if let scope = parameters { |
|
| 1308 | + | set count = scope.entries.len; |
|
| 1309 | + | } |
|
| 1310 | + | if count <> regions.len { |
|
| 1311 | + | throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len })); |
|
| 1312 | + | } |
|
| 1313 | + | let scope = parameters else panic "applyNominalRegions: empty application"; |
|
| 1314 | + | let map = regionSubstitution(self, scope); |
|
| 1315 | + | for region, i in regions { |
|
| 1316 | + | set map.arguments[i] = try resolveRegion(self, region); |
|
| 1317 | + | } |
|
| 1318 | + | try validateRegionArguments(self, &map, site); |
|
| 1319 | + | return internNominalApplication(self, base, &map); |
|
| 1320 | + | } |
|
| 1321 | + | ||
| 1322 | + | /// Complete nominal views stored inline within an applied type. |
|
| 1323 | + | /// Pointer, slice, and cell targets have independent storage layouts. |
|
| 1324 | + | unsafe fn resolveInlineTypeViews 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node) |
|
| 1325 | + | throws (ResolveError) |
|
| 1326 | + | { |
|
| 1327 | + | match ty { |
|
| 1328 | + | case Type::Nominal(info) => try ensureNominalResolved(self, info, site), |
|
| 1329 | + | case Type::Array(array) => try resolveInlineTypeViews(self, *array.item, site), |
|
| 1330 | + | case Type::Optional(inner) => try resolveInlineTypeViews(self, *inner, site), |
|
| 1331 | + | else => { |
|
| 1332 | + | }, |
|
| 1333 | + | } |
|
| 1334 | + | } |
|
| 1335 | + | ||
| 1336 | + | /// Resolve a substituted member view with the source declaration's shared layout. |
|
| 1337 | + | unsafe fn resolveNominalApplication 'arena (self: &mut Resolver 'arena, applied: *unsafe NominalApplication, site: *ast::Node) |
|
| 1338 | + | throws (ResolveError) |
|
| 1339 | + | { |
|
| 1340 | + | try ensureNominalResolved(self, applied.base, site); |
|
| 1341 | + | let map = regionSubstitution(self, applied.parameters); |
|
| 1342 | + | for argument, i in applied.arguments { |
|
| 1343 | + | set map.arguments[i] = argument; |
|
| 1344 | + | } |
|
| 1345 | + | let allocator = alloc::arenaAllocator(self.arena); |
|
| 1346 | + | match *applied.base { |
|
| 1347 | + | case NominalType::Record(body) => { |
|
| 1348 | + | let mut fields: *unsafe mut [RecordField] = &mut []; |
|
| 1349 | + | for field in body.fields { |
|
| 1350 | + | let fieldType = substituteRegions(self, &map, field.fieldType); |
|
| 1351 | + | try resolveInlineTypeViews(self, fieldType, site); |
|
| 1352 | + | fields.append(RecordField { |
|
| 1353 | + | name: field.name, |
|
| 1354 | + | fieldType, |
|
| 1355 | + | offset: field.offset, |
|
| 1356 | + | }, allocator); |
|
| 1357 | + | } |
|
| 1358 | + | set *applied.view = NominalType::Record(RecordType { |
|
| 1359 | + | regions: body.regions, |
|
| 1360 | + | application: applied, |
|
| 1361 | + | fields, |
|
| 1362 | + | labeled: body.labeled, |
|
| 1363 | + | layout: body.layout, |
|
| 1364 | + | declaredLinear: body.declaredLinear, |
|
| 1365 | + | declaredCopy: body.declaredCopy, |
|
| 1366 | + | }); |
|
| 1367 | + | } |
|
| 1368 | + | case NominalType::Union(body) => { |
|
| 1369 | + | let mut variants: *unsafe mut [UnionVariant] = &mut []; |
|
| 1370 | + | for variant in body.variants { |
|
| 1371 | + | let valueType = substituteRegions(self, &map, variant.valueType); |
|
| 1372 | + | try resolveInlineTypeViews(self, valueType, site); |
|
| 1373 | + | variants.append(UnionVariant { |
|
| 1374 | + | name: variant.name, |
|
| 1375 | + | valueType, |
|
| 1376 | + | symbol: variant.symbol, |
|
| 1377 | + | }, allocator); |
|
| 1378 | + | } |
|
| 1379 | + | set *applied.view = NominalType::Union(UnionType { |
|
| 1380 | + | regions: body.regions, |
|
| 1381 | + | application: applied, |
|
| 1382 | + | variants, |
|
| 1383 | + | layout: body.layout, |
|
| 1384 | + | valOffset: body.valOffset, |
|
| 1385 | + | isAllVoid: body.isAllVoid, |
|
| 1386 | + | declaredLinear: body.declaredLinear, |
|
| 1387 | + | declaredCopy: body.declaredCopy, |
|
| 1388 | + | }); |
|
| 1389 | + | } |
|
| 1390 | + | else => panic "resolveNominalApplication: unresolved base", |
|
| 1391 | + | } |
|
| 1392 | + | } |
|
| 1393 | + | ||
| 1394 | + | /// Complete all applied member views before semantic metadata reaches lowering. |
|
| 1395 | + | unsafe fn resolveNominalApplications 'arena (self: &mut Resolver 'arena, site: *ast::Node) throws (ResolveError) { |
|
| 1396 | + | let mut end: ?*unsafe NominalApplication = nil; |
|
| 1397 | + | loop { |
|
| 1398 | + | let first = self.applications; |
|
| 1399 | + | let mut cursor = first; |
|
| 1400 | + | while cursor <> end { |
|
| 1401 | + | let applied = cursor else panic "resolveNominalApplications: invalid frontier"; |
|
| 1402 | + | try ensureNominalResolved(self, applied.view, site); |
|
| 1403 | + | set cursor = applied.next; |
|
| 1404 | + | } |
|
| 1405 | + | if self.applications == first { |
|
| 1406 | + | return; |
|
| 1407 | + | } |
|
| 1408 | + | set end = first; |
|
| 1409 | + | } |
|
| 1410 | + | } |
|
| 1411 | + | ||
| 980 | 1412 | /// Allocate a function type descriptor and return a pointer to it. |
|
| 981 | - | unsafe fn allocFnType(self: &mut Resolver, info: FnType) -> *FnType { |
|
| 1413 | + | unsafe fn allocFnType 'arena (self: &mut Resolver 'arena, info: FnType) -> *FnType { |
|
| 982 | 1414 | let entry = try! alloc::alloc( |
|
| 983 | - | &mut self.arena, @sizeOf(FnType), @alignOf(FnType) |
|
| 1415 | + | &mut *self.arena, @sizeOf(FnType), @alignOf(FnType) |
|
| 984 | 1416 | ) as *mut FnType; |
|
| 985 | 1417 | ||
| 986 | 1418 | set *entry = info; |
|
| 987 | 1419 | ||
| 988 | 1420 | return entry; |
|
| 989 | 1421 | } |
|
| 990 | 1422 | ||
| 991 | 1423 | /// Returns an error, if any, associated with the given node. |
|
| 992 | - | fn errorForNode(self: &Resolver, node: *ast::Node) -> ?Error { |
|
| 1424 | + | fn errorForNode 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Error { |
|
| 993 | 1425 | for i in 0..self.errors.len { |
|
| 994 | 1426 | let err = self.errors.entries[i]; |
|
| 995 | 1427 | if err.node == node { |
|
| 996 | 1428 | return err; |
|
| 997 | 1429 | } |
| 999 | 1431 | return nil; |
|
| 1000 | 1432 | } |
|
| 1001 | 1433 | ||
| 1002 | 1434 | /// Storage buffers used by the analyzer. |
|
| 1003 | 1435 | export record ResolverStorage { |
|
| 1004 | - | /// Unified arena for symbols, scopes, and nominal type. |
|
| 1005 | - | arena: alloc::Arena, |
|
| 1006 | 1436 | /// Node semantic metadata indexed by node ID. |
|
| 1007 | 1437 | nodeData: *mut [NodeData], |
|
| 1008 | 1438 | /// Package scope. |
|
| 1009 | 1439 | pkgScope: *unsafe mut Scope, |
|
| 1010 | 1440 | /// Error storage. |
| 1018 | 1448 | /// Root AST node. |
|
| 1019 | 1449 | rootAst: *ast::Node, |
|
| 1020 | 1450 | } |
|
| 1021 | 1451 | ||
| 1022 | 1452 | /// Construct a resolver with module context and backing storage. |
|
| 1023 | - | export unsafe fn resolver( |
|
| 1453 | + | /// The arena owner and backing bytes must retain stable addresses during use. |
|
| 1454 | + | /// Arena reclamation can occur only after all metadata uses end. |
|
| 1455 | + | export unsafe fn resolver 'arena ( |
|
| 1456 | + | arena: &'arena mut alloc::Arena, |
|
| 1024 | 1457 | storage: ResolverStorage, |
|
| 1025 | 1458 | config: Config |
|
| 1026 | - | ) -> Resolver { |
|
| 1027 | - | let case ResolverStorage { arena: initialArena, nodeData, pkgScope, errors } = storage else panic "expected resolver storage"; |
|
| 1028 | - | let mut arena = initialArena; |
|
| 1459 | + | ) -> Resolver 'arena { |
|
| 1460 | + | let case ResolverStorage { nodeData, pkgScope, errors } = storage else panic "expected resolver storage"; |
|
| 1029 | 1461 | let symbols = try! alloc::allocRawSlice( |
|
| 1030 | - | &mut arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS |
|
| 1462 | + | arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS |
|
| 1031 | 1463 | ) as *unsafe mut [*unsafe mut Symbol]; |
|
| 1032 | 1464 | ||
| 1033 | 1465 | // Initialize the root scope. |
|
| 1034 | 1466 | // TODO: Set this up when declaring `PKG_SCOPE`, not here. |
|
| 1035 | 1467 | set *pkgScope = Scope { |
| 1057 | 1489 | let mut moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES] = undefined; |
|
| 1058 | 1490 | // TODO: Simplify. |
|
| 1059 | 1491 | for i in 0..moduleScopes.len { |
|
| 1060 | 1492 | set moduleScopes[i] = nil; |
|
| 1061 | 1493 | } |
|
| 1062 | - | return Resolver { |
|
| 1494 | + | return Resolver 'arena { |
|
| 1495 | + | regionScope: nil, |
|
| 1496 | + | applications: nil, |
|
| 1063 | 1497 | scope: pkgScope, |
|
| 1064 | 1498 | pkgScope: pkgScope, |
|
| 1065 | 1499 | loopStack: undefined, |
|
| 1066 | 1500 | loopDepth: 0, |
|
| 1067 | 1501 | currentFn: nil, |
| 1083 | 1517 | }; |
|
| 1084 | 1518 | } |
|
| 1085 | 1519 | ||
| 1086 | 1520 | /// Capture the current errors in an immutable arena allocation. |
|
| 1087 | 1521 | /// The allocation must remain valid while the diagnostics are used. |
|
| 1088 | - | export unsafe fn diagnostics(self: &mut Resolver) -> Diagnostics { |
|
| 1522 | + | export unsafe fn diagnostics 'arena (self: &mut Resolver 'arena) -> Diagnostics { |
|
| 1089 | 1523 | let count = self.errors.len; |
|
| 1090 | 1524 | let entries = try! alloc::allocSlice( |
|
| 1091 | - | &mut self.arena, @sizeOf(Error), @alignOf(Error), count |
|
| 1525 | + | self.arena, @sizeOf(Error), @alignOf(Error), count |
|
| 1092 | 1526 | ) as *mut [Error]; |
|
| 1093 | 1527 | for i in 0..self.errors.len { |
|
| 1094 | 1528 | set entries[i] = self.errors.entries[i]; |
|
| 1095 | 1529 | } |
|
| 1096 | 1530 | return Diagnostics { errors: entries }; |
| 1108 | 1542 | } |
|
| 1109 | 1543 | return errs[index]; |
|
| 1110 | 1544 | } |
|
| 1111 | 1545 | ||
| 1112 | 1546 | /// Record an error diagnostic and return an error sentinel suitable for throwing. |
|
| 1113 | - | fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 1547 | + | fn emitError 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 1114 | 1548 | // If our error list is full, just return an error without recording it. |
|
| 1115 | 1549 | if self.errors.len >= self.errors.entries.len { |
|
| 1116 | 1550 | return ResolveError::Failure; |
|
| 1117 | 1551 | } |
|
| 1118 | 1552 | // Don't record more than one error per node. |
| 1125 | 1559 | ||
| 1126 | 1560 | return ResolveError::Failure; |
|
| 1127 | 1561 | } |
|
| 1128 | 1562 | ||
| 1129 | 1563 | /// Like [`emitError`], but for type mismatches specifically. |
|
| 1130 | - | unsafe fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError { |
|
| 1564 | + | unsafe fn emitTypeMismatch 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError { |
|
| 1131 | 1565 | return emitError(self, node, ErrorKind::TypeMismatch(mismatch)); |
|
| 1132 | 1566 | } |
|
| 1133 | 1567 | ||
| 1134 | 1568 | /// Allocate a scope object with the given symbol capacity. |
|
| 1135 | - | unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope { |
|
| 1569 | + | unsafe fn allocScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope { |
|
| 1136 | 1570 | // Check for an existing scope for this node, and don't allocate a new |
|
| 1137 | 1571 | // one in that case. |
|
| 1138 | 1572 | if let scope = scopeFor(self, owner) { |
|
| 1139 | 1573 | return scope; |
|
| 1140 | 1574 | } |
|
| 1141 | 1575 | assert owner.id < self.nodeData.entries.len, "allocScope: node ID out of bounds"; |
|
| 1142 | - | let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(Scope), @alignOf(Scope)); |
|
| 1576 | + | let p = try! alloc::allocRaw(self.arena, @sizeOf(Scope), @alignOf(Scope)); |
|
| 1143 | 1577 | let entry = p as *unsafe mut Scope; |
|
| 1144 | 1578 | ||
| 1145 | 1579 | // Allocate symbols from the arena. |
|
| 1146 | 1580 | let symbols = try! alloc::allocRawSlice( |
|
| 1147 | - | &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity |
|
| 1581 | + | self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity |
|
| 1148 | 1582 | ) as *unsafe mut [*unsafe mut Symbol]; |
|
| 1149 | 1583 | ||
| 1150 | 1584 | set *entry = Scope { owner, parent: nil, moduleId: nil, symbols, symbolsLen: 0 }; |
|
| 1151 | 1585 | set self.nodeData.entries[owner.id].scope = entry; |
|
| 1152 | 1586 |
| 1154 | 1588 | } |
|
| 1155 | 1589 | ||
| 1156 | 1590 | /// Enter a new local scope that is the child of the current scope. |
|
| 1157 | 1591 | /// This creates a parent/child relationship that means that lookups in the |
|
| 1158 | 1592 | /// child scope can recurse upwards. |
|
| 1159 | - | export unsafe fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *unsafe Scope { |
|
| 1593 | + | export unsafe fn enterScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node) -> *unsafe Scope { |
|
| 1160 | 1594 | let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS); |
|
| 1161 | 1595 | set scope.parent = self.scope; |
|
| 1162 | 1596 | set self.scope = scope; |
|
| 1163 | 1597 | return scope; |
|
| 1164 | 1598 | } |
|
| 1165 | 1599 | ||
| 1166 | 1600 | /// Enter a module scope. Returns an object that can be used to exit the scope. |
|
| 1167 | - | export unsafe fn enterModuleScope(self: &mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope { |
|
| 1601 | + | export unsafe fn enterModuleScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope { |
|
| 1168 | 1602 | let prevScope = self.scope; |
|
| 1169 | 1603 | let prevMod = self.currentMod; |
|
| 1170 | 1604 | let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS); |
|
| 1171 | 1605 | ||
| 1172 | 1606 | set self.scope = scope; |
| 1177 | 1611 | ||
| 1178 | 1612 | return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod }; |
|
| 1179 | 1613 | } |
|
| 1180 | 1614 | ||
| 1181 | 1615 | /// Enter a sub-module. Changes the current scope into that of the sub-module. |
|
| 1182 | - | unsafe fn enterSubModule(self: &mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) { |
|
| 1616 | + | unsafe fn enterSubModule 'arena (self: &mut Resolver 'arena, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) { |
|
| 1183 | 1617 | let modEntry = module::findChild(self.moduleGraph, name, self.currentMod) |
|
| 1184 | 1618 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 1185 | - | let modRoot = modEntry.ast |
|
| 1619 | + | let modRoot = module::astFor(modEntry) |
|
| 1186 | 1620 | else panic "enterSubModule: analyzing module that wasn't parsed"; |
|
| 1187 | 1621 | ||
| 1188 | 1622 | return enterModuleScope(self, modRoot, modEntry); |
|
| 1189 | 1623 | } |
|
| 1190 | 1624 | ||
| 1191 | 1625 | /// Exit a module scope, given the object returned by `enterModuleScope`. |
|
| 1192 | - | export fn exitModuleScope(self: &mut Resolver, entry: ModuleScope) { |
|
| 1626 | + | export fn exitModuleScope 'arena (self: &mut Resolver 'arena, entry: ModuleScope) { |
|
| 1193 | 1627 | set self.scope = entry.prevScope; |
|
| 1194 | 1628 | set self.currentMod = entry.prevMod; |
|
| 1195 | 1629 | } |
|
| 1196 | 1630 | ||
| 1197 | 1631 | /// Exit the most recent scope. |
|
| 1198 | - | export unsafe fn exitScope(self: &mut Resolver) { |
|
| 1632 | + | export unsafe fn exitScope 'arena (self: &mut Resolver 'arena) { |
|
| 1199 | 1633 | let parent = self.scope.parent else { |
|
| 1200 | 1634 | // TODO: This should be a panic, but one of the tests hits this |
|
| 1201 | 1635 | // clause, which might be a bug in the generator. |
|
| 1202 | 1636 | return; |
|
| 1203 | 1637 | }; |
|
| 1204 | 1638 | set self.scope = parent; |
|
| 1205 | 1639 | } |
|
| 1206 | 1640 | ||
| 1207 | 1641 | /// Visit the body of a loop while tracking nesting depth. |
|
| 1208 | - | unsafe fn visitLoop(self: &mut Resolver, body: *ast::Node) -> Type |
|
| 1642 | + | unsafe fn visitLoop 'arena (self: &mut Resolver 'arena, body: *ast::Node) -> Type |
|
| 1209 | 1643 | throws (ResolveError) |
|
| 1210 | 1644 | { |
|
| 1211 | 1645 | assert self.loopDepth < MAX_LOOP_DEPTH, "visitLoop: loop nesting depth exceeded"; |
|
| 1212 | 1646 | set self.loopStack[self.loopDepth] = LoopCtx { hasBreak: false }; |
|
| 1213 | 1647 | set self.loopDepth += 1; |
| 1225 | 1659 | } |
|
| 1226 | 1660 | return Type::Never; |
|
| 1227 | 1661 | } |
|
| 1228 | 1662 | ||
| 1229 | 1663 | /// Require that loop control statements appear inside a loop. |
|
| 1230 | - | unsafe fn ensureInsideLoop(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 1664 | + | unsafe fn ensureInsideLoop 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) { |
|
| 1231 | 1665 | if self.loopDepth == 0 { |
|
| 1232 | 1666 | throw emitError(self, node, ErrorKind::InvalidLoopControl); |
|
| 1233 | 1667 | } |
|
| 1234 | 1668 | } |
|
| 1235 | 1669 | ||
| 1236 | 1670 | /// Bind a loop pattern to the provided type. |
|
| 1237 | - | unsafe fn bindForLoopPattern(self: &mut Resolver, pattern: *ast::Node, ty: Type, mutable: bool) |
|
| 1671 | + | unsafe fn bindForLoopPattern 'arena (self: &mut Resolver 'arena, pattern: *ast::Node, ty: Type, mutable: bool) |
|
| 1238 | 1672 | throws (ResolveError) |
|
| 1239 | 1673 | { |
|
| 1240 | 1674 | match pattern.value { |
|
| 1241 | 1675 | case ast::NodeValue::Placeholder, ast::NodeValue::Ident(_) => { |
|
| 1242 | 1676 | let _ = try bindValueIdent(self, pattern, pattern, ty, mutable, 0, 0); |
| 1247 | 1681 | } |
|
| 1248 | 1682 | } |
|
| 1249 | 1683 | } |
|
| 1250 | 1684 | ||
| 1251 | 1685 | /// Set the expected return type for a new function body. |
|
| 1252 | - | unsafe fn enterFn(self: &mut Resolver, node: *ast::Node, ty: &FnType) { |
|
| 1686 | + | unsafe fn enterFn 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: &FnType) { |
|
| 1253 | 1687 | assert self.currentFn == nil, "enterFn: already in a function"; |
|
| 1254 | 1688 | set self.currentFn = *ty; |
|
| 1255 | 1689 | set self.currentFnNode = node; |
|
| 1256 | 1690 | enterScope(self, node); |
|
| 1257 | 1691 | } |
|
| 1258 | 1692 | ||
| 1259 | 1693 | /// Clear the expected return type when leaving a function body. |
|
| 1260 | - | unsafe fn exitFn(self: &mut Resolver) { |
|
| 1694 | + | unsafe fn exitFn 'arena (self: &mut Resolver 'arena) { |
|
| 1261 | 1695 | if self.currentFn == nil { |
|
| 1262 | 1696 | // TODO: This should be a panic, but one of the tests hits this |
|
| 1263 | 1697 | // clause, which might be a bug in the generator. |
|
| 1264 | 1698 | return; |
|
| 1265 | 1699 | } |
| 1267 | 1701 | set self.currentFnNode = nil; |
|
| 1268 | 1702 | exitScope(self); |
|
| 1269 | 1703 | } |
|
| 1270 | 1704 | ||
| 1271 | 1705 | /// Extract the identifier text from a node. |
|
| 1272 | - | unsafe fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8] |
|
| 1706 | + | unsafe fn nodeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *[u8] |
|
| 1273 | 1707 | throws (ResolveError) |
|
| 1274 | 1708 | { |
|
| 1275 | 1709 | let case ast::NodeValue::Ident(name) = node.value |
|
| 1276 | 1710 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
|
| 1277 | 1711 | return name; |
|
| 1278 | 1712 | } |
|
| 1279 | 1713 | ||
| 1280 | 1714 | /// Associate a resolved symbol with an AST node. |
|
| 1281 | - | fn setNodeSymbol(self: &mut Resolver, node: *ast::Node, symbol: *unsafe mut Symbol) { |
|
| 1715 | + | fn setNodeSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node, symbol: *unsafe mut Symbol) { |
|
| 1282 | 1716 | if let existingSym = self.nodeData.entries[node.id].sym { |
|
| 1283 | 1717 | panic "setNodeSymbol: a symbol is already associated with this node"; |
|
| 1284 | 1718 | } |
|
| 1285 | 1719 | set self.nodeData.entries[node.id].sym = symbol; |
|
| 1286 | 1720 | } |
|
| 1287 | 1721 | ||
| 1288 | 1722 | /// Associate a resolved type with an AST node and return it. |
|
| 1289 | - | fn setNodeType(self: &mut Resolver, node: *ast::Node, ty: Type) -> Type { |
|
| 1723 | + | fn setNodeType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) -> Type { |
|
| 1290 | 1724 | if ty == Type::Unknown { |
|
| 1291 | 1725 | // In this case, we simply don't associate a type. |
|
| 1292 | 1726 | return ty; |
|
| 1293 | 1727 | } |
|
| 1294 | 1728 | set self.nodeData.entries[node.id].ty = ty; |
| 1307 | 1741 | } |
|
| 1308 | 1742 | return Type::Void; |
|
| 1309 | 1743 | } |
|
| 1310 | 1744 | ||
| 1311 | 1745 | /// Associate a coercion plan with an AST node. |
|
| 1312 | - | fn setNodeCoercion(self: &mut Resolver, node: *ast::Node, coercion: Coercion) -> Coercion { |
|
| 1746 | + | fn setNodeCoercion 'arena (self: &mut Resolver 'arena, node: *ast::Node, coercion: Coercion) -> Coercion { |
|
| 1313 | 1747 | if coercion == Coercion::Identity { |
|
| 1314 | 1748 | return coercion; |
|
| 1315 | 1749 | } |
|
| 1316 | 1750 | set self.nodeData.entries[node.id].coercion = coercion; |
|
| 1317 | 1751 | ||
| 1318 | 1752 | return coercion; |
|
| 1319 | 1753 | } |
|
| 1320 | 1754 | ||
| 1321 | 1755 | /// Associate a constant value with an AST node. |
|
| 1322 | - | fn setNodeConstValue(self: &mut Resolver, node: *ast::Node, value: ConstValue) { |
|
| 1756 | + | fn setNodeConstValue 'arena (self: &mut Resolver 'arena, node: *ast::Node, value: ConstValue) { |
|
| 1323 | 1757 | set self.nodeData.entries[node.id].constValue = value; |
|
| 1324 | 1758 | } |
|
| 1325 | 1759 | ||
| 1326 | 1760 | /// Associate a record field index with a record literal field node. |
|
| 1327 | - | fn setRecordFieldIndex(self: &mut Resolver, node: *ast::Node, index: u32) { |
|
| 1761 | + | fn setRecordFieldIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node, index: u32) { |
|
| 1328 | 1762 | set self.nodeData.entries[node.id].extra = NodeExtra::RecordField { index }; |
|
| 1329 | 1763 | } |
|
| 1330 | 1764 | ||
| 1331 | 1765 | /// Associate slice range metadata with a subscript expression. |
|
| 1332 | - | fn setSliceRangeInfo(self: &mut Resolver, node: *ast::Node, info: SliceRangeInfo) { |
|
| 1766 | + | fn setSliceRangeInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: SliceRangeInfo) { |
|
| 1333 | 1767 | set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info); |
|
| 1334 | 1768 | } |
|
| 1335 | 1769 | ||
| 1336 | 1770 | /// Associate union variant metadata with a pattern or constructor node. |
|
| 1337 | - | fn setVariantInfo(self: &mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) { |
|
| 1771 | + | fn setVariantInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, ordinal: u32, tag: u32) { |
|
| 1338 | 1772 | set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag }; |
|
| 1339 | 1773 | } |
|
| 1340 | 1774 | ||
| 1341 | 1775 | /// Associate trait method call metadata with a call node. |
|
| 1342 | - | fn setTraitMethodCall(self: &mut Resolver, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) { |
|
| 1776 | + | fn setTraitMethodCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) { |
|
| 1343 | 1777 | set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex }; |
|
| 1344 | 1778 | } |
|
| 1345 | 1779 | ||
| 1346 | 1780 | /// Associate for-loop metadata with a for-loop node. |
|
| 1347 | - | fn setForLoopInfo(self: &mut Resolver, node: *ast::Node, info: ForLoopInfo) { |
|
| 1781 | + | fn setForLoopInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: ForLoopInfo) { |
|
| 1348 | 1782 | set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info); |
|
| 1349 | 1783 | } |
|
| 1350 | 1784 | ||
| 1351 | 1785 | /// Retrieve the constant value associated with a node, if any. |
|
| 1352 | - | export fn constValueEntry(self: &Resolver, node: *ast::Node) -> ?ConstValue { |
|
| 1786 | + | export fn constValueEntry 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ConstValue { |
|
| 1353 | 1787 | return self.nodeData.entries[node.id].constValue; |
|
| 1354 | 1788 | } |
|
| 1355 | 1789 | ||
| 1356 | 1790 | /// Get the resolved record field index for a record literal field node. |
|
| 1357 | - | export fn recordFieldIndexFor(self: &Resolver, node: *ast::Node) -> ?u32 { |
|
| 1791 | + | export fn recordFieldIndexFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?u32 { |
|
| 1358 | 1792 | if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra { |
|
| 1359 | 1793 | return index; |
|
| 1360 | 1794 | } |
|
| 1361 | 1795 | return nil; |
|
| 1362 | 1796 | } |
|
| 1363 | 1797 | ||
| 1364 | 1798 | /// Get the slice range metadata for a subscript expression with a range index. |
|
| 1365 | - | export fn sliceRangeInfoFor(self: &Resolver, node: *ast::Node) -> ?SliceRangeInfo { |
|
| 1799 | + | export fn sliceRangeInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?SliceRangeInfo { |
|
| 1366 | 1800 | if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra { |
|
| 1367 | 1801 | return info; |
|
| 1368 | 1802 | } |
|
| 1369 | 1803 | return nil; |
|
| 1370 | 1804 | } |
|
| 1371 | 1805 | ||
| 1372 | 1806 | /// Get the for-loop metadata for a for-loop node. |
|
| 1373 | - | export fn forLoopInfoFor(self: &Resolver, node: *ast::Node) -> ?ForLoopInfo { |
|
| 1807 | + | export fn forLoopInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ForLoopInfo { |
|
| 1374 | 1808 | if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra { |
|
| 1375 | 1809 | return info; |
|
| 1376 | 1810 | } |
|
| 1377 | 1811 | return nil; |
|
| 1378 | 1812 | } |
|
| 1379 | 1813 | ||
| 1380 | 1814 | /// Associate match prong metadata with a match prong node. |
|
| 1381 | - | fn setProngCatchAll(self: &mut Resolver, node: *ast::Node, catchAll: bool) { |
|
| 1815 | + | fn setProngCatchAll 'arena (self: &mut Resolver 'arena, node: *ast::Node, catchAll: bool) { |
|
| 1382 | 1816 | set self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll }; |
|
| 1383 | 1817 | } |
|
| 1384 | 1818 | ||
| 1385 | 1819 | /// Check if a prong is catch-all. |
|
| 1386 | - | export fn isProngCatchAll(self: &Resolver, node: *ast::Node) -> bool { |
|
| 1820 | + | export fn isProngCatchAll 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool { |
|
| 1387 | 1821 | if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra { |
|
| 1388 | 1822 | return catchAll; |
|
| 1389 | 1823 | } |
|
| 1390 | 1824 | return false; |
|
| 1391 | 1825 | } |
|
| 1392 | 1826 | ||
| 1393 | 1827 | /// Set match metadata. |
|
| 1394 | - | fn setMatchConst(self: &mut Resolver, node: *ast::Node, isConst: bool) { |
|
| 1828 | + | fn setMatchConst 'arena (self: &mut Resolver 'arena, node: *ast::Node, isConst: bool) { |
|
| 1395 | 1829 | set self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst }; |
|
| 1396 | 1830 | } |
|
| 1397 | 1831 | ||
| 1398 | 1832 | /// Check if a match has all constant patterns. |
|
| 1399 | - | export fn isMatchConst(self: &Resolver, node: *ast::Node) -> bool { |
|
| 1833 | + | export fn isMatchConst 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool { |
|
| 1400 | 1834 | if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra { |
|
| 1401 | 1835 | return isConst; |
|
| 1402 | 1836 | } |
|
| 1403 | 1837 | return false; |
|
| 1404 | 1838 | } |
|
| 1405 | 1839 | ||
| 1406 | 1840 | /// Get the resolver metadata for a node. |
|
| 1407 | - | export fn nodeData(self: &Resolver, node: *ast::Node) -> NodeData { |
|
| 1841 | + | export fn nodeData 'arena (self: &Resolver 'arena, node: *ast::Node) -> NodeData { |
|
| 1408 | 1842 | return self.nodeData.entries[node.id]; |
|
| 1409 | 1843 | } |
|
| 1410 | 1844 | ||
| 1411 | 1845 | /// Get the type for a node, or `nil` if unknown. |
|
| 1412 | - | export fn typeFor(self: &Resolver, node: *ast::Node) -> ?Type { |
|
| 1846 | + | export fn typeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Type { |
|
| 1413 | 1847 | let ty = self.nodeData.entries[node.id].ty; |
|
| 1414 | 1848 | if ty == Type::Unknown { |
|
| 1415 | 1849 | return nil; |
|
| 1416 | 1850 | } |
|
| 1417 | 1851 | return ty; |
|
| 1418 | 1852 | } |
|
| 1419 | 1853 | ||
| 1420 | 1854 | /// Get the scope associated with a node. |
|
| 1421 | - | export fn scopeFor(self: &Resolver, node: *ast::Node) -> ?*unsafe mut Scope { |
|
| 1855 | + | export fn scopeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope { |
|
| 1422 | 1856 | return self.nodeData.entries[node.id].scope; |
|
| 1423 | 1857 | } |
|
| 1424 | 1858 | ||
| 1425 | 1859 | /// Get the symbol bound to a node. |
|
| 1426 | - | export fn symbolFor(self: &Resolver, node: *ast::Node) -> ?*unsafe mut Symbol { |
|
| 1860 | + | export fn symbolFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol { |
|
| 1427 | 1861 | return self.nodeData.entries[node.id].sym; |
|
| 1428 | 1862 | } |
|
| 1429 | 1863 | ||
| 1430 | 1864 | /// Get the coercion plan associated with a node, if any. |
|
| 1431 | - | export fn coercionFor(self: &Resolver, node: *ast::Node) -> ?Coercion { |
|
| 1865 | + | export fn coercionFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Coercion { |
|
| 1432 | 1866 | let c = self.nodeData.entries[node.id].coercion; |
|
| 1433 | 1867 | if c == Coercion::Identity { |
|
| 1434 | 1868 | return nil; |
|
| 1435 | 1869 | } |
|
| 1436 | 1870 | return c; |
|
| 1437 | 1871 | } |
|
| 1438 | 1872 | ||
| 1439 | 1873 | /// Get the module ID for a symbol by walking up its scope chain. |
|
| 1440 | - | export unsafe fn moduleIdForSymbol(self: &Resolver, sym: *unsafe Symbol) -> ?u16 { |
|
| 1874 | + | export unsafe fn moduleIdForSymbol 'arena (self: &Resolver 'arena, sym: *unsafe Symbol) -> ?u16 { |
|
| 1441 | 1875 | // For module-level symbols, return the cached module ID. |
|
| 1442 | 1876 | if let id = sym.moduleId { |
|
| 1443 | 1877 | return id; |
|
| 1444 | 1878 | } |
|
| 1445 | 1879 | // For module symbols, return the module ID directly. |
| 1453 | 1887 | return nil; |
|
| 1454 | 1888 | } |
|
| 1455 | 1889 | ||
| 1456 | 1890 | /// Get the binding node for a variant pattern. |
|
| 1457 | 1891 | /// Returns the argument node if this is a variant constructor with a non-placeholder binding. |
|
| 1458 | - | export unsafe fn variantPatternBinding(self: &Resolver, pattern: *ast::Node) -> ?*ast::Node { |
|
| 1892 | + | export unsafe fn variantPatternBinding 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> ?*ast::Node { |
|
| 1459 | 1893 | let case ast::NodeValue::Call(call) = pattern.value |
|
| 1460 | 1894 | else return nil; |
|
| 1461 | 1895 | let sym = symbolFor(self, call.callee) |
|
| 1462 | 1896 | else return nil; |
|
| 1463 | 1897 | let case SymbolData::Variant { .. } = sym.data |
| 1473 | 1907 | } |
|
| 1474 | 1908 | return arg; |
|
| 1475 | 1909 | } |
|
| 1476 | 1910 | ||
| 1477 | 1911 | /// Allocate a new symbol, and return a reference to it. |
|
| 1478 | - | unsafe fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol { |
|
| 1479 | - | let sym = try! alloc::allocRaw(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol; |
|
| 1912 | + | unsafe fn allocSymbol 'arena (self: &mut Resolver 'arena, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol { |
|
| 1913 | + | let sym = try! alloc::allocRaw(self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol; |
|
| 1480 | 1914 | set *sym = Symbol { name, data, attrs, node, moduleId: nil }; |
|
| 1481 | 1915 | ||
| 1482 | 1916 | return sym; |
|
| 1483 | 1917 | } |
|
| 1484 | 1918 | ||
| 1485 | 1919 | /// Check that a type is boolean, otherwise throw an error. |
|
| 1486 | - | unsafe fn checkBoolean(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 1920 | + | unsafe fn checkBoolean 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 1487 | 1921 | return try checkEqual(self, node, Type::Bool); |
|
| 1488 | 1922 | } |
|
| 1489 | 1923 | ||
| 1490 | 1924 | /// Check that a type is numeric, otherwise throw an error. |
|
| 1491 | - | unsafe fn checkNumeric(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 1925 | + | unsafe fn checkNumeric 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 1492 | 1926 | let ty = try infer(self, node); |
|
| 1493 | 1927 | if not isNumericType(ty) { |
|
| 1494 | 1928 | throw emitError(self, node, ErrorKind::ExpectedNumeric); |
|
| 1495 | 1929 | } |
|
| 1496 | 1930 | return ty; |
| 1523 | 1957 | } |
|
| 1524 | 1958 | ||
| 1525 | 1959 | /// Get the layout of a type. |
|
| 1526 | 1960 | export unsafe fn getTypeLayout(ty: Type) -> Layout { |
|
| 1527 | 1961 | match ty { |
|
| 1528 | - | case Type::Pointer { .. } => return Layout { size: PTR_SIZE, alignment: PTR_SIZE }, |
|
| 1529 | - | case Type::Slice { .. }, Type::TraitObject { .. } => |
|
| 1962 | + | case Type::Pointer { .. } => return Layout { |
|
| 1963 | + | size: PTR_SIZE, alignment: PTR_SIZE |
|
| 1964 | + | }, |
|
| 1965 | + | case Type::Slice { .. }, Type::TraitObject { .. }, Type::Session(_) => |
|
| 1530 | 1966 | return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE }, |
|
| 1531 | 1967 | case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 }, |
|
| 1532 | 1968 | case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 }, |
|
| 1533 | 1969 | case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 }, |
|
| 1534 | 1970 | case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 }, |
|
| 1535 | 1971 | case Type::Int => return Layout { size: 8, alignment: 8 }, |
|
| 1536 | 1972 | case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 }, |
|
| 1537 | 1973 | case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE }, |
|
| 1974 | + | case Type::Cell { .. } => return Layout { |
|
| 1975 | + | size: PTR_SIZE, alignment: PTR_SIZE |
|
| 1976 | + | }, |
|
| 1538 | 1977 | case Type::Array(arr) => return getArrayLayout(arr), |
|
| 1539 | 1978 | case Type::Optional(inner) => return getOptionalLayout(*inner), |
|
| 1540 | 1979 | case Type::Nominal(info) => return getNominalLayout(*info), |
|
| 1541 | 1980 | else => { |
|
| 1542 | 1981 | panic "getTypeLayout: the given type cannot be layed out"; |
|
| 1543 | 1982 | } |
|
| 1544 | 1983 | } |
|
| 1545 | 1984 | } |
|
| 1546 | 1985 | ||
| 1547 | 1986 | /// Get the layout of a type or value. |
|
| 1548 | - | export unsafe fn getLayout(self: &Resolver, node: *ast::Node, ty: Type) -> Layout { |
|
| 1987 | + | export unsafe fn getLayout 'arena (self: &Resolver 'arena, node: *ast::Node, ty: Type) -> Layout { |
|
| 1549 | 1988 | let mut layout = getTypeLayout(ty); |
|
| 1550 | 1989 | // Check for symbol-specific alignment override. |
|
| 1551 | 1990 | if let sym = symbolFor(self, node) { |
|
| 1552 | 1991 | if let case SymbolData::Value { alignment, .. } = sym.data { |
|
| 1553 | 1992 | if alignment > 0 { |
| 1626 | 2065 | } |
|
| 1627 | 2066 | ||
| 1628 | 2067 | /// Get the layout of a nominal type. |
|
| 1629 | 2068 | export fn getNominalLayout(info: NominalType) -> Layout { |
|
| 1630 | 2069 | match info { |
|
| 1631 | - | case NominalType::Placeholder(_) => { |
|
| 1632 | - | panic "getNominalLayout: placeholder type"; |
|
| 2070 | + | case NominalType::Placeholder(_), NominalType::Resolving(_), NominalType::Application(_) => { |
|
| 2071 | + | panic "getNominalLayout: unresolved type"; |
|
| 1633 | 2072 | } |
|
| 1634 | 2073 | case NominalType::Record(recordType) => { |
|
| 1635 | - | return recordType.layout; |
|
| 2074 | + | return *recordType.layout; |
|
| 1636 | 2075 | } |
|
| 1637 | 2076 | case NominalType::Union(unionType) => { |
|
| 1638 | - | return unionType.layout; |
|
| 2077 | + | return *unionType.layout; |
|
| 1639 | 2078 | } |
|
| 1640 | 2079 | } |
|
| 1641 | 2080 | } |
|
| 1642 | 2081 | ||
| 1643 | 2082 | /// Get the layout of a result aggregate with a tag and the larger payload. |
| 1775 | 2214 | } |
|
| 1776 | 2215 | } |
|
| 1777 | 2216 | } |
|
| 1778 | 2217 | ||
| 1779 | 2218 | /// Ensure all nested nominal types in a type are resolved. |
|
| 1780 | - | unsafe fn ensureTypeResolved(self: &mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) { |
|
| 2219 | + | unsafe fn ensureTypeResolved 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node) throws (ResolveError) { |
|
| 1781 | 2220 | match ty { |
|
| 1782 | 2221 | case Type::Nominal(info) => try ensureNominalResolved(self, info, site), |
|
| 1783 | - | case Type::Slice { item, .. } => try ensureTypeResolved(self, *item, site), |
|
| 1784 | - | case Type::Pointer { .. } => {}, // Pointers have fixed layout, don't recurse. |
|
| 2222 | + | // Pointer and slice layouts do not depend on their element layout. |
|
| 2223 | + | case Type::Pointer { .. }, Type::Slice { .. } => { |
|
| 2224 | + | }, |
|
| 2225 | + | case Type::Cell { payload, .. } => try validateCellPayload(self, site, *payload), |
|
| 1785 | 2226 | case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site), |
|
| 1786 | 2227 | case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site), |
|
| 1787 | 2228 | else => {}, |
|
| 1788 | 2229 | } |
|
| 1789 | 2230 | } |
|
| 1790 | 2231 | ||
| 1791 | 2232 | /// Ensure a nominal type has its body resolved. |
|
| 1792 | - | unsafe fn ensureNominalResolved(self: &mut Resolver, tyInfo: *unsafe NominalType, site: *ast::Node) |
|
| 2233 | + | unsafe fn ensureNominalResolved 'arena (self: &mut Resolver 'arena, tyInfo: *unsafe NominalType, site: *ast::Node) |
|
| 1793 | 2234 | throws (ResolveError) |
|
| 1794 | 2235 | { |
|
| 2236 | + | if let case NominalType::Application(applied) = *tyInfo { |
|
| 2237 | + | try resolveNominalApplication(self, applied, site); |
|
| 2238 | + | return; |
|
| 2239 | + | } |
|
| 2240 | + | if let case NominalType::Resolving(_) = *tyInfo { |
|
| 2241 | + | throw emitError(self, site, ErrorKind::RecursiveType); |
|
| 2242 | + | } |
|
| 1795 | 2243 | if let case NominalType::Placeholder(declNode) = *tyInfo { |
|
| 1796 | 2244 | // When resolving on-demand (e.g. from a child module), switch to the |
|
| 1797 | 2245 | // declaring module's scope so field type lookups find the right symbols. |
|
| 1798 | 2246 | let prevScope = self.scope; |
|
| 1799 | 2247 | let prevMod = self.currentMod; |
| 1809 | 2257 | } |
|
| 1810 | 2258 | } |
|
| 1811 | 2259 | ||
| 1812 | 2260 | match declNode.value { |
|
| 1813 | 2261 | case ast::NodeValue::RecordDecl(decl) => { |
|
| 1814 | - | try resolveRecordBody(self, declNode, decl); |
|
| 2262 | + | try resolveRecordBody(self, declNode, decl) catch error { |
|
| 2263 | + | set self.scope = prevScope; |
|
| 2264 | + | set self.currentMod = prevMod; |
|
| 2265 | + | throw error; |
|
| 2266 | + | }; |
|
| 1815 | 2267 | } |
|
| 1816 | 2268 | case ast::NodeValue::UnionDecl(decl) => { |
|
| 1817 | - | try resolveUnionBody(self, declNode, decl); |
|
| 2269 | + | try resolveUnionBody(self, declNode, decl) catch error { |
|
| 2270 | + | set self.scope = prevScope; |
|
| 2271 | + | set self.currentMod = prevMod; |
|
| 2272 | + | throw error; |
|
| 2273 | + | }; |
|
| 1818 | 2274 | } |
|
| 1819 | 2275 | else => {}, |
|
| 1820 | 2276 | } |
|
| 1821 | 2277 | set self.scope = prevScope; |
|
| 1822 | 2278 | set self.currentMod = prevMod; |
|
| 1823 | 2279 | } |
|
| 1824 | 2280 | } |
|
| 1825 | 2281 | ||
| 1826 | 2282 | /// Check if all elements in a node list are assignable to the target type. |
|
| 1827 | - | unsafe fn isListAssignable(self: &mut Resolver, targetType: Type, items: *[*ast::Node]) -> bool { |
|
| 2283 | + | unsafe fn isListAssignable 'arena (self: &mut Resolver 'arena, targetType: Type, items: *[*ast::Node]) -> bool { |
|
| 1828 | 2284 | for itemNode in items { |
|
| 1829 | 2285 | let elemTy = typeFor(self, itemNode) |
|
| 1830 | 2286 | else return false; |
|
| 1831 | 2287 | if let _ = isAssignable(self, targetType, elemTy, itemNode) { |
|
| 1832 | 2288 | // Do nothing. |
| 1843 | 2299 | from: types::PointerClass, |
|
| 1844 | 2300 | inUnsafeContext: bool, |
|
| 1845 | 2301 | ) -> bool { |
|
| 1846 | 2302 | return to == from or ( |
|
| 1847 | 2303 | to == types::PointerClass::Ref |
|
| 1848 | - | and (from == types::PointerClass::Owned |
|
| 2304 | + | and (types::isReference(from) or from == types::PointerClass::Owned |
|
| 1849 | 2305 | or (from == types::PointerClass::Unsafe and inUnsafeContext)) |
|
| 1850 | 2306 | ); |
|
| 1851 | 2307 | } |
|
| 1852 | 2308 | ||
| 2309 | + | /// Limit an exclusive value's implicit borrow to its owner's borrow. |
|
| 2310 | + | unsafe fn assignableValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: Type) -> Type { |
|
| 2311 | + | match source { |
|
| 2312 | + | case Type::Pointer { class, target, mutable: true } => { |
|
| 2313 | + | let usable = pointerAddressClass(self, node, class, true); |
|
| 2314 | + | return Type::Pointer { class: usable, target, mutable: true }; |
|
| 2315 | + | } |
|
| 2316 | + | case Type::Slice { class, item, mutable: true } => { |
|
| 2317 | + | let usable = pointerAddressClass(self, node, class, true); |
|
| 2318 | + | return Type::Slice { class: usable, item, mutable: true }; |
|
| 2319 | + | } |
|
| 2320 | + | case Type::TraitObject { class, traitInfo, mutable: true } => { |
|
| 2321 | + | let usable = pointerAddressClass(self, node, class, true); |
|
| 2322 | + | return Type::TraitObject { class: usable, traitInfo, mutable: true }; |
|
| 2323 | + | } |
|
| 2324 | + | case Type::Optional(inner) => { |
|
| 2325 | + | let value = assignableValueType(self, node, *inner); |
|
| 2326 | + | if typesEqual(value, *inner) { |
|
| 2327 | + | return source; |
|
| 2328 | + | } |
|
| 2329 | + | return Type::Optional(allocType(self, value)); |
|
| 2330 | + | } |
|
| 2331 | + | else => return source, |
|
| 2332 | + | } |
|
| 2333 | + | } |
|
| 2334 | + | ||
| 1853 | 2335 | /// Check if the `from` type is assignable to the `to` type, and return a |
|
| 1854 | 2336 | /// coercion plan if so. |
|
| 1855 | 2337 | /// Referenced storage requires equal element types. Function values may gain |
|
| 1856 | 2338 | /// an unsafe call requirement. |
|
| 1857 | - | unsafe fn isAssignable(self: &mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion { |
|
| 2339 | + | unsafe fn isAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, rval: *ast::Node) -> ?Coercion { |
|
| 2340 | + | let from = assignableValueType(self, rval, source); |
|
| 1858 | 2341 | if to == Type::Unknown or from == Type::Unknown { |
|
| 1859 | 2342 | return nil; |
|
| 1860 | 2343 | } |
|
| 1861 | 2344 | if from == Type::Undefined { |
|
| 2345 | + | if containsRegion(to) { |
|
| 2346 | + | return nil; |
|
| 2347 | + | } |
|
| 1862 | 2348 | if to == Type::Never { |
|
| 1863 | 2349 | return nil; |
|
| 1864 | 2350 | } |
|
| 1865 | 2351 | // TODO: Don't let `undefined` be used in place of functions and other |
|
| 1866 | 2352 | // non-data types. |
| 1872 | 2358 | return Coercion::Identity; |
|
| 1873 | 2359 | } |
|
| 1874 | 2360 | if to == from { |
|
| 1875 | 2361 | return Coercion::Identity; |
|
| 1876 | 2362 | } |
|
| 2363 | + | if let case Type::Cell { class, payload } = to { |
|
| 2364 | + | let case Type::Cell { class: sourceClass, payload: sourcePayload } = from else return nil; |
|
| 2365 | + | if pointerClassesAssignable(class, sourceClass, self.inUnsafeContext) and typesEqual(*payload, *sourcePayload) { |
|
| 2366 | + | return Coercion::Identity; |
|
| 2367 | + | } |
|
| 2368 | + | return nil; |
|
| 2369 | + | } |
|
| 1877 | 2370 | if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to { |
|
| 1878 | 2371 | let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from |
|
| 1879 | 2372 | else return nil; |
|
| 1880 | 2373 | if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) { |
|
| 1881 | 2374 | return nil; |
| 2037 | 2530 | return fnSignatureEqual(a, b); |
|
| 2038 | 2531 | } |
|
| 2039 | 2532 | ||
| 2040 | 2533 | /// Compare parameter, return, and error types of functions. |
|
| 2041 | 2534 | fn fnSignatureEqual(a: &FnType, b: &FnType) -> bool { |
|
| 2535 | + | if a.regions <> b.regions { |
|
| 2536 | + | return false; |
|
| 2537 | + | } |
|
| 2042 | 2538 | if a.paramTypes.len <> b.paramTypes.len { |
|
| 2043 | 2539 | return false; |
|
| 2044 | 2540 | } |
|
| 2045 | 2541 | if a.throwList.len <> b.throwList.len { |
|
| 2046 | 2542 | return false; |
| 2078 | 2574 | else return false; |
|
| 2079 | 2575 | return aClass == bClass and aMutable == bMutable |
|
| 2080 | 2576 | and typesEqual(*aItem, *bItem); |
|
| 2081 | 2577 | } |
|
| 2082 | 2578 | match a { |
|
| 2579 | + | case Type::Cell { class, payload } => { |
|
| 2580 | + | let case Type::Cell { class: otherClass, payload: other } = b else return false; |
|
| 2581 | + | return class == otherClass and typesEqual(*payload, *other); |
|
| 2582 | + | } |
|
| 2083 | 2583 | case Type::Array(aa) => { |
|
| 2084 | 2584 | let case Type::Array(ab) = b else return false; |
|
| 2085 | 2585 | return aa.length == ab.length and typesEqual(*aa.item, *ab.item); |
|
| 2086 | 2586 | } |
|
| 2087 | 2587 | case Type::Optional(oa) => { |
| 2094 | 2594 | } |
|
| 2095 | 2595 | else => return false, |
|
| 2096 | 2596 | } |
|
| 2097 | 2597 | } |
|
| 2098 | 2598 | ||
| 2599 | + | /// Compare types after lexical region arguments are erased. |
|
| 2600 | + | /// Nominal types retain their source declaration identity. |
|
| 2601 | + | export unsafe fn erasedTypesEqual(a: Type, b: Type) -> bool { |
|
| 2602 | + | if typesEqual(a, b) { |
|
| 2603 | + | return true; |
|
| 2604 | + | } |
|
| 2605 | + | match a { |
|
| 2606 | + | case Type::Cell { class, payload } => { |
|
| 2607 | + | let case Type::Cell { class: otherClass, payload: other } = b else return false; |
|
| 2608 | + | return erasedClassesEqual(class, otherClass) and erasedTypesEqual(*payload, *other); |
|
| 2609 | + | } |
|
| 2610 | + | case Type::Session(_) => { |
|
| 2611 | + | let case Type::Session(_) = b else return false; |
|
| 2612 | + | return true; |
|
| 2613 | + | } |
|
| 2614 | + | case Type::Nominal(left) => { |
|
| 2615 | + | let case Type::Nominal(right) = b else return false; |
|
| 2616 | + | let mut leftBase = left; |
|
| 2617 | + | let mut rightBase = right; |
|
| 2618 | + | if let app = nominalApplication(left) { |
|
| 2619 | + | set leftBase = app.base; |
|
| 2620 | + | } |
|
| 2621 | + | if let app = nominalApplication(right) { |
|
| 2622 | + | set rightBase = app.base; |
|
| 2623 | + | } |
|
| 2624 | + | return leftBase == rightBase; |
|
| 2625 | + | } |
|
| 2626 | + | case Type::Pointer { class, target, mutable } => { |
|
| 2627 | + | let case Type::Pointer { class: otherClass, target: other, mutable: otherMutable } = b |
|
| 2628 | + | else return false; |
|
| 2629 | + | return erasedClassesEqual(class, otherClass) and mutable == otherMutable |
|
| 2630 | + | and erasedTypesEqual(*target, *other); |
|
| 2631 | + | } |
|
| 2632 | + | case Type::Slice { class, item, mutable } => { |
|
| 2633 | + | let case Type::Slice { class: otherClass, item: other, mutable: otherMutable } = b |
|
| 2634 | + | else return false; |
|
| 2635 | + | return erasedClassesEqual(class, otherClass) and mutable == otherMutable |
|
| 2636 | + | and erasedTypesEqual(*item, *other); |
|
| 2637 | + | } |
|
| 2638 | + | case Type::TraitObject { class, traitInfo, mutable } => { |
|
| 2639 | + | let case Type::TraitObject { class: otherClass, traitInfo: other, mutable: otherMutable } = b |
|
| 2640 | + | else return false; |
|
| 2641 | + | return erasedClassesEqual(class, otherClass) and mutable == otherMutable and traitInfo == other; |
|
| 2642 | + | } |
|
| 2643 | + | case Type::Array(left) => { |
|
| 2644 | + | let case Type::Array(right) = b else return false; |
|
| 2645 | + | return left.length == right.length and erasedTypesEqual(*left.item, *right.item); |
|
| 2646 | + | } |
|
| 2647 | + | case Type::Optional(left) => { |
|
| 2648 | + | let case Type::Optional(right) = b else return false; |
|
| 2649 | + | return erasedTypesEqual(*left, *right); |
|
| 2650 | + | } |
|
| 2651 | + | case Type::Fn(left) => { |
|
| 2652 | + | let case Type::Fn(right) = b else return false; |
|
| 2653 | + | if left.isUnsafe <> right.isUnsafe or left.paramTypes.len <> right.paramTypes.len |
|
| 2654 | + | or left.throwList.len <> right.throwList.len { |
|
| 2655 | + | return false; |
|
| 2656 | + | } |
|
| 2657 | + | for ty, i in left.paramTypes { |
|
| 2658 | + | if not erasedTypesEqual(*ty, *right.paramTypes[i]) { |
|
| 2659 | + | return false; |
|
| 2660 | + | } |
|
| 2661 | + | } |
|
| 2662 | + | for ty, i in left.throwList { |
|
| 2663 | + | if not erasedTypesEqual(*ty, *right.throwList[i]) { |
|
| 2664 | + | return false; |
|
| 2665 | + | } |
|
| 2666 | + | } |
|
| 2667 | + | return erasedTypesEqual(*left.returnType, *right.returnType); |
|
| 2668 | + | } |
|
| 2669 | + | else => return false, |
|
| 2670 | + | } |
|
| 2671 | + | } |
|
| 2672 | + | ||
| 2673 | + | /// Compare pointer classes without lexical region identities. |
|
| 2674 | + | fn erasedClassesEqual(a: types::PointerClass, b: types::PointerClass) -> bool { |
|
| 2675 | + | return a == b or (types::isReference(a) and types::isReference(b)); |
|
| 2676 | + | } |
|
| 2677 | + | ||
| 2678 | + | /// Require distinct runtime tags for errors with different source types. |
|
| 2679 | + | unsafe fn validateErrorTag 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type, errors: *[*Type]) throws (ResolveError) { |
|
| 2680 | + | for other in errors { |
|
| 2681 | + | if not typesEqual(ty, *other) and erasedTypesEqual(ty, *other) { |
|
| 2682 | + | throw emitError(self, node, ErrorKind::AmbiguousRegionalError); |
|
| 2683 | + | } |
|
| 2684 | + | } |
|
| 2685 | + | } |
|
| 2686 | + | ||
| 2099 | 2687 | /// Return whether `ty` is a direct reference. |
|
| 2100 | 2688 | export fn isRefType(ty: Type) -> bool { |
|
| 2101 | 2689 | match ty { |
|
| 2102 | - | case Type::Pointer { class: types::PointerClass::Ref, .. }, |
|
| 2103 | - | Type::Slice { class: types::PointerClass::Ref, .. }, |
|
| 2104 | - | Type::TraitObject { class: types::PointerClass::Ref, .. } => return true, |
|
| 2690 | + | case Type::Cell { class, .. } => return types::isReference(class), |
|
| 2691 | + | case Type::Pointer { class, .. } => return types::isReference(class), |
|
| 2692 | + | case Type::Slice { class, .. } => return types::isReference(class), |
|
| 2693 | + | case Type::TraitObject { class, .. } => return types::isReference(class), |
|
| 2105 | 2694 | else => return false, |
|
| 2106 | 2695 | } |
|
| 2107 | 2696 | } |
|
| 2108 | 2697 | ||
| 2109 | - | /// Return whether a type contains a reference. |
|
| 2110 | - | fn containsRef(ty: Type) -> bool { |
|
| 2111 | - | if isRefType(ty) { |
|
| 2698 | + | /// Get the region of a direct named reference. |
|
| 2699 | + | fn referenceRegion(ty: Type) -> ?*unsafe types::Region { |
|
| 2700 | + | let mut class = types::PointerClass::Ref; |
|
| 2701 | + | match ty { |
|
| 2702 | + | case Type::Cell { class: cellClass, .. } => set class = cellClass, |
|
| 2703 | + | case Type::Pointer { class: pointerClass, .. } => set class = pointerClass, |
|
| 2704 | + | case Type::Slice { class: sliceClass, .. } => set class = sliceClass, |
|
| 2705 | + | case Type::TraitObject { class: objectClass, .. } => set class = objectClass, |
|
| 2706 | + | else => return nil, |
|
| 2707 | + | } |
|
| 2708 | + | if let case types::PointerClass::Region(region) = class { |
|
| 2709 | + | return region; |
|
| 2710 | + | } |
|
| 2711 | + | return nil; |
|
| 2712 | + | } |
|
| 2713 | + | ||
| 2714 | + | /// Require every free region in a value type to remain in lexical scope. |
|
| 2715 | + | unsafe fn validateRegionDependencies 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) |
|
| 2716 | + | throws (ResolveError) |
|
| 2717 | + | { |
|
| 2718 | + | try validateRegionStorage(self, node, ty, nil); |
|
| 2719 | + | } |
|
| 2720 | + | ||
| 2721 | + | /// Check a dependency against storage lifetime or current lexical visibility. |
|
| 2722 | + | unsafe fn regionCoversStorage( |
|
| 2723 | + | scope: ?*RegionScope, dependency: *unsafe types::Region, destination: ?*unsafe types::Region |
|
| 2724 | + | ) -> bool { |
|
| 2725 | + | if let region = destination { |
|
| 2726 | + | return types::regionContains(dependency, region); |
|
| 2727 | + | } |
|
| 2728 | + | return regionInScope(scope, dependency); |
|
| 2729 | + | } |
|
| 2730 | + | ||
| 2731 | + | /// Require stored references to cover the lifetime of checked destination storage. |
|
| 2732 | + | unsafe fn validateRegionalStore 'arena (self: &mut Resolver 'arena, place: *ast::Node, value: *ast::Node, ty: Type) |
|
| 2733 | + | throws (ResolveError) |
|
| 2734 | + | { |
|
| 2735 | + | if let case types::PointerClass::Region(region) = addressStorageClass(self, place) { |
|
| 2736 | + | try validateRegionStorage(self, value, ty, region); |
|
| 2737 | + | } |
|
| 2738 | + | } |
|
| 2739 | + | ||
| 2740 | + | /// Require all type dependencies to cover the destination or active lexical scope. |
|
| 2741 | + | unsafe fn validateRegionStorage 'arena ( |
|
| 2742 | + | self: &mut Resolver 'arena, node: *ast::Node, ty: Type, destination: ?*unsafe types::Region |
|
| 2743 | + | ) |
|
| 2744 | + | throws (ResolveError) |
|
| 2745 | + | { |
|
| 2746 | + | if let case Type::Session(region) = ty { |
|
| 2747 | + | if not regionCoversStorage(self.regionScope, region, destination) { |
|
| 2748 | + | throw emitError(self, node, ErrorKind::RegionEscape(region.name)); |
|
| 2749 | + | } |
|
| 2750 | + | } |
|
| 2751 | + | if let region = referenceRegion(ty) { |
|
| 2752 | + | if not regionCoversStorage(self.regionScope, region, destination) { |
|
| 2753 | + | throw emitError(self, node, ErrorKind::RegionEscape(region.name)); |
|
| 2754 | + | } |
|
| 2755 | + | } |
|
| 2756 | + | match ty { |
|
| 2757 | + | case Type::Pointer { target, .. } => try validateRegionStorage(self, node, *target, destination), |
|
| 2758 | + | case Type::Slice { item, .. } => try validateRegionStorage(self, node, *item, destination), |
|
| 2759 | + | case Type::Cell { payload, .. } => try validateRegionStorage(self, node, *payload, destination), |
|
| 2760 | + | case Type::Array(array) => try validateRegionStorage(self, node, *array.item, destination), |
|
| 2761 | + | case Type::Optional(inner) => try validateRegionStorage(self, node, *inner, destination), |
|
| 2762 | + | case Type::Nominal(info) => { |
|
| 2763 | + | if let applied = nominalApplication(info) { |
|
| 2764 | + | for region in applied.arguments { |
|
| 2765 | + | if not regionCoversStorage(self.regionScope, region, destination) { |
|
| 2766 | + | throw emitError(self, node, ErrorKind::RegionEscape(region.name)); |
|
| 2767 | + | } |
|
| 2768 | + | } |
|
| 2769 | + | } |
|
| 2770 | + | } |
|
| 2771 | + | case Type::Fn(info) => { |
|
| 2772 | + | if info.regions <> nil { |
|
| 2773 | + | return; |
|
| 2774 | + | } |
|
| 2775 | + | for parameter in info.paramTypes { |
|
| 2776 | + | try validateRegionStorage(self, node, *parameter, destination); |
|
| 2777 | + | } |
|
| 2778 | + | for error in info.throwList { |
|
| 2779 | + | try validateRegionStorage(self, node, *error, destination); |
|
| 2780 | + | } |
|
| 2781 | + | try validateRegionStorage(self, node, *info.returnType, destination); |
|
| 2782 | + | } |
|
| 2783 | + | else => { |
|
| 2784 | + | }, |
|
| 2785 | + | } |
|
| 2786 | + | } |
|
| 2787 | + | ||
| 2788 | + | /// Return whether a type has an explicit region dependency. |
|
| 2789 | + | unsafe fn containsRegion(ty: Type) -> bool { |
|
| 2790 | + | match ty { |
|
| 2791 | + | case Type::Cell { class, payload } => { |
|
| 2792 | + | if let case types::PointerClass::Region(_) = class { |
|
| 2793 | + | return true; |
|
| 2794 | + | } |
|
| 2795 | + | return containsRegion(*payload); |
|
| 2796 | + | } |
|
| 2797 | + | case Type::Session(_) => return true, |
|
| 2798 | + | case Type::Pointer { class, target, .. } => { |
|
| 2799 | + | if let case types::PointerClass::Region(_) = class { |
|
| 2800 | + | return true; |
|
| 2801 | + | } |
|
| 2802 | + | return containsRegion(*target); |
|
| 2803 | + | } |
|
| 2804 | + | case Type::Slice { class, item, .. } => { |
|
| 2805 | + | if let case types::PointerClass::Region(_) = class { |
|
| 2806 | + | return true; |
|
| 2807 | + | } |
|
| 2808 | + | return containsRegion(*item); |
|
| 2809 | + | } |
|
| 2810 | + | case Type::TraitObject { class, .. } => { |
|
| 2811 | + | if let case types::PointerClass::Region(_) = class { |
|
| 2812 | + | return true; |
|
| 2813 | + | } |
|
| 2814 | + | return false; |
|
| 2815 | + | } |
|
| 2816 | + | case Type::Array(array) => return containsRegion(*array.item), |
|
| 2817 | + | case Type::Optional(inner) => return containsRegion(*inner), |
|
| 2818 | + | case Type::Fn(info) => { |
|
| 2819 | + | if info.regions <> nil or containsRegion(*info.returnType) { |
|
| 2820 | + | return true; |
|
| 2821 | + | } |
|
| 2822 | + | for param in info.paramTypes { |
|
| 2823 | + | if containsRegion(*param) { |
|
| 2824 | + | return true; |
|
| 2825 | + | } |
|
| 2826 | + | } |
|
| 2827 | + | for error in info.throwList { |
|
| 2828 | + | if containsRegion(*error) { |
|
| 2829 | + | return true; |
|
| 2830 | + | } |
|
| 2831 | + | } |
|
| 2832 | + | return false; |
|
| 2833 | + | } |
|
| 2834 | + | case Type::Nominal(info) => return nominalApplication(info) <> nil, |
|
| 2835 | + | else => return false, |
|
| 2836 | + | } |
|
| 2837 | + | } |
|
| 2838 | + | ||
| 2839 | + | /// Return whether a stored type contains a reference without a named region. |
|
| 2840 | + | unsafe fn containsUnscopedRef(ty: Type) -> bool { |
|
| 2841 | + | if isRefType(ty) and referenceRegion(ty) == nil { |
|
| 2112 | 2842 | return true; |
|
| 2113 | 2843 | } |
|
| 2114 | 2844 | if let case Type::Pointer { target, .. } = ty { |
|
| 2115 | - | return containsRef(*target); |
|
| 2845 | + | return containsUnscopedRef(*target); |
|
| 2116 | 2846 | } |
|
| 2117 | 2847 | if let case Type::Slice { item, .. } = ty { |
|
| 2118 | - | return containsRef(*item); |
|
| 2848 | + | return containsUnscopedRef(*item); |
|
| 2119 | 2849 | } |
|
| 2120 | 2850 | match ty { |
|
| 2121 | - | case Type::Array(array) => return containsRef(*array.item), |
|
| 2122 | - | case Type::Optional(inner) => return containsRef(*inner), |
|
| 2851 | + | case Type::Cell { payload, .. } => return containsUnscopedRef(*payload), |
|
| 2852 | + | case Type::Array(array) => return containsUnscopedRef(*array.item), |
|
| 2853 | + | case Type::Optional(inner) => return containsUnscopedRef(*inner), |
|
| 2854 | + | case Type::Fn(info) => return info.regions <> nil, |
|
| 2123 | 2855 | // Nominal declarations validate their own fields and variants. |
|
| 2124 | 2856 | // Treating them as leaves also terminates recursive pointer types. |
|
| 2125 | 2857 | case Type::Nominal(_) => return false, |
|
| 2126 | 2858 | else => return false, |
|
| 2127 | 2859 | } |
|
| 2128 | 2860 | } |
|
| 2129 | 2861 | ||
| 2130 | 2862 | /// Return whether `ty` may be duplicated implicitly. |
|
| 2131 | 2863 | export unsafe fn isCopy(ty: Type) -> bool { |
|
| 2132 | 2864 | match ty { |
|
| 2865 | + | case Type::Session(_) => return false, |
|
| 2133 | 2866 | case Type::Pointer { class, mutable, .. } => |
|
| 2134 | 2867 | return class == types::PointerClass::Unsafe or not mutable, |
|
| 2135 | 2868 | case Type::Slice { class, mutable, .. } => |
|
| 2136 | 2869 | return class == types::PointerClass::Unsafe or not mutable, |
|
| 2137 | 2870 | case Type::TraitObject { class, mutable, .. } => |
|
| 2138 | 2871 | return class == types::PointerClass::Unsafe or not mutable, |
|
| 2139 | 2872 | case Type::Array(array) => return isCopy(*array.item), |
|
| 2140 | 2873 | case Type::Optional(inner) => return isCopy(*inner), |
|
| 2141 | 2874 | case Type::Nominal(NominalType::Record(recInfo)) => return recInfo.declaredCopy, |
|
| 2142 | 2875 | case Type::Nominal(NominalType::Union(unionType)) => return unionType.declaredCopy, |
|
| 2143 | - | case Type::Nominal(NominalType::Placeholder(_)) => return false, |
|
| 2876 | + | case Type::Nominal(NominalType::Application(applied)) => return isCopy(Type::Nominal(applied.base)), |
|
| 2877 | + | case Type::Nominal(NominalType::Placeholder(_)), Type::Nominal(NominalType::Resolving(_)) => return false, |
|
| 2144 | 2878 | else => return true, |
|
| 2145 | 2879 | } |
|
| 2146 | 2880 | } |
|
| 2147 | 2881 | ||
| 2148 | 2882 | /// Return whether a type must be consumed exactly once. |
|
| 2149 | 2883 | export unsafe fn isLinear(ty: Type) -> bool { |
|
| 2150 | 2884 | match ty { |
|
| 2885 | + | case Type::Nominal(NominalType::Application(applied)) => return isLinear(Type::Nominal(applied.base)), |
|
| 2151 | 2886 | case Type::Array(array) => return isLinear(*array.item), |
|
| 2152 | 2887 | case Type::Optional(inner) => return isLinear(*inner), |
|
| 2153 | 2888 | case Type::Nominal(NominalType::Record(recInfo)) => { |
|
| 2154 | 2889 | if recInfo.declaredLinear { |
|
| 2155 | 2890 | return true; |
| 2182 | 2917 | } |
|
| 2183 | 2918 | ||
| 2184 | 2919 | /// Return whether `ty` is a direct unsafe pointer-like value. |
|
| 2185 | 2920 | fn isUnsafePointerType(ty: Type) -> bool { |
|
| 2186 | 2921 | match ty { |
|
| 2187 | - | case Type::Pointer { class: types::PointerClass::Unsafe, .. }, |
|
| 2922 | + | case Type::Cell { class: types::PointerClass::Unsafe, .. }, |
|
| 2923 | + | Type::Pointer { class: types::PointerClass::Unsafe, .. }, |
|
| 2188 | 2924 | Type::Slice { class: types::PointerClass::Unsafe, .. }, |
|
| 2189 | 2925 | Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return true, |
|
| 2190 | 2926 | else => return false, |
|
| 2191 | 2927 | } |
|
| 2192 | 2928 | } |
| 2267 | 3003 | return false; |
|
| 2268 | 3004 | } |
|
| 2269 | 3005 | ||
| 2270 | 3006 | /// Check if the `from` type is assignable to the `to` type, and return a |
|
| 2271 | 3007 | /// coercion plan if so, or throw an error if not. |
|
| 2272 | - | unsafe fn expectAssignable(self: &mut Resolver, to: Type, from: Type, site: *ast::Node) -> Coercion throws (ResolveError) { |
|
| 3008 | + | unsafe fn expectAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, site: *ast::Node) -> Coercion throws (ResolveError) { |
|
| 3009 | + | let from = assignableValueType(self, site, source); |
|
| 2273 | 3010 | if isRefType(to) and isUnsafePointerType(from) { |
|
| 2274 | 3011 | try requireUnsafe(self, site); |
|
| 2275 | 3012 | } |
|
| 2276 | 3013 | // Ensure any nested nominal types are resolved before checking assignability. |
|
| 2277 | 3014 | try ensureTypeResolved(self, to, site); |
| 2283 | 3020 | actual: from, |
|
| 2284 | 3021 | }); |
|
| 2285 | 3022 | } |
|
| 2286 | 3023 | ||
| 2287 | 3024 | /// Check that a type is optional, otherwise throw an error. |
|
| 2288 | - | unsafe fn checkOptional(self: &mut Resolver, node: *ast::Node) -> *Type |
|
| 3025 | + | unsafe fn checkOptional 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *Type |
|
| 2289 | 3026 | throws (ResolveError) |
|
| 2290 | 3027 | { |
|
| 2291 | 3028 | if let case Type::Optional(inner) = try infer(self, node) { |
|
| 2292 | 3029 | return inner; |
|
| 2293 | 3030 | } |
|
| 2294 | 3031 | throw emitError(self, node, ErrorKind::ExpectedOptional); |
|
| 2295 | 3032 | } |
|
| 2296 | 3033 | ||
| 2297 | 3034 | /// Check that a node's type is equal to the expected type. |
|
| 2298 | - | unsafe fn checkEqual(self: &mut Resolver, node: *ast::Node, expected: Type) -> Type |
|
| 3035 | + | unsafe fn checkEqual 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type |
|
| 2299 | 3036 | throws (ResolveError) |
|
| 2300 | 3037 | { |
|
| 2301 | 3038 | let actualTy = try visit(self, node, expected); |
|
| 2302 | 3039 | if actualTy <> expected { |
|
| 2303 | 3040 | throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: actualTy }); |
|
| 2304 | 3041 | } |
|
| 2305 | 3042 | return actualTy; |
|
| 2306 | 3043 | } |
|
| 2307 | 3044 | ||
| 2308 | 3045 | /// Bind an identifier in the given scope. |
|
| 2309 | - | unsafe fn bindIdent( |
|
| 2310 | - | self: &mut Resolver, |
|
| 3046 | + | unsafe fn bindIdent 'arena ( |
|
| 3047 | + | self: &mut Resolver 'arena, |
|
| 2311 | 3048 | name: *[u8], |
|
| 2312 | 3049 | owner: *ast::Node, |
|
| 2313 | 3050 | data: SymbolData, |
|
| 2314 | 3051 | attrs: u32, |
|
| 2315 | 3052 | scope: *unsafe mut Scope |
| 2320 | 3057 | ||
| 2321 | 3058 | return sym; |
|
| 2322 | 3059 | } |
|
| 2323 | 3060 | ||
| 2324 | 3061 | /// Add a symbol to the given scope. |
|
| 2325 | - | unsafe fn addSymbolToScope(self: &mut Resolver, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) { |
|
| 3062 | + | unsafe fn addSymbolToScope 'arena (self: &mut Resolver 'arena, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) { |
|
| 2326 | 3063 | for i in 0..scope.symbolsLen { |
|
| 2327 | 3064 | if scope.symbols[i].name == sym.name { |
|
| 2328 | 3065 | throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name)); |
|
| 2329 | 3066 | } |
|
| 2330 | 3067 | } |
| 2342 | 3079 | set scope.symbolsLen += 1; |
|
| 2343 | 3080 | } |
|
| 2344 | 3081 | ||
| 2345 | 3082 | /// Bind a value identifier in the current scope. |
|
| 2346 | 3083 | /// Returns `nil` if the identifier is a placeholder (`_`). |
|
| 2347 | - | unsafe fn bindValueIdent( |
|
| 2348 | - | self: &mut Resolver, |
|
| 3084 | + | unsafe fn bindValueIdent 'arena ( |
|
| 3085 | + | self: &mut Resolver 'arena, |
|
| 2349 | 3086 | ident: *ast::Node, |
|
| 2350 | 3087 | owner: *ast::Node, |
|
| 2351 | 3088 | type: Type, |
|
| 2352 | 3089 | mutable: bool, |
|
| 2353 | 3090 | alignment: u32, |
| 2370 | 3107 | } |
|
| 2371 | 3108 | return sym; |
|
| 2372 | 3109 | } |
|
| 2373 | 3110 | ||
| 2374 | 3111 | /// Bind a constant identifier in the current scope. |
|
| 2375 | - | unsafe fn bindConstIdent( |
|
| 2376 | - | self: &mut Resolver, |
|
| 3112 | + | unsafe fn bindConstIdent 'arena ( |
|
| 3113 | + | self: &mut Resolver 'arena, |
|
| 2377 | 3114 | ident: *ast::Node, |
|
| 2378 | 3115 | owner: *ast::Node, |
|
| 2379 | 3116 | type: Type, |
|
| 2380 | 3117 | val: ?ConstValue, |
|
| 2381 | 3118 | attrs: u32 |
| 2391 | 3128 | } |
|
| 2392 | 3129 | ||
| 2393 | 3130 | /// Bind a module identifier in the given scope. |
|
| 2394 | 3131 | /// This is used when declaring modules with `mod` or |
|
| 2395 | 3132 | /// importing modules with `use`. |
|
| 2396 | - | unsafe fn bindModuleIdent( |
|
| 2397 | - | self: &mut Resolver, |
|
| 3133 | + | unsafe fn bindModuleIdent 'arena ( |
|
| 3134 | + | self: &mut Resolver 'arena, |
|
| 2398 | 3135 | entry: *module::ModuleEntry, |
|
| 2399 | 3136 | scope: *unsafe mut Scope, |
|
| 2400 | 3137 | owner: *ast::Node, |
|
| 2401 | 3138 | attrs: u32, |
|
| 2402 | 3139 | bindingScope: *unsafe mut Scope |
| 2406 | 3143 | ||
| 2407 | 3144 | return try bindIdent(self, name, owner, data, attrs, bindingScope); |
|
| 2408 | 3145 | } |
|
| 2409 | 3146 | ||
| 2410 | 3147 | /// Bind a type identifier in the current scope. |
|
| 2411 | - | unsafe fn bindTypeIdent( |
|
| 2412 | - | self: &mut Resolver, |
|
| 3148 | + | unsafe fn bindTypeIdent 'arena ( |
|
| 3149 | + | self: &mut Resolver 'arena, |
|
| 2413 | 3150 | ident: *ast::Node, |
|
| 2414 | 3151 | owner: *ast::Node, |
|
| 2415 | 3152 | type: *unsafe mut NominalType, |
|
| 2416 | 3153 | attrs: u32 |
|
| 2417 | 3154 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 2493 | 3230 | } |
|
| 2494 | 3231 | ||
| 2495 | 3232 | /// Flatten an identifier or scope access chain into an array of name segments. |
|
| 2496 | 3233 | /// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`. |
|
| 2497 | 3234 | /// Return the number of segments written to the buffer. |
|
| 2498 | - | unsafe fn flattenPath( |
|
| 2499 | - | self: &mut Resolver, |
|
| 3235 | + | unsafe fn flattenPath 'arena ( |
|
| 3236 | + | self: &mut Resolver 'arena, |
|
| 2500 | 3237 | node: *ast::Node, |
|
| 2501 | 3238 | buf: &mut [*[u8]] |
|
| 2502 | 3239 | ) -> u32 throws (ResolveError) { |
|
| 2503 | 3240 | let mut out: u32 = 0; |
|
| 2504 | 3241 |
| 2548 | 3285 | } |
|
| 2549 | 3286 | } |
|
| 2550 | 3287 | ||
| 2551 | 3288 | /// Get the parent module scope for the current module. |
|
| 2552 | 3289 | /// Returns the scope of the parent module, or `nil` if this is a root module. |
|
| 2553 | - | unsafe fn getParentModuleScope(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) { |
|
| 3290 | + | unsafe fn getParentModuleScope 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) { |
|
| 2554 | 3291 | let currentMod = module::get(self.moduleGraph, self.currentMod) |
|
| 2555 | 3292 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 2556 | 3293 | let parentId = currentMod.parent |
|
| 2557 | 3294 | else return nil; // No parent module. |
|
| 2558 | 3295 | ||
| 2559 | 3296 | return self.moduleScopes[parentId as u32]; |
|
| 2560 | 3297 | } |
|
| 2561 | 3298 | ||
| 2562 | 3299 | /// Check if a node has `super` at its root (e.g. `super::x` or `super::Union::Variant`). |
|
| 2563 | 3300 | /// Returns the parent scope and the original node so `flattenPath` can strip `super`. |
|
| 2564 | - | unsafe fn checkSuperAccess( |
|
| 2565 | - | self: &mut Resolver, |
|
| 3301 | + | unsafe fn checkSuperAccess 'arena ( |
|
| 3302 | + | self: &mut Resolver 'arena, |
|
| 2566 | 3303 | node: *ast::Node |
|
| 2567 | 3304 | ) -> ?SuperAccessResult throws (ResolveError) { |
|
| 2568 | 3305 | // TODO: Maybe we should deal with `super` after the path is flattened. |
|
| 2569 | 3306 | if let case ast::NodeValue::ScopeAccess(access) = node.value { |
|
| 2570 | 3307 | // Direct super access: `super::x`. |
| 2604 | 3341 | return symModuleId == currentModuleId; |
|
| 2605 | 3342 | } |
|
| 2606 | 3343 | ||
| 2607 | 3344 | /// Resolve an access node (eg. `lang::resolver::MAX_ERRORS`) to a symbol, |
|
| 2608 | 3345 | /// starting from the given scope. |
|
| 2609 | - | unsafe fn resolveAccess( |
|
| 2610 | - | self: &mut Resolver, |
|
| 3346 | + | unsafe fn resolveAccess 'arena ( |
|
| 3347 | + | self: &mut Resolver 'arena, |
|
| 2611 | 3348 | node: *ast::Node, |
|
| 2612 | 3349 | access: ast::Access, |
|
| 2613 | 3350 | scope: *unsafe Scope |
|
| 2614 | 3351 | ) -> *unsafe mut Symbol throws (ResolveError) { |
|
| 3352 | + | if let case ast::NodeValue::RegionApply { .. } = access.parent.value { |
|
| 3353 | + | let ty = try infer(self, access.parent); |
|
| 3354 | + | let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
|
| 3355 | + | try ensureNominalResolved(self, info, access.parent); |
|
| 3356 | + | let case NominalType::Union(body) = *info else throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
|
| 3357 | + | let name = try nodeName(self, access.child); |
|
| 3358 | + | let symbol = try resolveUnionVariantAccess(self, node, access, body, name); |
|
| 3359 | + | setNodeType(self, node, ty); |
|
| 3360 | + | return symbol; |
|
| 3361 | + | } |
|
| 2615 | 3362 | // Handle `super` access by adjusting scope and node. |
|
| 2616 | 3363 | let mut startScope = scope; |
|
| 2617 | 3364 | let mut pathNode = node; |
|
| 2618 | 3365 | if let superAccess = try checkSuperAccess(self, node) { |
|
| 2619 | 3366 | set startScope = superAccess.scope; |
| 2627 | 3374 | return try resolvePath(self, node, access, &buffer[..pathLen], startScope); |
|
| 2628 | 3375 | } |
|
| 2629 | 3376 | ||
| 2630 | 3377 | /// Resolve a path (eg. ["lang", "resolver", "MAX_ERRORS"]) to a symbol, |
|
| 2631 | 3378 | /// starting from the given scope. |
|
| 2632 | - | unsafe fn resolvePath( |
|
| 2633 | - | self: &mut Resolver, |
|
| 3379 | + | unsafe fn resolvePath 'arena ( |
|
| 3380 | + | self: &mut Resolver 'arena, |
|
| 2634 | 3381 | node: *ast::Node, |
|
| 2635 | 3382 | access: ast::Access, |
|
| 2636 | 3383 | path: &[*[u8]], |
|
| 2637 | 3384 | scope: *unsafe Scope |
|
| 2638 | 3385 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 2678 | 3425 | throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
|
| 2679 | 3426 | } |
|
| 2680 | 3427 | ||
| 2681 | 3428 | /// Resolve a module path (e.g., `foo::bar::baz`) to a module entry and scope. |
|
| 2682 | 3429 | /// This traverses the module hierarchy, checking visibility at each step. |
|
| 2683 | - | unsafe fn resolveModulePath( |
|
| 2684 | - | self: &mut Resolver, |
|
| 3430 | + | unsafe fn resolveModulePath 'arena ( |
|
| 3431 | + | self: &mut Resolver 'arena, |
|
| 2685 | 3432 | module: *ast::Node |
|
| 2686 | 3433 | ) -> ResolvedModule throws (ResolveError) { |
|
| 2687 | 3434 | let mut startScope = self.scope; |
|
| 2688 | 3435 | let mut pathNode = module; |
|
| 2689 | 3436 |
| 2709 | 3456 | ||
| 2710 | 3457 | return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym); |
|
| 2711 | 3458 | } |
|
| 2712 | 3459 | ||
| 2713 | 3460 | /// Recursively resolve the remaining path segments by traversing child modules. |
|
| 2714 | - | unsafe fn resolveModulePathRecursive( |
|
| 2715 | - | self: &mut Resolver, |
|
| 3461 | + | unsafe fn resolveModulePathRecursive 'arena ( |
|
| 3462 | + | self: &mut Resolver 'arena, |
|
| 2716 | 3463 | node: *ast::Node, |
|
| 2717 | 3464 | path: &[*[u8]], |
|
| 2718 | 3465 | sym: *unsafe Symbol |
|
| 2719 | 3466 | ) -> ResolvedModule throws (ResolveError) { |
|
| 2720 | 3467 | let case SymbolData::Module { entry, scope } = sym.data |
| 2737 | 3484 | childSym |
|
| 2738 | 3485 | ); |
|
| 2739 | 3486 | } |
|
| 2740 | 3487 | ||
| 2741 | 3488 | /// Resolve a type name, which could be an identifier or scoped path. |
|
| 2742 | - | unsafe fn resolveTypeName(self: &mut Resolver, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) { |
|
| 3489 | + | unsafe fn resolveTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) { |
|
| 2743 | 3490 | match node.value { |
|
| 2744 | 3491 | case ast::NodeValue::Ident(name) => { |
|
| 2745 | 3492 | let sym = findTypeSymbol(self.scope, name) |
|
| 2746 | 3493 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 2747 | 3494 | let case SymbolData::Type(ty) = sym.data |
| 2768 | 3515 | /// Visit a top-level declaration in the declaration phase. |
|
| 2769 | 3516 | /// This binds all names and analyzes signatures, types, and initializers. |
|
| 2770 | 3517 | /// Function bodies are deferred to the definition phase. |
|
| 2771 | 3518 | /// |
|
| 2772 | 3519 | /// Nb. User-defined types are already handled by this point. |
|
| 2773 | - | unsafe fn visitDecl(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 3520 | + | unsafe fn visitDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) { |
|
| 2774 | 3521 | match node.value { |
|
| 2775 | 3522 | case ast::NodeValue::FnDecl(_), |
|
| 2776 | 3523 | ast::NodeValue::ConstDecl(_), |
|
| 2777 | 3524 | ast::NodeValue::Mod(_), |
|
| 2778 | 3525 | ast::NodeValue::Use(_) => { |
|
| 2779 | 3526 | // Handled in previous passes. |
|
| 2780 | 3527 | } |
|
| 2781 | 3528 | case ast::NodeValue::StaticDecl(_) => { |
|
| 2782 | 3529 | try infer(self, node); |
|
| 2783 | 3530 | } |
|
| 2784 | - | case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 2785 | - | try resolveInstanceDecl(self, node, traitName, targetType, methods); |
|
| 3531 | + | case ast::NodeValue::InstanceDecl { traitName, targetType, regions, methods } => { |
|
| 3532 | + | try resolveInstanceDecl(self, node, traitName, targetType, regions, methods); |
|
| 2786 | 3533 | } |
|
| 2787 | - | case ast::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, attrs } => { |
|
| 2788 | - | try resolveMethodDecl(self, node, name, receiverName, receiverType, sig, attrs); |
|
| 3534 | + | case ast::NodeValue::MethodDecl { |
|
| 3535 | + | .. |
|
| 3536 | + | } => { |
|
| 3537 | + | try resolveMethodDecl(self, node); |
|
| 2789 | 3538 | } |
|
| 2790 | 3539 | else => { |
|
| 2791 | 3540 | // Ignore non-declaration nodes. |
|
| 2792 | 3541 | } |
|
| 2793 | 3542 | } |
|
| 2794 | 3543 | } |
|
| 2795 | 3544 | ||
| 2796 | 3545 | /// Require an unsafe function or block. |
|
| 2797 | - | unsafe fn requireUnsafe(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 3546 | + | unsafe fn requireUnsafe 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) { |
|
| 2798 | 3547 | if not self.inUnsafeContext { |
|
| 2799 | 3548 | throw emitError(self, node, ErrorKind::UnsafeOperation); |
|
| 2800 | 3549 | } |
|
| 2801 | 3550 | } |
|
| 2802 | 3551 | ||
| 2803 | 3552 | /// Require an unsafe context for any access to an unsafe static. |
|
| 2804 | - | unsafe fn checkStaticAccess(self: &mut Resolver, node: *ast::Node, sym: &Symbol) |
|
| 3553 | + | unsafe fn checkStaticAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, sym: &Symbol) |
|
| 2805 | 3554 | throws (ResolveError) |
|
| 2806 | 3555 | { |
|
| 2807 | 3556 | if let case ast::NodeValue::StaticDecl(_) = sym.node.value { |
|
| 2808 | 3557 | if ast::hasAttribute(sym.attrs, ast::Attribute::Unsafe) { |
|
| 2809 | 3558 | try requireUnsafe(self, node); |
|
| 2810 | 3559 | } |
|
| 2811 | 3560 | } |
|
| 2812 | 3561 | } |
|
| 2813 | 3562 | ||
| 2814 | 3563 | /// Reject calls from safe code through unsafe function types. |
|
| 2815 | - | unsafe fn checkUnsafeCall(self: &mut Resolver, node: *ast::Node, info: *FnType) |
|
| 3564 | + | unsafe fn checkUnsafeCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: *FnType) |
|
| 2816 | 3565 | throws (ResolveError) |
|
| 2817 | 3566 | { |
|
| 2818 | 3567 | if info.isUnsafe and not self.inUnsafeContext { |
|
| 2819 | 3568 | throw emitError(self, node, ErrorKind::UnsafeCall); |
|
| 2820 | 3569 | } |
|
| 2821 | 3570 | } |
|
| 2822 | 3571 | ||
| 2823 | 3572 | /// Visit a top-level definition, recursing into sub-modules. |
|
| 2824 | - | unsafe fn visitDef(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 3573 | + | unsafe fn visitDef 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) { |
|
| 2825 | 3574 | match node.value { |
|
| 2826 | 3575 | case ast::NodeValue::FnDecl(decl) => { |
|
| 2827 | 3576 | try resolveFnDeclBody(self, node, decl) catch { |
|
| 2828 | 3577 | return; |
|
| 2829 | 3578 | }; |
|
| 2830 | 3579 | } |
|
| 2831 | 3580 | case ast::NodeValue::Mod(decl) => { |
|
| 2832 | - | if not shouldAnalyzeModule(self, decl.attrs) { |
|
| 3581 | + | let modName = try nodeName(self, decl.name); |
|
| 3582 | + | if not shouldAnalyzeModule(self, decl.attrs, modName) { |
|
| 2833 | 3583 | return; |
|
| 2834 | 3584 | } |
|
| 2835 | - | let modName = try nodeName(self, decl.name); |
|
| 2836 | 3585 | let submod = try enterSubModule(self, modName, node); |
|
| 2837 | 3586 | let case ast::NodeValue::Block(block) = submod.root.value |
|
| 2838 | 3587 | else panic "visitDef: expected block for module root"; |
|
| 2839 | 3588 | try resolveModuleDefs(self, &block) catch e { |
|
| 2840 | 3589 | exitModuleScope(self, submod); |
| 2849 | 3598 | // Skip: already analyzed in declaration phase. |
|
| 2850 | 3599 | } |
|
| 2851 | 3600 | case ast::NodeValue::InstanceDecl { methods, .. } => { |
|
| 2852 | 3601 | try resolveInstanceMethodBodies(self, methods); |
|
| 2853 | 3602 | } |
|
| 2854 | - | case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } => { |
|
| 2855 | - | try resolveMethodBody(self, node, receiverName, sig, body); |
|
| 3603 | + | case ast::NodeValue::MethodDecl { |
|
| 3604 | + | .. |
|
| 3605 | + | } => { |
|
| 3606 | + | try resolveMethodBody(self, node); |
|
| 2856 | 3607 | } |
|
| 2857 | 3608 | else => { |
|
| 2858 | 3609 | // FIXME: This allows module-level statements that should |
|
| 2859 | 3610 | // normally only be valid inside function bodies. We currently |
|
| 2860 | 3611 | // need this because of how tests are written, but it should |
| 2865 | 3616 | } |
|
| 2866 | 3617 | } |
|
| 2867 | 3618 | } |
|
| 2868 | 3619 | ||
| 2869 | 3620 | /// Try to infer a node's type. |
|
| 2870 | - | unsafe fn infer(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 3621 | + | unsafe fn infer 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 2871 | 3622 | return try visit(self, node, Type::Unknown); |
|
| 2872 | 3623 | } |
|
| 2873 | 3624 | ||
| 2874 | - | /// Reject nested references while allowing a direct parameter or local reference. |
|
| 2875 | - | unsafe fn validateValueTypeReferences(self: &mut Resolver, node: *ast::Node, ty: Type) |
|
| 3625 | + | /// Permit named reference dependencies and direct call-scoped or local references. |
|
| 3626 | + | unsafe fn validateValueTypeReferences 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) |
|
| 2876 | 3627 | throws (ResolveError) |
|
| 2877 | 3628 | { |
|
| 3629 | + | try validateRegionDependencies(self, node, ty); |
|
| 3630 | + | if let case Type::Fn(info) = ty; info.regions == nil { |
|
| 3631 | + | return; |
|
| 3632 | + | } |
|
| 2878 | 3633 | if isRefType(ty) { |
|
| 2879 | 3634 | if let case Type::Pointer { target, .. } = ty { |
|
| 2880 | - | if containsRef(*target) { |
|
| 3635 | + | if containsUnscopedRef(*target) { |
|
| 2881 | 3636 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 2882 | 3637 | } |
|
| 2883 | 3638 | } else if let case Type::Slice { item, .. } = ty { |
|
| 2884 | - | if containsRef(*item) { |
|
| 3639 | + | if containsUnscopedRef(*item) { |
|
| 2885 | 3640 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 2886 | 3641 | } |
|
| 2887 | 3642 | } |
|
| 2888 | - | } else if containsRef(ty) { |
|
| 3643 | + | } else if containsUnscopedRef(ty) { |
|
| 2889 | 3644 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 2890 | 3645 | } |
|
| 2891 | 3646 | } |
|
| 2892 | 3647 | ||
| 2893 | 3648 | /// Require a type that may be stored or escape a call. |
|
| 2894 | - | unsafe fn ensureStorableType(self: &mut Resolver, node: *ast::Node, ty: Type) |
|
| 3649 | + | unsafe fn ensureStorableType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) |
|
| 2895 | 3650 | throws (ResolveError) |
|
| 2896 | 3651 | { |
|
| 2897 | - | if containsRef(ty) { |
|
| 3652 | + | try validateRegionDependencies(self, node, ty); |
|
| 3653 | + | if containsUnscopedRef(ty) { |
|
| 2898 | 3654 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 2899 | 3655 | } |
|
| 2900 | 3656 | } |
|
| 2901 | 3657 | ||
| 2902 | 3658 | /// Resolve a type signature node. |
|
| 2903 | - | unsafe fn resolveValueType(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 3659 | + | unsafe fn resolveValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 2904 | 3660 | let ty = try visit(self, node, Type::Unknown); |
|
| 3661 | + | if let case Type::Nominal(info) = ty { |
|
| 3662 | + | try requireNominalArguments(self, info, node); |
|
| 3663 | + | } |
|
| 2905 | 3664 | // Opaque value types are not allowed. |
|
| 2906 | 3665 | if ty == Type::Opaque { |
|
| 2907 | 3666 | throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed); |
|
| 2908 | 3667 | } |
|
| 2909 | 3668 | try validateValueTypeReferences(self, node, ty); |
|
| 2910 | 3669 | return ty; |
|
| 2911 | 3670 | } |
|
| 2912 | 3671 | ||
| 2913 | 3672 | /// Analyze a node's type and check that it can be assigned to the expected type. |
|
| 2914 | - | unsafe fn checkAssignable(self: &mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) { |
|
| 3673 | + | unsafe fn checkAssignable 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type throws (ResolveError) { |
|
| 2915 | 3674 | let actual = try visit(self, node, expected); |
|
| 2916 | 3675 | let _ = try expectAssignable(self, expected, actual, node); |
|
| 3676 | + | if isRefType(expected) and isMutablePointerLike(expected) and isMutablePointerLike(actual) { |
|
| 3677 | + | if not try canMutateThrough(self, node) { |
|
| 3678 | + | throw emitError(self, node, ErrorKind::ImmutableBinding); |
|
| 3679 | + | } |
|
| 3680 | + | } |
|
| 2917 | 3681 | return actual; |
|
| 2918 | 3682 | } |
|
| 2919 | 3683 | ||
| 2920 | 3684 | /// Analyze a node and propagate the resolved type. |
|
| 2921 | 3685 | /// The `hint` parameter provides type context for inference and validation. |
|
| 2922 | 3686 | /// When `nil`, the type must be inferred from the expression itself. |
|
| 2923 | - | unsafe fn visit(self: &mut Resolver, node: *ast::Node, hint: Type) -> Type |
|
| 3687 | + | unsafe fn visit 'arena (self: &mut Resolver 'arena, node: *ast::Node, hint: Type) -> Type |
|
| 2924 | 3688 | throws (ResolveError) |
|
| 2925 | 3689 | { |
|
| 2926 | 3690 | if let ty = typeFor(self, node) { |
|
| 2927 | - | return ty; |
|
| 3691 | + | // An optional context completes a nil expression's storage type. |
|
| 3692 | + | if ty <> Type::Nil or not isOptionalType(hint) { |
|
| 3693 | + | return ty; |
|
| 3694 | + | } |
|
| 2928 | 3695 | } |
|
| 2929 | 3696 | match node.value { |
|
| 2930 | 3697 | case ast::NodeValue::Ident(name) => { |
|
| 2931 | 3698 | let sym = findAnySymbol(self.scope, name) |
|
| 2932 | 3699 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
| 2940 | 3707 | setNodeConstValue(self, node, val); |
|
| 2941 | 3708 | } |
|
| 2942 | 3709 | return setNodeType(self, node, type); |
|
| 2943 | 3710 | }, |
|
| 2944 | 3711 | case SymbolData::Type(t) => |
|
| 2945 | - | return setNodeType(self, node, Type::Nominal(t)), |
|
| 3712 | + | return setNodeType(self, node, Type::Nominal(hintedNominal(t, hint))), |
|
| 2946 | 3713 | case SymbolData::Variant { .. } => |
|
| 2947 | 3714 | return Type::Void, |
|
| 2948 | 3715 | case SymbolData::Module { .. } => |
|
| 2949 | 3716 | throw emitError(self, node, ErrorKind::UnexpectedModuleName), |
|
| 2950 | 3717 | case SymbolData::Trait(_) => |
|
| 2951 | 3718 | throw emitError(self, node, ErrorKind::UnexpectedTraitName), |
|
| 2952 | 3719 | } |
|
| 2953 | 3720 | }, |
|
| 2954 | - | case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal), |
|
| 3721 | + | case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal, hint), |
|
| 2955 | 3722 | case ast::NodeValue::FieldAccess(access) => return try resolveFieldAccess(self, node, access), |
|
| 2956 | 3723 | case ast::NodeValue::BinOp(binop) => return try resolveBinOp(self, node, binop), |
|
| 3724 | + | case ast::NodeValue::RegionBlock { region, bindings, body, isSession } => { |
|
| 3725 | + | if isSession { |
|
| 3726 | + | return try resolveSessionBlock(self, node, region, bindings, body); |
|
| 3727 | + | } |
|
| 3728 | + | return try resolveBorrowBlock(self, node, region, bindings, body); |
|
| 3729 | + | } |
|
| 2957 | 3730 | case ast::NodeValue::Block(block) => return try resolveBlock(self, node, block), |
|
| 2958 | 3731 | case ast::NodeValue::Let(decl) => return try resolveLet(self, node, decl), |
|
| 2959 | 3732 | case ast::NodeValue::ConstDecl(decl) => return try resolveConstOrStatic( |
|
| 2960 | 3733 | self, node, decl.ident, decl.type, decl.value, decl.attrs, true |
|
| 2961 | 3734 | ), |
|
| 2962 | 3735 | case ast::NodeValue::StaticDecl(decl) => return try resolveConstOrStatic( |
|
| 2963 | 3736 | self, node, decl.ident, decl.type, decl.value, decl.attrs, false |
|
| 2964 | 3737 | ), |
|
| 2965 | 3738 | case ast::NodeValue::FnParam(param) => return try resolveFnParam(self, node, param), |
|
| 2966 | 3739 | case ast::NodeValue::If(cond) => return try resolveIf(self, node, cond), |
|
| 2967 | - | case ast::NodeValue::CondExpr(cond) => return try resolveCondExpr(self, node, cond), |
|
| 3740 | + | case ast::NodeValue::CondExpr(cond) => return try resolveCondExpr(self, node, cond, hint), |
|
| 2968 | 3741 | case ast::NodeValue::IfLet(cond) => return try resolveIfLet(self, node, cond), |
|
| 2969 | 3742 | case ast::NodeValue::While(loopNode) => return try resolveWhile(self, node, loopNode), |
|
| 2970 | 3743 | case ast::NodeValue::WhileLet(loopNode) => return try resolveWhileLet(self, node, loopNode), |
|
| 2971 | 3744 | case ast::NodeValue::For(loopNode) => return try resolveFor(self, node, loopNode), |
|
| 2972 | 3745 | case ast::NodeValue::Loop { body } => { |
| 2991 | 3764 | case ast::NodeValue::Assign(assign) => return try resolveAssign(self, node, assign), |
|
| 2992 | 3765 | case ast::NodeValue::RecordLit(lit) => return try resolveRecordLit(self, node, lit, hint), |
|
| 2993 | 3766 | case ast::NodeValue::ArrayLit(items) => return try resolveArrayLit(self, node, items, hint), |
|
| 2994 | 3767 | case ast::NodeValue::ArrayRepeatLit(lit) => return try resolveArrayRepeat(self, node, lit, hint), |
|
| 2995 | 3768 | case ast::NodeValue::Subscript { container, index } => return try resolveSubscript(self, node, container, index), |
|
| 2996 | - | case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access), |
|
| 3769 | + | case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access, hint), |
|
| 2997 | 3770 | case ast::NodeValue::AddressOf(addr) => return try resolveAddressOf(self, node, addr, hint), |
|
| 2998 | 3771 | case ast::NodeValue::Deref(target) => return try resolveDeref(self, node, target, hint), |
|
| 2999 | 3772 | case ast::NodeValue::As(expr) => return try resolveAs(self, node, expr), |
|
| 3000 | 3773 | case ast::NodeValue::Range(range) => return try resolveRange(self, node, range), |
|
| 3001 | 3774 | case ast::NodeValue::Try(expr) => return try resolveTry(self, node, expr, hint), |
| 3022 | 3795 | case ast::NodeValue::ExprStmt(expr) => { |
|
| 3023 | 3796 | // Pass `Void` as expected type to indicate value is discarded. |
|
| 3024 | 3797 | let exprTy = try visit(self, expr, Type::Void); |
|
| 3025 | 3798 | return setNodeType(self, node, Type::Never if exprTy == Type::Never else Type::Void); |
|
| 3026 | 3799 | }, |
|
| 3800 | + | case ast::NodeValue::RegionApply { value, regions } => |
|
| 3801 | + | return try resolveRegionApply(self, node, value, regions), |
|
| 3027 | 3802 | case ast::NodeValue::TypeSig(sig) => return try inferTypeSig(self, node, sig), |
|
| 3028 | 3803 | case ast::NodeValue::Super => { |
|
| 3029 | 3804 | // `super` by itself is invalid, must be used in scope access. |
|
| 3030 | 3805 | throw emitError(self, node, ErrorKind::InvalidModulePath); |
|
| 3031 | 3806 | }, |
| 3075 | 3850 | } |
|
| 3076 | 3851 | } |
|
| 3077 | 3852 | } |
|
| 3078 | 3853 | ||
| 3079 | 3854 | /// Visit an optional node when present. |
|
| 3080 | - | unsafe fn visitOptional(self: &mut Resolver, node: ?*ast::Node, hint: Type) -> ?Type |
|
| 3855 | + | unsafe fn visitOptional 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, hint: Type) -> ?Type |
|
| 3081 | 3856 | throws (ResolveError) |
|
| 3082 | 3857 | { |
|
| 3083 | 3858 | if let n = node { |
|
| 3084 | 3859 | return try visit(self, n, hint); |
|
| 3085 | 3860 | } |
|
| 3086 | 3861 | return nil; |
|
| 3087 | 3862 | } |
|
| 3088 | 3863 | ||
| 3089 | 3864 | /// Visit every node contained in a list, returning the last resolved type. |
|
| 3090 | - | unsafe fn visitList(self: &mut Resolver, list: *[*ast::Node]) -> Type |
|
| 3865 | + | unsafe fn visitList 'arena (self: &mut Resolver 'arena, list: *[*ast::Node]) -> Type |
|
| 3091 | 3866 | throws (ResolveError) |
|
| 3092 | 3867 | { |
|
| 3093 | 3868 | let mut diverges = false; |
|
| 3094 | 3869 | for item in list { |
|
| 3095 | 3870 | if try infer(self, item) == Type::Never { |
| 3101 | 3876 | } |
|
| 3102 | 3877 | return Type::Void; |
|
| 3103 | 3878 | } |
|
| 3104 | 3879 | ||
| 3105 | 3880 | /// Collect attribute flags applied to a declaration. |
|
| 3106 | - | fn resolveAttributes(self: &mut Resolver, attrs: ?ast::Attributes) -> u32 { |
|
| 3881 | + | fn resolveAttributes(attrs: ?ast::Attributes) -> u32 { |
|
| 3107 | 3882 | let list = attrs else return 0; |
|
| 3108 | - | let attrNodes = list.list; |
|
| 3109 | 3883 | let mut mask: u32 = 0; |
|
| 3110 | 3884 | ||
| 3111 | - | for node in attrNodes { |
|
| 3885 | + | for node in list.list { |
|
| 3112 | 3886 | let case ast::NodeValue::Attribute(attr) = node.value |
|
| 3113 | 3887 | else panic "resolveAttributes: invalid attribute node"; |
|
| 3114 | 3888 | set mask |= (attr as u32); |
|
| 3115 | 3889 | } |
|
| 3116 | 3890 | return mask; |
|
| 3117 | 3891 | } |
|
| 3118 | 3892 | ||
| 3119 | 3893 | /// Ensure the `default` attribute is only applied to functions. |
|
| 3120 | - | unsafe fn ensureDefaultAttrNotAllowed(self: &mut Resolver, node: *ast::Node, attrs: u32) |
|
| 3894 | + | unsafe fn ensureDefaultAttrNotAllowed 'arena (self: &mut Resolver 'arena, node: *ast::Node, attrs: u32) |
|
| 3121 | 3895 | throws (ResolveError) |
|
| 3122 | 3896 | { |
|
| 3123 | 3897 | let defaultBit = ast::Attribute::Default as u32; |
|
| 3124 | 3898 | if (attrs & defaultBit) <> 0 { |
|
| 3125 | 3899 | throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn); |
|
| 3126 | 3900 | } |
|
| 3127 | 3901 | } |
|
| 3128 | 3902 | ||
| 3129 | 3903 | /// Analyze a block node, allocating a nested lexical scope. |
|
| 3130 | - | unsafe fn resolveBlock(self: &mut Resolver, node: *ast::Node, block: ast::Block) -> Type |
|
| 3904 | + | unsafe fn resolveBlock 'arena (self: &mut Resolver 'arena, node: *ast::Node, block: ast::Block) -> Type |
|
| 3131 | 3905 | throws (ResolveError) |
|
| 3132 | 3906 | { |
|
| 3133 | 3907 | enterScope(self, node); |
|
| 3134 | 3908 | let wasUnsafe = self.inUnsafeContext; |
|
| 3135 | 3909 | set self.inUnsafeContext = wasUnsafe or block.isUnsafe; |
| 3145 | 3919 | set self.inUnsafeContext = wasUnsafe; |
|
| 3146 | 3920 | ||
| 3147 | 3921 | return setNodeType(self, node, blockTy); |
|
| 3148 | 3922 | } |
|
| 3149 | 3923 | ||
| 3150 | - | /// Analyze a `let` declaration and bind its identifier. |
|
| 3151 | - | unsafe fn resolveLet(self: &mut Resolver, node: *ast::Node, decl: ast::Let) -> Type |
|
| 3924 | + | /// Introduce a concrete region under an explicit parent or enclosing region block. |
|
| 3925 | + | unsafe fn borrowRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *RegionScope throws (ResolveError) { |
|
| 3926 | + | let case ast::NodeValue::Region { name, parent } = node.value |
|
| 3927 | + | else panic "borrowRegion: invalid region node"; |
|
| 3928 | + | if findRegion(self.regionScope, name) <> nil { |
|
| 3929 | + | throw emitError(self, node, ErrorKind::DuplicateBinding(name)); |
|
| 3930 | + | } |
|
| 3931 | + | let mut enclosing: ?*unsafe types::Region = nil; |
|
| 3932 | + | let mut current = self.regionScope; |
|
| 3933 | + | while let scope = current { |
|
| 3934 | + | for region in scope.entries { |
|
| 3935 | + | if region.origin == types::RegionOrigin::Block { |
|
| 3936 | + | set enclosing = region; |
|
| 3937 | + | break; |
|
| 3938 | + | } |
|
| 3939 | + | } |
|
| 3940 | + | if enclosing <> nil { |
|
| 3941 | + | break; |
|
| 3942 | + | } |
|
| 3943 | + | set current = scope.parent; |
|
| 3944 | + | } |
|
| 3945 | + | if let parentNode = parent { |
|
| 3946 | + | set enclosing = try resolveRegion(self, parentNode); |
|
| 3947 | + | } |
|
| 3948 | + | let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region)) |
|
| 3949 | + | as *unsafe mut types::Region; |
|
| 3950 | + | set *region = types::Region { id: node.id, origin: types::RegionOrigin::Block, name, parent: enclosing }; |
|
| 3951 | + | let entries = try! alloc::allocRawSlice( |
|
| 3952 | + | self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), 1 |
|
| 3953 | + | ) as *unsafe mut [*unsafe mut types::Region]; |
|
| 3954 | + | set entries[0] = region; |
|
| 3955 | + | let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope; |
|
| 3956 | + | set *scope = RegionScope { entries, parent: self.regionScope }; |
|
| 3957 | + | return scope; |
|
| 3958 | + | } |
|
| 3959 | + | ||
| 3960 | + | /// Qualify an existing-place borrow with its block's region. |
|
| 3961 | + | unsafe fn qualifyBlockBorrow 'arena ( |
|
| 3962 | + | self: &mut Resolver 'arena, node: *ast::Node, ty: Type, region: *unsafe types::Region |
|
| 3963 | + | ) -> Type throws (ResolveError) { |
|
| 3964 | + | let mut class = types::PointerClass::Ref; |
|
| 3965 | + | match ty { |
|
| 3966 | + | case Type::Cell { class: cellClass, .. } => set class = cellClass, |
|
| 3967 | + | case Type::Pointer { class: pointerClass, .. } => set class = pointerClass, |
|
| 3968 | + | case Type::Slice { class: sliceClass, .. } => set class = sliceClass, |
|
| 3969 | + | else => throw emitError(self, node, ErrorKind::RefBinding), |
|
| 3970 | + | } |
|
| 3971 | + | if let case types::PointerClass::Region(source) = class { |
|
| 3972 | + | if not types::regionContains(source, region) { |
|
| 3973 | + | throw emitError(self, node, ErrorKind::RegionParent(region.name)); |
|
| 3974 | + | } |
|
| 3975 | + | } |
|
| 3976 | + | match ty { |
|
| 3977 | + | case Type::Pointer { target, mutable, .. } => |
|
| 3978 | + | return Type::Pointer { class: types::PointerClass::Region(region), target, mutable }, |
|
| 3979 | + | case Type::Slice { item, mutable, .. } => |
|
| 3980 | + | return Type::Slice { class: types::PointerClass::Region(region), item, mutable }, |
|
| 3981 | + | else => throw emitError(self, node, ErrorKind::RefBinding), |
|
| 3982 | + | } |
|
| 3983 | + | } |
|
| 3984 | + | ||
| 3985 | + | /// Check source places before publishing the region's bindings to its body. |
|
| 3986 | + | unsafe fn resolveBorrowBlock 'arena ( |
|
| 3987 | + | self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node, |
|
| 3988 | + | bindings: *[*ast::Node], body: *ast::Node |
|
| 3989 | + | ) -> Type throws (ResolveError) { |
|
| 3990 | + | if self.currentFn == nil { |
|
| 3991 | + | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 3992 | + | } |
|
| 3993 | + | let scope = try borrowRegion(self, regionNode); |
|
| 3994 | + | let region = scope.entries[0]; |
|
| 3995 | + | for bindingNode in bindings { |
|
| 3996 | + | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
|
| 3997 | + | else panic "resolveBorrowBlock: invalid binding node"; |
|
| 3998 | + | let case ast::NodeValue::AddressOf(address) = binding.value.value |
|
| 3999 | + | else panic "resolveBorrowBlock: invalid source node"; |
|
| 4000 | + | let ty = try infer(self, binding.value); |
|
| 4001 | + | if not ast::isPlaceExpr(address.target) or borrowPlace(self, address.target).root == nil { |
|
| 4002 | + | throw emitError(self, binding.value, ErrorKind::RefBinding); |
|
| 4003 | + | } |
|
| 4004 | + | let qualified = try qualifyBlockBorrow(self, binding.value, ty, region); |
|
| 4005 | + | setNodeType(self, binding.value, qualified); |
|
| 4006 | + | } |
|
| 4007 | + | let previous = self.regionScope; |
|
| 4008 | + | set self.regionScope = scope; |
|
| 4009 | + | set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope); |
|
| 4010 | + | enterScope(self, node); |
|
| 4011 | + | let result = try resolveBorrowBody(self, bindings, body) catch error { |
|
| 4012 | + | exitScope(self); |
|
| 4013 | + | set self.regionScope = previous; |
|
| 4014 | + | throw error; |
|
| 4015 | + | }; |
|
| 4016 | + | exitScope(self); |
|
| 4017 | + | set self.regionScope = previous; |
|
| 4018 | + | return setNodeType(self, node, result); |
|
| 4019 | + | } |
|
| 4020 | + | ||
| 4021 | + | /// Bind a checked region's source references and resolve its statement body. |
|
| 4022 | + | unsafe fn resolveBorrowBody 'arena (self: &mut Resolver 'arena, bindings: *[*ast::Node], body: *ast::Node) -> Type |
|
| 4023 | + | throws (ResolveError) |
|
| 4024 | + | { |
|
| 4025 | + | for bindingNode in bindings { |
|
| 4026 | + | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
|
| 4027 | + | else panic "resolveBorrowBody: invalid binding node"; |
|
| 4028 | + | try resolveLet(self, bindingNode, ast::borrowBinding(binding)); |
|
| 4029 | + | } |
|
| 4030 | + | return try infer(self, body); |
|
| 4031 | + | } |
|
| 4032 | + | ||
| 4033 | + | /// Find a declaration by spelling when the name is not interned. |
|
| 4034 | + | unsafe fn findSpelledSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol { |
|
| 4035 | + | for i in 0..scope.symbolsLen { |
|
| 4036 | + | let symbol = scope.symbols[i]; |
|
| 4037 | + | if mem::eq(symbol.name, name) { |
|
| 4038 | + | return symbol; |
|
| 4039 | + | } |
|
| 4040 | + | } |
|
| 4041 | + | return nil; |
|
| 4042 | + | } |
|
| 4043 | + | ||
| 4044 | + | /// Look up a compiler-known declaration in the standard allocation module. |
|
| 4045 | + | unsafe fn allocationSymbol 'arena (self: &Resolver 'arena, name: *[u8]) -> ?*unsafe mut Symbol { |
|
| 4046 | + | let mut scope = self.pkgScope; |
|
| 4047 | + | for segment in ["std", "lang", "alloc"] { |
|
| 4048 | + | let symbol = findSpelledSymbol(scope, segment) else return nil; |
|
| 4049 | + | let case SymbolData::Module { scope: child, .. } = symbol.data else return nil; |
|
| 4050 | + | set scope = child; |
|
| 4051 | + | } |
|
| 4052 | + | return findSpelledSymbol(scope, name); |
|
| 4053 | + | } |
|
| 4054 | + | ||
| 4055 | + | /// Bind an allocation interface while retaining the source arena until region exit. |
|
| 4056 | + | unsafe fn resolveSessionBlock 'arena ( |
|
| 4057 | + | self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node, |
|
| 4058 | + | bindings: *[*ast::Node], body: *ast::Node |
|
| 4059 | + | ) -> Type throws (ResolveError) { |
|
| 4060 | + | if self.currentFn == nil or bindings.len <> 1 { |
|
| 4061 | + | throw emitError(self, node, ErrorKind::InvalidSessionSource); |
|
| 4062 | + | } |
|
| 4063 | + | let bindingNode = bindings[0]; |
|
| 4064 | + | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
|
| 4065 | + | else panic "resolveSessionBlock: invalid binding"; |
|
| 4066 | + | let case ast::NodeValue::AddressOf(address) = binding.value.value |
|
| 4067 | + | else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource); |
|
| 4068 | + | let ty = try infer(self, binding.value); |
|
| 4069 | + | let case Type::Pointer { target, .. } = ty |
|
| 4070 | + | else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource); |
|
| 4071 | + | let allocTrait = allocationSymbol(self, "Alloc") |
|
| 4072 | + | else throw emitError(self, node, ErrorKind::InvalidSessionSource); |
|
| 4073 | + | let case SymbolData::Trait(allocInfo) = allocTrait.data |
|
| 4074 | + | else throw emitError(self, node, ErrorKind::InvalidSessionSource); |
|
| 4075 | + | let allocModule = moduleIdForSymbol(self, allocTrait) |
|
| 4076 | + | else throw emitError(self, node, ErrorKind::InvalidSessionSource); |
|
| 4077 | + | let mut allocInstance: ?*unsafe InstanceEntry = nil; |
|
| 4078 | + | for i in 0..self.instancesLen { |
|
| 4079 | + | let candidate: *unsafe InstanceEntry = &self.instances[i]; |
|
| 4080 | + | if candidate.traitType.moduleId == allocModule and mem::eq(candidate.traitType.name, allocInfo.name) |
|
| 4081 | + | and erasedTypesEqual(candidate.concreteType, *target) |
|
| 4082 | + | { |
|
| 4083 | + | set allocInstance = candidate; |
|
| 4084 | + | break; |
|
| 4085 | + | } |
|
| 4086 | + | } |
|
| 4087 | + | let selected = allocInstance |
|
| 4088 | + | else throw emitError(self, node, ErrorKind::InvalidSessionSource); |
|
| 4089 | + | if not address.mutable or not ast::isPlaceExpr(address.target) |
|
| 4090 | + | or borrowPlace(self, address.target).root == nil |
|
| 4091 | + | { |
|
| 4092 | + | throw emitError(self, binding.value, ErrorKind::InvalidSessionSource); |
|
| 4093 | + | } |
|
| 4094 | + | let _ = setNodeCoercion(self, binding.value, Coercion::TraitObject { |
|
| 4095 | + | traitInfo: allocInfo, inst: selected, |
|
| 4096 | + | }); |
|
| 4097 | + | let scope = try borrowRegion(self, regionNode); |
|
| 4098 | + | let region = scope.entries[0]; |
|
| 4099 | + | if let sourceRegion = referenceRegion(ty) { |
|
| 4100 | + | set region.parent = sourceRegion; |
|
| 4101 | + | } |
|
| 4102 | + | setNodeType(self, binding.value, try qualifyBlockBorrow(self, binding.value, ty, region)); |
|
| 4103 | + | let previous = self.regionScope; |
|
| 4104 | + | set self.regionScope = scope; |
|
| 4105 | + | set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope); |
|
| 4106 | + | enterScope(self, node); |
|
| 4107 | + | let result = try resolveSessionBody(self, bindingNode, binding, region, body) catch error { |
|
| 4108 | + | exitScope(self); |
|
| 4109 | + | set self.regionScope = previous; |
|
| 4110 | + | throw error; |
|
| 4111 | + | }; |
|
| 4112 | + | exitScope(self); |
|
| 4113 | + | set self.regionScope = previous; |
|
| 4114 | + | return setNodeType(self, node, result); |
|
| 4115 | + | } |
|
| 4116 | + | ||
| 4117 | + | /// Introduce the opaque session value and check its body. |
|
| 4118 | + | unsafe fn resolveSessionBody 'arena ( |
|
| 4119 | + | self: &mut Resolver 'arena, node: *ast::Node, binding: ast::Arg, |
|
| 4120 | + | region: *unsafe types::Region, body: *ast::Node |
|
| 4121 | + | ) -> Type throws (ResolveError) { |
|
| 4122 | + | let ident = binding.label else panic "resolveSessionBody: missing binding name"; |
|
| 4123 | + | let _ = try bindValueIdent(self, ident, node, Type::Session(region), false, 0, 0); |
|
| 4124 | + | return try infer(self, body); |
|
| 4125 | + | } |
|
| 4126 | + | ||
| 4127 | + | /// Analyze a `let` declaration and bind its identifier. |
|
| 4128 | + | unsafe fn resolveLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Let) -> Type |
|
| 3152 | 4129 | throws (ResolveError) |
|
| 3153 | 4130 | { |
|
| 3154 | 4131 | let mut alignment: u32 = 0; // Zero is default. |
|
| 3155 | 4132 | let mut bindingTy = Type::Unknown; |
|
| 3156 | 4133 | let mut valueTy = Type::Unknown; |
| 3170 | 4147 | try validateValueTypeReferences(self, node, bindingTy); |
|
| 3171 | 4148 | if isRefType(bindingTy) { |
|
| 3172 | 4149 | if self.currentFn == nil { |
|
| 3173 | 4150 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 3174 | 4151 | } |
|
| 3175 | - | if decl.mutable { |
|
| 4152 | + | if decl.mutable and (referenceRegion(bindingTy) == nil or not isCopy(bindingTy)) { |
|
| 3176 | 4153 | throw emitError(self, node, ErrorKind::RefBinding); |
|
| 3177 | 4154 | } |
|
| 3178 | 4155 | } |
|
| 3179 | 4156 | // Variables cannot have void type. |
|
| 3180 | 4157 | if bindingTy == Type::Void { |
| 3195 | 4172 | // Alignment must be zero or a power of two. |
|
| 3196 | 4173 | if alignment <> 0 and (alignment & (alignment - 1)) <> 0 { |
|
| 3197 | 4174 | throw emitError(self, decl.value, ErrorKind::InvalidAlignmentValue(alignment)); |
|
| 3198 | 4175 | } |
|
| 3199 | 4176 | let _ = try bindValueIdent(self, decl.ident, node, bindingTy, decl.mutable, alignment, 0); |
|
| 3200 | - | setNodeType(self, decl.value, bindingTy); |
|
| 4177 | + | ||
| 4178 | + | // Untyped initializers use the declared storage type. |
|
| 4179 | + | if not isTypeInferrable(valueTy) { |
|
| 4180 | + | setNodeType(self, decl.value, bindingTy); |
|
| 4181 | + | } |
|
| 3201 | 4182 | ||
| 3202 | 4183 | return Type::Never if valueTy == Type::Never else Type::Void; |
|
| 3203 | 4184 | } |
|
| 3204 | 4185 | ||
| 3205 | 4186 | /// Check whether a node is an integer literal, optionally under unary negation. |
| 3215 | 4196 | else => return false, |
|
| 3216 | 4197 | } |
|
| 3217 | 4198 | } |
|
| 3218 | 4199 | ||
| 3219 | 4200 | /// Determine whether a node represents a compile-time constant expression. |
|
| 3220 | - | export unsafe fn isConstExpr(self: &Resolver, node: *ast::Node) -> bool { |
|
| 4201 | + | export unsafe fn isConstExpr 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool { |
|
| 3221 | 4202 | match node.value { |
|
| 3222 | 4203 | case ast::NodeValue::Bool(_), |
|
| 3223 | 4204 | ast::NodeValue::Char(_), |
|
| 3224 | 4205 | ast::NodeValue::Number(_), |
|
| 3225 | 4206 | ast::NodeValue::String(_), |
| 3335 | 4316 | return ConstValue::Int(constIntFromBits(raw, bits, true)), |
|
| 3336 | 4317 | } |
|
| 3337 | 4318 | } |
|
| 3338 | 4319 | ||
| 3339 | 4320 | /// Return the constant `u32` value for a slice bound when known. |
|
| 3340 | - | fn constSliceIndex(self: &mut Resolver, node: *ast::Node) -> ?u32 { |
|
| 4321 | + | fn constSliceIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?u32 { |
|
| 3341 | 4322 | let value = constValueEntry(self, node) |
|
| 3342 | 4323 | else return nil; |
|
| 3343 | 4324 | let case ConstValue::Int(int) = value |
|
| 3344 | 4325 | else return nil; |
|
| 3345 | 4326 | if int.negative { |
| 3353 | 4334 | /// This function ensures that a node represents a valid, non-negative integer constant |
|
| 3354 | 4335 | /// that fits within a machine word. It is used for contexts requiring compile-time |
|
| 3355 | 4336 | /// non-negative integers, such as array sizes and alignment specifications. |
|
| 3356 | 4337 | /// |
|
| 3357 | 4338 | /// Returns the unsigned magnitude of the constant as `u32`. |
|
| 3358 | - | unsafe fn checkSizeInt(self: &mut Resolver, node: *ast::Node) -> u32 |
|
| 4339 | + | unsafe fn checkSizeInt 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> u32 |
|
| 3359 | 4340 | throws (ResolveError) |
|
| 3360 | 4341 | { |
|
| 3361 | 4342 | // First traverse the node expect a numeric type. |
|
| 3362 | 4343 | let _ = try checkNumeric(self, node); |
|
| 3363 | 4344 |
| 3380 | 4361 | ||
| 3381 | 4362 | /// Check that constructor arguments match record fields. |
|
| 3382 | 4363 | /// |
|
| 3383 | 4364 | /// Verifies argument count matches field count, and that each argument is |
|
| 3384 | 4365 | /// assignable to its corresponding field type. |
|
| 3385 | - | unsafe fn checkRecordConstructorArgs(self: &mut Resolver, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType) |
|
| 4366 | + | unsafe fn checkRecordConstructorArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType) |
|
| 3386 | 4367 | throws (ResolveError) |
|
| 3387 | 4368 | { |
|
| 3388 | 4369 | try checkRecordArity(self, args, recInfo, node); |
|
| 3389 | 4370 | for arg, i in args { |
|
| 3390 | 4371 | let fieldType = recInfo.fields[i].fieldType; |
|
| 3391 | 4372 | try checkAssignable(self, arg, fieldType); |
|
| 3392 | 4373 | } |
|
| 3393 | 4374 | } |
|
| 3394 | 4375 | ||
| 3395 | 4376 | /// Check that the argument count of a constructor pattern or call matches the record field count. |
|
| 3396 | - | unsafe fn checkRecordArity(self: &mut Resolver, args: *[*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 4377 | + | unsafe fn checkRecordArity 'arena (self: &mut Resolver 'arena, args: *[*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3397 | 4378 | if args.len <> recInfo.fields.len { |
|
| 3398 | 4379 | throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
|
| 3399 | 4380 | expected: recInfo.fields.len as u32, |
|
| 3400 | 4381 | actual: args.len, |
|
| 3401 | 4382 | })); |
|
| 3402 | 4383 | } |
|
| 3403 | 4384 | } |
|
| 3404 | 4385 | ||
| 3405 | 4386 | /// Helper for analyzing `constant` and `static` declarations. |
|
| 3406 | - | unsafe fn resolveConstOrStatic( |
|
| 3407 | - | self: &mut Resolver, |
|
| 4387 | + | unsafe fn resolveConstOrStatic 'arena ( |
|
| 4388 | + | self: &mut Resolver 'arena, |
|
| 3408 | 4389 | node: *ast::Node, |
|
| 3409 | 4390 | ident: *ast::Node, |
|
| 3410 | 4391 | typeNode: *ast::Node, |
|
| 3411 | 4392 | valueNode: *ast::Node, |
|
| 3412 | 4393 | attrList: ?ast::Attributes, |
|
| 3413 | 4394 | isConst: bool |
|
| 3414 | 4395 | ) -> Type throws (ResolveError) { |
|
| 3415 | - | let attrs = resolveAttributes(self, attrList); |
|
| 4396 | + | let attrs = resolveAttributes(attrList); |
|
| 3416 | 4397 | let bindingTy = try infer(self, typeNode); |
|
| 4398 | + | if containsRegion(bindingTy) { |
|
| 4399 | + | throw emitError(self, typeNode, ErrorKind::InvalidRefPosition); |
|
| 4400 | + | } |
|
| 3417 | 4401 | try ensureStorableType(self, typeNode, bindingTy); |
|
| 3418 | 4402 | let wasUnsafe = self.inUnsafeContext; |
|
| 3419 | 4403 | set self.inUnsafeContext = wasUnsafe or ( |
|
| 3420 | 4404 | not isConst and ast::hasAttribute(attrs, ast::Attribute::Unsafe) |
|
| 3421 | 4405 | ); |
| 3446 | 4430 | ||
| 3447 | 4431 | return Type::Void; |
|
| 3448 | 4432 | } |
|
| 3449 | 4433 | ||
| 3450 | 4434 | /// Analyze a function declaration signature and bind the function name. |
|
| 3451 | - | unsafe fn resolveFnDecl(self: &mut Resolver, node: *ast::Node, decl: ast::FnDecl) -> Type |
|
| 4435 | + | unsafe fn resolveFnDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type |
|
| 4436 | + | throws (ResolveError) |
|
| 4437 | + | { |
|
| 4438 | + | let previous = self.regionScope; |
|
| 4439 | + | set self.regionScope = try bindRegions(self, node, decl.regions); |
|
| 4440 | + | let result = try resolveFnSignature(self, node, decl) catch error { |
|
| 4441 | + | set self.regionScope = previous; |
|
| 4442 | + | throw error; |
|
| 4443 | + | }; |
|
| 4444 | + | set self.regionScope = previous; |
|
| 4445 | + | return result; |
|
| 4446 | + | } |
|
| 4447 | + | ||
| 4448 | + | /// Resolve a function signature in its declared region environment. |
|
| 4449 | + | unsafe fn resolveFnSignature 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type |
|
| 3452 | 4450 | throws (ResolveError) |
|
| 3453 | 4451 | { |
|
| 3454 | - | let attrMask = resolveAttributes(self, decl.attrs); |
|
| 4452 | + | let attrMask = resolveAttributes(decl.attrs); |
|
| 3455 | 4453 | let mut retTy = Type::Void; |
|
| 3456 | 4454 | if let retNode = decl.sig.returnType { |
|
| 3457 | 4455 | set retTy = try infer(self, retNode); |
|
| 3458 | 4456 | try ensureStorableType(self, retNode, retTy); |
|
| 3459 | 4457 | } |
|
| 3460 | - | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 4458 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 3461 | 4459 | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 3462 | 4460 | let mut throwList: *mut [*Type] = &mut []; |
|
| 3463 | 4461 | let mut fnType = FnType { |
|
| 4462 | + | regions: self.regionScope, |
|
| 3464 | 4463 | paramTypes: &[], |
|
| 3465 | 4464 | returnType: allocType(self, retTy), |
|
| 3466 | 4465 | throwList: &[], |
|
| 3467 | 4466 | isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe), |
|
| 3468 | 4467 | }; |
| 3494 | 4493 | for throwNode in decl.sig.throwList { |
|
| 3495 | 4494 | let throwTy = try infer(self, throwNode) catch e { |
|
| 3496 | 4495 | exitFn(self); |
|
| 3497 | 4496 | throw e; |
|
| 3498 | 4497 | }; |
|
| 4498 | + | try validateErrorTag(self, throwNode, throwTy, &throwList[..]); |
|
| 3499 | 4499 | throwList.append(allocType(self, throwTy), a); |
|
| 3500 | 4500 | try ensureStorableType(self, throwNode, throwTy); |
|
| 3501 | 4501 | } |
|
| 3502 | 4502 | exitFn(self); |
|
| 3503 | 4503 | set fnType.paramTypes = ¶mTypes[..]; |
| 3510 | 4510 | ||
| 3511 | 4511 | return ty; |
|
| 3512 | 4512 | } |
|
| 3513 | 4513 | ||
| 3514 | 4514 | /// Analyze a function body. |
|
| 3515 | - | unsafe fn resolveFnDeclBody(self: &mut Resolver, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) { |
|
| 4515 | + | unsafe fn resolveFnDeclBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) { |
|
| 3516 | 4516 | let sym = symbolFor(self, node) else { |
|
| 3517 | 4517 | // The function declaration failed to type check, therefore |
|
| 3518 | 4518 | // no symbol was associated with it. |
|
| 3519 | 4519 | return; |
|
| 3520 | 4520 | }; |
| 3529 | 4529 | throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody); |
|
| 3530 | 4530 | } |
|
| 3531 | 4531 | if isExtern { |
|
| 3532 | 4532 | throw emitError(self, node, ErrorKind::FnUnexpectedBody); |
|
| 3533 | 4533 | } |
|
| 3534 | - | try resolveExecutableBody(self, node, fnType, nil, decl.sig.params, body); |
|
| 4534 | + | let previous = self.regionScope; |
|
| 4535 | + | set self.regionScope = try bindRegions(self, node, decl.regions); |
|
| 4536 | + | try resolveExecutableBody(self, node, fnType, nil, decl.sig.params, body) catch error { |
|
| 4537 | + | set self.regionScope = previous; |
|
| 4538 | + | throw error; |
|
| 4539 | + | }; |
|
| 4540 | + | set self.regionScope = previous; |
|
| 3535 | 4541 | } else if not isExtern { |
|
| 3536 | 4542 | throw emitError(self, node, ErrorKind::FnMissingBody); |
|
| 3537 | 4543 | } |
|
| 3538 | 4544 | } |
|
| 3539 | 4545 | ||
| 3540 | 4546 | /// Resolve a function or method body and restore the enclosing context. |
|
| 3541 | - | unsafe fn resolveExecutableBody( |
|
| 3542 | - | self: &mut Resolver, |
|
| 4547 | + | unsafe fn resolveExecutableBody 'arena ( |
|
| 4548 | + | self: &mut Resolver 'arena, |
|
| 3543 | 4549 | node: *ast::Node, |
|
| 3544 | 4550 | fnType: *FnType, |
|
| 3545 | 4551 | receiverName: ?*ast::Node, |
|
| 3546 | 4552 | params: *[*ast::Node], |
|
| 3547 | 4553 | body: *ast::Node, |
| 3563 | 4569 | } |
|
| 3564 | 4570 | } |
|
| 3565 | 4571 | ||
| 3566 | 4572 | /// Check parameters, body types, and ownership. |
|
| 3567 | 4573 | /// Return whether a required return is missing. |
|
| 3568 | - | unsafe fn checkExecutableBody( |
|
| 3569 | - | self: &mut Resolver, |
|
| 4574 | + | unsafe fn checkExecutableBody 'arena ( |
|
| 4575 | + | self: &mut Resolver 'arena, |
|
| 3570 | 4576 | fnType: *FnType, |
|
| 3571 | 4577 | receiverName: ?*ast::Node, |
|
| 3572 | 4578 | params: *[*ast::Node], |
|
| 3573 | 4579 | body: *ast::Node, |
|
| 3574 | 4580 | ) -> bool throws (ResolveError) { |
| 3590 | 4596 | try checkLinearFn(self, receiverName, params, body); |
|
| 3591 | 4597 | return false; |
|
| 3592 | 4598 | } |
|
| 3593 | 4599 | ||
| 3594 | 4600 | /// Analyze a function parameter and bind its identifier. |
|
| 3595 | - | unsafe fn resolveFnParam(self: &mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type |
|
| 4601 | + | unsafe fn resolveFnParam 'arena (self: &mut Resolver 'arena, node: *ast::Node, param: ast::FnParam) -> Type |
|
| 3596 | 4602 | throws (ResolveError) |
|
| 3597 | 4603 | { |
|
| 3598 | 4604 | let ty = try resolveValueType(self, param.type); |
|
| 3599 | 4605 | let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0); |
|
| 3600 | 4606 |
| 3608 | 4614 | /// The declaration permits implicit copies. |
|
| 3609 | 4615 | copy: bool, |
|
| 3610 | 4616 | } |
|
| 3611 | 4617 | ||
| 3612 | 4618 | /// Resolve compiler-known ownership markers from a derive list. |
|
| 3613 | - | unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *[*ast::Node]) -> OwnershipMarkers |
|
| 4619 | + | unsafe fn resolveOwnershipMarkers 'arena (self: &mut Resolver 'arena, derives: *[*ast::Node]) -> OwnershipMarkers |
|
| 3614 | 4620 | throws (ResolveError) |
|
| 3615 | 4621 | { |
|
| 3616 | 4622 | let mut result = OwnershipMarkers { linear: false, copy: false }; |
|
| 3617 | 4623 | for derive in derives { |
|
| 4624 | + | if let case ast::NodeValue::Region { .. } = derive.value { |
|
| 4625 | + | continue; |
|
| 4626 | + | } |
|
| 3618 | 4627 | let name = try nodeName(self, derive); |
|
| 3619 | 4628 | if mem::eq(name, "Once") { |
|
| 3620 | 4629 | if result.linear { |
|
| 3621 | 4630 | throw emitError(self, derive, ErrorKind::DuplicateBinding(name)); |
|
| 3622 | 4631 | } |
| 3639 | 4648 | } |
|
| 3640 | 4649 | return result; |
|
| 3641 | 4650 | } |
|
| 3642 | 4651 | ||
| 3643 | 4652 | /// Resolve record fields from a node list. |
|
| 3644 | - | unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType |
|
| 4653 | + | unsafe fn resolveRecordFields 'arena (self: &mut Resolver 'arena, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType |
|
| 3645 | 4654 | throws (ResolveError) |
|
| 3646 | 4655 | { |
|
| 3647 | - | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 4656 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 3648 | 4657 | let mut result: *unsafe mut [RecordField] = &mut []; |
|
| 3649 | 4658 | let mut currentOffset: u32 = 0; |
|
| 3650 | 4659 | let mut maxAlignment: u32 = 1; |
|
| 3651 | 4660 | ||
| 3652 | 4661 | if fields.len > parser::MAX_RECORD_FIELDS { |
|
| 3653 | 4662 | throw emitError(self, node, ErrorKind::Internal); |
|
| 3654 | 4663 | } |
|
| 3655 | - | // TODO: Add cycle detection to catch invalid recursive types like `record A { a: A }`. |
|
| 3656 | 4664 | for field in fields { |
|
| 3657 | 4665 | let case ast::NodeValue::RecordField { |
|
| 3658 | 4666 | field: fieldNode, |
|
| 3659 | 4667 | type: typeNode, |
|
| 3660 | 4668 | value: valueNode |
| 3694 | 4702 | let recordLayout = Layout { |
|
| 3695 | 4703 | size: mem::alignUp(currentOffset, maxAlignment), |
|
| 3696 | 4704 | alignment: maxAlignment |
|
| 3697 | 4705 | }; |
|
| 3698 | 4706 | return RecordType { |
|
| 4707 | + | regions: nil, |
|
| 4708 | + | application: nil, |
|
| 3699 | 4709 | fields: &result[..], |
|
| 3700 | 4710 | labeled, |
|
| 3701 | - | layout: recordLayout, |
|
| 4711 | + | layout: allocLayout(self, recordLayout), |
|
| 3702 | 4712 | declaredLinear: false, |
|
| 3703 | 4713 | declaredCopy: false, |
|
| 3704 | 4714 | }; |
|
| 3705 | 4715 | } |
|
| 3706 | 4716 | ||
| 3707 | 4717 | /// Resolve record field types for a named record declaration. |
|
| 3708 | - | unsafe fn resolveRecordBody(self: &mut Resolver, node: *ast::Node, decl: ast::RecordDecl) |
|
| 4718 | + | unsafe fn resolveRecordBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl) |
|
| 4719 | + | throws (ResolveError) |
|
| 4720 | + | { |
|
| 4721 | + | let previous = self.regionScope; |
|
| 4722 | + | set self.regionScope = try bindRegions(self, node, decl.regions); |
|
| 4723 | + | try resolveRecordContents(self, node, decl) catch error { |
|
| 4724 | + | set self.regionScope = previous; |
|
| 4725 | + | throw error; |
|
| 4726 | + | }; |
|
| 4727 | + | set self.regionScope = previous; |
|
| 4728 | + | } |
|
| 4729 | + | ||
| 4730 | + | /// Resolve record contents in the declaration's region environment. |
|
| 4731 | + | unsafe fn resolveRecordContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl) |
|
| 3709 | 4732 | throws (ResolveError) |
|
| 3710 | 4733 | { |
|
| 3711 | 4734 | // Get the type symbol that was bound to this declaration node. |
|
| 3712 | 4735 | // If there's no symbol, it's because an earlier phase failed. |
|
| 3713 | 4736 | let sym = symbolFor(self, node) |
| 3718 | 4741 | // Skip if already resolved. |
|
| 3719 | 4742 | if let case NominalType::Record(_) = *nominalTy { |
|
| 3720 | 4743 | return; |
|
| 3721 | 4744 | } |
|
| 3722 | 4745 | let markers = try resolveOwnershipMarkers(self, decl.derives); |
|
| 3723 | - | let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled); |
|
| 4746 | + | set *nominalTy = NominalType::Resolving(node); |
|
| 4747 | + | let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled) catch error { |
|
| 4748 | + | set *nominalTy = NominalType::Placeholder(node); |
|
| 4749 | + | throw error; |
|
| 4750 | + | }; |
|
| 4751 | + | set recordType.regions = self.regionScope; |
|
| 3724 | 4752 | if markers.copy { |
|
| 3725 | 4753 | for field in recordType.fields { |
|
| 3726 | 4754 | if not isCopy(field.fieldType) { |
|
| 3727 | 4755 | throw emitError(self, node, ErrorKind::CopyContainsNonCopy); |
|
| 3728 | 4756 | } |
| 3733 | 4761 | ||
| 3734 | 4762 | set *nominalTy = NominalType::Record(recordType); |
|
| 3735 | 4763 | } |
|
| 3736 | 4764 | ||
| 3737 | 4765 | /// Bind a type name. |
|
| 3738 | - | unsafe fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol |
|
| 4766 | + | unsafe fn bindTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol |
|
| 3739 | 4767 | throws (ResolveError) |
|
| 3740 | 4768 | { |
|
| 3741 | - | let attrMask = resolveAttributes(self, attrs); |
|
| 4769 | + | let attrMask = resolveAttributes(attrs); |
|
| 3742 | 4770 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
|
| 3743 | 4771 | ||
| 3744 | 4772 | // Create a placeholder nominal type that will be replaced in |
|
| 3745 | 4773 | // the next phase. |
|
| 3746 | 4774 | let nominalTy = allocNominalType(self, NominalType::Placeholder(node)); |
|
| 3747 | 4775 | ||
| 3748 | 4776 | return try bindTypeIdent(self, name, node, nominalTy, attrMask); |
|
| 3749 | 4777 | } |
|
| 3750 | 4778 | ||
| 3751 | 4779 | /// Allocate a trait type descriptor and return a pointer to it. |
|
| 3752 | - | unsafe fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *unsafe mut TraitType { |
|
| 3753 | - | let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType)); |
|
| 4780 | + | unsafe fn allocTraitType 'arena (self: &mut Resolver 'arena, name: *[u8]) -> *unsafe mut TraitType { |
|
| 4781 | + | let p = try! alloc::allocRaw(self.arena, @sizeOf(TraitType), @alignOf(TraitType)); |
|
| 3754 | 4782 | let entry = p as *unsafe mut TraitType; |
|
| 3755 | - | set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] }; |
|
| 4783 | + | set *entry = TraitType { name, moduleId: self.currentMod, methods: &mut [], supertraits: &mut [] }; |
|
| 3756 | 4784 | ||
| 3757 | 4785 | return entry; |
|
| 3758 | 4786 | } |
|
| 3759 | 4787 | ||
| 3760 | 4788 | /// Bind a trait name in the current scope. |
|
| 3761 | - | unsafe fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol |
|
| 4789 | + | unsafe fn bindTraitName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol |
|
| 3762 | 4790 | throws (ResolveError) |
|
| 3763 | 4791 | { |
|
| 3764 | - | let attrMask = resolveAttributes(self, attrs); |
|
| 4792 | + | let attrMask = resolveAttributes(attrs); |
|
| 3765 | 4793 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
|
| 3766 | 4794 | ||
| 3767 | 4795 | let traitName = try nodeName(self, name); |
|
| 3768 | 4796 | let traitType = allocTraitType(self, traitName); |
|
| 3769 | 4797 | let data = SymbolData::Trait(traitType); |
| 3777 | 4805 | } |
|
| 3778 | 4806 | ||
| 3779 | 4807 | /// Find a trait method by name. |
|
| 3780 | 4808 | export unsafe fn findTraitMethod(traitType: *unsafe TraitType, name: *[u8]) -> ?*unsafe TraitMethod { |
|
| 3781 | 4809 | for i in 0..traitType.methods.len { |
|
| 3782 | - | if traitType.methods[i].name == name { |
|
| 4810 | + | if mem::eq(traitType.methods[i].name, name) { |
|
| 3783 | 4811 | return &traitType.methods[i]; |
|
| 3784 | 4812 | } |
|
| 3785 | 4813 | } |
|
| 3786 | 4814 | return nil; |
|
| 3787 | 4815 | } |
|
| 3788 | 4816 | ||
| 3789 | 4817 | /// Resolve a trait declaration body: supertrait methods, then own methods. |
|
| 3790 | - | unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node]) |
|
| 4818 | + | unsafe fn resolveTraitBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node]) |
|
| 3791 | 4819 | throws (ResolveError) |
|
| 3792 | 4820 | { |
|
| 3793 | 4821 | let sym = symbolFor(self, node) |
|
| 3794 | 4822 | else return; |
|
| 3795 | 4823 | let case SymbolData::Trait(traitType) = sym.data |
| 3812 | 4840 | try resolveTraitBody(self, superSym.node, inheritedTraits, inheritedMethods); |
|
| 3813 | 4841 | } |
|
| 3814 | 4842 | ||
| 3815 | 4843 | setNodeSymbol(self, superNode, superSym); |
|
| 3816 | 4844 | ||
| 3817 | - | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 4845 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 3818 | 4846 | if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS { |
|
| 3819 | 4847 | throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch { |
|
| 3820 | 4848 | expected: ast::MAX_TRAIT_METHODS, |
|
| 3821 | 4849 | actual: traitType.methods.len as u32 + superTrait.methods.len as u32, |
|
| 3822 | 4850 | })); |
| 3843 | 4871 | actual: traitType.methods.len as u32 + methods.len as u32, |
|
| 3844 | 4872 | })); |
|
| 3845 | 4873 | } |
|
| 3846 | 4874 | ||
| 3847 | 4875 | for methodNode in methods { |
|
| 3848 | - | let case ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs } = methodNode.value |
|
| 4876 | + | let case ast::NodeValue::TraitMethodSig { name, modifiers, receiver, sig } = methodNode.value |
|
| 3849 | 4877 | else continue; |
|
| 4878 | + | let attrs = modifiers.attrs; |
|
| 3850 | 4879 | let methodName = try nodeName(self, name); |
|
| 3851 | - | let attrMask = resolveAttributes(self, attrs); |
|
| 4880 | + | let attrMask = resolveAttributes(attrs); |
|
| 4881 | + | let previousRegions = self.regionScope; |
|
| 4882 | + | set self.regionScope = try bindRegions(self, methodNode, modifiers.regions); |
|
| 3852 | 4883 | ||
| 3853 | 4884 | // Reject duplicate method names. |
|
| 3854 | 4885 | if let _ = findTraitMethod(traitType, methodName) { |
|
| 3855 | 4886 | throw emitError(self, name, ErrorKind::DuplicateBinding(methodName)); |
|
| 3856 | 4887 | } |
|
| 3857 | 4888 | // Determine the receiver class and mutability, and validate that it |
|
| 3858 | 4889 | // points to the declaring trait. |
|
| 3859 | 4890 | let case ast::NodeValue::TypeSig(typeSig) = receiver.value |
|
| 3860 | 4891 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 3861 | 4892 | let case ast::TypeSig::Pointer { |
|
| 3862 | - | class: receiverClass, valueType: receiverValueType, mutable, |
|
| 4893 | + | class: receiverSyntax, valueType: receiverValueType, mutable, |
|
| 3863 | 4894 | } = typeSig |
|
| 3864 | 4895 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 4896 | + | let receiverClass = resolvePointerClass(receiverSyntax); |
|
| 3865 | 4897 | let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value |
|
| 3866 | 4898 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 3867 | 4899 | let case ast::TypeSig::Nominal(nameNode) = innerSig |
|
| 3868 | 4900 | else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 3869 | 4901 | let receiverTargetName = try nodeName(self, nameNode); |
|
| 3870 | 4902 | ||
| 3871 | 4903 | if receiverTargetName <> traitType.name { |
|
| 3872 | 4904 | throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch); |
|
| 3873 | 4905 | } |
|
| 3874 | 4906 | // Resolve parameter types and return type. |
|
| 3875 | - | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 4907 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 3876 | 4908 | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 3877 | 4909 | let mut throwList: *mut [*Type] = &mut []; |
|
| 3878 | 4910 | let mut retType = allocType(self, Type::Void); |
|
| 3879 | 4911 | ||
| 3880 | 4912 | if sig.params.len > MAX_FN_PARAMS { |
| 3882 | 4914 | expected: MAX_FN_PARAMS, |
|
| 3883 | 4915 | actual: sig.params.len, |
|
| 3884 | 4916 | })); |
|
| 3885 | 4917 | } |
|
| 3886 | 4918 | for paramNode in sig.params { |
|
| 3887 | - | let paramTy = try infer(self, paramNode); |
|
| 4919 | + | let case ast::NodeValue::FnParam(param) = paramNode.value |
|
| 4920 | + | else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier); |
|
| 4921 | + | let paramTy = try resolveValueType(self, param.type); |
|
| 3888 | 4922 | paramTypes.append(allocType(self, paramTy), a); |
|
| 3889 | 4923 | } |
|
| 3890 | 4924 | if let ret = sig.returnType { |
|
| 3891 | 4925 | set retType = allocType(self, try infer(self, ret)); |
|
| 3892 | 4926 | } |
| 3897 | 4931 | actual: sig.throwList.len, |
|
| 3898 | 4932 | })); |
|
| 3899 | 4933 | } |
|
| 3900 | 4934 | for throwNode in sig.throwList { |
|
| 3901 | 4935 | let throwTy = try infer(self, throwNode); |
|
| 4936 | + | try validateErrorTag(self, throwNode, throwTy, &throwList[..]); |
|
| 3902 | 4937 | throwList.append(allocType(self, throwTy), a); |
|
| 3903 | 4938 | } |
|
| 3904 | 4939 | let fnType = FnType { |
|
| 4940 | + | regions: self.regionScope, |
|
| 3905 | 4941 | paramTypes: ¶mTypes[..], |
|
| 3906 | 4942 | returnType: retType, |
|
| 3907 | 4943 | throwList: &throwList[..], |
|
| 3908 | 4944 | isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe), |
|
| 3909 | 4945 | }; |
| 3914 | 4950 | receiverClass, |
|
| 3915 | 4951 | index: traitType.methods.len as u32, |
|
| 3916 | 4952 | }, a); |
|
| 3917 | 4953 | ||
| 3918 | 4954 | setNodeType(self, methodNode, Type::Void); |
|
| 4955 | + | set self.regionScope = previousRegions; |
|
| 3919 | 4956 | } |
|
| 3920 | 4957 | } |
|
| 3921 | 4958 | ||
| 3922 | 4959 | /// Resolve a name path node to a symbol. |
|
| 3923 | 4960 | /// Used for trait and type references in instance declarations and trait objects. |
|
| 3924 | - | unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *unsafe mut Symbol |
|
| 4961 | + | unsafe fn resolveNamePath 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe mut Symbol |
|
| 3925 | 4962 | throws (ResolveError) |
|
| 3926 | 4963 | { |
|
| 3927 | 4964 | match node.value { |
|
| 3928 | 4965 | case ast::NodeValue::Ident(name) => { |
|
| 3929 | 4966 | let sym = findAnySymbol(self.scope, name) |
| 3938 | 4975 | throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
|
| 3939 | 4976 | } |
|
| 3940 | 4977 | } |
|
| 3941 | 4978 | } |
|
| 3942 | 4979 | ||
| 4980 | + | /// Join instance and method region parameters into one function binder. |
|
| 4981 | + | unsafe fn instanceMethodRegions 'arena ( |
|
| 4982 | + | self: &mut Resolver 'arena, instanceRegions: *[*ast::Node], methodRegions: *[*ast::Node] |
|
| 4983 | + | ) -> *[*ast::Node] { |
|
| 4984 | + | let count = instanceRegions.len + methodRegions.len; |
|
| 4985 | + | if count == 0 { |
|
| 4986 | + | return &[]; |
|
| 4987 | + | } |
|
| 4988 | + | let allocator = alloc::arenaAllocator(self.arena); |
|
| 4989 | + | let mut nodes: *mut [*ast::Node] = &mut []; |
|
| 4990 | + | for region in instanceRegions { |
|
| 4991 | + | nodes.append(region, allocator); |
|
| 4992 | + | } |
|
| 4993 | + | for region in methodRegions { |
|
| 4994 | + | nodes.append(region, allocator); |
|
| 4995 | + | } |
|
| 4996 | + | return &nodes[..]; |
|
| 4997 | + | } |
|
| 4998 | + | ||
| 4999 | + | /// Map one region binder to a contiguous part of another binder. |
|
| 5000 | + | unsafe fn mapRegionScopes 'arena ( |
|
| 5001 | + | self: &mut Resolver 'arena, source: ?*RegionScope, target: ?*RegionScope, |
|
| 5002 | + | offset: u32, site: *ast::Node |
|
| 5003 | + | ) -> ?RegionSubstitution throws (ResolveError) { |
|
| 5004 | + | let sourceScope = source else return nil; |
|
| 5005 | + | let targetScope = target else throw emitError(self, site, ErrorKind::Internal); |
|
| 5006 | + | if offset + sourceScope.entries.len > targetScope.entries.len { |
|
| 5007 | + | throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { |
|
| 5008 | + | expected: sourceScope.entries.len, |
|
| 5009 | + | actual: targetScope.entries.len - offset, |
|
| 5010 | + | })); |
|
| 5011 | + | } |
|
| 5012 | + | let map = regionSubstitution(self, sourceScope); |
|
| 5013 | + | for _, i in sourceScope.entries { |
|
| 5014 | + | set map.arguments[i] = targetScope.entries[offset + i]; |
|
| 5015 | + | } |
|
| 5016 | + | try validateRegionArguments(self, &map, site); |
|
| 5017 | + | return map; |
|
| 5018 | + | } |
|
| 5019 | + | ||
| 5020 | + | /// Resolved implementation of one trait method. |
|
| 5021 | + | record ResolvedInstanceMethod: Copy { |
|
| 5022 | + | /// Canonical trait method. |
|
| 5023 | + | method: *unsafe TraitMethod, |
|
| 5024 | + | /// Concrete function symbol. |
|
| 5025 | + | symbol: *unsafe mut Symbol, |
|
| 5026 | + | } |
|
| 5027 | + | ||
| 5028 | + | /// Shared declaration state for instance method resolution. |
|
| 5029 | + | record InstanceMethodContext: Copy { |
|
| 5030 | + | /// Implemented trait. |
|
| 5031 | + | traitInfo: *unsafe TraitType, |
|
| 5032 | + | /// Instance target with declaration regions applied. |
|
| 5033 | + | concreteType: Type, |
|
| 5034 | + | /// Instance region nodes in declaration order. |
|
| 5035 | + | regions: *[*ast::Node], |
|
| 5036 | + | /// Bound instance region scope. |
|
| 5037 | + | scope: ?*RegionScope, |
|
| 5038 | + | } |
|
| 5039 | + | ||
| 5040 | + | /// Resolve one instance method in its combined region environment. |
|
| 5041 | + | unsafe fn resolveInstanceMethod 'arena ( |
|
| 5042 | + | self: &mut Resolver 'arena, methodNode: *ast::Node, |
|
| 5043 | + | context: &InstanceMethodContext |
|
| 5044 | + | ) -> ResolvedInstanceMethod throws (ResolveError) { |
|
| 5045 | + | let case ast::NodeValue::MethodDecl { |
|
| 5046 | + | name, modifiers, receiverType, sig, .. |
|
| 5047 | + | } = methodNode.value else panic "resolveInstanceMethod: invalid method"; |
|
| 5048 | + | let combinedRegions = instanceMethodRegions(self, context.regions, modifiers.regions); |
|
| 5049 | + | let methodScope = try bindRegions(self, methodNode, combinedRegions); |
|
| 5050 | + | set self.regionScope = methodScope; |
|
| 5051 | + | ||
| 5052 | + | let methodName = try nodeName(self, name); |
|
| 5053 | + | let attrMask = resolveAttributes(modifiers.attrs); |
|
| 5054 | + | let tm = findTraitMethod(context.traitInfo, methodName) |
|
| 5055 | + | else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName)); |
|
| 5056 | + | if ast::hasAttribute(attrMask, ast::Attribute::Unsafe) <> tm.fnType.isUnsafe { |
|
| 5057 | + | throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch); |
|
| 5058 | + | } |
|
| 5059 | + | ||
| 5060 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { |
|
| 5061 | + | class: receiverSyntax, valueType, mutable: receiverMut, |
|
| 5062 | + | }) = receiverType.value else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 5063 | + | let receiverClass = resolvePointerClass(receiverSyntax); |
|
| 5064 | + | if receiverClass <> tm.receiverClass { |
|
| 5065 | + | throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 5066 | + | } |
|
| 5067 | + | let annotatedTy = try infer(self, valueType); |
|
| 5068 | + | let mut expectedConcrete = context.concreteType; |
|
| 5069 | + | if let map = try mapRegionScopes(self, context.scope, methodScope, 0, methodNode) { |
|
| 5070 | + | set expectedConcrete = substituteRegions(self, &map, context.concreteType); |
|
| 5071 | + | } |
|
| 5072 | + | if not typesEqual(annotatedTy, expectedConcrete) { |
|
| 5073 | + | throw emitTypeMismatch(self, receiverType, TypeMismatch { expected: expectedConcrete, actual: annotatedTy }); |
|
| 5074 | + | } |
|
| 5075 | + | if tm.mutable and not receiverMut { |
|
| 5076 | + | throw emitError(self, receiverType, ErrorKind::ImmutableBinding); |
|
| 5077 | + | } |
|
| 5078 | + | if receiverMut and not tm.mutable { |
|
| 5079 | + | throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch); |
|
| 5080 | + | } |
|
| 5081 | + | ||
| 5082 | + | let mut traitFn = tm.fnType; |
|
| 5083 | + | let mut traitRegionCount: u32 = 0; |
|
| 5084 | + | if let traitScope = tm.fnType.regions { |
|
| 5085 | + | set traitRegionCount = traitScope.entries.len; |
|
| 5086 | + | } |
|
| 5087 | + | if traitRegionCount <> modifiers.regions.len { |
|
| 5088 | + | throw emitError(self, methodNode, ErrorKind::RegionArgumentCount(CountMismatch { |
|
| 5089 | + | expected: traitRegionCount, actual: modifiers.regions.len, |
|
| 5090 | + | })); |
|
| 5091 | + | } |
|
| 5092 | + | if let map = try mapRegionScopes(self, tm.fnType.regions, methodScope, context.regions.len, methodNode) { |
|
| 5093 | + | set traitFn = substituteFnRegions(self, &map, tm.fnType, nil); |
|
| 5094 | + | } |
|
| 5095 | + | if sig.params.len <> traitFn.paramTypes.len { |
|
| 5096 | + | throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 5097 | + | expected: traitFn.paramTypes.len, actual: sig.params.len, |
|
| 5098 | + | })); |
|
| 5099 | + | } |
|
| 5100 | + | ||
| 5101 | + | let allocator = alloc::arenaAllocator(self.arena); |
|
| 5102 | + | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 5103 | + | let receiverPtrType = Type::Pointer { |
|
| 5104 | + | class: receiverClass, target: allocType(self, annotatedTy), mutable: receiverMut, |
|
| 5105 | + | }; |
|
| 5106 | + | paramTypes.append(allocType(self, receiverPtrType), allocator); |
|
| 5107 | + | for paramNode, i in sig.params { |
|
| 5108 | + | let case ast::NodeValue::FnParam(param) = paramNode.value |
|
| 5109 | + | else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier); |
|
| 5110 | + | let instanceParamTy = try resolveValueType(self, param.type); |
|
| 5111 | + | if not typesEqual(instanceParamTy, *traitFn.paramTypes[i]) { |
|
| 5112 | + | throw emitTypeMismatch(self, paramNode, TypeMismatch { |
|
| 5113 | + | expected: *traitFn.paramTypes[i], actual: instanceParamTy, |
|
| 5114 | + | }); |
|
| 5115 | + | } |
|
| 5116 | + | paramTypes.append(allocType(self, instanceParamTy), allocator); |
|
| 5117 | + | } |
|
| 5118 | + | let mut returnType = Type::Void; |
|
| 5119 | + | if let returnNode = sig.returnType { |
|
| 5120 | + | set returnType = try resolveValueType(self, returnNode); |
|
| 5121 | + | } |
|
| 5122 | + | if not typesEqual(returnType, *traitFn.returnType) { |
|
| 5123 | + | throw emitTypeMismatch(self, methodNode, TypeMismatch { |
|
| 5124 | + | expected: *traitFn.returnType, actual: returnType, |
|
| 5125 | + | }); |
|
| 5126 | + | } |
|
| 5127 | + | if sig.throwList.len <> traitFn.throwList.len { |
|
| 5128 | + | throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch { |
|
| 5129 | + | expected: traitFn.throwList.len, actual: sig.throwList.len, |
|
| 5130 | + | })); |
|
| 5131 | + | } |
|
| 5132 | + | let mut throwList: *mut [*Type] = &mut []; |
|
| 5133 | + | for throwNode, i in sig.throwList { |
|
| 5134 | + | let throwType = try resolveValueType(self, throwNode); |
|
| 5135 | + | if not typesEqual(throwType, *traitFn.throwList[i]) { |
|
| 5136 | + | throw emitTypeMismatch(self, throwNode, TypeMismatch { |
|
| 5137 | + | expected: *traitFn.throwList[i], actual: throwType, |
|
| 5138 | + | }); |
|
| 5139 | + | } |
|
| 5140 | + | throwList.append(allocType(self, throwType), allocator); |
|
| 5141 | + | } |
|
| 5142 | + | ||
| 5143 | + | let fnType = FnType { |
|
| 5144 | + | regions: methodScope, paramTypes: ¶mTypes[..], |
|
| 5145 | + | returnType: allocType(self, returnType), throwList: &throwList[..], |
|
| 5146 | + | isUnsafe: tm.fnType.isUnsafe, |
|
| 5147 | + | }; |
|
| 5148 | + | let fnTy = Type::Fn(allocFnType(self, fnType)); |
|
| 5149 | + | let sym = allocSymbol(self, SymbolData::Value { |
|
| 5150 | + | mutable: false, alignment: 0, type: fnTy, addressTaken: false, |
|
| 5151 | + | }, methodName, methodNode, attrMask); |
|
| 5152 | + | setNodeSymbol(self, methodNode, sym); |
|
| 5153 | + | setNodeType(self, methodNode, fnTy); |
|
| 5154 | + | setNodeType(self, name, fnTy); |
|
| 5155 | + | set self.regionScope = context.scope; |
|
| 5156 | + | return ResolvedInstanceMethod { method: tm, symbol: sym }; |
|
| 5157 | + | } |
|
| 5158 | + | ||
| 3943 | 5159 | /// Resolve an instance declaration. |
|
| 3944 | 5160 | /// Validates that the trait exists, the target type exists, and all methods |
|
| 3945 | 5161 | /// match the trait's signatures. |
|
| 3946 | - | unsafe fn resolveInstanceDecl( |
|
| 3947 | - | self: &mut Resolver, |
|
| 5162 | + | unsafe fn resolveInstanceDecl 'arena ( |
|
| 5163 | + | self: &mut Resolver 'arena, |
|
| 5164 | + | node: *ast::Node, |
|
| 5165 | + | traitName: *ast::Node, |
|
| 5166 | + | targetType: *ast::Node, |
|
| 5167 | + | regions: *[*ast::Node], |
|
| 5168 | + | methods: *[*ast::Node] |
|
| 5169 | + | ) throws (ResolveError) { |
|
| 5170 | + | let previous = self.regionScope; |
|
| 5171 | + | set self.regionScope = try bindRegions(self, node, regions); |
|
| 5172 | + | try resolveInstanceContents(self, node, traitName, targetType, regions, methods) catch error { |
|
| 5173 | + | set self.regionScope = previous; |
|
| 5174 | + | throw error; |
|
| 5175 | + | }; |
|
| 5176 | + | set self.regionScope = previous; |
|
| 5177 | + | } |
|
| 5178 | + | ||
| 5179 | + | /// Resolve an instance in its declaration region environment. |
|
| 5180 | + | unsafe fn resolveInstanceContents 'arena ( |
|
| 5181 | + | self: &mut Resolver 'arena, |
|
| 3948 | 5182 | node: *ast::Node, |
|
| 3949 | 5183 | traitName: *ast::Node, |
|
| 3950 | 5184 | targetType: *ast::Node, |
|
| 5185 | + | regions: *[*ast::Node], |
|
| 3951 | 5186 | methods: *[*ast::Node] |
|
| 3952 | 5187 | ) throws (ResolveError) { |
|
| 5188 | + | let instanceScope = self.regionScope; |
|
| 3953 | 5189 | // Look up the trait. |
|
| 3954 | 5190 | let traitSym = try resolveNamePath(self, traitName); |
|
| 3955 | 5191 | let case SymbolData::Trait(traitInfo) = traitSym.data |
|
| 3956 | 5192 | else throw emitError(self, traitName, ErrorKind::Internal); |
|
| 3957 | 5193 |
| 3964 | 5200 | setNodeSymbol(self, targetType, typeSym); |
|
| 3965 | 5201 | // Ensure the concrete type body is resolved. |
|
| 3966 | 5202 | try ensureNominalResolved(self, nominalTy, targetType); |
|
| 3967 | 5203 | ||
| 3968 | 5204 | // Reject duplicate instance for the same (trait, type) pair. |
|
| 3969 | - | let concreteType = Type::Nominal(nominalTy); |
|
| 5205 | + | let mut concreteInfo = nominalTy; |
|
| 5206 | + | if regions.len > 0 { |
|
| 5207 | + | set concreteInfo = try applyNominalRegions(self, nominalTy, regions, targetType); |
|
| 5208 | + | } else { |
|
| 5209 | + | try requireNominalArguments(self, nominalTy, targetType); |
|
| 5210 | + | } |
|
| 5211 | + | let concreteType = Type::Nominal(concreteInfo); |
|
| 3970 | 5212 | if let _ = findInstance(self, traitInfo, concreteType) { |
|
| 3971 | 5213 | throw emitError(self, node, ErrorKind::DuplicateInstance); |
|
| 3972 | 5214 | } |
|
| 3973 | 5215 | ||
| 3974 | 5216 | // Build the instance entry. |
|
| 3975 | 5217 | if self.instancesLen >= MAX_INSTANCES { |
|
| 3976 | 5218 | throw emitError(self, node, ErrorKind::Internal); |
|
| 3977 | 5219 | } |
|
| 3978 | 5220 | let methodSlice = try! alloc::allocRawSlice( |
|
| 3979 | - | &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32 |
|
| 5221 | + | self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32 |
|
| 3980 | 5222 | ) as *unsafe mut [*unsafe mut Symbol]; |
|
| 3981 | 5223 | let mut entry = InstanceEntry { |
|
| 3982 | 5224 | traitType: traitInfo, |
|
| 3983 | 5225 | concreteType, |
|
| 3984 | 5226 | concreteTypeName: typeSym.name, |
|
| 3985 | 5227 | moduleId: self.currentMod, |
|
| 3986 | 5228 | methods: methodSlice, |
|
| 3987 | 5229 | }; |
|
| 3988 | 5230 | // Track which trait methods are covered by the instance. |
|
| 3989 | 5231 | let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS]; |
|
| 5232 | + | let methodContext = InstanceMethodContext { |
|
| 5233 | + | traitInfo, concreteType, regions, scope: instanceScope, |
|
| 5234 | + | }; |
|
| 3990 | 5235 | ||
| 3991 | 5236 | // Match each instance method to a trait method. |
|
| 3992 | 5237 | for methodNode in methods { |
|
| 3993 | - | let case ast::NodeValue::MethodDecl { |
|
| 3994 | - | name, receiverName, receiverType, sig, body, attrs, |
|
| 3995 | - | } = methodNode.value else continue; |
|
| 3996 | - | ||
| 3997 | - | let methodName = try nodeName(self, name); |
|
| 3998 | - | let attrMask = resolveAttributes(self, attrs); |
|
| 3999 | - | ||
| 4000 | - | // Find the matching trait method. |
|
| 4001 | - | let tm = findTraitMethod(traitInfo, methodName) |
|
| 4002 | - | else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName)); |
|
| 4003 | - | let instanceUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe); |
|
| 4004 | - | if instanceUnsafe <> tm.fnType.isUnsafe { |
|
| 4005 | - | throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch); |
|
| 4006 | - | } |
|
| 4007 | - | ||
| 4008 | - | // Determine receiver mutability and validate receiver type. |
|
| 4009 | - | // The receiver must be `*Type` or `*mut Type`. |
|
| 4010 | - | let case ast::NodeValue::TypeSig(typeSig) = receiverType.value |
|
| 4011 | - | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 4012 | - | let case ast::TypeSig::Pointer { |
|
| 4013 | - | class: receiverClass, valueType, mutable: receiverMut, |
|
| 4014 | - | } = typeSig |
|
| 4015 | - | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 4016 | - | if receiverClass <> tm.receiverClass { |
|
| 4017 | - | throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 4018 | - | } |
|
| 4019 | - | ||
| 4020 | - | // Validate that the receiver type annotation matches the |
|
| 4021 | - | // concrete type from the instance declaration. |
|
| 4022 | - | let annotatedTy = try infer(self, valueType); |
|
| 4023 | - | if not typesEqual(annotatedTy, concreteType) { |
|
| 4024 | - | throw emitTypeMismatch(self, receiverType, TypeMismatch { |
|
| 4025 | - | expected: concreteType, |
|
| 4026 | - | actual: annotatedTy, |
|
| 4027 | - | }); |
|
| 4028 | - | } |
|
| 4029 | - | ||
| 4030 | - | // Check receiver mutability matches in both directions. |
|
| 4031 | - | if tm.mutable and not receiverMut { |
|
| 4032 | - | throw emitError(self, receiverType, ErrorKind::ImmutableBinding); |
|
| 4033 | - | } |
|
| 4034 | - | if receiverMut and not tm.mutable { |
|
| 4035 | - | throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch); |
|
| 4036 | - | } |
|
| 4037 | - | ||
| 4038 | - | // Build the function type for the instance method. |
|
| 4039 | - | // The receiver becomes the first parameter. |
|
| 4040 | - | let receiverPtrType = Type::Pointer { |
|
| 4041 | - | class: receiverClass, |
|
| 4042 | - | target: allocType(self, concreteType), |
|
| 4043 | - | mutable: receiverMut, |
|
| 4044 | - | }; |
|
| 4045 | - | ||
| 4046 | - | // Validate that the instance method's signature matches the |
|
| 4047 | - | // trait method's signature exactly (params, return type, throws). |
|
| 4048 | - | if sig.params.len <> tm.fnType.paramTypes.len { |
|
| 4049 | - | throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 4050 | - | expected: tm.fnType.paramTypes.len as u32, |
|
| 4051 | - | actual: sig.params.len, |
|
| 4052 | - | })); |
|
| 4053 | - | } |
|
| 4054 | - | for paramNode, j in sig.params { |
|
| 4055 | - | let case ast::NodeValue::FnParam(param) = paramNode.value |
|
| 4056 | - | else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier); |
|
| 4057 | - | let instanceParamTy = try resolveValueType(self, param.type); |
|
| 4058 | - | if not typesEqual(instanceParamTy, *tm.fnType.paramTypes[j]) { |
|
| 4059 | - | throw emitTypeMismatch(self, paramNode, TypeMismatch { |
|
| 4060 | - | expected: *tm.fnType.paramTypes[j], |
|
| 4061 | - | actual: instanceParamTy, |
|
| 4062 | - | }); |
|
| 4063 | - | } |
|
| 4064 | - | } |
|
| 4065 | - | let mut instanceRetTy = Type::Void; |
|
| 4066 | - | if let retNode = sig.returnType { |
|
| 4067 | - | set instanceRetTy = try resolveValueType(self, retNode); |
|
| 4068 | - | } |
|
| 4069 | - | if not typesEqual(instanceRetTy, *tm.fnType.returnType) { |
|
| 4070 | - | throw emitTypeMismatch(self, methodNode, TypeMismatch { |
|
| 4071 | - | expected: *tm.fnType.returnType, |
|
| 4072 | - | actual: instanceRetTy, |
|
| 4073 | - | }); |
|
| 4074 | - | } |
|
| 4075 | - | if sig.throwList.len <> tm.fnType.throwList.len { |
|
| 4076 | - | throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch { |
|
| 4077 | - | expected: tm.fnType.throwList.len as u32, |
|
| 4078 | - | actual: sig.throwList.len, |
|
| 4079 | - | })); |
|
| 4080 | - | } |
|
| 4081 | - | for throwNode, j in sig.throwList { |
|
| 4082 | - | let instanceThrowTy = try resolveValueType(self, throwNode); |
|
| 4083 | - | if not typesEqual(instanceThrowTy, *tm.fnType.throwList[j]) { |
|
| 4084 | - | throw emitTypeMismatch(self, throwNode, TypeMismatch { |
|
| 4085 | - | expected: *tm.fnType.throwList[j], |
|
| 4086 | - | actual: instanceThrowTy, |
|
| 4087 | - | }); |
|
| 4088 | - | } |
|
| 4089 | - | } |
|
| 4090 | - | ||
| 4091 | - | // Build final function type: receiver plus trait's canonical types. |
|
| 4092 | - | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 4093 | - | // TODO: Improve this pattern, maybe via something like `(&[]).append(..)`? |
|
| 4094 | - | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 4095 | - | paramTypes.append(allocType(self, receiverPtrType), a); |
|
| 4096 | - | ||
| 4097 | - | for ty in tm.fnType.paramTypes { |
|
| 4098 | - | paramTypes.append(ty, a); |
|
| 4099 | - | } |
|
| 4100 | - | let fnType = FnType { |
|
| 4101 | - | paramTypes: ¶mTypes[..], |
|
| 4102 | - | returnType: tm.fnType.returnType, |
|
| 4103 | - | throwList: tm.fnType.throwList, |
|
| 4104 | - | isUnsafe: tm.fnType.isUnsafe, |
|
| 4105 | - | }; |
|
| 4106 | - | ||
| 4107 | - | // Create a symbol for the instance method without binding it into the |
|
| 4108 | - | // module scope. Instance methods are dispatched via v-table, so they |
|
| 4109 | - | // must not pollute the enclosing scope. |
|
| 4110 | - | let fnTy = Type::Fn(allocFnType(self, fnType)); |
|
| 4111 | - | let mName = try nodeName(self, name); |
|
| 4112 | - | let sym = allocSymbol(self, SymbolData::Value { |
|
| 4113 | - | mutable: false, alignment: 0, type: fnTy, addressTaken: false, |
|
| 4114 | - | }, mName, methodNode, attrMask); |
|
| 4115 | - | ||
| 4116 | - | setNodeSymbol(self, methodNode, sym); |
|
| 4117 | - | setNodeType(self, methodNode, fnTy); |
|
| 4118 | - | setNodeType(self, name, fnTy); |
|
| 4119 | - | ||
| 4120 | - | // Store in instance entry at the matching v-table slot. |
|
| 4121 | - | set entry.methods[tm.index] = sym; |
|
| 4122 | - | set covered[tm.index] = true; |
|
| 5238 | + | let resolved = try resolveInstanceMethod( |
|
| 5239 | + | self, methodNode, &methodContext |
|
| 5240 | + | ); |
|
| 5241 | + | set entry.methods[resolved.method.index] = resolved.symbol; |
|
| 5242 | + | set covered[resolved.method.index] = true; |
|
| 4123 | 5243 | } |
|
| 4124 | 5244 | ||
| 4125 | 5245 | // Fill inherited method slots from supertrait instances. |
|
| 4126 | 5246 | for superTrait in traitInfo.supertraits { |
|
| 4127 | 5247 | let superInst = findInstance(self, superTrait, concreteType) |
| 4147 | 5267 | ||
| 4148 | 5268 | setNodeType(self, node, Type::Void); |
|
| 4149 | 5269 | } |
|
| 4150 | 5270 | ||
| 4151 | 5271 | /// Resolve instance method bodies. |
|
| 4152 | - | unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *[*ast::Node]) |
|
| 5272 | + | unsafe fn resolveInstanceMethodBodies 'arena (self: &mut Resolver 'arena, methods: *[*ast::Node]) |
|
| 4153 | 5273 | throws (ResolveError) |
|
| 4154 | 5274 | { |
|
| 4155 | 5275 | for methodNode in methods { |
|
| 4156 | - | let case ast::NodeValue::MethodDecl { |
|
| 4157 | - | name, receiverName, receiverType, sig, body, .. |
|
| 4158 | - | } = methodNode.value else continue; |
|
| 5276 | + | let case ast::NodeValue::MethodDecl { .. } = methodNode.value else continue; |
|
| 4159 | 5277 | ||
| 4160 | 5278 | // Symbol may be absent if [`resolveInstanceDecl`] reported an error |
|
| 4161 | 5279 | // for this method (eg. unknown method name). Skip gracefully. |
|
| 4162 | - | let sym = symbolFor(self, methodNode) |
|
| 4163 | - | else continue; |
|
| 5280 | + | if symbolFor(self, methodNode) == nil { |
|
| 5281 | + | continue; |
|
| 5282 | + | } |
|
| 4164 | 5283 | ||
| 4165 | - | try resolveMethodBody(self, methodNode, receiverName, sig, body); |
|
| 5284 | + | try resolveMethodBody(self, methodNode); |
|
| 4166 | 5285 | } |
|
| 4167 | 5286 | } |
|
| 4168 | 5287 | ||
| 4169 | 5288 | /// Resolve a method body shared by instance methods and standalone methods. |
|
| 4170 | 5289 | /// Binds the receiver and parameters, then type-checks the body. |
|
| 4171 | - | unsafe fn resolveMethodBody( |
|
| 4172 | - | self: &mut Resolver, |
|
| 5290 | + | unsafe fn resolveMethodBody 'arena ( |
|
| 5291 | + | self: &mut Resolver 'arena, |
|
| 4173 | 5292 | node: *ast::Node, |
|
| 4174 | - | receiverName: *ast::Node, |
|
| 4175 | - | sig: ast::FnSig, |
|
| 4176 | - | body: *ast::Node, |
|
| 4177 | 5293 | ) throws (ResolveError) { |
|
| 5294 | + | let case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } = node.value |
|
| 5295 | + | else panic "resolveMethodBody: invalid method"; |
|
| 4178 | 5296 | let sym = symbolFor(self, node) |
|
| 4179 | 5297 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 4180 | 5298 | let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data |
|
| 4181 | 5299 | else panic "resolveMethodBody: expected value symbol"; |
|
| 4182 | - | try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body); |
|
| 5300 | + | let previous = self.regionScope; |
|
| 5301 | + | set self.regionScope = fnType.regions; |
|
| 5302 | + | try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body) catch error { |
|
| 5303 | + | set self.regionScope = previous; |
|
| 5304 | + | throw error; |
|
| 5305 | + | }; |
|
| 5306 | + | set self.regionScope = previous; |
|
| 4183 | 5307 | } |
|
| 4184 | 5308 | ||
| 4185 | 5309 | /// Resolve a standalone method declaration (signature only). |
|
| 4186 | 5310 | /// Validates the receiver type and registers the method in the method table. |
|
| 4187 | 5311 | ||
| 4188 | 5312 | /// Extract the type name from a resolved receiver type node. |
|
| 4189 | - | unsafe fn receiverTypeName( |
|
| 4190 | - | self: &mut Resolver, |
|
| 5313 | + | unsafe fn receiverTypeName 'arena ( |
|
| 5314 | + | self: &mut Resolver 'arena, |
|
| 4191 | 5315 | receiverType: *ast::Node, |
|
| 4192 | 5316 | ) -> *[u8] throws (ResolveError) { |
|
| 4193 | 5317 | let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) = |
|
| 4194 | 5318 | receiverType.value |
|
| 4195 | 5319 | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 4196 | - | let case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(nameNode)) = valueType.value |
|
| 4197 | - | else throw emitError(self, receiverType, ErrorKind::Internal); |
|
| 5320 | + | let mut nameNode: *ast::Node = valueType; |
|
| 5321 | + | match valueType.value { |
|
| 5322 | + | case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(name)) => set nameNode = name, |
|
| 5323 | + | case ast::NodeValue::TypeSig(ast::TypeSig::Applied { name, .. }) => set nameNode = name, |
|
| 5324 | + | else => throw emitError(self, receiverType, ErrorKind::Internal), |
|
| 5325 | + | } |
|
| 4198 | 5326 | let sym = symbolFor(self, nameNode) |
|
| 4199 | 5327 | else throw emitError(self, receiverType, ErrorKind::Internal); |
|
| 4200 | 5328 | ||
| 4201 | 5329 | return sym.name; |
|
| 4202 | 5330 | } |
|
| 4203 | 5331 | ||
| 4204 | 5332 | /// Resolve and register a standalone method declaration. |
|
| 4205 | - | unsafe fn resolveMethodDecl( |
|
| 4206 | - | self: &mut Resolver, |
|
| 5333 | + | unsafe fn resolveMethodDecl 'arena ( |
|
| 5334 | + | self: &mut Resolver 'arena, |
|
| 5335 | + | node: *ast::Node, |
|
| 5336 | + | ) throws (ResolveError) { |
|
| 5337 | + | let case ast::NodeValue::MethodDecl { modifiers, .. } = node.value |
|
| 5338 | + | else panic "resolveMethodDecl: invalid method"; |
|
| 5339 | + | let previous = self.regionScope; |
|
| 5340 | + | set self.regionScope = try bindRegions(self, node, modifiers.regions); |
|
| 5341 | + | try resolveMethodSignature(self, node) catch error { |
|
| 5342 | + | set self.regionScope = previous; |
|
| 5343 | + | throw error; |
|
| 5344 | + | }; |
|
| 5345 | + | set self.regionScope = previous; |
|
| 5346 | + | } |
|
| 5347 | + | ||
| 5348 | + | /// Resolve a standalone method signature in its region environment. |
|
| 5349 | + | unsafe fn resolveMethodSignature 'arena ( |
|
| 5350 | + | self: &mut Resolver 'arena, |
|
| 4207 | 5351 | node: *ast::Node, |
|
| 4208 | - | name: *ast::Node, |
|
| 4209 | - | receiverName: *ast::Node, |
|
| 4210 | - | receiverType: *ast::Node, |
|
| 4211 | - | sig: ast::FnSig, |
|
| 4212 | - | attrs: ?ast::Attributes, |
|
| 4213 | 5352 | ) throws (ResolveError) { |
|
| 5353 | + | let case ast::NodeValue::MethodDecl { |
|
| 5354 | + | name, modifiers, receiverType, sig, .. |
|
| 5355 | + | } = node.value else panic "resolveMethodSignature: invalid method"; |
|
| 4214 | 5356 | // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a |
|
| 4215 | 5357 | // nominal type. |
|
| 4216 | 5358 | let fullReceiverTy = try infer(self, receiverType); |
|
| 4217 | 5359 | let case Type::Pointer { |
|
| 4218 | 5360 | class: receiverClass, target: receiverTarget, mutable: receiverMut, |
| 4224 | 5366 | try ensureNominalResolved(self, nominalTy, receiverType); |
|
| 4225 | 5367 | ||
| 4226 | 5368 | // Get the type name from the inner type node's symbol. |
|
| 4227 | 5369 | let typeName = try receiverTypeName(self, receiverType); |
|
| 4228 | 5370 | let methodName = try nodeName(self, name); |
|
| 4229 | - | let attrMask = resolveAttributes(self, attrs); |
|
| 5371 | + | let attrMask = resolveAttributes(modifiers.attrs); |
|
| 4230 | 5372 | ||
| 4231 | 5373 | // Reject duplicate method for the same (type, name). |
|
| 4232 | 5374 | if let _ = findMethod(self, concreteType, methodName) { |
|
| 4233 | 5375 | throw emitError(self, name, ErrorKind::DuplicateBinding(methodName)); |
|
| 4234 | 5376 | } |
|
| 4235 | 5377 | ||
| 4236 | 5378 | // Resolve parameter types. |
|
| 4237 | - | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 5379 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 4238 | 5380 | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 4239 | 5381 | ||
| 4240 | 5382 | // Receiver is the first parameter. |
|
| 4241 | 5383 | let receiverPtrType = Type::Pointer { |
|
| 4242 | 5384 | class: receiverClass, |
| 4260 | 5402 | ||
| 4261 | 5403 | // Resolve throw list. |
|
| 4262 | 5404 | let mut throwTypes: *mut [*Type] = &mut []; |
|
| 4263 | 5405 | for throwNode in sig.throwList { |
|
| 4264 | 5406 | let throwTy = try resolveValueType(self, throwNode); |
|
| 5407 | + | try validateErrorTag(self, throwNode, throwTy, &throwTypes[..]); |
|
| 4265 | 5408 | throwTypes.append(allocType(self, throwTy), a); |
|
| 4266 | 5409 | } |
|
| 4267 | 5410 | ||
| 4268 | 5411 | let retTypePtr = allocType(self, returnType); |
|
| 4269 | 5412 | let throwList = &throwTypes[..]; |
|
| 4270 | 5413 | ||
| 4271 | 5414 | let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe); |
|
| 4272 | 5415 | // Full function type (receiver + params) for lowering. |
|
| 4273 | 5416 | let fullFnType = FnType { |
|
| 5417 | + | regions: self.regionScope, |
|
| 4274 | 5418 | paramTypes: ¶mTypes[..], |
|
| 4275 | 5419 | returnType: retTypePtr, |
|
| 4276 | 5420 | throwList, |
|
| 4277 | 5421 | isUnsafe, |
|
| 4278 | 5422 | }; |
|
| 4279 | 5423 | let fnTy = Type::Fn(allocFnType(self, fullFnType)); |
|
| 4280 | 5424 | ||
| 4281 | 5425 | // Function type excluding receiver, for call arg checking. |
|
| 4282 | 5426 | let checkFnType = FnType { |
|
| 5427 | + | regions: self.regionScope, |
|
| 4283 | 5428 | paramTypes: ¶mTypes[1..], |
|
| 4284 | 5429 | returnType: retTypePtr, |
|
| 4285 | 5430 | throwList, |
|
| 4286 | 5431 | isUnsafe, |
|
| 4287 | 5432 | }; |
| 4310 | 5455 | }; |
|
| 4311 | 5456 | set self.methodsLen += 1; |
|
| 4312 | 5457 | } |
|
| 4313 | 5458 | ||
| 4314 | 5459 | /// Look up an instance entry by trait and concrete type. |
|
| 4315 | - | unsafe fn findInstance(self: &Resolver, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry { |
|
| 5460 | + | unsafe fn findInstance 'arena (self: &Resolver 'arena, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry { |
|
| 4316 | 5461 | for i in 0..self.instancesLen { |
|
| 4317 | 5462 | let entry: *unsafe InstanceEntry = &self.instances[i]; |
|
| 4318 | - | if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) { |
|
| 5463 | + | if entry.traitType == traitInfo and erasedTypesEqual(entry.concreteType, concreteType) { |
|
| 4319 | 5464 | return entry; |
|
| 4320 | 5465 | } |
|
| 4321 | 5466 | } |
|
| 4322 | 5467 | return nil; |
|
| 4323 | 5468 | } |
|
| 4324 | 5469 | ||
| 4325 | 5470 | /// Look up a standalone method by concrete type and name. |
|
| 4326 | - | export unsafe fn findMethod(self: &Resolver, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry { |
|
| 5471 | + | export unsafe fn findMethod 'arena (self: &Resolver 'arena, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry { |
|
| 4327 | 5472 | for i in 0..self.methodsLen { |
|
| 4328 | 5473 | let entry: *unsafe MethodEntry = &self.methods[i]; |
|
| 4329 | - | if typesEqual(entry.concreteType, concreteType) and entry.name == name { |
|
| 5474 | + | if erasedTypesEqual(entry.concreteType, concreteType) and entry.name == name { |
|
| 4330 | 5475 | return entry; |
|
| 4331 | 5476 | } |
|
| 4332 | 5477 | } |
|
| 4333 | 5478 | return nil; |
|
| 4334 | 5479 | } |
|
| 4335 | 5480 | ||
| 4336 | 5481 | /// Look up a standalone method entry by its symbol. |
|
| 4337 | - | export unsafe fn findMethodBySymbol(self: &Resolver, sym: *unsafe mut Symbol) -> ?*unsafe MethodEntry { |
|
| 5482 | + | export unsafe fn findMethodBySymbol 'arena (self: &Resolver 'arena, sym: *unsafe mut Symbol) -> ?*unsafe MethodEntry { |
|
| 4338 | 5483 | for i in 0..self.methodsLen { |
|
| 4339 | 5484 | let entry: *unsafe MethodEntry = &self.methods[i]; |
|
| 4340 | 5485 | if entry.symbol == sym { |
|
| 4341 | 5486 | return entry; |
|
| 4342 | 5487 | } |
|
| 4343 | 5488 | } |
|
| 4344 | 5489 | return nil; |
|
| 4345 | 5490 | } |
|
| 4346 | 5491 | ||
| 4347 | 5492 | /// Resolve union variant types after all type names are bound (Phase 2 of type resolution). |
|
| 4348 | - | unsafe fn resolveUnionBody(self: &mut Resolver, node: *ast::Node, decl: ast::UnionDecl) |
|
| 5493 | + | unsafe fn resolveUnionBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl) |
|
| 5494 | + | throws (ResolveError) |
|
| 5495 | + | { |
|
| 5496 | + | let previous = self.regionScope; |
|
| 5497 | + | set self.regionScope = try bindRegions(self, node, decl.regions); |
|
| 5498 | + | try resolveUnionContents(self, node, decl) catch error { |
|
| 5499 | + | set self.regionScope = previous; |
|
| 5500 | + | throw error; |
|
| 5501 | + | }; |
|
| 5502 | + | set self.regionScope = previous; |
|
| 5503 | + | } |
|
| 5504 | + | ||
| 5505 | + | /// Resolve union contents in the declaration's region environment. |
|
| 5506 | + | unsafe fn resolveUnionContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl) |
|
| 4349 | 5507 | throws (ResolveError) |
|
| 4350 | 5508 | { |
|
| 4351 | 5509 | // Get the type symbol that was bound to this declaration node. |
|
| 4352 | 5510 | // If there's no symbol, it's because an earlier phase failed. |
|
| 4353 | 5511 | let sym = symbolFor(self, node) |
| 4358 | 5516 | // Check if already resolved, in which case there's no need to |
|
| 4359 | 5517 | // do it again. |
|
| 4360 | 5518 | if let case NominalType::Union(_) = *nominalTy { |
|
| 4361 | 5519 | return; |
|
| 4362 | 5520 | } |
|
| 4363 | - | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 5521 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 4364 | 5522 | let mut variants: *unsafe mut [UnionVariant] = &mut []; |
|
| 4365 | 5523 | ||
| 4366 | - | // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion |
|
| 4367 | - | // when a variant references this union type (e.g. record payloads with `*[Self]`). |
|
| 4368 | - | // TODO: It would be best to have a resolving state eg. `Visiting` for this situation. |
|
| 4369 | 5524 | let markers = try resolveOwnershipMarkers(self, decl.derives); |
|
| 4370 | - | set *nominalTy = NominalType::Union(UnionType { |
|
| 4371 | - | variants: &[], |
|
| 4372 | - | layout: Layout { size: 0, alignment: 0 }, |
|
| 4373 | - | valOffset: 0, |
|
| 4374 | - | isAllVoid: true, |
|
| 4375 | - | declaredLinear: markers.linear, |
|
| 4376 | - | declaredCopy: markers.copy, |
|
| 4377 | - | }); |
|
| 5525 | + | set *nominalTy = NominalType::Resolving(node); |
|
| 4378 | 5526 | ||
| 4379 | 5527 | assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded"; |
|
| 4380 | 5528 | let mut iota: u32 = 0; |
|
| 4381 | 5529 | for variantNode, i in decl.variants { |
|
| 4382 | 5530 | let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value |
| 4385 | 5533 | // Resolve the variant's payload type if present. |
|
| 4386 | 5534 | let mut variantType = Type::Void; |
|
| 4387 | 5535 | if let typeNode = variantDecl.type { |
|
| 4388 | 5536 | set variantType = try infer(self, typeNode); |
|
| 4389 | 5537 | try ensureStorableType(self, typeNode, variantType); |
|
| 5538 | + | try ensureTypeResolved(self, variantType, typeNode); |
|
| 4390 | 5539 | } |
|
| 4391 | 5540 | // Process the variant's explicit discriminant value if present. |
|
| 4392 | 5541 | try visitOptional(self, variantDecl.value, variantType); |
|
| 4393 | 5542 | let tag = variantTag(variantDecl, &mut iota); |
|
| 4394 | 5543 | // Create a symbol for this variant. |
| 4410 | 5559 | } |
|
| 4411 | 5560 | let info = computeUnionLayout(&variants[..]); |
|
| 4412 | 5561 | ||
| 4413 | 5562 | // Update the nominal type with the resolved variants. |
|
| 4414 | 5563 | set *nominalTy = NominalType::Union(UnionType { |
|
| 5564 | + | regions: self.regionScope, |
|
| 5565 | + | application: nil, |
|
| 4415 | 5566 | variants: &variants[..], |
|
| 4416 | - | layout: info.layout, |
|
| 5567 | + | layout: allocLayout(self, info.layout), |
|
| 4417 | 5568 | valOffset: info.valOffset, |
|
| 4418 | 5569 | isAllVoid: info.isAllVoid, |
|
| 4419 | 5570 | declaredLinear: markers.linear, |
|
| 4420 | 5571 | declaredCopy: markers.copy, |
|
| 4421 | 5572 | }); |
|
| 4422 | 5573 | } |
|
| 4423 | 5574 | ||
| 4424 | - | /// Check if a module should be analyzed based on its attributes and build configuration. |
|
| 4425 | - | fn shouldAnalyzeModule(self: &Resolver, attrs: ?ast::Attributes) -> bool { |
|
| 5575 | + | /// Check whether an attributed module or import is active in this build. |
|
| 5576 | + | /// A test module is active only when its source module was registered. |
|
| 5577 | + | unsafe fn shouldAnalyzeModule 'arena (self: &Resolver 'arena, attrs: ?ast::Attributes, name: ?*[u8]) -> bool { |
|
| 4426 | 5578 | if let attributes = attrs { |
|
| 4427 | - | // Skip test modules unless we're building in test mode. |
|
| 4428 | - | if ast::attributesContains(&attributes, ast::Attribute::Test) and not self.config.buildTest { |
|
| 4429 | - | return false; |
|
| 5579 | + | if ast::attributesContains(&attributes, ast::Attribute::Test) { |
|
| 5580 | + | if not self.config.buildTest { |
|
| 5581 | + | return false; |
|
| 5582 | + | } |
|
| 5583 | + | if let moduleName = name { |
|
| 5584 | + | return module::findChild(self.moduleGraph, moduleName, self.currentMod) <> nil; |
|
| 5585 | + | } |
|
| 4430 | 5586 | } |
|
| 4431 | 5587 | } |
|
| 4432 | 5588 | return true; |
|
| 4433 | 5589 | } |
|
| 4434 | 5590 | ||
| 4435 | 5591 | /// Analyze a module during the graph analysis phase. |
|
| 4436 | - | unsafe fn resolveModGraph(self: &mut Resolver, node: *ast::Node, decl: ast::Mod) |
|
| 5592 | + | unsafe fn resolveModGraph 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod) |
|
| 4437 | 5593 | throws (ResolveError) |
|
| 4438 | 5594 | { |
|
| 4439 | - | if not shouldAnalyzeModule(self, decl.attrs) { |
|
| 5595 | + | let modName = try nodeName(self, decl.name); |
|
| 5596 | + | if not shouldAnalyzeModule(self, decl.attrs, modName) { |
|
| 4440 | 5597 | return; |
|
| 4441 | 5598 | } |
|
| 4442 | - | let modName = try nodeName(self, decl.name); |
|
| 4443 | - | let attrMask = resolveAttributes(self, decl.attrs); |
|
| 5599 | + | let attrMask = resolveAttributes(decl.attrs); |
|
| 4444 | 5600 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
|
| 4445 | 5601 | let submod = try enterSubModule(self, modName, node); |
|
| 4446 | 5602 | ||
| 4447 | 5603 | // Bind the module symbol in the outer scope, ie. where the `mod` statement is. |
|
| 4448 | 5604 | try bindModuleIdent(self, submod.entry, submod.newScope, submod.root, attrMask, submod.prevScope); |
| 4452 | 5608 | ||
| 4453 | 5609 | exitModuleScope(self, submod); |
|
| 4454 | 5610 | } |
|
| 4455 | 5611 | ||
| 4456 | 5612 | /// Analyze a module in the declaration phase. |
|
| 4457 | - | unsafe fn resolveModDecl(self: &mut Resolver, node: *ast::Node, decl: ast::Mod) |
|
| 5613 | + | unsafe fn resolveModDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod) |
|
| 4458 | 5614 | throws (ResolveError) |
|
| 4459 | 5615 | { |
|
| 4460 | - | if not shouldAnalyzeModule(self, decl.attrs) { |
|
| 4461 | - | return; |
|
| 4462 | - | } |
|
| 4463 | 5616 | // Find module under the current module. |
|
| 4464 | 5617 | let modName = try nodeName(self, decl.name); |
|
| 5618 | + | if not shouldAnalyzeModule(self, decl.attrs, modName) { |
|
| 5619 | + | return; |
|
| 5620 | + | } |
|
| 4465 | 5621 | let submod = try enterSubModule(self, modName, node); |
|
| 4466 | 5622 | let case ast::NodeValue::Block(block) = submod.root.value |
|
| 4467 | 5623 | else panic "resolveModDecl: expected block for module root"; |
|
| 4468 | 5624 | try resolveModuleDecls(self, &block); |
|
| 4469 | 5625 | ||
| 4470 | 5626 | exitModuleScope(self, submod); |
|
| 4471 | 5627 | } |
|
| 4472 | 5628 | ||
| 4473 | 5629 | /// Analyze a `use` statement and create a symbol for the imported module. |
|
| 4474 | - | unsafe fn resolveUse(self: &mut Resolver, node: *ast::Node, decl: ast::Use) -> Type |
|
| 5630 | + | unsafe fn resolveUse 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Use) -> Type |
|
| 4475 | 5631 | throws (ResolveError) |
|
| 4476 | 5632 | { |
|
| 5633 | + | if not shouldAnalyzeModule(self, decl.attrs, nil) { |
|
| 5634 | + | return Type::Void; |
|
| 5635 | + | } |
|
| 4477 | 5636 | let resolved = try resolveModulePath(self, decl.path); |
|
| 4478 | - | let attrMask = resolveAttributes(self, decl.attrs); |
|
| 5637 | + | let attrMask = resolveAttributes(decl.attrs); |
|
| 4479 | 5638 | ||
| 4480 | 5639 | if decl.wildcard { |
|
| 4481 | 5640 | // Import all public symbols from the target module. |
|
| 4482 | 5641 | for i in 0..resolved.scope.symbolsLen { |
|
| 4483 | 5642 | let sym = resolved.scope.symbols[i]; |
| 4498 | 5657 | } |
|
| 4499 | 5658 | return Type::Void; |
|
| 4500 | 5659 | } |
|
| 4501 | 5660 | ||
| 4502 | 5661 | /// Analyze a standard `if` statement. |
|
| 4503 | - | unsafe fn resolveIf(self: &mut Resolver, node: *ast::Node, cond: ast::If) -> Type |
|
| 5662 | + | unsafe fn resolveIf 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::If) -> Type |
|
| 4504 | 5663 | throws (ResolveError) |
|
| 4505 | 5664 | { |
|
| 4506 | 5665 | try checkBoolean(self, cond.condition); |
|
| 4507 | 5666 | let thenTy = try visit(self, cond.thenBranch, Type::Void); |
|
| 4508 | 5667 | let elseTy = try visitOptional(self, cond.elseBranch, Type::Void); |
|
| 4509 | 5668 | ||
| 4510 | 5669 | return setNodeType(self, node, unifyBranches(thenTy, elseTy)); |
|
| 4511 | 5670 | } |
|
| 4512 | 5671 | ||
| 4513 | 5672 | /// Analyze a conditional expression. |
|
| 4514 | - | unsafe fn resolveCondExpr(self: &mut Resolver, node: *ast::Node, cond: ast::CondExpr) -> Type |
|
| 5673 | + | unsafe fn resolveCondExpr 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::CondExpr, hint: Type) -> Type |
|
| 4515 | 5674 | throws (ResolveError) |
|
| 4516 | 5675 | { |
|
| 4517 | 5676 | try checkBoolean(self, cond.condition); |
|
| 4518 | - | let thenTy = try infer(self, cond.thenExpr); |
|
| 4519 | - | let elseTy = try infer(self, cond.elseExpr); |
|
| 5677 | + | let thenValue = try visit(self, cond.thenExpr, hint); |
|
| 5678 | + | let thenTy = assignableValueType(self, cond.thenExpr, thenValue); |
|
| 5679 | + | let elseValue = try visit(self, cond.elseExpr, hint); |
|
| 5680 | + | let elseTy = assignableValueType(self, cond.elseExpr, elseValue); |
|
| 4520 | 5681 | ||
| 4521 | 5682 | // Either branch may supply the concrete type for an otherwise context- |
|
| 4522 | 5683 | // dependent expression, such as an unsuffixed integer or `nil`. |
|
| 4523 | 5684 | if let coercion = isAssignable(self, thenTy, elseTy, cond.elseExpr) { |
|
| 4524 | 5685 | setNodeCoercion(self, cond.elseExpr, coercion); |
| 4532 | 5693 | ||
| 4533 | 5694 | return setNodeType(self, node, thenTy); |
|
| 4534 | 5695 | } |
|
| 4535 | 5696 | ||
| 4536 | 5697 | /// Analyze a pattern match structure (used by if-let, while-let). |
|
| 4537 | - | unsafe fn resolvePatternMatch(self: &mut Resolver, node: *ast::Node, pat: &ast::PatternMatch) |
|
| 5698 | + | unsafe fn resolvePatternMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, pat: &ast::PatternMatch) |
|
| 4538 | 5699 | throws (ResolveError) |
|
| 4539 | 5700 | { |
|
| 4540 | 5701 | match pat.kind { |
|
| 4541 | 5702 | case ast::PatternKind::Case => { |
|
| 4542 | 5703 | // Analyze pattern against scrutinee type. |
| 4560 | 5721 | try checkBoolean(self, guard); |
|
| 4561 | 5722 | } |
|
| 4562 | 5723 | } |
|
| 4563 | 5724 | ||
| 4564 | 5725 | /// Analyze an `if let` or `if let case` pattern binding. |
|
| 4565 | - | unsafe fn resolveIfLet(self: &mut Resolver, node: *ast::Node, cond: ast::IfLet) -> Type |
|
| 5726 | + | unsafe fn resolveIfLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::IfLet) -> Type |
|
| 4566 | 5727 | throws (ResolveError) |
|
| 4567 | 5728 | { |
|
| 4568 | 5729 | enterScope(self, node); |
|
| 4569 | 5730 | try resolvePatternMatch(self, node, &cond.pattern); |
|
| 4570 | 5731 |
| 4598 | 5759 | ||
| 4599 | 5760 | /// Analyze a case pattern for match, if-case, let-case, or while-case. |
|
| 4600 | 5761 | /// |
|
| 4601 | 5762 | /// At the top level, bare identifiers are compared against existing values. |
|
| 4602 | 5763 | /// Inside destructuring patterns (arrays, records), identifiers become bindings. |
|
| 4603 | - | unsafe fn resolveCasePattern( |
|
| 4604 | - | self: &mut Resolver, |
|
| 5764 | + | unsafe fn resolveCasePattern 'arena ( |
|
| 5765 | + | self: &mut Resolver 'arena, |
|
| 4605 | 5766 | pattern: *ast::Node, |
|
| 4606 | 5767 | scrutineeTy: Type, |
|
| 4607 | 5768 | mode: IdentMode, |
|
| 4608 | 5769 | matchBy: MatchBy |
|
| 4609 | 5770 | ) throws (ResolveError) { |
| 4625 | 5786 | return; |
|
| 4626 | 5787 | } |
|
| 4627 | 5788 | case NominalType::Record(recInfo) => { |
|
| 4628 | 5789 | match pattern.value { |
|
| 4629 | 5790 | case ast::NodeValue::Call(_), ast::NodeValue::RecordLit(_) => { |
|
| 4630 | - | try bindRecordPatternFields(self, pattern, recInfo, matchBy); |
|
| 5791 | + | try resolveRecordPattern(self, pattern, scrutineeTy, recInfo, matchBy); |
|
| 4631 | 5792 | return; |
|
| 4632 | 5793 | } else => {} |
|
| 4633 | 5794 | } |
|
| 4634 | 5795 | } else => {} |
|
| 4635 | 5796 | } |
| 4667 | 5828 | } |
|
| 4668 | 5829 | } |
|
| 4669 | 5830 | } |
|
| 4670 | 5831 | ||
| 4671 | 5832 | /// Analyze a traditional `while` loop. |
|
| 4672 | - | unsafe fn resolveWhile(self: &mut Resolver, node: *ast::Node, loopNode: ast::While) -> Type |
|
| 5833 | + | unsafe fn resolveWhile 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::While) -> Type |
|
| 4673 | 5834 | throws (ResolveError) |
|
| 4674 | 5835 | { |
|
| 4675 | 5836 | try checkBoolean(self, loopNode.condition); |
|
| 4676 | 5837 | let loopTy = try visitLoop(self, loopNode.body); |
|
| 4677 | 5838 | try visitOptional(self, loopNode.elseBranch, Type::Void); |
| 4681 | 5842 | } |
|
| 4682 | 5843 | return setNodeType(self, node, Type::Void); |
|
| 4683 | 5844 | } |
|
| 4684 | 5845 | ||
| 4685 | 5846 | /// Analyze a `while let` loop with pattern binding. |
|
| 4686 | - | unsafe fn resolveWhileLet(self: &mut Resolver, node: *ast::Node, loopNode: ast::WhileLet) -> Type |
|
| 5847 | + | unsafe fn resolveWhileLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::WhileLet) -> Type |
|
| 4687 | 5848 | throws (ResolveError) |
|
| 4688 | 5849 | { |
|
| 4689 | 5850 | enterScope(self, node); |
|
| 4690 | 5851 | try resolvePatternMatch(self, node, &loopNode.pattern); |
|
| 4691 | 5852 |
| 4696 | 5857 | ||
| 4697 | 5858 | return setNodeType(self, node, Type::Void); |
|
| 4698 | 5859 | } |
|
| 4699 | 5860 | ||
| 4700 | 5861 | /// Analyze a `for` loop, binding iteration variables. |
|
| 4701 | - | unsafe fn resolveFor(self: &mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type |
|
| 5862 | + | unsafe fn resolveFor 'arena (self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For) -> Type |
|
| 4702 | 5863 | throws (ResolveError) |
|
| 4703 | 5864 | { |
|
| 4704 | 5865 | let iterableTy = try infer(self, forStmt.iterable); |
|
| 4705 | 5866 | ||
| 4706 | 5867 | // Extract binding names for the lowerer. |
| 4813 | 5974 | ||
| 4814 | 5975 | /// Check whether a pattern contains nested sub-patterns that further |
|
| 4815 | 5976 | /// refine the match beyond the outer variant (e.g. nested union variant |
|
| 4816 | 5977 | /// tests or literal comparisons). Used to allow the same outer variant |
|
| 4817 | 5978 | /// to appear in multiple match arms. |
|
| 4818 | - | fn hasNestedRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool { |
|
| 5979 | + | fn hasNestedRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool { |
|
| 4819 | 5980 | for i in 0..patternSubCount(pattern) { |
|
| 4820 | 5981 | if let sub = patternSubElement(pattern, i) { |
|
| 4821 | 5982 | if isRefiningPattern(self, sub) { |
|
| 4822 | 5983 | return true; |
|
| 4823 | 5984 | } |
| 4828 | 5989 | ||
| 4829 | 5990 | /// Check whether a single pattern node is a refining pattern that tests |
|
| 4830 | 5991 | /// a value rather than just binding it. Union variants, literals, and |
|
| 4831 | 5992 | /// scope accesses are refining; identifiers, placeholders, and plain |
|
| 4832 | 5993 | /// record destructurings are not. |
|
| 4833 | - | fn isRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool { |
|
| 5994 | + | fn isRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool { |
|
| 4834 | 5995 | match pattern.value { |
|
| 4835 | 5996 | case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => |
|
| 4836 | 5997 | return false, |
|
| 4837 | 5998 | case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => { |
|
| 4838 | 5999 | if let keyNode = patternVariantKeyNode(pattern) { |
| 4895 | 6056 | return true; |
|
| 4896 | 6057 | } |
|
| 4897 | 6058 | ||
| 4898 | 6059 | /// Analyze a match prong, checking for duplicate catch-alls. Returns the |
|
| 4899 | 6060 | /// unified match type. |
|
| 4900 | - | unsafe fn resolveMatchProng( |
|
| 4901 | - | self: &mut Resolver, |
|
| 6061 | + | unsafe fn resolveMatchProng 'arena ( |
|
| 6062 | + | self: &mut Resolver 'arena, |
|
| 4902 | 6063 | prongNode: *ast::Node, |
|
| 4903 | 6064 | prong: ast::MatchProng, |
|
| 4904 | 6065 | subjectTy: Type, |
|
| 4905 | 6066 | state: &mut MatchState, |
|
| 4906 | 6067 | matchType: Type, |
| 4929 | 6090 | return try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy); |
|
| 4930 | 6091 | } |
|
| 4931 | 6092 | ||
| 4932 | 6093 | /// Analyze a `match` expression. Dispatches to specialized functions based on |
|
| 4933 | 6094 | /// the subject type. |
|
| 4934 | - | unsafe fn resolveMatch(self: &mut Resolver, node: *ast::Node, sw: ast::Match) -> Type |
|
| 6095 | + | unsafe fn resolveMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match) -> Type |
|
| 4935 | 6096 | throws (ResolveError) |
|
| 4936 | 6097 | { |
|
| 4937 | 6098 | let subjectTy = try infer(self, sw.subject); |
|
| 4938 | 6099 | if isUnsafePointerType(subjectTy) { |
|
| 4939 | 6100 | try requireUnsafe(self, sw.subject); |
| 4943 | 6104 | if let case Type::Optional(inner) = subject.effectiveTy { |
|
| 4944 | 6105 | try resolveMatchOptional(self, node, sw, inner, subject.by); |
|
| 4945 | 6106 | } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy { |
|
| 4946 | 6107 | try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by); |
|
| 4947 | 6108 | } else { |
|
| 4948 | - | try resolveMatchGeneric(self, node, sw, subject.effectiveTy); |
|
| 6109 | + | try resolveMatchGeneric(self, node, sw, subject.effectiveTy, subject.by); |
|
| 4949 | 6110 | } |
|
| 4950 | 6111 | ||
| 4951 | 6112 | // Mark last non-guarded prong as exhaustive. |
|
| 4952 | 6113 | let lastProng = sw.prongs[sw.prongs.len - 1]; |
|
| 4953 | 6114 | let case ast::NodeValue::MatchProng(p) = lastProng.value |
| 4960 | 6121 | }; |
|
| 4961 | 6122 | return ty; |
|
| 4962 | 6123 | } |
|
| 4963 | 6124 | ||
| 4964 | 6125 | /// Analyze a `match` expression on an optional subject. |
|
| 4965 | - | unsafe fn resolveMatchOptional( |
|
| 4966 | - | self: &mut Resolver, |
|
| 6126 | + | unsafe fn resolveMatchOptional 'arena ( |
|
| 6127 | + | self: &mut Resolver 'arena, |
|
| 4967 | 6128 | node: *ast::Node, |
|
| 4968 | 6129 | sw: ast::Match, |
|
| 4969 | 6130 | innerTy: *Type, |
|
| 4970 | 6131 | matchBy: MatchBy |
|
| 4971 | 6132 | ) -> Type throws (ResolveError) |
| 5033 | 6194 | } |
|
| 5034 | 6195 | return setNodeType(self, node, matchType); |
|
| 5035 | 6196 | } |
|
| 5036 | 6197 | ||
| 5037 | 6198 | /// Analyze a `match` expression on a union subject. |
|
| 5038 | - | unsafe fn resolveMatchUnion( |
|
| 5039 | - | self: &mut Resolver, |
|
| 6199 | + | unsafe fn resolveMatchUnion 'arena ( |
|
| 6200 | + | self: &mut Resolver 'arena, |
|
| 5040 | 6201 | node: *ast::Node, |
|
| 5041 | 6202 | sw: ast::Match, |
|
| 5042 | 6203 | subjectTy: Type, |
|
| 5043 | 6204 | info: UnionType, |
|
| 5044 | 6205 | matchBy: MatchBy |
| 5089 | 6250 | return setNodeType(self, node, matchType); |
|
| 5090 | 6251 | } |
|
| 5091 | 6252 | ||
| 5092 | 6253 | /// Analyze a `match` expression on a generic subject type. Requires exhaustiveness: |
|
| 5093 | 6254 | /// booleans must cover both `true` and `false`, other types require a catch-all. |
|
| 5094 | - | unsafe fn resolveMatchGeneric(self: &mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type |
|
| 6255 | + | unsafe fn resolveMatchGeneric 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match, subjectTy: Type, matchBy: MatchBy) -> Type |
|
| 5095 | 6256 | throws (ResolveError) |
|
| 5096 | 6257 | { |
|
| 5097 | 6258 | let prongs = sw.prongs; |
|
| 5098 | 6259 | let mut state = MatchState { catchAll: false, isConst: true }; |
|
| 5099 | 6260 | let mut matchType = Type::Never; |
| 5104 | 6265 | for prongNode in prongs { |
|
| 5105 | 6266 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
|
| 5106 | 6267 | else panic "resolveMatchGeneric: expected match prong"; |
|
| 5107 | 6268 | ||
| 5108 | 6269 | set matchType = try resolveMatchProng( |
|
| 5109 | - | self, prongNode, prong, subjectTy, &mut state, matchType, MatchBy::Value |
|
| 6270 | + | self, prongNode, prong, subjectTy, &mut state, matchType, matchBy |
|
| 5110 | 6271 | ); |
|
| 5111 | 6272 | // Track boolean coverage. Guarded prongs don't count as covering. |
|
| 5112 | 6273 | if let case ast::ProngArm::Case(patterns) = prong.arm { |
|
| 5113 | 6274 | for p in patterns { |
|
| 5114 | 6275 | if prong.guard == nil { |
| 5158 | 6319 | ||
| 5159 | 6320 | return setNodeType(self, node, matchType); |
|
| 5160 | 6321 | } |
|
| 5161 | 6322 | ||
| 5162 | 6323 | /// Analyze a single `match` prong branch. Returns the unified match type. |
|
| 5163 | - | unsafe fn visitMatchProng( |
|
| 5164 | - | self: &mut Resolver, |
|
| 6324 | + | unsafe fn visitMatchProng 'arena ( |
|
| 6325 | + | self: &mut Resolver 'arena, |
|
| 5165 | 6326 | node: *ast::Node, |
|
| 5166 | 6327 | prongNode: ast::MatchProng, |
|
| 5167 | 6328 | subjectTy: Type, |
|
| 5168 | 6329 | matchType: Type, |
|
| 5169 | 6330 | matchBy: MatchBy |
| 5178 | 6339 | ||
| 5179 | 6340 | return unifyBranches(matchType, prongTy); |
|
| 5180 | 6341 | } |
|
| 5181 | 6342 | ||
| 5182 | 6343 | /// Analyze the contents of a `match` prong while inside the prong scope. |
|
| 5183 | - | unsafe fn resolveMatchProngBody( |
|
| 5184 | - | self: &mut Resolver, |
|
| 6344 | + | unsafe fn resolveMatchProngBody 'arena ( |
|
| 6345 | + | self: &mut Resolver 'arena, |
|
| 5185 | 6346 | prong: ast::MatchProng, |
|
| 5186 | 6347 | subjectTy: Type, |
|
| 5187 | 6348 | matchBy: MatchBy |
|
| 5188 | 6349 | ) -> Type throws (ResolveError) { |
|
| 5189 | 6350 | match prong.arm { |
| 5207 | 6368 | } |
|
| 5208 | 6369 | return try visit(self, prong.body, Type::Void); |
|
| 5209 | 6370 | } |
|
| 5210 | 6371 | ||
| 5211 | 6372 | /// Ensure a scope access pattern references a compatible union variant. |
|
| 5212 | - | unsafe fn resolveUnionScopePattern( |
|
| 5213 | - | self: &mut Resolver, |
|
| 6373 | + | unsafe fn resolveUnionScopePattern 'arena ( |
|
| 6374 | + | self: &mut Resolver 'arena, |
|
| 5214 | 6375 | pattern: *ast::Node, |
|
| 5215 | 6376 | access: ast::Access, |
|
| 5216 | 6377 | subjectTy: Type, |
|
| 5217 | 6378 | unionType: UnionType |
|
| 5218 | 6379 | ) throws (ResolveError) { |
| 5233 | 6394 | throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name)); |
|
| 5234 | 6395 | } |
|
| 5235 | 6396 | } |
|
| 5236 | 6397 | ||
| 5237 | 6398 | /// Validate and bind a union constructor call used as a `match` pattern. |
|
| 5238 | - | unsafe fn resolveUnionCallPattern( |
|
| 5239 | - | self: &mut Resolver, |
|
| 6399 | + | unsafe fn resolveUnionCallPattern 'arena ( |
|
| 6400 | + | self: &mut Resolver 'arena, |
|
| 5240 | 6401 | pattern: *ast::Node, |
|
| 5241 | 6402 | call: ast::Call, |
|
| 5242 | 6403 | subjectTy: Type, |
|
| 5243 | 6404 | unionType: UnionType, |
|
| 5244 | 6405 | matchBy: MatchBy |
| 5257 | 6418 | throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name)); |
|
| 5258 | 6419 | } |
|
| 5259 | 6420 | } |
|
| 5260 | 6421 | ||
| 5261 | 6422 | /// Bind the payload introduced by a union constructor pattern. |
|
| 5262 | - | unsafe fn bindUnionPatternPayload( |
|
| 5263 | - | self: &mut Resolver, |
|
| 6423 | + | unsafe fn bindUnionPatternPayload 'arena ( |
|
| 6424 | + | self: &mut Resolver 'arena, |
|
| 5264 | 6425 | pattern: *ast::Node, |
|
| 5265 | 6426 | call: ast::Call, |
|
| 5266 | 6427 | variantName: *[u8], |
|
| 5267 | 6428 | payloadTy: Type, |
|
| 5268 | 6429 | matchBy: MatchBy |
| 5271 | 6432 | throw emitError( |
|
| 5272 | 6433 | self, pattern, ErrorKind::UnionVariantPayloadMissing(variantName) |
|
| 5273 | 6434 | ); |
|
| 5274 | 6435 | } |
|
| 5275 | 6436 | // All variant payloads are records. |
|
| 6437 | + | try ensureTypeResolved(self, payloadTy, pattern); |
|
| 5276 | 6438 | let recInfo = getRecord(payloadTy) |
|
| 5277 | 6439 | else panic "bindUnionPatternPayload: payload is not a record"; |
|
| 5278 | 6440 | ||
| 5279 | 6441 | try bindRecordPatternFields(self, pattern, recInfo, matchBy); |
|
| 5280 | 6442 | } |
|
| 5281 | 6443 | ||
| 5282 | 6444 | /// Bind a pattern variable. For ref matches, wraps the type in a pointer. |
|
| 5283 | - | unsafe fn bindPatternVar(self: &mut Resolver, binding: *ast::Node, ty: Type, matchBy: MatchBy) |
|
| 6445 | + | unsafe fn bindPatternVar 'arena (self: &mut Resolver 'arena, binding: *ast::Node, ty: Type, matchBy: MatchBy) |
|
| 5284 | 6446 | throws (ResolveError) |
|
| 5285 | 6447 | { |
|
| 5286 | 6448 | let mut bindTy = ty; |
|
| 5287 | 6449 | match matchBy { |
|
| 5288 | 6450 | case MatchBy::Value => {} |
| 5310 | 6472 | try resolveCasePattern(self, binding, ty, IdentMode::Bind, matchBy); |
|
| 5311 | 6473 | } |
|
| 5312 | 6474 | } |
|
| 5313 | 6475 | } |
|
| 5314 | 6476 | ||
| 6477 | + | /// Check a record pattern's exact nominal type before binding its fields. |
|
| 6478 | + | unsafe fn resolveRecordPattern 'arena ( |
|
| 6479 | + | self: &mut Resolver 'arena, pattern: *ast::Node, subjectTy: Type, body: RecordType, matchBy: MatchBy |
|
| 6480 | + | ) throws (ResolveError) { |
|
| 6481 | + | let mut name: ?*ast::Node = nil; |
|
| 6482 | + | match pattern.value { |
|
| 6483 | + | case ast::NodeValue::Call(call) => set name = call.callee, |
|
| 6484 | + | case ast::NodeValue::RecordLit(lit) => set name = lit.typeName, |
|
| 6485 | + | else => panic "resolveRecordPattern: expected record pattern", |
|
| 6486 | + | } |
|
| 6487 | + | if let typeName = name { |
|
| 6488 | + | let actual = try visit(self, typeName, subjectTy); |
|
| 6489 | + | let symbol = symbolFor(self, typeName) else throw emitError(self, typeName, ErrorKind::ExpectedRecord); |
|
| 6490 | + | let case SymbolData::Type(_) = symbol.data else throw emitError(self, typeName, ErrorKind::ExpectedRecord); |
|
| 6491 | + | if not typesEqual(actual, subjectTy) { |
|
| 6492 | + | throw emitTypeMismatch(self, typeName, TypeMismatch { expected: subjectTy, actual }); |
|
| 6493 | + | } |
|
| 6494 | + | } |
|
| 6495 | + | setNodeType(self, pattern, subjectTy); |
|
| 6496 | + | try bindRecordPatternFields(self, pattern, body, matchBy); |
|
| 6497 | + | } |
|
| 6498 | + | ||
| 5315 | 6499 | /// Bind record pattern fields to variables in the current scope. |
|
| 5316 | - | unsafe fn bindRecordPatternFields( |
|
| 5317 | - | self: &mut Resolver, |
|
| 6500 | + | unsafe fn bindRecordPatternFields 'arena ( |
|
| 6501 | + | self: &mut Resolver 'arena, |
|
| 5318 | 6502 | pattern: *ast::Node, |
|
| 5319 | 6503 | recInfo: RecordType, |
|
| 5320 | 6504 | matchBy: MatchBy |
|
| 5321 | 6505 | ) throws (ResolveError) { |
|
| 5322 | 6506 | match pattern.value { |
| 5352 | 6536 | else => throw emitError(self, pattern, ErrorKind::Internal) |
|
| 5353 | 6537 | } |
|
| 5354 | 6538 | } |
|
| 5355 | 6539 | ||
| 5356 | 6540 | /// Validate and bind a record literal pattern for matching labeled union variants. |
|
| 5357 | - | unsafe fn resolveUnionRecordPattern( |
|
| 5358 | - | self: &mut Resolver, |
|
| 6541 | + | unsafe fn resolveUnionRecordPattern 'arena ( |
|
| 6542 | + | self: &mut Resolver 'arena, |
|
| 5359 | 6543 | pattern: *ast::Node, |
|
| 5360 | 6544 | lit: ast::RecordLit, |
|
| 5361 | 6545 | subjectTy: Type, |
|
| 5362 | 6546 | unionType: UnionType, |
|
| 5363 | 6547 | matchBy: MatchBy |
| 5382 | 6566 | setVariantInfo(self, pattern, index, tag); |
|
| 5383 | 6567 | ||
| 5384 | 6568 | if variant.valueType == Type::Void { |
|
| 5385 | 6569 | throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name)); |
|
| 5386 | 6570 | } |
|
| 6571 | + | try ensureTypeResolved(self, variant.valueType, pattern); |
|
| 5387 | 6572 | let recInfo = getRecord(variant.valueType) |
|
| 5388 | 6573 | else panic "resolveUnionRecordPattern: payload is not a record"; |
|
| 5389 | 6574 | ||
| 5390 | 6575 | try bindRecordPatternFields(self, pattern, recInfo, matchBy); |
|
| 5391 | 6576 | } |
|
| 5392 | 6577 | ||
| 5393 | 6578 | /// Analyze a pattern appearing in a union case. |
|
| 5394 | - | unsafe fn resolveUnionPattern( |
|
| 5395 | - | self: &mut Resolver, |
|
| 6579 | + | unsafe fn resolveUnionPattern 'arena ( |
|
| 6580 | + | self: &mut Resolver 'arena, |
|
| 5396 | 6581 | pattern: *ast::Node, |
|
| 5397 | 6582 | subjectTy: Type, |
|
| 5398 | 6583 | unionType: UnionType, |
|
| 5399 | 6584 | matchBy: MatchBy |
|
| 5400 | 6585 | ) throws (ResolveError) { |
| 5446 | 6631 | } |
|
| 5447 | 6632 | return false; |
|
| 5448 | 6633 | } |
|
| 5449 | 6634 | ||
| 5450 | 6635 | /// Analyze a `let-else` guard. |
|
| 5451 | - | unsafe fn resolveLetElse(self: &mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type |
|
| 6636 | + | unsafe fn resolveLetElse 'arena (self: &mut Resolver 'arena, node: *ast::Node, letElse: ast::LetElse) -> Type |
|
| 5452 | 6637 | throws (ResolveError) |
|
| 5453 | 6638 | { |
|
| 5454 | 6639 | let pat = letElse.pattern; |
|
| 5455 | 6640 | let exprTy = try infer(self, pat.scrutinee); |
|
| 5456 | 6641 |
| 5493 | 6678 | } |
|
| 5494 | 6679 | return setNodeType(self, node, Type::Void); |
|
| 5495 | 6680 | } |
|
| 5496 | 6681 | ||
| 5497 | 6682 | /// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`. |
|
| 5498 | - | unsafe fn resolveBuiltinCall( |
|
| 5499 | - | self: &mut Resolver, |
|
| 6683 | + | unsafe fn resolveBuiltinCall 'arena ( |
|
| 6684 | + | self: &mut Resolver 'arena, |
|
| 5500 | 6685 | node: *ast::Node, |
|
| 5501 | 6686 | kind: ast::Builtin, |
|
| 5502 | 6687 | args: *[*ast::Node] |
|
| 5503 | 6688 | ) -> Type throws (ResolveError) { |
|
| 5504 | 6689 | // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`. |
| 5557 | 6742 | negative: false, |
|
| 5558 | 6743 | })); |
|
| 5559 | 6744 | return setNodeType(self, node, Type::U32); |
|
| 5560 | 6745 | } |
|
| 5561 | 6746 | ||
| 5562 | - | /// Validate call arguments against a function type: check argument count, |
|
| 5563 | - | /// type-check each argument, and verify that throwing functions use `try`. |
|
| 5564 | - | unsafe fn checkCallArgs(self: &mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx) |
|
| 5565 | - | throws (ResolveError) |
|
| 5566 | - | { |
|
| 5567 | - | if ctx == CallCtx::Normal and info.throwList.len > 0 { |
|
| 5568 | - | throw emitError(self, node, ErrorKind::MissingTry); |
|
| 6747 | + | /// Allocate an initially empty argument map for a region-parameterized signature. |
|
| 6748 | + | unsafe fn regionSubstitution 'arena (self: &mut Resolver 'arena, parameters: *RegionScope) -> RegionSubstitution { |
|
| 6749 | + | let count = parameters.entries.len; |
|
| 6750 | + | let arguments = try! alloc::allocRawSlice( |
|
| 6751 | + | self.arena, @sizeOf(?*unsafe types::Region), @alignOf(?*unsafe types::Region), count |
|
| 6752 | + | ) as *unsafe mut [?*unsafe types::Region]; |
|
| 6753 | + | for i in 0..count { |
|
| 6754 | + | set arguments[i] = nil; |
|
| 5569 | 6755 | } |
|
| 5570 | - | if call.args.len <> info.paramTypes.len as u32 { |
|
| 5571 | - | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 5572 | - | expected: info.paramTypes.len as u32, |
|
| 5573 | - | actual: call.args.len, |
|
| 5574 | - | })); |
|
| 6756 | + | return RegionSubstitution { parameters, arguments }; |
|
| 6757 | + | } |
|
| 6758 | + | ||
| 6759 | + | /// Find the position of a region in a substitution's declared parameter list. |
|
| 6760 | + | unsafe fn regionParameter(map: &RegionSubstitution, region: *unsafe types::Region) -> ?u32 { |
|
| 6761 | + | for parameter, i in map.parameters.entries { |
|
| 6762 | + | if parameter.id == region.id { |
|
| 6763 | + | return i; |
|
| 6764 | + | } |
|
| 5575 | 6765 | } |
|
| 5576 | - | for argNode, i in call.args { |
|
| 5577 | - | let expectedTy = *info.paramTypes[i]; |
|
| 6766 | + | return nil; |
|
| 6767 | + | } |
|
| 5578 | 6768 | ||
| 5579 | - | try checkAssignable(self, argNode, expectedTy); |
|
| 6769 | + | /// Infer one region argument from a pair of reference classes. |
|
| 6770 | + | unsafe fn inferRegionClass 'arena ( |
|
| 6771 | + | self: &mut Resolver 'arena, map: &RegionSubstitution, |
|
| 6772 | + | expected: types::PointerClass, actual: types::PointerClass, site: *ast::Node |
|
| 6773 | + | ) throws (ResolveError) { |
|
| 6774 | + | let case types::PointerClass::Region(parameter) = expected else return; |
|
| 6775 | + | let index = regionParameter(map, parameter) else return; |
|
| 6776 | + | let case types::PointerClass::Region(argument) = actual |
|
| 6777 | + | else throw emitError(self, site, ErrorKind::RegionInference(parameter.name)); |
|
| 6778 | + | if let previous = map.arguments[index]; previous.id <> argument.id { |
|
| 6779 | + | throw emitError(self, site, ErrorKind::RegionInference(parameter.name)); |
|
| 5580 | 6780 | } |
|
| 6781 | + | set map.arguments[index] = argument; |
|
| 5581 | 6782 | } |
|
| 5582 | 6783 | ||
| 5583 | - | /// Analyze a function call expression. |
|
| 5584 | - | unsafe fn resolveCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, ctx: CallCtx) -> Type |
|
| 5585 | - | throws (ResolveError) |
|
| 6784 | + | /// Infer regions through matching type structure without adding lifetime subtyping. |
|
| 6785 | + | unsafe fn inferRegionArguments 'arena ( |
|
| 6786 | + | self: &mut Resolver 'arena, map: &RegionSubstitution, expected: Type, actual: Type, site: *ast::Node |
|
| 6787 | + | ) throws (ResolveError) { |
|
| 6788 | + | match expected { |
|
| 6789 | + | case Type::Cell { class, payload } => { |
|
| 6790 | + | let case Type::Cell { class: otherClass, payload: other } = actual else return; |
|
| 6791 | + | try inferRegionClass(self, map, class, otherClass, site); |
|
| 6792 | + | try inferRegionArguments(self, map, *payload, *other, site); |
|
| 6793 | + | } |
|
| 6794 | + | case Type::Session(region) => { |
|
| 6795 | + | let case Type::Session(other) = actual else return; |
|
| 6796 | + | try inferRegionClass(self, map, types::PointerClass::Region(region), |
|
| 6797 | + | types::PointerClass::Region(other), site); |
|
| 6798 | + | } |
|
| 6799 | + | case Type::Pointer { class, target, .. } => { |
|
| 6800 | + | let case Type::Pointer { class: otherClass, target: otherTarget, .. } = actual else return; |
|
| 6801 | + | try inferRegionClass(self, map, class, otherClass, site); |
|
| 6802 | + | try inferRegionArguments(self, map, *target, *otherTarget, site); |
|
| 6803 | + | } |
|
| 6804 | + | case Type::Slice { class, item, .. } => { |
|
| 6805 | + | let case Type::Slice { class: otherClass, item: otherItem, .. } = actual else return; |
|
| 6806 | + | try inferRegionClass(self, map, class, otherClass, site); |
|
| 6807 | + | try inferRegionArguments(self, map, *item, *otherItem, site); |
|
| 6808 | + | } |
|
| 6809 | + | case Type::TraitObject { class, .. } => { |
|
| 6810 | + | if let case Type::TraitObject { class: otherClass, .. } = actual { |
|
| 6811 | + | try inferRegionClass(self, map, class, otherClass, site); |
|
| 6812 | + | } |
|
| 6813 | + | } |
|
| 6814 | + | case Type::Array(array) => { |
|
| 6815 | + | if let case Type::Array(other) = actual { |
|
| 6816 | + | try inferRegionArguments(self, map, *array.item, *other.item, site); |
|
| 6817 | + | } |
|
| 6818 | + | } |
|
| 6819 | + | case Type::Optional(inner) => { |
|
| 6820 | + | if let case Type::Optional(other) = actual { |
|
| 6821 | + | try inferRegionArguments(self, map, *inner, *other, site); |
|
| 6822 | + | } else { |
|
| 6823 | + | try inferRegionArguments(self, map, *inner, actual, site); |
|
| 6824 | + | } |
|
| 6825 | + | } |
|
| 6826 | + | case Type::Fn(info) => { |
|
| 6827 | + | let case Type::Fn(other) = actual else return; |
|
| 6828 | + | if info.paramTypes.len <> other.paramTypes.len or info.throwList.len <> other.throwList.len { |
|
| 6829 | + | return; |
|
| 6830 | + | } |
|
| 6831 | + | for parameter, i in info.paramTypes { |
|
| 6832 | + | try inferRegionArguments(self, map, *parameter, *other.paramTypes[i], site); |
|
| 6833 | + | } |
|
| 6834 | + | for error, i in info.throwList { |
|
| 6835 | + | try inferRegionArguments(self, map, *error, *other.throwList[i], site); |
|
| 6836 | + | } |
|
| 6837 | + | try inferRegionArguments(self, map, *info.returnType, *other.returnType, site); |
|
| 6838 | + | } |
|
| 6839 | + | case Type::Nominal(info) => { |
|
| 6840 | + | let applied = nominalApplication(info) else return; |
|
| 6841 | + | let case Type::Nominal(otherInfo) = actual else return; |
|
| 6842 | + | let other = nominalApplication(otherInfo) else return; |
|
| 6843 | + | if applied.base <> other.base { |
|
| 6844 | + | return; |
|
| 6845 | + | } |
|
| 6846 | + | for region, i in applied.arguments { |
|
| 6847 | + | try inferRegionClass(self, map, types::PointerClass::Region(region), |
|
| 6848 | + | types::PointerClass::Region(other.arguments[i]), site); |
|
| 6849 | + | } |
|
| 6850 | + | } |
|
| 6851 | + | else => { |
|
| 6852 | + | } |
|
| 6853 | + | } |
|
| 6854 | + | } |
|
| 6855 | + | ||
| 6856 | + | /// Require a total substitution whose arguments satisfy each parent relation. |
|
| 6857 | + | unsafe fn validateRegionArguments 'arena (self: &mut Resolver 'arena, map: &RegionSubstitution, site: *ast::Node) |
|
| 6858 | + | throws (ResolveError) |
|
| 6859 | + | { |
|
| 6860 | + | for parameter, i in map.parameters.entries { |
|
| 6861 | + | if map.arguments[i] == nil { |
|
| 6862 | + | throw emitError(self, site, ErrorKind::RegionInference(parameter.name)); |
|
| 6863 | + | } |
|
| 6864 | + | } |
|
| 6865 | + | for parameter, i in map.parameters.entries { |
|
| 6866 | + | let parent = parameter.parent else continue; |
|
| 6867 | + | let index = regionParameter(map, parent) else panic "validateRegionArguments: unknown parent"; |
|
| 6868 | + | let parentArgument = map.arguments[index] else panic "validateRegionArguments: missing parent argument"; |
|
| 6869 | + | let argument = map.arguments[i] else panic "validateRegionArguments: missing argument"; |
|
| 6870 | + | if not types::regionContains(parentArgument, argument) { |
|
| 6871 | + | throw emitError(self, site, ErrorKind::RegionParent(parameter.name)); |
|
| 6872 | + | } |
|
| 6873 | + | } |
|
| 6874 | + | } |
|
| 6875 | + | ||
| 6876 | + | /// Substitute a reference's region while preserving its ownership class. |
|
| 6877 | + | unsafe fn substituteRegionClass(map: &RegionSubstitution, class: types::PointerClass) -> types::PointerClass { |
|
| 6878 | + | let case types::PointerClass::Region(region) = class else return class; |
|
| 6879 | + | let index = regionParameter(map, region) else return class; |
|
| 6880 | + | let argument = map.arguments[index] else panic "substituteRegionClass: missing argument"; |
|
| 6881 | + | return types::PointerClass::Region(argument); |
|
| 6882 | + | } |
|
| 6883 | + | ||
| 6884 | + | /// Substitute free region arguments in a type without changing its runtime layout. |
|
| 6885 | + | unsafe fn substituteRegions 'arena (self: &mut Resolver 'arena, map: &RegionSubstitution, ty: Type) -> Type { |
|
| 6886 | + | match ty { |
|
| 6887 | + | case Type::Cell { class, payload } => |
|
| 6888 | + | return Type::Cell { class: substituteRegionClass(map, class), payload: allocType(self, substituteRegions(self, map, *payload)) }, |
|
| 6889 | + | case Type::Session(region) => { |
|
| 6890 | + | let index = regionParameter(map, region) else return ty; |
|
| 6891 | + | let argument = map.arguments[index] else panic "substituteRegions: missing session region"; |
|
| 6892 | + | return Type::Session(argument); |
|
| 6893 | + | } |
|
| 6894 | + | case Type::Pointer { class, target, mutable } => { |
|
| 6895 | + | let targetType = substituteRegions(self, map, *target); |
|
| 6896 | + | return Type::Pointer { class: substituteRegionClass(map, class), target: allocType(self, targetType), mutable }; |
|
| 6897 | + | } |
|
| 6898 | + | case Type::Slice { class, item, mutable } => { |
|
| 6899 | + | let itemType = substituteRegions(self, map, *item); |
|
| 6900 | + | return Type::Slice { class: substituteRegionClass(map, class), item: allocType(self, itemType), mutable }; |
|
| 6901 | + | } |
|
| 6902 | + | case Type::TraitObject { class, traitInfo, mutable } => |
|
| 6903 | + | return Type::TraitObject { class: substituteRegionClass(map, class), traitInfo, mutable }, |
|
| 6904 | + | case Type::Array(array) => { |
|
| 6905 | + | let itemType = substituteRegions(self, map, *array.item); |
|
| 6906 | + | return Type::Array(ArrayType { item: allocType(self, itemType), length: array.length }); |
|
| 6907 | + | } |
|
| 6908 | + | case Type::Optional(inner) => { |
|
| 6909 | + | let innerType = substituteRegions(self, map, *inner); |
|
| 6910 | + | return Type::Optional(allocType(self, innerType)); |
|
| 6911 | + | } |
|
| 6912 | + | case Type::Fn(info) => return Type::Fn(substituteFnRegions(self, map, info, info.regions)), |
|
| 6913 | + | case Type::Nominal(info) => { |
|
| 6914 | + | let applied = nominalApplication(info) else return ty; |
|
| 6915 | + | let arguments = regionSubstitution(self, applied.parameters); |
|
| 6916 | + | for region, i in applied.arguments { |
|
| 6917 | + | let class = substituteRegionClass(map, types::PointerClass::Region(region)); |
|
| 6918 | + | let case types::PointerClass::Region(argument) = class else panic; |
|
| 6919 | + | set arguments.arguments[i] = argument; |
|
| 6920 | + | } |
|
| 6921 | + | return Type::Nominal(internNominalApplication(self, applied.base, &arguments)); |
|
| 6922 | + | } |
|
| 6923 | + | else => return ty, |
|
| 6924 | + | } |
|
| 6925 | + | } |
|
| 6926 | + | ||
| 6927 | + | /// Create a substituted signature with the specified remaining region binder. |
|
| 6928 | + | unsafe fn substituteFnRegions 'arena ( |
|
| 6929 | + | self: &mut Resolver 'arena, map: &RegionSubstitution, info: *FnType, regions: ?*RegionScope |
|
| 6930 | + | ) -> *FnType { |
|
| 6931 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 6932 | + | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 6933 | + | let mut throwList: *mut [*Type] = &mut []; |
|
| 6934 | + | for parameter in info.paramTypes { |
|
| 6935 | + | let ty = substituteRegions(self, map, *parameter); |
|
| 6936 | + | paramTypes.append(allocType(self, ty), a); |
|
| 6937 | + | } |
|
| 6938 | + | for error in info.throwList { |
|
| 6939 | + | let ty = substituteRegions(self, map, *error); |
|
| 6940 | + | throwList.append(allocType(self, ty), a); |
|
| 6941 | + | } |
|
| 6942 | + | let returnType = substituteRegions(self, map, *info.returnType); |
|
| 6943 | + | return allocFnType(self, FnType { |
|
| 6944 | + | regions, |
|
| 6945 | + | paramTypes: ¶mTypes[..], |
|
| 6946 | + | returnType: allocType(self, returnType), |
|
| 6947 | + | throwList: &throwList[..], |
|
| 6948 | + | isUnsafe: info.isUnsafe, |
|
| 6949 | + | }); |
|
| 6950 | + | } |
|
| 6951 | + | ||
| 6952 | + | /// Preserve a call-scoped pointer class while inferring its region-bearing contents. |
|
| 6953 | + | /// Named reference regions are inferred from the source storage. |
|
| 6954 | + | unsafe fn regionInputHint 'arena (self: &mut Resolver 'arena, expected: Type) -> Type { |
|
| 6955 | + | if let case Type::Optional(inner) = expected { |
|
| 6956 | + | return regionInputHint(self, *inner); |
|
| 6957 | + | } |
|
| 6958 | + | match expected { |
|
| 6959 | + | case Type::Pointer { class, mutable, .. } => { |
|
| 6960 | + | if let case types::PointerClass::Region(_) = class { |
|
| 6961 | + | return Type::Unknown; |
|
| 6962 | + | } |
|
| 6963 | + | return Type::Pointer { class, target: allocType(self, Type::Unknown), mutable }; |
|
| 6964 | + | } |
|
| 6965 | + | case Type::Slice { class, mutable, .. } => { |
|
| 6966 | + | if let case types::PointerClass::Region(_) = class { |
|
| 6967 | + | return Type::Unknown; |
|
| 6968 | + | } |
|
| 6969 | + | return Type::Slice { class, item: allocType(self, Type::Unknown), mutable }; |
|
| 6970 | + | } |
|
| 6971 | + | else => return Type::Unknown, |
|
| 6972 | + | } |
|
| 6973 | + | } |
|
| 6974 | + | ||
| 6975 | + | /// Infer a source function's region arguments from its call inputs. |
|
| 6976 | + | unsafe fn instantiateCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType) -> *FnType |
|
| 6977 | + | throws (ResolveError) |
|
| 6978 | + | { |
|
| 6979 | + | let parameters = info.regions else return info; |
|
| 6980 | + | if call.args.len <> info.paramTypes.len { |
|
| 6981 | + | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 6982 | + | expected: info.paramTypes.len, actual: call.args.len, |
|
| 6983 | + | })); |
|
| 6984 | + | } |
|
| 6985 | + | let map = regionSubstitution(self, parameters); |
|
| 6986 | + | for argument, i in call.args { |
|
| 6987 | + | let expected = *info.paramTypes[i]; |
|
| 6988 | + | if containsRegion(expected) { |
|
| 6989 | + | let actual = try visit(self, argument, regionInputHint(self, expected)); |
|
| 6990 | + | try inferRegionArguments(self, &map, expected, actual, argument); |
|
| 6991 | + | } |
|
| 6992 | + | } |
|
| 6993 | + | try validateRegionArguments(self, &map, node); |
|
| 6994 | + | return substituteFnRegions(self, &map, info, nil); |
|
| 6995 | + | } |
|
| 6996 | + | ||
| 6997 | + | /// Infer a method's region arguments from its receiver and call arguments. |
|
| 6998 | + | unsafe fn instantiateMethodCall 'arena ( |
|
| 6999 | + | self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, |
|
| 7000 | + | receiver: *ast::Node, receiverType: Type, method: *unsafe MethodEntry |
|
| 7001 | + | ) -> *FnType throws (ResolveError) { |
|
| 7002 | + | let parameters = method.fnType.regions else return method.fnType; |
|
| 7003 | + | if call.args.len <> method.fnType.paramTypes.len { |
|
| 7004 | + | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 7005 | + | expected: method.fnType.paramTypes.len, actual: call.args.len, |
|
| 7006 | + | })); |
|
| 7007 | + | } |
|
| 7008 | + | let map = regionSubstitution(self, parameters); |
|
| 7009 | + | try inferRegionArguments(self, &map, method.concreteType, receiverType, receiver); |
|
| 7010 | + | for argument, i in call.args { |
|
| 7011 | + | let expected = *method.fnType.paramTypes[i]; |
|
| 7012 | + | if containsRegion(expected) { |
|
| 7013 | + | let actual = try visit(self, argument, regionInputHint(self, expected)); |
|
| 7014 | + | try inferRegionArguments(self, &map, expected, actual, argument); |
|
| 7015 | + | } |
|
| 7016 | + | } |
|
| 7017 | + | try validateRegionArguments(self, &map, node); |
|
| 7018 | + | return substituteFnRegions(self, &map, method.fnType, nil); |
|
| 7019 | + | } |
|
| 7020 | + | ||
| 7021 | + | /// Apply explicit current-scope regions to a function or nominal type. |
|
| 7022 | + | unsafe fn resolveRegionApply 'arena ( |
|
| 7023 | + | self: &mut Resolver 'arena, node: *ast::Node, value: *ast::Node, regions: *[*ast::Node] |
|
| 7024 | + | ) -> Type throws (ResolveError) { |
|
| 7025 | + | let ty = try infer(self, value); |
|
| 7026 | + | if let case Type::Nominal(base) = ty { |
|
| 7027 | + | let symbol = symbolFor(self, value) else throw emitError(self, node, ErrorKind::CannotInferType); |
|
| 7028 | + | let case SymbolData::Type(_) = symbol.data else throw emitError(self, node, ErrorKind::CannotInferType); |
|
| 7029 | + | let applied = try applyNominalRegions(self, base, regions, node); |
|
| 7030 | + | try ensureNominalResolved(self, applied, node); |
|
| 7031 | + | setNodeSymbol(self, node, symbol); |
|
| 7032 | + | return setNodeType(self, node, Type::Nominal(applied)); |
|
| 7033 | + | } |
|
| 7034 | + | let case Type::Fn(info) = ty else throw emitError(self, node, ErrorKind::CannotInferType); |
|
| 7035 | + | let mut count: u32 = 0; |
|
| 7036 | + | if let scope = info.regions { |
|
| 7037 | + | set count = scope.entries.len; |
|
| 7038 | + | } |
|
| 7039 | + | if count <> regions.len { |
|
| 7040 | + | throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len })); |
|
| 7041 | + | } |
|
| 7042 | + | let scope = info.regions else panic "resolveRegionApply: empty region application"; |
|
| 7043 | + | let map = regionSubstitution(self, scope); |
|
| 7044 | + | for region, i in regions { |
|
| 7045 | + | set map.arguments[i] = try resolveRegion(self, region); |
|
| 7046 | + | } |
|
| 7047 | + | try validateRegionArguments(self, &map, node); |
|
| 7048 | + | let applied = substituteFnRegions(self, &map, info, nil); |
|
| 7049 | + | if let symbol = symbolFor(self, value) { |
|
| 7050 | + | setNodeSymbol(self, node, symbol); |
|
| 7051 | + | } |
|
| 7052 | + | return setNodeType(self, node, Type::Fn(applied)); |
|
| 7053 | + | } |
|
| 7054 | + | ||
| 7055 | + | /// Validate call arguments against a function type: check argument count, |
|
| 7056 | + | /// type-check each argument, and verify that throwing functions use `try`. |
|
| 7057 | + | unsafe fn checkCallArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx) |
|
| 7058 | + | throws (ResolveError) |
|
| 7059 | + | { |
|
| 7060 | + | if ctx == CallCtx::Normal and info.throwList.len > 0 { |
|
| 7061 | + | throw emitError(self, node, ErrorKind::MissingTry); |
|
| 7062 | + | } |
|
| 7063 | + | if call.args.len <> info.paramTypes.len as u32 { |
|
| 7064 | + | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 7065 | + | expected: info.paramTypes.len as u32, |
|
| 7066 | + | actual: call.args.len, |
|
| 7067 | + | })); |
|
| 7068 | + | } |
|
| 7069 | + | for argNode, i in call.args { |
|
| 7070 | + | let expectedTy = *info.paramTypes[i]; |
|
| 7071 | + | ||
| 7072 | + | try checkAssignable(self, argNode, expectedTy); |
|
| 7073 | + | } |
|
| 7074 | + | } |
|
| 7075 | + | ||
| 7076 | + | /// Return whether a value can be discarded by bulk arena reclamation. |
|
| 7077 | + | /// Pointer lifetimes are checked when their values are constructed. |
|
| 7078 | + | unsafe fn isBulkDiscardable(ty: Type) -> bool { |
|
| 7079 | + | match ty { |
|
| 7080 | + | case Type::Void, Type::Bool, Type::U8, Type::U16, Type::U32, Type::U64, |
|
| 7081 | + | Type::I8, Type::I16, Type::I32, Type::I64, Type::Fn(_) => return true, |
|
| 7082 | + | case Type::Pointer { .. }, Type::Slice { .. }, Type::TraitObject { .. } => return true, |
|
| 7083 | + | case Type::Cell { .. } => return true, |
|
| 7084 | + | case Type::Array(array) => return isBulkDiscardable(*array.item), |
|
| 7085 | + | case Type::Optional(inner) => return isBulkDiscardable(*inner), |
|
| 7086 | + | case Type::Nominal(NominalType::Record(recordType)) => { |
|
| 7087 | + | if recordType.declaredLinear { |
|
| 7088 | + | return false; |
|
| 7089 | + | } |
|
| 7090 | + | for field in recordType.fields { |
|
| 7091 | + | if not isBulkDiscardable(field.fieldType) { |
|
| 7092 | + | return false; |
|
| 7093 | + | } |
|
| 7094 | + | } |
|
| 7095 | + | return true; |
|
| 7096 | + | } |
|
| 7097 | + | case Type::Nominal(NominalType::Union(unionType)) => { |
|
| 7098 | + | if unionType.declaredLinear { |
|
| 7099 | + | return false; |
|
| 7100 | + | } |
|
| 7101 | + | for variant in unionType.variants { |
|
| 7102 | + | if not isBulkDiscardable(variant.valueType) { |
|
| 7103 | + | return false; |
|
| 7104 | + | } |
|
| 7105 | + | } |
|
| 7106 | + | return true; |
|
| 7107 | + | } |
|
| 7108 | + | else => return false, |
|
| 7109 | + | } |
|
| 7110 | + | } |
|
| 7111 | + | ||
| 7112 | + | /// Compute the end of an aligned layout within the allocator's byte-count range. |
|
| 7113 | + | fn allocationLayoutEnd(offset: u64, layout: Layout) -> ?u64 { |
|
| 7114 | + | let mut aligned = offset; |
|
| 7115 | + | if layout.alignment > 0 { |
|
| 7116 | + | let mask = (layout.alignment - 1) as u64; |
|
| 7117 | + | set aligned = (offset + mask) & ~mask; |
|
| 7118 | + | } |
|
| 7119 | + | let end = aligned + layout.size as u64; |
|
| 7120 | + | if end > 4294967295 { |
|
| 7121 | + | return nil; |
|
| 7122 | + | } |
|
| 7123 | + | return end; |
|
| 7124 | + | } |
|
| 7125 | + | ||
| 7126 | + | /// Check allocation layout arithmetic independently of the stored narrow offsets. |
|
| 7127 | + | unsafe fn hasAllocationLayout(ty: Type) -> bool { |
|
| 7128 | + | let layout = getTypeLayout(ty); |
|
| 7129 | + | match ty { |
|
| 7130 | + | case Type::Cell { .. } => return true, |
|
| 7131 | + | case Type::Array(array) => { |
|
| 7132 | + | if not hasAllocationLayout(*array.item) { |
|
| 7133 | + | return false; |
|
| 7134 | + | } |
|
| 7135 | + | let item = getTypeLayout(*array.item); |
|
| 7136 | + | return item.size as u64 * array.length as u64 == layout.size as u64; |
|
| 7137 | + | } |
|
| 7138 | + | case Type::Optional(inner) => { |
|
| 7139 | + | if not hasAllocationLayout(*inner) { |
|
| 7140 | + | return false; |
|
| 7141 | + | } |
|
| 7142 | + | if isNullableType(*inner) { |
|
| 7143 | + | return true; |
|
| 7144 | + | } |
|
| 7145 | + | let end = allocationLayoutEnd(1, getTypeLayout(*inner)) else return false; |
|
| 7146 | + | let total = allocationLayoutEnd(end, Layout { size: 0, alignment: layout.alignment }) else return false; |
|
| 7147 | + | return total == layout.size as u64; |
|
| 7148 | + | } |
|
| 7149 | + | case Type::Nominal(NominalType::Record(recordType)) => { |
|
| 7150 | + | let mut offset: u64 = 0; |
|
| 7151 | + | for field in recordType.fields { |
|
| 7152 | + | if not hasAllocationLayout(field.fieldType) { |
|
| 7153 | + | return false; |
|
| 7154 | + | } |
|
| 7155 | + | let fieldLayout = getTypeLayout(field.fieldType); |
|
| 7156 | + | let end = allocationLayoutEnd(offset, fieldLayout) else return false; |
|
| 7157 | + | let start = end - fieldLayout.size as u64; |
|
| 7158 | + | if start > 2147483647 or field.offset < 0 or start <> field.offset as u64 { |
|
| 7159 | + | return false; |
|
| 7160 | + | } |
|
| 7161 | + | set offset = end; |
|
| 7162 | + | } |
|
| 7163 | + | let total = allocationLayoutEnd(offset, Layout { size: 0, alignment: layout.alignment }) else return false; |
|
| 7164 | + | return total == layout.size as u64; |
|
| 7165 | + | } |
|
| 7166 | + | case Type::Nominal(NominalType::Union(unionType)) => { |
|
| 7167 | + | let mut payloadSize: u32 = 0; |
|
| 7168 | + | let mut alignment: u32 = 1; |
|
| 7169 | + | for variant in unionType.variants { |
|
| 7170 | + | if not hasAllocationLayout(variant.valueType) { |
|
| 7171 | + | return false; |
|
| 7172 | + | } |
|
| 7173 | + | let item = getTypeLayout(variant.valueType); |
|
| 7174 | + | set payloadSize = max(payloadSize, item.size); |
|
| 7175 | + | set alignment = max(alignment, item.alignment); |
|
| 7176 | + | } |
|
| 7177 | + | let end = allocationLayoutEnd(1, Layout { size: payloadSize, alignment }) else return false; |
|
| 7178 | + | let total = allocationLayoutEnd(end, Layout { size: 0, alignment }) else return false; |
|
| 7179 | + | return total == layout.size as u64 and end - payloadSize as u64 == unionType.valOffset as u64; |
|
| 7180 | + | } |
|
| 7181 | + | else => return true, |
|
| 7182 | + | } |
|
| 7183 | + | } |
|
| 7184 | + | ||
| 7185 | + | /// Validate the reservation ABI used by typed session allocation. |
|
| 7186 | + | unsafe fn sessionRuntime 'arena ( |
|
| 7187 | + | self: &mut Resolver 'arena, node: *ast::Node, slice: bool |
|
| 7188 | + | ) -> *unsafe TraitMethod |
|
| 7189 | + | throws (ResolveError) |
|
| 7190 | + | { |
|
| 7191 | + | let allocTrait = allocationSymbol(self, "Alloc") |
|
| 7192 | + | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7193 | + | let case SymbolData::Trait(allocInfo) = allocTrait.data |
|
| 7194 | + | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7195 | + | let name = "reserveSlice" if slice else "reserve"; |
|
| 7196 | + | let method = findTraitMethod(allocInfo, name) |
|
| 7197 | + | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7198 | + | let error = allocationSymbol(self, "AllocError") |
|
| 7199 | + | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7200 | + | let case SymbolData::Type(errorType) = error.data |
|
| 7201 | + | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7202 | + | let count: u32 = 3 if slice else 2; |
|
| 7203 | + | let info = method.fnType; |
|
| 7204 | + | if info.regions <> nil or not info.isUnsafe or info.paramTypes.len <> count or info.throwList.len <> 1 { |
|
| 7205 | + | throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7206 | + | } |
|
| 7207 | + | if not typesEqual(*info.throwList[0], Type::Nominal(errorType)) { |
|
| 7208 | + | throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7209 | + | } |
|
| 7210 | + | for i in 0..count { |
|
| 7211 | + | if *info.paramTypes[i] <> Type::U32 { |
|
| 7212 | + | throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7213 | + | } |
|
| 7214 | + | } |
|
| 7215 | + | let expected = Type::Slice { class: types::PointerClass::Unsafe, item: allocType(self, Type::Opaque), mutable: true } |
|
| 7216 | + | if slice else Type::Pointer { |
|
| 7217 | + | class: types::PointerClass::Unsafe, target: allocType(self, Type::Opaque), mutable: true |
|
| 7218 | + | }; |
|
| 7219 | + | if not typesEqual(*info.returnType, expected) { |
|
| 7220 | + | throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7221 | + | } |
|
| 7222 | + | return method; |
|
| 7223 | + | } |
|
| 7224 | + | ||
| 7225 | + | /// Check initialized session allocation and retain its source region in the result. |
|
| 7226 | + | unsafe fn resolveSessionAllocation 'arena ( |
|
| 7227 | + | self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, access: ast::Access, |
|
| 7228 | + | region: *unsafe types::Region, ctx: CallCtx, hint: Type |
|
| 7229 | + | ) -> Type throws (ResolveError) { |
|
| 7230 | + | let name = try nodeName(self, access.child); |
|
| 7231 | + | let mut kind = SessionAllocationKind::New; |
|
| 7232 | + | if mem::eq(name, "copy") { |
|
| 7233 | + | set kind = SessionAllocationKind::Copy; |
|
| 7234 | + | } |
|
| 7235 | + | else if mem::eq(name, "fill") { |
|
| 7236 | + | set kind = SessionAllocationKind::Fill; |
|
| 7237 | + | } |
|
| 7238 | + | else if not mem::eq(name, "new") { |
|
| 7239 | + | throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(name)); |
|
| 7240 | + | } |
|
| 7241 | + | let count: u32 = 2 if kind == SessionAllocationKind::Fill else 1; |
|
| 7242 | + | if call.args.len <> count { |
|
| 7243 | + | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { expected: count, actual: call.args.len })); |
|
| 7244 | + | } |
|
| 7245 | + | let slice = kind <> SessionAllocationKind::New; |
|
| 7246 | + | let mut itemHint = Type::Unknown; |
|
| 7247 | + | if kind == SessionAllocationKind::New { |
|
| 7248 | + | if let case Type::Pointer { target, .. } = hint { |
|
| 7249 | + | set itemHint = *target; |
|
| 7250 | + | } |
|
| 7251 | + | } else if kind == SessionAllocationKind::Fill { |
|
| 7252 | + | if let case Type::Slice { item, .. } = hint { |
|
| 7253 | + | set itemHint = *item; |
|
| 7254 | + | } |
|
| 7255 | + | } else { |
|
| 7256 | + | set itemHint = Type::Slice { |
|
| 7257 | + | class: types::PointerClass::Ref, item: allocType(self, Type::Unknown), mutable: false, |
|
| 7258 | + | }; |
|
| 7259 | + | } |
|
| 7260 | + | let valueType = try visit(self, call.args[0], itemHint); |
|
| 7261 | + | let mut itemType = valueType; |
|
| 7262 | + | let mut parameter = valueType; |
|
| 7263 | + | if kind == SessionAllocationKind::Copy { |
|
| 7264 | + | let case Type::Slice { item, .. } = valueType |
|
| 7265 | + | else throw emitError(self, call.args[0], ErrorKind::ExpectedIndexable); |
|
| 7266 | + | set itemType = *item; |
|
| 7267 | + | set parameter = Type::Slice { class: types::PointerClass::Ref, item, mutable: false }; |
|
| 7268 | + | } |
|
| 7269 | + | if not isTypeInferrable(itemType) or itemType == Type::Void or itemType == Type::Opaque { |
|
| 7270 | + | throw emitError(self, call.args[0], ErrorKind::CannotInferType); |
|
| 7271 | + | } |
|
| 7272 | + | try ensureStorableType(self, call.args[0], itemType); |
|
| 7273 | + | try ensureTypeResolved(self, itemType, call.args[0]); |
|
| 7274 | + | try validateRegionStorage(self, call.args[0], itemType, region); |
|
| 7275 | + | if not isBulkDiscardable(itemType) or (slice and not isCopy(itemType)) { |
|
| 7276 | + | throw emitError(self, call.args[0], ErrorKind::InvalidAllocationValue); |
|
| 7277 | + | } |
|
| 7278 | + | if not hasAllocationLayout(itemType) { |
|
| 7279 | + | throw emitError(self, call.args[0], ErrorKind::InvalidAllocationLayout); |
|
| 7280 | + | } |
|
| 7281 | + | let runtime = try sessionRuntime(self, node, slice); |
|
| 7282 | + | let runtimeType = runtime.fnType; |
|
| 7283 | + | let item = allocType(self, itemType); |
|
| 7284 | + | let result = Type::Slice { class: types::PointerClass::Region(region), item, mutable: true } |
|
| 7285 | + | if slice else Type::Pointer { |
|
| 7286 | + | class: types::PointerClass::Region(region), target: item, mutable: true |
|
| 7287 | + | }; |
|
| 7288 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 7289 | + | let mut parameters: *mut [*Type] = &mut []; |
|
| 7290 | + | parameters.append(allocType(self, parameter), a); |
|
| 7291 | + | if kind == SessionAllocationKind::Fill { |
|
| 7292 | + | parameters.append(allocType(self, Type::U32), a); |
|
| 7293 | + | } |
|
| 7294 | + | let info = allocFnType(self, FnType { |
|
| 7295 | + | regions: nil, paramTypes: ¶meters[..], returnType: allocType(self, result), |
|
| 7296 | + | throwList: runtimeType.throwList, isUnsafe: false, |
|
| 7297 | + | }); |
|
| 7298 | + | try checkCallArgs(self, node, call, info, ctx); |
|
| 7299 | + | setNodeType(self, call.callee, Type::Fn(info)); |
|
| 7300 | + | let allocTrait = allocationSymbol(self, "Alloc") else panic; |
|
| 7301 | + | let case SymbolData::Trait(traitInfo) = allocTrait.data else panic; |
|
| 7302 | + | set self.nodeData.entries[node.id].extra = NodeExtra::SessionAllocation(SessionAllocation { |
|
| 7303 | + | kind, item, traitInfo, methodIndex: runtime.index, |
|
| 7304 | + | }); |
|
| 7305 | + | return setNodeType(self, node, result); |
|
| 7306 | + | } |
|
| 7307 | + | ||
| 7308 | + | /// Return whether a nominal declaration explicitly derives Copy. |
|
| 7309 | + | fn nominalDeclaresCopy(node: *ast::Node) -> bool { |
|
| 7310 | + | let mut derives: *[*ast::Node] = &[]; |
|
| 7311 | + | match node.value { |
|
| 7312 | + | case ast::NodeValue::RecordDecl(decl) => set derives = decl.derives, |
|
| 7313 | + | case ast::NodeValue::UnionDecl(decl) => set derives = decl.derives, |
|
| 7314 | + | else => return false, |
|
| 7315 | + | } |
|
| 7316 | + | for derive in derives { |
|
| 7317 | + | if let case ast::NodeValue::Ident(name) = derive.value { |
|
| 7318 | + | if mem::eq(name, "Copy") { |
|
| 7319 | + | return true; |
|
| 7320 | + | } |
|
| 7321 | + | } |
|
| 7322 | + | } |
|
| 7323 | + | return false; |
|
| 7324 | + | } |
|
| 7325 | + | ||
| 7326 | + | /// Require a complete payload that can be copied and discarded by value. |
|
| 7327 | + | unsafe fn validateCellPayload 'arena (self: &mut Resolver 'arena, node: *ast::Node, payload: Type) throws (ResolveError) { |
|
| 7328 | + | try ensureStorableType(self, node, payload); |
|
| 7329 | + | if let case Type::Nominal(info) = payload { |
|
| 7330 | + | let mut source = info; |
|
| 7331 | + | if let case NominalType::Application(applied) = *source { |
|
| 7332 | + | set source = applied.base; |
|
| 7333 | + | } |
|
| 7334 | + | if let case NominalType::Resolving(decl) = *source { |
|
| 7335 | + | if nominalDeclaresCopy(decl) { |
|
| 7336 | + | return; |
|
| 7337 | + | } |
|
| 7338 | + | throw emitError(self, node, ErrorKind::InvalidCellPayload); |
|
| 7339 | + | } |
|
| 7340 | + | } |
|
| 7341 | + | try ensureTypeResolved(self, payload, node); |
|
| 7342 | + | if not isTypeInferrable(payload) or payload == Type::Void or payload == Type::Opaque |
|
| 7343 | + | or not isCopy(payload) or not isBulkDiscardable(payload) |
|
| 7344 | + | { |
|
| 7345 | + | throw emitError(self, node, ErrorKind::InvalidCellPayload); |
|
| 7346 | + | } |
|
| 7347 | + | if not hasAllocationLayout(payload) { |
|
| 7348 | + | throw emitError(self, node, ErrorKind::InvalidAllocationLayout); |
|
| 7349 | + | } |
|
| 7350 | + | } |
|
| 7351 | + | ||
| 7352 | + | /// Analyze a function call expression. |
|
| 7353 | + | unsafe fn resolveCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, ctx: CallCtx, hint: Type) -> Type |
|
| 7354 | + | throws (ResolveError) |
|
| 5586 | 7355 | { |
|
| 5587 | 7356 | // Intercept method calls on slices before inferring the callee. |
|
| 5588 | 7357 | if let case ast::NodeValue::FieldAccess(access) = call.callee.value { |
|
| 5589 | 7358 | let parentTy = try infer(self, access.parent); |
|
| 5590 | 7359 | if isUnsafePointerType(parentTy) { |
|
| 5591 | 7360 | try requireUnsafe(self, access.parent); |
|
| 5592 | 7361 | } |
|
| 5593 | 7362 | let subjectTy = autoDeref(parentTy); |
|
| 7363 | + | if let case Type::Session(region) = subjectTy { |
|
| 7364 | + | return try resolveSessionAllocation(self, node, call, access, region, ctx, hint); |
|
| 7365 | + | } |
|
| 5594 | 7366 | ||
| 5595 | 7367 | if let case Type::Slice { item, mutable, .. } = subjectTy { |
|
| 5596 | 7368 | let methodName = try nodeName(self, access.child); |
|
| 5597 | 7369 | if methodName == "append" { |
|
| 5598 | 7370 | return try resolveSliceAppend( |
| 5604 | 7376 | self, node, access.parent, call.args, item, mutable |
|
| 5605 | 7377 | ); |
|
| 5606 | 7378 | } |
|
| 5607 | 7379 | } |
|
| 5608 | 7380 | } |
|
| 5609 | - | let calleeTy = try infer(self, call.callee); |
|
| 7381 | + | let calleeTy = try visit(self, call.callee, hint); |
|
| 5610 | 7382 | if let case Type::Fn(info) = calleeTy { |
|
| 5611 | 7383 | try checkUnsafeCall(self, call.callee, info); |
|
| 5612 | 7384 | } |
|
| 5613 | 7385 | ||
| 5614 | 7386 | // Check if callee is a union variant and dispatch to constructor handler. |
|
| 5615 | 7387 | // TODO: Move this out. We should decide on this earlier, based on the callee. |
|
| 5616 | 7388 | if let calleeSym = symbolFor(self, call.callee) { |
|
| 5617 | 7389 | if let case SymbolData::Variant { decl, .. } = calleeSym.data { |
|
| 5618 | 7390 | // TODO: Don't pass the callee type, pass the union type by getting it from |
|
| 5619 | 7391 | // the symbol. |
|
| 5620 | - | let declSym = symbolFor(self, decl) else panic; |
|
| 5621 | - | let case SymbolData::Type(ty) = declSym.data else panic; |
|
| 5622 | - | ||
| 7392 | + | let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid variant type"; |
|
| 5623 | 7393 | return try resolveUnionConstructorCall(self, node, call, ty); |
|
| 5624 | 7394 | } |
|
| 5625 | 7395 | // Check if callee is an unlabeled record type for constructor call syntax. |
|
| 5626 | - | if let case SymbolData::Type(ty) = calleeSym.data { |
|
| 7396 | + | if let case SymbolData::Type(_) = calleeSym.data { |
|
| 7397 | + | let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid type callee"; |
|
| 7398 | + | try requireNominalArguments(self, ty, call.callee); |
|
| 5627 | 7399 | // Ensure the record body is resolved before checking if labeled. |
|
| 5628 | 7400 | try ensureNominalResolved(self, ty, call.callee); |
|
| 5629 | 7401 | if let case NominalType::Record(recInfo) = *ty { |
|
| 5630 | 7402 | if not recInfo.labeled { |
|
| 5631 | 7403 | return try resolveRecordConstructorCall(self, node, call, ty); |
| 5646 | 7418 | let methodName = try nodeName(self, access.child); |
|
| 5647 | 7419 | let method = findTraitMethod(traitInfo, methodName) |
|
| 5648 | 7420 | else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName)); |
|
| 5649 | 7421 | ||
| 5650 | 7422 | // Reject mutable-receiver methods called on immutable trait objects. |
|
| 5651 | - | if method.mutable and not objMutable { |
|
| 5652 | - | throw emitError(self, access.parent, ErrorKind::ImmutableBinding); |
|
| 7423 | + | if method.mutable { |
|
| 7424 | + | if not objMutable or not try canMutateThrough(self, access.parent) { |
|
| 7425 | + | throw emitError(self, access.parent, ErrorKind::ImmutableBinding); |
|
| 7426 | + | } |
|
| 5653 | 7427 | } |
|
| 5654 | - | try checkCallArgs(self, node, call, method.fnType, ctx); |
|
| 7428 | + | let applied = try instantiateCall(self, node, call, method.fnType); |
|
| 7429 | + | try checkCallArgs(self, node, call, applied, ctx); |
|
| 5655 | 7430 | setTraitMethodCall(self, node, traitInfo, method.index); |
|
| 5656 | 7431 | ||
| 5657 | - | return setNodeType(self, node, *method.fnType.returnType); |
|
| 7432 | + | return setNodeType(self, node, *applied.returnType); |
|
| 5658 | 7433 | } |
|
| 5659 | 7434 | ||
| 5660 | 7435 | // Check for a standalone method call on a concrete type. |
|
| 5661 | 7436 | if let case Type::Nominal(_) = subjectTy { |
|
| 5662 | 7437 | let methodName = try nodeName(self, access.child); |
| 5668 | 7443 | if not try canMutateThrough(self, access.parent) { |
|
| 5669 | 7444 | throw emitError(self, access.parent, ErrorKind::ImmutableBinding); |
|
| 5670 | 7445 | } |
|
| 5671 | 7446 | } |
|
| 5672 | 7447 | // Check arguments (excluding receiver). |
|
| 5673 | - | try checkCallArgs(self, node, call, method.fnType, ctx); |
|
| 7448 | + | let applied = try instantiateMethodCall( |
|
| 7449 | + | self, node, call, access.parent, subjectTy, method |
|
| 7450 | + | ); |
|
| 7451 | + | try checkCallArgs(self, node, call, applied, ctx); |
|
| 5674 | 7452 | set self.nodeData.entries[node.id].extra = NodeExtra::MethodCall { method }; |
|
| 5675 | 7453 | ||
| 5676 | - | return setNodeType(self, node, *method.fnType.returnType); |
|
| 7454 | + | return setNodeType(self, node, *applied.returnType); |
|
| 5677 | 7455 | } |
|
| 5678 | 7456 | } |
|
| 5679 | 7457 | } |
|
| 5680 | 7458 | let case Type::Fn(info) = calleeTy else { |
|
| 5681 | 7459 | throw emitError(self, call.callee, ErrorKind::TypeMismatch(TypeMismatch { |
|
| 5682 | 7460 | expected: Type::Unknown, |
|
| 5683 | 7461 | actual: calleeTy, |
|
| 5684 | 7462 | })); |
|
| 5685 | 7463 | }; |
|
| 5686 | - | try checkCallArgs(self, node, call, info, ctx); |
|
| 7464 | + | let applied = try instantiateCall(self, node, call, info); |
|
| 7465 | + | try checkCallArgs(self, node, call, applied, ctx); |
|
| 5687 | 7466 | // Associate function type to callee. |
|
| 5688 | - | setNodeType(self, call.callee, calleeTy); |
|
| 7467 | + | setNodeType(self, call.callee, Type::Fn(applied)); |
|
| 5689 | 7468 | ||
| 5690 | 7469 | // Associate return type to call. |
|
| 5691 | - | return setNodeType(self, node, *info.returnType); |
|
| 7470 | + | return setNodeType(self, node, *applied.returnType); |
|
| 5692 | 7471 | } |
|
| 5693 | 7472 | ||
| 5694 | 7473 | /// Check the allocator layout and the callback ABI used by slice append. |
|
| 5695 | 7474 | unsafe fn isSliceAllocator(ty: Type) -> bool { |
|
| 5696 | 7475 | let case Type::Nominal(NominalType::Record(rec)) = ty else return false; |
| 5720 | 7499 | else return false; |
|
| 5721 | 7500 | return class == types::PointerClass::Owned and mutable and *result == Type::Opaque; |
|
| 5722 | 7501 | } |
|
| 5723 | 7502 | ||
| 5724 | 7503 | /// Resolve `slice.append(val, allocator)`. |
|
| 5725 | - | unsafe fn resolveSliceAppend( |
|
| 5726 | - | self: &mut Resolver, |
|
| 7504 | + | unsafe fn resolveSliceAppend 'arena ( |
|
| 7505 | + | self: &mut Resolver 'arena, |
|
| 5727 | 7506 | node: *ast::Node, |
|
| 5728 | 7507 | parent: *ast::Node, |
|
| 5729 | 7508 | parentType: Type, |
|
| 5730 | 7509 | args: *[*ast::Node], |
|
| 5731 | 7510 | elemType: *Type, |
| 5755 | 7534 | // Return the parent's type so the caller can rebind: |
|
| 5756 | 7535 | return setNodeType(self, node, parentType); |
|
| 5757 | 7536 | } |
|
| 5758 | 7537 | ||
| 5759 | 7538 | /// Resolve `slice.delete(index)`. |
|
| 5760 | - | unsafe fn resolveSliceDelete( |
|
| 5761 | - | self: &mut Resolver, |
|
| 7539 | + | unsafe fn resolveSliceDelete 'arena ( |
|
| 7540 | + | self: &mut Resolver 'arena, |
|
| 5762 | 7541 | node: *ast::Node, |
|
| 5763 | 7542 | parent: *ast::Node, |
|
| 5764 | 7543 | args: *[*ast::Node], |
|
| 5765 | 7544 | elemType: *Type, |
|
| 5766 | 7545 | mutable: bool |
| 5779 | 7558 | ||
| 5780 | 7559 | return setNodeType(self, node, Type::Void); |
|
| 5781 | 7560 | } |
|
| 5782 | 7561 | ||
| 5783 | 7562 | /// Analyze an assignment expression. |
|
| 5784 | - | unsafe fn resolveAssign(self: &mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type |
|
| 7563 | + | unsafe fn resolveAssign 'arena (self: &mut Resolver 'arena, node: *ast::Node, assign: ast::Assign) -> Type |
|
| 5785 | 7564 | throws (ResolveError) |
|
| 5786 | 7565 | { |
|
| 5787 | 7566 | // Slice assignment: `slice[range] = value`. |
|
| 5788 | 7567 | if let case ast::NodeValue::Subscript { container, index } = assign.left.value { |
|
| 5789 | 7568 | if let case ast::NodeValue::Range(range) = index.value { |
| 5824 | 7603 | ); |
|
| 5825 | 7604 | } |
|
| 5826 | 7605 | } else { |
|
| 5827 | 7606 | try checkAssignable(self, assign.right, *item); |
|
| 5828 | 7607 | } |
|
| 7608 | + | try validateRegionalStore(self, assign.left, assign.right, *item); |
|
| 5829 | 7609 | setSliceRangeInfo(self, node, SliceRangeInfo { itemType: item, mutable: true, capacity }); |
|
| 5830 | 7610 | setNodeType(self, assign.left, *item); |
|
| 5831 | 7611 | ||
| 5832 | 7612 | return setNodeType(self, node, Type::Void); |
|
| 5833 | 7613 | } |
|
| 5834 | 7614 | } |
|
| 5835 | 7615 | let leftTy = try infer(self, assign.left); |
|
| 5836 | 7616 | ||
| 7617 | + | if let case ast::NodeValue::Deref(target) = assign.left.value { |
|
| 7618 | + | if let case Type::Cell { class, .. } = try infer(self, target) { |
|
| 7619 | + | try checkAssignable(self, assign.right, leftTy); |
|
| 7620 | + | if let case types::PointerClass::Region(region) = class { |
|
| 7621 | + | try validateRegionStorage(self, assign.right, leftTy, region); |
|
| 7622 | + | } else if class == types::PointerClass::Owned and containsRegion(leftTy) { |
|
| 7623 | + | throw emitError(self, assign.right, ErrorKind::InvalidCellPayload); |
|
| 7624 | + | } |
|
| 7625 | + | return setNodeType(self, node, leftTy); |
|
| 7626 | + | } |
|
| 7627 | + | } |
|
| 7628 | + | ||
| 5837 | 7629 | // Check if the left-hand side can be assigned to by checking if it's a mutable location. |
|
| 5838 | 7630 | if not try canBorrowMutFrom(self, assign.left) { |
|
| 5839 | 7631 | throw emitError(self, assign.left, ErrorKind::ImmutableBinding); |
|
| 5840 | 7632 | } |
|
| 5841 | 7633 | try checkAssignable(self, assign.right, leftTy); |
|
| 7634 | + | try validateRegionalStore(self, assign.left, assign.right, leftTy); |
|
| 5842 | 7635 | ||
| 5843 | 7636 | return setNodeType(self, node, leftTy); |
|
| 5844 | 7637 | } |
|
| 5845 | 7638 | ||
| 5846 | 7639 | /// Ensure slice range bounds are valid `u32` values. |
|
| 5847 | - | unsafe fn checkSliceRangeIndices(self: &mut Resolver, range: ast::Range) throws (ResolveError) { |
|
| 7640 | + | unsafe fn checkSliceRangeIndices 'arena (self: &mut Resolver 'arena, range: ast::Range) throws (ResolveError) { |
|
| 5848 | 7641 | if let start = range.start { |
|
| 5849 | 7642 | try checkIndex(self, start); |
|
| 5850 | 7643 | } |
|
| 5851 | 7644 | if let end = range.end { |
|
| 5852 | 7645 | try checkIndex(self, end); |
|
| 5853 | 7646 | } |
|
| 5854 | 7647 | } |
|
| 5855 | 7648 | ||
| 5856 | 7649 | /// Emit an error when a slice range with compile-tyime values exceeds the array length. |
|
| 5857 | - | unsafe fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) { |
|
| 7650 | + | unsafe fn validateArraySliceBounds 'arena (self: &mut Resolver 'arena, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) { |
|
| 5858 | 7651 | let mut startVal: ?u32 = nil; |
|
| 5859 | 7652 | let mut endVal: ?u32 = length; |
|
| 5860 | 7653 | ||
| 5861 | 7654 | if let startNode = range.start { |
|
| 5862 | 7655 | if let val = constSliceIndex(self, startNode) { |
| 5882 | 7675 | } |
|
| 5883 | 7676 | ||
| 5884 | 7677 | /// Check that an index expression has an unsigned integer type. |
|
| 5885 | 7678 | /// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals. |
|
| 5886 | 7679 | /// Smaller types are widened to `u32` via a numeric cast coercion. |
|
| 5887 | - | unsafe fn checkIndex(self: &mut Resolver, indexNode: *ast::Node) throws (ResolveError) { |
|
| 7680 | + | unsafe fn checkIndex 'arena (self: &mut Resolver 'arena, indexNode: *ast::Node) throws (ResolveError) { |
|
| 5888 | 7681 | let indexTy = try visit(self, indexNode, Type::U32); |
|
| 5889 | 7682 | if indexTy == Type::Int or indexTy == Type::U32 { |
|
| 5890 | 7683 | let _ = try expectAssignable(self, Type::U32, indexTy, indexNode); |
|
| 5891 | 7684 | return; |
|
| 5892 | 7685 | } |
| 5904 | 7697 | } |
|
| 5905 | 7698 | } |
|
| 5906 | 7699 | } |
|
| 5907 | 7700 | ||
| 5908 | 7701 | /// Analyze an array or slice subscript expression. |
|
| 5909 | - | unsafe fn resolveSubscript(self: &mut Resolver, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type |
|
| 7702 | + | unsafe fn resolveSubscript 'arena (self: &mut Resolver 'arena, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type |
|
| 5910 | 7703 | throws (ResolveError) |
|
| 5911 | 7704 | { |
|
| 5912 | 7705 | // Range subscripts always require `&` to form a slice. |
|
| 5913 | 7706 | if let case ast::NodeValue::Range(range) = indexNode.value { |
|
| 5914 | 7707 | let _ = try infer(self, indexNode); |
| 5947 | 7740 | } |
|
| 5948 | 7741 | return nil; |
|
| 5949 | 7742 | } |
|
| 5950 | 7743 | ||
| 5951 | 7744 | /// Analyze a union constructor call with payload. |
|
| 5952 | - | unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type |
|
| 7745 | + | unsafe fn resolveUnionConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type |
|
| 5953 | 7746 | throws (ResolveError) |
|
| 5954 | 7747 | { |
|
| 5955 | 7748 | // Get the union nominal type. |
|
| 5956 | 7749 | let case NominalType::Union(unionType) = *unionNominal |
|
| 5957 | 7750 | else panic "resolveUnionConstructorCall: not a union type"; |
| 5966 | 7759 | setVariantInfo(self, node, index, tag); |
|
| 5967 | 7760 | ||
| 5968 | 7761 | // Check if this variant expects a payload. |
|
| 5969 | 7762 | let payloadType = variant.valueType; |
|
| 5970 | 7763 | if payloadType <> Type::Void { |
|
| 7764 | + | try ensureTypeResolved(self, payloadType, node); |
|
| 5971 | 7765 | let recInfo = getRecord(payloadType) |
|
| 5972 | 7766 | else panic "resolveUnionVariantConstructor: payload is not a record"; |
|
| 5973 | 7767 | try checkRecordConstructorArgs(self, node, call.args, recInfo); |
|
| 5974 | 7768 | } else { |
|
| 5975 | 7769 | if call.args.len > 0 { |
| 5982 | 7776 | /// Analyze an unlabeled record constructor call. |
|
| 5983 | 7777 | /// |
|
| 5984 | 7778 | /// Handles the syntax `R(a, b)` for unlabeled records, checking that the |
|
| 5985 | 7779 | /// number of arguments matches the record's field count and that each argument |
|
| 5986 | 7780 | /// is assignable to its corresponding field type. |
|
| 5987 | - | unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type |
|
| 7781 | + | unsafe fn resolveRecordConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type |
|
| 5988 | 7782 | throws (ResolveError) |
|
| 5989 | 7783 | { |
|
| 5990 | 7784 | let case NominalType::Record(recInfo) = *recordType |
|
| 5991 | 7785 | else panic "resolveRecordConstructorCall: not a record type"; |
|
| 5992 | 7786 |
| 5994 | 7788 | return setNodeType(self, node, Type::Nominal(recordType)); |
|
| 5995 | 7789 | } |
|
| 5996 | 7790 | ||
| 5997 | 7791 | /// Resolve the type name of a record literal, handling both record types and |
|
| 5998 | 7792 | /// union variant payloads like `Union::Variant { ... }`. |
|
| 5999 | - | unsafe fn resolveRecordLitType( |
|
| 6000 | - | self: &mut Resolver, node: *ast::Node, typeIdent: *ast::Node |
|
| 7793 | + | unsafe fn resolveRecordLitType 'arena ( |
|
| 7794 | + | self: &mut Resolver 'arena, node: *ast::Node, typeIdent: *ast::Node, hint: Type |
|
| 6001 | 7795 | ) -> ResolvedRecordLitType |
|
| 6002 | 7796 | throws (ResolveError) |
|
| 6003 | 7797 | { |
|
| 7798 | + | if let case ast::NodeValue::RegionApply { .. } = typeIdent.value { |
|
| 7799 | + | let ty = try infer(self, typeIdent); |
|
| 7800 | + | let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::ExpectedRecord); |
|
| 7801 | + | return ResolvedRecordLitType { recordType: info, resultType: ty }; |
|
| 7802 | + | } |
|
| 6004 | 7803 | // Check if this is a scope access that might be a union variant. |
|
| 6005 | 7804 | if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value { |
|
| 6006 | 7805 | let scope = self.scope; |
|
| 6007 | 7806 | let sym = try resolveAccess(self, typeIdent, access, scope); |
|
| 6008 | 7807 | ||
| 6009 | 7808 | // Check if resolved symbol is a union variant. |
|
| 6010 | 7809 | if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data { |
|
| 6011 | - | // Get the union type from the variant's declaration. |
|
| 6012 | - | let declSym = symbolFor(self, decl) |
|
| 6013 | - | else throw emitError(self, node, ErrorKind::Internal); |
|
| 6014 | - | let case SymbolData::Type(unionNominalType) = declSym.data |
|
| 6015 | - | else throw emitError(self, node, ErrorKind::Internal); |
|
| 6016 | - | ||
| 6017 | - | // Get the variant's payload type. |
|
| 6018 | - | let case Type::Nominal(payloadInfo) = type |
|
| 7810 | + | let sourceTy = typeFor(self, typeIdent) else panic "resolveRecordLitType: missing union type"; |
|
| 7811 | + | let case Type::Nominal(source) = sourceTy else panic "resolveRecordLitType: invalid union type"; |
|
| 7812 | + | let unionNominalType = hintedNominal(source, hint); |
|
| 7813 | + | try requireNominalArguments(self, unionNominalType, typeIdent); |
|
| 7814 | + | try ensureNominalResolved(self, unionNominalType, typeIdent); |
|
| 7815 | + | let case NominalType::Union(body) = *unionNominalType else panic; |
|
| 7816 | + | let case Type::Nominal(payloadInfo) = body.variants[ordinal].valueType |
|
| 6019 | 7817 | else throw emitError(self, node, ErrorKind::ExpectedRecord); |
|
| 6020 | 7818 | ||
| 6021 | 7819 | // Store the variant index for the lowerer. |
|
| 6022 | 7820 | setVariantInfo(self, node, ordinal, index); |
|
| 6023 | 7821 |
| 6041 | 7839 | resultType: Type::Nominal(tyInfo), |
|
| 6042 | 7840 | }; |
|
| 6043 | 7841 | } |
|
| 6044 | 7842 | ||
| 6045 | 7843 | /// Analyze a record literal expression. |
|
| 6046 | - | unsafe fn resolveRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 7844 | + | unsafe fn resolveRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 6047 | 7845 | throws (ResolveError) |
|
| 6048 | 7846 | { |
|
| 6049 | 7847 | // If no type name, infer an anonymous tuple type. |
|
| 6050 | 7848 | let typeIdent = lit.typeName else { |
|
| 6051 | 7849 | return try resolveAnonRecordLit(self, node, lit, hint); |
|
| 6052 | 7850 | }; |
|
| 6053 | 7851 | // Resolve the type name, handling both record types and union variants. |
|
| 6054 | - | let resolved = try resolveRecordLitType(self, node, typeIdent); |
|
| 6055 | - | let tyInfo = resolved.recordType; |
|
| 6056 | - | let resultType = resolved.resultType; |
|
| 7852 | + | let resolved = try resolveRecordLitType(self, node, typeIdent, hint); |
|
| 7853 | + | let mut tyInfo = resolved.recordType; |
|
| 7854 | + | let mut resultType = resolved.resultType; |
|
| 7855 | + | let mut target = hint; |
|
| 7856 | + | if let case Type::Optional(inner) = target { |
|
| 7857 | + | set target = *inner; |
|
| 7858 | + | } |
|
| 7859 | + | if nominalApplication(tyInfo) == nil { |
|
| 7860 | + | if let case Type::Nominal(info) = target { |
|
| 7861 | + | if let applied = nominalApplication(info); applied.base == tyInfo { |
|
| 7862 | + | set tyInfo = info; |
|
| 7863 | + | set resultType = target; |
|
| 7864 | + | } |
|
| 7865 | + | } |
|
| 7866 | + | } |
|
| 7867 | + | try requireNominalArguments(self, tyInfo, typeIdent); |
|
| 6057 | 7868 | ||
| 6058 | 7869 | // Lazily resolve record body if not yet done. |
|
| 6059 | 7870 | try ensureNominalResolved(self, tyInfo, typeIdent); |
|
| 6060 | 7871 | let case NominalType::Record(recordType) = *tyInfo |
|
| 6061 | 7872 | else throw emitError(self, node, ErrorKind::ExpectedRecord); |
| 6098 | 7909 | } |
|
| 6099 | 7910 | return setNodeType(self, node, resultType); |
|
| 6100 | 7911 | } |
|
| 6101 | 7912 | ||
| 6102 | 7913 | /// Analyze an anonymous record literal, checking fields against the hint type. |
|
| 6103 | - | unsafe fn resolveAnonRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 7914 | + | unsafe fn resolveAnonRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 6104 | 7915 | throws (ResolveError) |
|
| 6105 | 7916 | { |
|
| 6106 | 7917 | // Unwrap optional hint to get the inner record type. |
|
| 6107 | 7918 | let mut innerHint = hint; |
|
| 6108 | 7919 | if let case Type::Optional(inner) = hint { |
| 6156 | 7967 | } |
|
| 6157 | 7968 | return setNodeType(self, node, innerHint); |
|
| 6158 | 7969 | } |
|
| 6159 | 7970 | ||
| 6160 | 7971 | /// Analyze an array literal expression. |
|
| 6161 | - | unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type |
|
| 7972 | + | unsafe fn resolveArrayLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type |
|
| 6162 | 7973 | throws (ResolveError) |
|
| 6163 | 7974 | { |
|
| 6164 | 7975 | let length = items.len; |
|
| 6165 | 7976 | let mut expectedTy: Type = Type::Unknown; |
|
| 6166 | 7977 |
| 6188 | 7999 | let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length }); |
|
| 6189 | 8000 | return setNodeType(self, node, arrayTy); |
|
| 6190 | 8001 | } |
|
| 6191 | 8002 | ||
| 6192 | 8003 | /// Analyze an array repeat literal expression. |
|
| 6193 | - | unsafe fn resolveArrayRepeat(self: &mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type |
|
| 8004 | + | unsafe fn resolveArrayRepeat 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type |
|
| 6194 | 8005 | throws (ResolveError) |
|
| 6195 | 8006 | { |
|
| 6196 | 8007 | let mut itemHint = hint; |
|
| 6197 | 8008 | if let case Type::Array(ary) = hint { |
|
| 6198 | 8009 | set itemHint = *ary.item; |
| 6209 | 8020 | }); |
|
| 6210 | 8021 | return setNodeType(self, node, arrayTy); |
|
| 6211 | 8022 | } |
|
| 6212 | 8023 | ||
| 6213 | 8024 | /// Resolve union variant access. |
|
| 6214 | - | unsafe fn resolveUnionVariantAccess( |
|
| 6215 | - | self: &mut Resolver, |
|
| 8025 | + | unsafe fn resolveUnionVariantAccess 'arena ( |
|
| 8026 | + | self: &mut Resolver 'arena, |
|
| 6216 | 8027 | node: *ast::Node, |
|
| 6217 | 8028 | access: ast::Access, |
|
| 6218 | 8029 | unionType: UnionType, |
|
| 6219 | 8030 | variantName: *[u8] |
|
| 6220 | 8031 | ) -> *unsafe mut Symbol throws (ResolveError) { |
| 6237 | 8048 | } |
|
| 6238 | 8049 | throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName)); |
|
| 6239 | 8050 | } |
|
| 6240 | 8051 | ||
| 6241 | 8052 | /// Analyze a scope access expression. |
|
| 6242 | - | unsafe fn resolveScopeAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type |
|
| 8053 | + | unsafe fn resolveScopeAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access, hint: Type) -> Type |
|
| 6243 | 8054 | throws (ResolveError) |
|
| 6244 | 8055 | { |
|
| 6245 | 8056 | let scope = self.scope; |
|
| 6246 | 8057 | let sym = try resolveAccess(self, node, access, scope); |
|
| 6247 | 8058 | try checkStaticAccess(self, node, sym); |
| 6260 | 8071 | setNodeSymbol(self, node, sym); |
|
| 6261 | 8072 | set ty = type; |
|
| 6262 | 8073 | } |
|
| 6263 | 8074 | case SymbolData::Type(t) => { |
|
| 6264 | 8075 | setNodeSymbol(self, node, sym); |
|
| 6265 | - | set ty = Type::Nominal(t); |
|
| 8076 | + | set ty = Type::Nominal(hintedNominal(t, hint)); |
|
| 6266 | 8077 | } |
|
| 6267 | 8078 | case SymbolData::Variant { index, .. } => { |
|
| 6268 | 8079 | let ty = typeFor(self, node) |
|
| 6269 | 8080 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 8081 | + | let case Type::Nominal(info) = ty else panic "resolveScopeAccess: invalid variant type"; |
|
| 8082 | + | let applied = hintedNominal(info, hint); |
|
| 8083 | + | try requireNominalArguments(self, applied, node); |
|
| 8084 | + | try ensureNominalResolved(self, applied, node); |
|
| 8085 | + | let variantTy = Type::Nominal(applied); |
|
| 6270 | 8086 | // For unions without payload, store the variant index as a constant. |
|
| 6271 | - | if isVoidUnion(ty) { |
|
| 8087 | + | if isVoidUnion(variantTy) { |
|
| 6272 | 8088 | setNodeConstValue(self, node, ConstValue::Int(ConstInt { |
|
| 6273 | 8089 | magnitude: index as u64, |
|
| 6274 | 8090 | bits: 32, |
|
| 6275 | 8091 | signed: false, |
|
| 6276 | 8092 | negative: false, |
|
| 6277 | 8093 | })); |
|
| 6278 | 8094 | } |
|
| 6279 | - | return setNodeType(self, node, ty); |
|
| 8095 | + | return setNodeType(self, node, variantTy); |
|
| 6280 | 8096 | } |
|
| 6281 | 8097 | case SymbolData::Module { .. } => { |
|
| 6282 | 8098 | throw emitError(self, node, ErrorKind::UnexpectedModuleName); |
|
| 6283 | 8099 | } |
|
| 6284 | 8100 | case SymbolData::Trait(_) => { // Trait names are not values. |
| 6287 | 8103 | } |
|
| 6288 | 8104 | return setNodeType(self, node, ty); |
|
| 6289 | 8105 | } |
|
| 6290 | 8106 | ||
| 6291 | 8107 | /// Analyze a field access expression. |
|
| 6292 | - | unsafe fn resolveFieldAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type |
|
| 8108 | + | unsafe fn resolveFieldAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access) -> Type |
|
| 6293 | 8109 | throws (ResolveError) |
|
| 6294 | 8110 | { |
|
| 6295 | 8111 | let parentTy = try infer(self, access.parent); |
|
| 6296 | 8112 | if isUnsafePointerType(parentTy) { |
|
| 6297 | 8113 | try requireUnsafe(self, access.parent); |
|
| 6298 | 8114 | } |
|
| 6299 | 8115 | let subjectTy = autoDeref(parentTy); |
|
| 8116 | + | try ensureTypeResolved(self, subjectTy, access.parent); |
|
| 6300 | 8117 | ||
| 6301 | 8118 | if let case Type::Slice { class, item, mutable } = subjectTy { |
|
| 6302 | 8119 | let fieldNode = access.child; |
|
| 6303 | 8120 | let fieldName = try nodeName(self, fieldNode); |
|
| 6304 | 8121 | if mem::eq(fieldName, PTR_FIELD) { |
| 6366 | 8183 | throw emitError(self, access.parent, ErrorKind::ExpectedRecord); |
|
| 6367 | 8184 | } |
|
| 6368 | 8185 | } |
|
| 6369 | 8186 | } |
|
| 6370 | 8187 | ||
| 6371 | - | /// Check target mutability for implicit pointer access. |
|
| 6372 | - | unsafe fn canMutateThrough(self: &mut Resolver, node: *ast::Node) -> bool |
|
| 6373 | - | throws (ResolveError) |
|
| 6374 | - | { |
|
| 6375 | - | let ty = try infer(self, node); |
|
| 8188 | + | /// Return whether a pointer-like value grants mutable access. |
|
| 8189 | + | fn isMutablePointerLike(ty: Type) -> bool { |
|
| 6376 | 8190 | match ty { |
|
| 6377 | 8191 | case Type::Pointer { mutable, .. } => return mutable, |
|
| 6378 | 8192 | case Type::Slice { mutable, .. } => return mutable, |
|
| 6379 | - | else => return try canBorrowMutFrom(self, node), |
|
| 8193 | + | case Type::TraitObject { mutable, .. } => return mutable, |
|
| 8194 | + | else => return false, |
|
| 6380 | 8195 | } |
|
| 6381 | 8196 | } |
|
| 6382 | 8197 | ||
| 6383 | - | /// Determine whether an expression can yield a mutable location for borrowing. |
|
| 6384 | - | unsafe fn canBorrowMutFrom(self: &mut Resolver, node: *ast::Node) -> bool |
|
| 8198 | + | /// Check exclusive access to an element or field of a container. |
|
| 8199 | + | unsafe fn canAccessExclusiveProjection 'arena (self: &mut Resolver 'arena, container: *ast::Node) -> bool |
|
| 6385 | 8200 | throws (ResolveError) |
|
| 6386 | 8201 | { |
|
| 6387 | - | match node.value { |
|
| 6388 | - | case ast::NodeValue::Ident(name) => { |
|
| 6389 | - | let sym = findValueSymbol(self.scope, name) |
|
| 6390 | - | else return false; |
|
| 6391 | - | let case SymbolData::Value { mutable, .. } = sym.data |
|
| 6392 | - | else return false; |
|
| 6393 | - | return mutable; |
|
| 8202 | + | let ty = try infer(self, container); |
|
| 8203 | + | if let case Type::Slice { mutable: false, .. } = autoDeref(ty) { |
|
| 8204 | + | return false; |
|
| 8205 | + | } |
|
| 8206 | + | match ty { |
|
| 8207 | + | case Type::Pointer { class, mutable, .. } => { |
|
| 8208 | + | if not mutable { |
|
| 8209 | + | return false; |
|
| 8210 | + | } |
|
| 8211 | + | if class == types::PointerClass::Unsafe { |
|
| 8212 | + | return true; |
|
| 8213 | + | } |
|
| 6394 | 8214 | } |
|
| 6395 | - | case ast::NodeValue::FieldAccess(access) => { |
|
| 8215 | + | case Type::Slice { class, mutable, .. } => { |
|
| 8216 | + | if not mutable { |
|
| 8217 | + | return false; |
|
| 8218 | + | } |
|
| 8219 | + | if class == types::PointerClass::Unsafe { |
|
| 8220 | + | return true; |
|
| 8221 | + | } |
|
| 8222 | + | } |
|
| 8223 | + | else => { |
|
| 8224 | + | }, |
|
| 8225 | + | } |
|
| 8226 | + | return try canAccessExclusiveHandle(self, container); |
|
| 8227 | + | } |
|
| 8228 | + | ||
| 8229 | + | /// Check that a stored exclusive handle is not reached through shared access. |
|
| 8230 | + | unsafe fn canAccessExclusiveHandle 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool |
|
| 8231 | + | throws (ResolveError) |
|
| 8232 | + | { |
|
| 8233 | + | match node.value { |
|
| 8234 | + | case ast::NodeValue::FieldAccess(access) => |
|
| 8235 | + | return try canAccessExclusiveProjection(self, access.parent), |
|
| 8236 | + | case ast::NodeValue::Subscript { container, .. } => |
|
| 8237 | + | return try canAccessExclusiveProjection(self, container), |
|
| 8238 | + | case ast::NodeValue::Deref(inner) => |
|
| 8239 | + | return try canAccessExclusiveProjection(self, inner), |
|
| 8240 | + | case ast::NodeValue::As(expr) => |
|
| 8241 | + | return try canAccessExclusiveHandle(self, expr.value), |
|
| 8242 | + | case ast::NodeValue::CondExpr(cond) => { |
|
| 8243 | + | if not try canAccessExclusiveHandle(self, cond.thenExpr) { |
|
| 8244 | + | return false; |
|
| 8245 | + | } |
|
| 8246 | + | return try canAccessExclusiveHandle(self, cond.elseExpr); |
|
| 8247 | + | } |
|
| 8248 | + | else => return true, |
|
| 8249 | + | } |
|
| 8250 | + | } |
|
| 8251 | + | ||
| 8252 | + | /// Check target mutability for implicit pointer access. |
|
| 8253 | + | unsafe fn canMutateThrough 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool |
|
| 8254 | + | throws (ResolveError) |
|
| 8255 | + | { |
|
| 8256 | + | let ty = try infer(self, node); |
|
| 8257 | + | match ty { |
|
| 8258 | + | case Type::Pointer { class, mutable, .. } => { |
|
| 8259 | + | if not mutable { |
|
| 8260 | + | return false; |
|
| 8261 | + | } |
|
| 8262 | + | if class == types::PointerClass::Unsafe { |
|
| 8263 | + | return true; |
|
| 8264 | + | } |
|
| 8265 | + | return try canAccessExclusiveHandle(self, node); |
|
| 8266 | + | } |
|
| 8267 | + | case Type::Slice { class, mutable, .. } => { |
|
| 8268 | + | if not mutable { |
|
| 8269 | + | return false; |
|
| 8270 | + | } |
|
| 8271 | + | if class == types::PointerClass::Unsafe { |
|
| 8272 | + | return true; |
|
| 8273 | + | } |
|
| 8274 | + | return try canAccessExclusiveHandle(self, node); |
|
| 8275 | + | } |
|
| 8276 | + | case Type::TraitObject { class, mutable, .. } => { |
|
| 8277 | + | if not mutable { |
|
| 8278 | + | return false; |
|
| 8279 | + | } |
|
| 8280 | + | if class == types::PointerClass::Unsafe { |
|
| 8281 | + | return true; |
|
| 8282 | + | } |
|
| 8283 | + | return try canAccessExclusiveHandle(self, node); |
|
| 8284 | + | } |
|
| 8285 | + | else => return try canBorrowMutFrom(self, node), |
|
| 8286 | + | } |
|
| 8287 | + | } |
|
| 8288 | + | ||
| 8289 | + | /// Determine whether an expression can yield a mutable location for borrowing. |
|
| 8290 | + | unsafe fn canBorrowMutFrom 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool |
|
| 8291 | + | throws (ResolveError) |
|
| 8292 | + | { |
|
| 8293 | + | match node.value { |
|
| 8294 | + | case ast::NodeValue::Ident(name) => { |
|
| 8295 | + | let sym = findValueSymbol(self.scope, name) |
|
| 8296 | + | else return false; |
|
| 8297 | + | let case SymbolData::Value { mutable, .. } = sym.data |
|
| 8298 | + | else return false; |
|
| 8299 | + | return mutable; |
|
| 8300 | + | } |
|
| 8301 | + | case ast::NodeValue::FieldAccess(access) => { |
|
| 6396 | 8302 | let parentTy = try infer(self, access.parent); |
|
| 6397 | 8303 | if let case Type::Slice { .. } = autoDeref(parentTy) { |
|
| 6398 | 8304 | try requireUnsafe(self, node); |
|
| 6399 | 8305 | } |
|
| 6400 | 8306 | return try canMutateThrough(self, access.parent); |
| 6415 | 8321 | let containerTy = try infer(self, container); |
|
| 6416 | 8322 | // Subscript auto-derefs pointers, so check the actual indexed type. |
|
| 6417 | 8323 | let subjectTy = autoDeref(containerTy); |
|
| 6418 | 8324 | ||
| 6419 | 8325 | if let case Type::Slice { mutable, .. } = subjectTy { |
|
| 6420 | - | return mutable; |
|
| 8326 | + | if not mutable { |
|
| 8327 | + | return false; |
|
| 8328 | + | } |
|
| 8329 | + | return try canMutateThrough(self, container); |
|
| 6421 | 8330 | } |
|
| 6422 | 8331 | if let case Type::Array(_) = subjectTy { |
|
| 6423 | 8332 | return try canMutateThrough(self, container); |
|
| 6424 | 8333 | } |
|
| 6425 | 8334 | return false; |
| 6442 | 8351 | return false; |
|
| 6443 | 8352 | } |
|
| 6444 | 8353 | case ast::NodeValue::Deref(inner) => { |
|
| 6445 | 8354 | let innerTy = try infer(self, inner); |
|
| 6446 | 8355 | ||
| 6447 | - | if let case Type::Pointer { mutable, .. } = innerTy { |
|
| 6448 | - | return mutable; |
|
| 8356 | + | if let case Type::Pointer { .. } = innerTy { |
|
| 8357 | + | return try canMutateThrough(self, inner); |
|
| 6449 | 8358 | } |
|
| 6450 | - | if let case Type::Slice { mutable, .. } = innerTy { |
|
| 6451 | - | return mutable; |
|
| 8359 | + | if let case Type::Slice { .. } = innerTy { |
|
| 8360 | + | return try canMutateThrough(self, inner); |
|
| 6452 | 8361 | } |
|
| 6453 | 8362 | // Record deref: mutability depends on the inner binding. |
|
| 6454 | 8363 | if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy { |
|
| 6455 | 8364 | if not recInfo.labeled and recInfo.fields.len == 1 { |
|
| 6456 | 8365 | return try canBorrowMutFrom(self, inner); |
| 6462 | 8371 | return false; |
|
| 6463 | 8372 | } |
|
| 6464 | 8373 | } |
|
| 6465 | 8374 | } |
|
| 6466 | 8375 | ||
| 8376 | + | /// Restrict a pointee lifetime to the borrow of its exclusive owner. |
|
| 8377 | + | unsafe fn constrainAddressClass(storage: types::PointerClass, owner: types::PointerClass) -> types::PointerClass { |
|
| 8378 | + | if owner == types::PointerClass::Owned or owner == types::PointerClass::Unsafe { |
|
| 8379 | + | return storage; |
|
| 8380 | + | } |
|
| 8381 | + | if owner == types::PointerClass::Ref { |
|
| 8382 | + | return owner; |
|
| 8383 | + | } |
|
| 8384 | + | let case types::PointerClass::Region(ownerRegion) = owner else panic; |
|
| 8385 | + | if let case types::PointerClass::Region(storageRegion) = storage { |
|
| 8386 | + | if types::regionContains(storageRegion, ownerRegion) { |
|
| 8387 | + | return owner; |
|
| 8388 | + | } |
|
| 8389 | + | if types::regionContains(ownerRegion, storageRegion) { |
|
| 8390 | + | return storage; |
|
| 8391 | + | } |
|
| 8392 | + | return types::PointerClass::Ref; |
|
| 8393 | + | } |
|
| 8394 | + | if storage == types::PointerClass::Owned { |
|
| 8395 | + | return owner; |
|
| 8396 | + | } |
|
| 8397 | + | return storage; |
|
| 8398 | + | } |
|
| 8399 | + | ||
| 8400 | + | /// Get the usable pointee lifetime of a pointer or slice value. |
|
| 8401 | + | unsafe fn pointerAddressClass 'arena ( |
|
| 8402 | + | self: &Resolver 'arena, node: *ast::Node, class: types::PointerClass, mutable: bool |
|
| 8403 | + | ) -> types::PointerClass { |
|
| 8404 | + | if not mutable or class == types::PointerClass::Unsafe { |
|
| 8405 | + | return class; |
|
| 8406 | + | } |
|
| 8407 | + | return constrainAddressClass(class, exclusiveOwnerClass(self, node)); |
|
| 8408 | + | } |
|
| 8409 | + | ||
| 8410 | + | /// Get the lifetime of storage selected by a pointer or slice subscript. |
|
| 8411 | + | unsafe fn indexedPointerClass 'arena (self: &Resolver 'arena, container: *ast::Node) -> ?types::PointerClass { |
|
| 8412 | + | let ty = typeFor(self, container) else return nil; |
|
| 8413 | + | if let case Type::Pointer { class, target, mutable } = ty { |
|
| 8414 | + | let parentClass = pointerAddressClass(self, container, class, mutable); |
|
| 8415 | + | if let case Type::Slice { class: sliceClass, mutable: sliceMutable, .. } = *target { |
|
| 8416 | + | if not sliceMutable or sliceClass == types::PointerClass::Unsafe { |
|
| 8417 | + | return sliceClass; |
|
| 8418 | + | } |
|
| 8419 | + | return constrainAddressClass(sliceClass, parentClass); |
|
| 8420 | + | } |
|
| 8421 | + | return parentClass; |
|
| 8422 | + | } |
|
| 8423 | + | if let case Type::Slice { class, mutable, .. } = ty { |
|
| 8424 | + | return pointerAddressClass(self, container, class, mutable); |
|
| 8425 | + | } |
|
| 8426 | + | return nil; |
|
| 8427 | + | } |
|
| 8428 | + | ||
| 8429 | + | /// Get the borrow that controls access to a stored exclusive handle. |
|
| 8430 | + | /// Directly owned values have no additional borrow restriction. |
|
| 8431 | + | unsafe fn exclusiveOwnerClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass { |
|
| 8432 | + | match node.value { |
|
| 8433 | + | case ast::NodeValue::FieldAccess(access) => { |
|
| 8434 | + | if let ty = typeFor(self, access.parent) { |
|
| 8435 | + | match ty { |
|
| 8436 | + | case Type::Pointer { class, mutable, .. } => |
|
| 8437 | + | return pointerAddressClass(self, access.parent, class, mutable), |
|
| 8438 | + | case Type::Slice { class, mutable, .. } => |
|
| 8439 | + | return pointerAddressClass(self, access.parent, class, mutable), |
|
| 8440 | + | else => { |
|
| 8441 | + | }, |
|
| 8442 | + | } |
|
| 8443 | + | } |
|
| 8444 | + | return exclusiveOwnerClass(self, access.parent); |
|
| 8445 | + | } |
|
| 8446 | + | case ast::NodeValue::Subscript { container, .. } => { |
|
| 8447 | + | if let class = indexedPointerClass(self, container) { |
|
| 8448 | + | return class; |
|
| 8449 | + | } |
|
| 8450 | + | return exclusiveOwnerClass(self, container); |
|
| 8451 | + | } |
|
| 8452 | + | case ast::NodeValue::Deref(target) => { |
|
| 8453 | + | if let ty = typeFor(self, target) { |
|
| 8454 | + | if let case Type::Pointer { class, mutable, .. } = ty { |
|
| 8455 | + | return pointerAddressClass(self, target, class, mutable); |
|
| 8456 | + | } |
|
| 8457 | + | } |
|
| 8458 | + | return exclusiveOwnerClass(self, target); |
|
| 8459 | + | } |
|
| 8460 | + | case ast::NodeValue::As(expr) => return exclusiveOwnerClass(self, expr.value), |
|
| 8461 | + | case ast::NodeValue::CondExpr(cond) => |
|
| 8462 | + | return constrainAddressClass(exclusiveOwnerClass(self, cond.thenExpr), exclusiveOwnerClass(self, cond.elseExpr)), |
|
| 8463 | + | else => return types::PointerClass::Owned, |
|
| 8464 | + | } |
|
| 8465 | + | } |
|
| 8466 | + | ||
| 6467 | 8467 | /// Return the storage class of an addressed location. |
|
| 6468 | - | unsafe fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass { |
|
| 8468 | + | unsafe fn addressStorageClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass { |
|
| 6469 | 8469 | match node.value { |
|
| 6470 | 8470 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => { |
|
| 6471 | 8471 | if let sym = symbolFor(self, node) { |
|
| 6472 | 8472 | match sym.node.value { |
|
| 6473 | 8473 | case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) => |
| 6476 | 8476 | } |
|
| 6477 | 8477 | } |
|
| 6478 | 8478 | } |
|
| 6479 | 8479 | case ast::NodeValue::FieldAccess(access) => { |
|
| 6480 | 8480 | if let ty = typeFor(self, access.parent) { |
|
| 6481 | - | if let case Type::Pointer { class, .. } = ty { |
|
| 6482 | - | return class; |
|
| 8481 | + | if let case Type::Pointer { class, mutable, .. } = ty { |
|
| 8482 | + | return pointerAddressClass(self, access.parent, class, mutable); |
|
| 6483 | 8483 | } |
|
| 6484 | 8484 | } |
|
| 6485 | 8485 | return addressStorageClass(self, access.parent); |
|
| 6486 | 8486 | } |
|
| 6487 | 8487 | case ast::NodeValue::Subscript { container, .. } => { |
|
| 6488 | - | if let ty = typeFor(self, container) { |
|
| 6489 | - | if let case Type::Slice { class, .. } = autoDeref(ty) { |
|
| 6490 | - | return class; |
|
| 6491 | - | } |
|
| 6492 | - | if let case Type::Pointer { class, .. } = ty { |
|
| 6493 | - | return class; |
|
| 6494 | - | } |
|
| 8488 | + | if let class = indexedPointerClass(self, container) { |
|
| 8489 | + | return class; |
|
| 6495 | 8490 | } |
|
| 6496 | 8491 | return addressStorageClass(self, container); |
|
| 6497 | 8492 | } |
|
| 6498 | 8493 | case ast::NodeValue::Deref(target) => { |
|
| 6499 | 8494 | if let ty = typeFor(self, target) { |
|
| 6500 | - | if let case Type::Pointer { class, .. } = ty { |
|
| 6501 | - | return class; |
|
| 8495 | + | if let case Type::Pointer { class, mutable, .. } = ty { |
|
| 8496 | + | return pointerAddressClass(self, target, class, mutable); |
|
| 6502 | 8497 | } |
|
| 6503 | 8498 | } |
|
| 6504 | 8499 | return addressStorageClass(self, target); |
|
| 6505 | 8500 | } |
|
| 6506 | 8501 | else => {} |
|
| 6507 | 8502 | } |
|
| 6508 | 8503 | return types::PointerClass::Ref; |
|
| 6509 | 8504 | } |
|
| 6510 | 8505 | ||
| 6511 | 8506 | /// Select an address type without extending the target storage lifetime. |
|
| 6512 | - | unsafe fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass |
|
| 8507 | + | unsafe fn addressClass 'arena (self: &mut Resolver 'arena, target: *ast::Node, hint: Type) -> types::PointerClass |
|
| 6513 | 8508 | throws (ResolveError) |
|
| 6514 | 8509 | { |
|
| 6515 | 8510 | if isUnsafePointerType(hint) { |
|
| 6516 | 8511 | try requireUnsafe(self, target); |
|
| 6517 | 8512 | return types::PointerClass::Unsafe; |
|
| 6518 | 8513 | } |
|
| 6519 | 8514 | if isRefType(hint) { |
|
| 8515 | + | if referenceRegion(hint) <> nil { |
|
| 8516 | + | return addressStorageClass(self, target); |
|
| 8517 | + | } |
|
| 6520 | 8518 | return types::PointerClass::Ref; |
|
| 6521 | 8519 | } |
|
| 6522 | 8520 | match target.value { |
|
| 6523 | 8521 | case ast::NodeValue::ArrayLit(_), ast::NodeValue::ArrayRepeatLit(_) => { |
|
| 6524 | 8522 | if isConstExpr(self, target) { |
| 6528 | 8526 | else => {} |
|
| 6529 | 8527 | } |
|
| 6530 | 8528 | return addressStorageClass(self, target); |
|
| 6531 | 8529 | } |
|
| 6532 | 8530 | ||
| 8531 | + | /// Return whether a place projects into a cell payload snapshot. |
|
| 8532 | + | unsafe fn isCellPayloadPlace 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool |
|
| 8533 | + | throws (ResolveError) |
|
| 8534 | + | { |
|
| 8535 | + | match node.value { |
|
| 8536 | + | case ast::NodeValue::Deref(target) => { |
|
| 8537 | + | if let case Type::Cell { .. } = try infer(self, target) { |
|
| 8538 | + | return true; |
|
| 8539 | + | } |
|
| 8540 | + | } |
|
| 8541 | + | case ast::NodeValue::FieldAccess(access) => return try isCellPayloadPlace(self, access.parent), |
|
| 8542 | + | case ast::NodeValue::Subscript { container, .. } => return try isCellPayloadPlace(self, container), |
|
| 8543 | + | else => {} |
|
| 8544 | + | } |
|
| 8545 | + | return false; |
|
| 8546 | + | } |
|
| 8547 | + | ||
| 8548 | + | /// Return whether a typed expression accesses a whole cell payload. |
|
| 8549 | + | export unsafe fn isCellDeref 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool { |
|
| 8550 | + | if let case ast::NodeValue::Deref(target) = node.value { |
|
| 8551 | + | if let ty = typeFor(self, target) { |
|
| 8552 | + | if let case Type::Cell { .. } = ty { |
|
| 8553 | + | return true; |
|
| 8554 | + | } |
|
| 8555 | + | } |
|
| 8556 | + | } |
|
| 8557 | + | return false; |
|
| 8558 | + | } |
|
| 8559 | + | ||
| 6533 | 8560 | /// Analyze an address-of expression. |
|
| 6534 | - | unsafe fn resolveAddressOf(self: &mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type |
|
| 8561 | + | unsafe fn resolveAddressOf 'arena (self: &mut Resolver 'arena, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type |
|
| 6535 | 8562 | throws (ResolveError) |
|
| 6536 | 8563 | { |
|
| 8564 | + | if try isCellPayloadPlace(self, addr.target) { |
|
| 8565 | + | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
|
| 8566 | + | } |
|
| 6537 | 8567 | if addr.mutable { |
|
| 6538 | 8568 | if not try canBorrowMutFrom(self, addr.target) { |
|
| 6539 | 8569 | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
|
| 6540 | 8570 | } |
|
| 6541 | 8571 | } |
| 6615 | 8645 | }; |
|
| 6616 | 8646 | return setNodeType(self, node, pointerTy); |
|
| 6617 | 8647 | } |
|
| 6618 | 8648 | ||
| 6619 | 8649 | /// Analyze a dereference expression. |
|
| 6620 | - | unsafe fn resolveDeref(self: &mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type |
|
| 8650 | + | unsafe fn resolveDeref 'arena (self: &mut Resolver 'arena, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type |
|
| 6621 | 8651 | throws (ResolveError) |
|
| 6622 | 8652 | { |
|
| 6623 | 8653 | let operandTy = try visit(self, targetNode, hint); |
|
| 8654 | + | if let case Type::Cell { class, payload } = operandTy { |
|
| 8655 | + | if class == types::PointerClass::Unsafe { |
|
| 8656 | + | try requireUnsafe(self, targetNode); |
|
| 8657 | + | } |
|
| 8658 | + | try validateCellPayload(self, node, *payload); |
|
| 8659 | + | return setNodeType(self, node, *payload); |
|
| 8660 | + | } |
|
| 6624 | 8661 | if let case Type::Pointer { class, target, .. } = operandTy { |
|
| 6625 | 8662 | if class == types::PointerClass::Unsafe { |
|
| 6626 | 8663 | try requireUnsafe(self, targetNode); |
|
| 6627 | 8664 | } |
|
| 6628 | 8665 | // Disallow dereferencing opaque pointers. |
| 6722 | 8759 | } |
|
| 6723 | 8760 | } |
|
| 6724 | 8761 | return false; |
|
| 6725 | 8762 | } |
|
| 6726 | 8763 | ||
| 8764 | + | /// Require casts into region-dependent storage to preserve its typed contents. |
|
| 8765 | + | fn regionalCastPreservesType(source: Type, target: Type) -> bool { |
|
| 8766 | + | if typesEqual(source, target) { |
|
| 8767 | + | return true; |
|
| 8768 | + | } |
|
| 8769 | + | if let case Type::Pointer { target: sourceItem, .. } = source { |
|
| 8770 | + | let case Type::Pointer { target: targetItem, .. } = target else return false; |
|
| 8771 | + | return typesEqual(*sourceItem, *targetItem); |
|
| 8772 | + | } |
|
| 8773 | + | if let case Type::Slice { item: sourceItem, .. } = source { |
|
| 8774 | + | let case Type::Slice { item: targetItem, .. } = target else return false; |
|
| 8775 | + | return typesEqual(*sourceItem, *targetItem); |
|
| 8776 | + | } |
|
| 8777 | + | return false; |
|
| 8778 | + | } |
|
| 8779 | + | ||
| 6727 | 8780 | /// Analyze an `as` cast expression. |
|
| 6728 | - | unsafe fn resolveAs(self: &mut Resolver, node: *ast::Node, expr: ast::As) -> Type |
|
| 8781 | + | unsafe fn resolveAs 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: ast::As) -> Type |
|
| 6729 | 8782 | throws (ResolveError) |
|
| 6730 | 8783 | { |
|
| 6731 | 8784 | let targetTy = try infer(self, expr.type); |
|
| 6732 | 8785 | let sourceTy = try visit(self, expr.value, targetTy); |
|
| 6733 | 8786 | if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) { |
| 6735 | 8788 | } |
|
| 6736 | 8789 | ||
| 6737 | 8790 | assert sourceTy <> Type::Unknown; |
|
| 6738 | 8791 | assert targetTy <> Type::Unknown; |
|
| 6739 | 8792 | ||
| 8793 | + | if let case Type::Cell { class, payload } = targetTy { |
|
| 8794 | + | if let case Type::Pointer { class: sourceClass, target, mutable: true } = sourceTy; |
|
| 8795 | + | sourceClass == class and class <> types::PointerClass::Unsafe and typesEqual(*target, *payload) |
|
| 8796 | + | { |
|
| 8797 | + | return setNodeType(self, node, targetTy); |
|
| 8798 | + | } |
|
| 8799 | + | if typesEqual(sourceTy, targetTy) { |
|
| 8800 | + | return setNodeType(self, node, targetTy); |
|
| 8801 | + | } |
|
| 8802 | + | throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast { from: sourceTy, to: targetTy })); |
|
| 8803 | + | } |
|
| 8804 | + | if containsRegion(targetTy) and not regionalCastPreservesType(sourceTy, targetTy) { |
|
| 8805 | + | throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast { |
|
| 8806 | + | from: sourceTy, to: targetTy, |
|
| 8807 | + | })); |
|
| 8808 | + | } |
|
| 6740 | 8809 | let mut valid = isValidCast(sourceTy, targetTy); |
|
| 6741 | 8810 | if let case Type::Pointer { |
|
| 6742 | 8811 | class: sourceClass, target: sourceTarget, mutable: sourceMutable, |
|
| 6743 | 8812 | } = sourceTy { |
|
| 6744 | 8813 | if let case Type::Pointer { |
|
| 6745 | 8814 | class: targetClass, target: targetTarget, mutable: targetMutable, |
|
| 6746 | 8815 | } = targetTy { |
|
| 6747 | - | if sourceClass == types::PointerClass::Ref and |
|
| 8816 | + | if types::isReference(sourceClass) and |
|
| 6748 | 8817 | targetClass == types::PointerClass::Unsafe and |
|
| 6749 | 8818 | (not targetMutable or sourceMutable) and |
|
| 6750 | 8819 | isValidCast(*sourceTarget, *targetTarget) |
|
| 6751 | 8820 | { |
|
| 6752 | 8821 | set valid = true; |
| 6757 | 8826 | class: sourceClass, item: sourceItem, mutable: sourceMutable, |
|
| 6758 | 8827 | } = sourceTy { |
|
| 6759 | 8828 | if let case Type::Slice { |
|
| 6760 | 8829 | class: targetClass, item: targetItem, mutable: targetMutable, |
|
| 6761 | 8830 | } = targetTy { |
|
| 6762 | - | if sourceClass == types::PointerClass::Ref and |
|
| 8831 | + | if types::isReference(sourceClass) and |
|
| 6763 | 8832 | targetClass == types::PointerClass::Unsafe and |
|
| 6764 | 8833 | (not targetMutable or sourceMutable) and |
|
| 6765 | 8834 | isValidCast(*sourceItem, *targetItem) |
|
| 6766 | 8835 | { |
|
| 6767 | 8836 | set valid = true; |
| 6797 | 8866 | to: targetTy, |
|
| 6798 | 8867 | })); |
|
| 6799 | 8868 | } |
|
| 6800 | 8869 | ||
| 6801 | 8870 | /// Analyze a range expression. |
|
| 6802 | - | unsafe fn resolveRange(self: &mut Resolver, node: *ast::Node, range: ast::Range) -> Type |
|
| 8871 | + | unsafe fn resolveRange 'arena (self: &mut Resolver 'arena, node: *ast::Node, range: ast::Range) -> Type |
|
| 6803 | 8872 | throws (ResolveError) |
|
| 6804 | 8873 | { |
|
| 6805 | 8874 | let mut start: ?*Type = nil; |
|
| 6806 | 8875 | let mut end: ?*Type = nil; |
|
| 6807 | 8876 |
| 6834 | 8903 | } |
|
| 6835 | 8904 | ||
| 6836 | 8905 | /// Analyze a `try` expression and its handlers. |
|
| 6837 | 8906 | /// The `expected` type is used to determine if the value is discarded (`Void`) |
|
| 6838 | 8907 | /// or if the catch expression needs type checking. |
|
| 6839 | - | unsafe fn resolveTry(self: &mut Resolver, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type |
|
| 8908 | + | unsafe fn resolveTry 'arena (self: &mut Resolver 'arena, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type |
|
| 6840 | 8909 | throws (ResolveError) |
|
| 6841 | 8910 | { |
|
| 6842 | 8911 | let call = tryExpr.expr; |
|
| 6843 | 8912 | let case ast::NodeValue::Call(callExpr) = call.value |
|
| 6844 | 8913 | else throw emitError(self, call, ErrorKind::TryNonThrowing); |
|
| 6845 | - | let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try); |
|
| 8914 | + | let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try, Type::Unknown); |
|
| 6846 | 8915 | ||
| 6847 | 8916 | // TODO: It's annoying that we need to re-fetch the function type after |
|
| 6848 | 8917 | // analyzing the call. |
|
| 6849 | 8918 | let calleeTy = typeFor(self, callExpr.callee) |
|
| 6850 | 8919 | else return setNodeType(self, node, resultTy); |
| 6892 | 8961 | return setNodeType(self, node, tryResultTy); |
|
| 6893 | 8962 | } |
|
| 6894 | 8963 | ||
| 6895 | 8964 | /// Check that a `catch` body is assignable to the expected result type, but only |
|
| 6896 | 8965 | /// in expression context (`hint` is neither `Unknown` nor `Void`). |
|
| 6897 | - | unsafe fn checkCatchBody(self: &mut Resolver, body: *ast::Node, resultTy: Type, hint: Type) |
|
| 8966 | + | unsafe fn checkCatchBody 'arena (self: &mut Resolver 'arena, body: *ast::Node, resultTy: Type, hint: Type) |
|
| 6898 | 8967 | throws (ResolveError) |
|
| 6899 | 8968 | { |
|
| 6900 | 8969 | if hint <> Type::Unknown and hint <> Type::Void { |
|
| 6901 | 8970 | try checkAssignable(self, body, resultTy); |
|
| 6902 | 8971 | } |
| 6905 | 8974 | /// Resolve catch clauses for a `try ... catch` expression. |
|
| 6906 | 8975 | /// |
|
| 6907 | 8976 | /// For a single untyped catch (with or without binding), resolves the catch |
|
| 6908 | 8977 | /// body and returns the result type. Multi-error callees with inferred bindings |
|
| 6909 | 8978 | /// are rejected; you must use typed catches. |
|
| 6910 | - | unsafe fn resolveTryCatches( |
|
| 6911 | - | self: &mut Resolver, |
|
| 8979 | + | unsafe fn resolveTryCatches 'arena ( |
|
| 8980 | + | self: &mut Resolver 'arena, |
|
| 6912 | 8981 | node: *ast::Node, |
|
| 6913 | 8982 | catches: *[*ast::Node], |
|
| 6914 | 8983 | calleeInfo: *FnType, |
|
| 6915 | 8984 | resultTy: Type, |
|
| 6916 | 8985 | hint: Type |
| 6945 | 9014 | ||
| 6946 | 9015 | /// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`). |
|
| 6947 | 9016 | /// |
|
| 6948 | 9017 | /// Validates that each type annotation is in the callee's throw list, that |
|
| 6949 | 9018 | /// there are no duplicate catch types, and that the clauses are exhaustive. |
|
| 6950 | - | unsafe fn resolveTypedCatches( |
|
| 6951 | - | self: &mut Resolver, |
|
| 9019 | + | unsafe fn resolveTypedCatches 'arena ( |
|
| 9020 | + | self: &mut Resolver 'arena, |
|
| 6952 | 9021 | node: *ast::Node, |
|
| 6953 | 9022 | catches: *[*ast::Node], |
|
| 6954 | 9023 | calleeInfo: *FnType, |
|
| 6955 | 9024 | resultTy: Type, |
|
| 6956 | 9025 | hint: Type |
| 7012 | 9081 | } |
|
| 7013 | 9082 | return catchTy if resultTy == Type::Never else resultTy; |
|
| 7014 | 9083 | } |
|
| 7015 | 9084 | ||
| 7016 | 9085 | /// Analyze a `throw` statement. |
|
| 7017 | - | unsafe fn resolveThrow(self: &mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type |
|
| 9086 | + | unsafe fn resolveThrow 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: *ast::Node) -> Type |
|
| 7018 | 9087 | throws (ResolveError) |
|
| 7019 | 9088 | { |
|
| 7020 | 9089 | let fnInfo = self.currentFn |
|
| 7021 | 9090 | else throw emitError(self, node, ErrorKind::ThrowRequiresThrows); |
|
| 7022 | 9091 | if fnInfo.throwList.len == 0 { |
| 7031 | 9100 | } |
|
| 7032 | 9101 | throw emitError(self, expr, ErrorKind::ThrowIncompatibleError); |
|
| 7033 | 9102 | } |
|
| 7034 | 9103 | ||
| 7035 | 9104 | /// Analyze a `return` statement. |
|
| 7036 | - | unsafe fn resolveReturn(self: &mut Resolver, node: *ast::Node, retVal: ?*ast::Node) -> Type |
|
| 9105 | + | unsafe fn resolveReturn 'arena (self: &mut Resolver 'arena, node: *ast::Node, retVal: ?*ast::Node) -> Type |
|
| 7037 | 9106 | throws (ResolveError) |
|
| 7038 | 9107 | { |
|
| 7039 | 9108 | let f = self.currentFn |
|
| 7040 | 9109 | else throw emitError(self, node, ErrorKind::UnexpectedReturn); |
|
| 7041 | 9110 | let expected = *f.returnType; |
| 7177 | 9246 | } |
|
| 7178 | 9247 | } |
|
| 7179 | 9248 | ||
| 7180 | 9249 | /// Try to constant-fold a binary operation on two resolved operands. |
|
| 7181 | 9250 | /// Only folds when the result type is concrete. |
|
| 7182 | - | fn tryFoldBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp, resultTy: Type) { |
|
| 9251 | + | fn tryFoldBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp, resultTy: Type) { |
|
| 7183 | 9252 | let leftVal = constValueEntry(self, binop.left) |
|
| 7184 | 9253 | else return; |
|
| 7185 | 9254 | let rightVal = constValueEntry(self, binop.right) |
|
| 7186 | 9255 | else return; |
|
| 7187 | 9256 |
| 7209 | 9278 | } |
|
| 7210 | 9279 | } |
|
| 7211 | 9280 | } |
|
| 7212 | 9281 | ||
| 7213 | 9282 | /// Analyze a binary expression. |
|
| 7214 | - | unsafe fn resolveBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp) -> Type |
|
| 9283 | + | unsafe fn resolveBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp) -> Type |
|
| 7215 | 9284 | throws (ResolveError) |
|
| 7216 | 9285 | { |
|
| 7217 | 9286 | let mut resultTy = Type::Unknown; |
|
| 7218 | 9287 | ||
| 7219 | 9288 | match binop.op { |
| 7267 | 9336 | // never on references. |
|
| 7268 | 9337 | if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy { |
|
| 7269 | 9338 | if *leftTarget == Type::Opaque { |
|
| 7270 | 9339 | throw emitError(self, node, ErrorKind::OpaquePointerArithmetic); |
|
| 7271 | 9340 | } |
|
| 7272 | - | if leftClass <> types::PointerClass::Ref |
|
| 9341 | + | if not types::isReference(leftClass) |
|
| 7273 | 9342 | and isNumericType(rightTy) |
|
| 7274 | 9343 | { |
|
| 7275 | 9344 | try requireUnsafe(self, node); |
|
| 7276 | 9345 | return setNodeType(self, node, leftTy); |
|
| 7277 | 9346 | } |
| 7279 | 9348 | if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy { |
|
| 7280 | 9349 | if *rightTarget == Type::Opaque { |
|
| 7281 | 9350 | throw emitError(self, node, ErrorKind::OpaquePointerArithmetic); |
|
| 7282 | 9351 | } |
|
| 7283 | 9352 | if binop.op == ast::BinaryOp::Add |
|
| 7284 | - | and rightClass <> types::PointerClass::Ref |
|
| 9353 | + | and not types::isReference(rightClass) |
|
| 7285 | 9354 | and isNumericType(leftTy) |
|
| 7286 | 9355 | { |
|
| 7287 | 9356 | try requireUnsafe(self, node); |
|
| 7288 | 9357 | return setNodeType(self, node, rightTy); |
|
| 7289 | 9358 | } |
| 7320 | 9389 | ||
| 7321 | 9390 | return setNodeType(self, node, resultTy); |
|
| 7322 | 9391 | } |
|
| 7323 | 9392 | ||
| 7324 | 9393 | /// Analyze a unary expression. |
|
| 7325 | - | unsafe fn resolveUnOp(self: &mut Resolver, node: *ast::Node, unop: ast::UnOp) -> Type |
|
| 9394 | + | unsafe fn resolveUnOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, unop: ast::UnOp) -> Type |
|
| 7326 | 9395 | throws (ResolveError) |
|
| 7327 | 9396 | { |
|
| 7328 | 9397 | let mut resultTy = Type::Unknown; |
|
| 7329 | 9398 | ||
| 7330 | 9399 | match unop.op { |
| 7365 | 9434 | }; |
|
| 7366 | 9435 | return setNodeType(self, node, resultTy); |
|
| 7367 | 9436 | } |
|
| 7368 | 9437 | ||
| 7369 | 9438 | /// Resolve a type signature node and set its type. |
|
| 7370 | - | unsafe fn inferTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type |
|
| 9439 | + | unsafe fn inferTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type |
|
| 7371 | 9440 | throws (ResolveError) |
|
| 7372 | 9441 | { |
|
| 7373 | 9442 | let resolved = try resolveTypeSig(self, node, sig); |
|
| 7374 | 9443 | ||
| 7375 | 9444 | return setNodeType(self, node, resolved); |
|
| 7376 | 9445 | } |
|
| 7377 | 9446 | ||
| 9447 | + | /// Convert a parsed pointer qualifier to its semantic class. |
|
| 9448 | + | fn resolvePointerClass(class: ast::PointerClass) -> types::PointerClass { |
|
| 9449 | + | match class { |
|
| 9450 | + | case ast::PointerClass::Owned => return types::PointerClass::Owned, |
|
| 9451 | + | case ast::PointerClass::Ref => return types::PointerClass::Ref, |
|
| 9452 | + | case ast::PointerClass::Unsafe => return types::PointerClass::Unsafe, |
|
| 9453 | + | } |
|
| 9454 | + | } |
|
| 9455 | + | ||
| 7378 | 9456 | /// Convert a type signature node into a type value. |
|
| 7379 | - | unsafe fn resolveTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type |
|
| 9457 | + | unsafe fn resolveTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type |
|
| 7380 | 9458 | throws (ResolveError) |
|
| 7381 | 9459 | { |
|
| 7382 | 9460 | match sig { |
|
| 9461 | + | case ast::TypeSig::Cell { class, payload } => { |
|
| 9462 | + | let inner = try infer(self, payload); |
|
| 9463 | + | try validateCellPayload(self, node, inner); |
|
| 9464 | + | return Type::Cell { class: resolvePointerClass(class), payload: allocType(self, inner) }; |
|
| 9465 | + | } |
|
| 9466 | + | case ast::TypeSig::RegionRef { region, type } => { |
|
| 9467 | + | let identity = try resolveRegion(self, region); |
|
| 9468 | + | let base = try infer(self, type); |
|
| 9469 | + | match base { |
|
| 9470 | + | case Type::Cell { payload, .. } => |
|
| 9471 | + | return Type::Cell { class: types::PointerClass::Region(identity), payload }, |
|
| 9472 | + | case Type::Pointer { target, mutable, .. } => |
|
| 9473 | + | return Type::Pointer { class: types::PointerClass::Region(identity), target, mutable }, |
|
| 9474 | + | case Type::Slice { item, mutable, .. } => |
|
| 9475 | + | return Type::Slice { class: types::PointerClass::Region(identity), item, mutable }, |
|
| 9476 | + | case Type::TraitObject { traitInfo, mutable, .. } => |
|
| 9477 | + | return Type::TraitObject { class: types::PointerClass::Region(identity), traitInfo, mutable }, |
|
| 9478 | + | else => throw emitError(self, node, ErrorKind::InvalidRefPosition), |
|
| 9479 | + | } |
|
| 9480 | + | } |
|
| 9481 | + | case ast::TypeSig::Applied { name, regions } => { |
|
| 9482 | + | if let case ast::NodeValue::Ident(spelling) = name.value; |
|
| 9483 | + | mem::eq(spelling, "Session") and findTypeSymbol(self.scope, spelling) == nil |
|
| 9484 | + | { |
|
| 9485 | + | if regions.len <> 1 { |
|
| 9486 | + | throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch { |
|
| 9487 | + | expected: 1, actual: regions.len, |
|
| 9488 | + | })); |
|
| 9489 | + | } |
|
| 9490 | + | return Type::Session(try resolveRegion(self, regions[0])); |
|
| 9491 | + | } |
|
| 9492 | + | let base = try resolveTypeName(self, name); |
|
| 9493 | + | return Type::Nominal(try applyNominalRegions(self, base, regions, node)); |
|
| 9494 | + | } |
|
| 7383 | 9495 | case ast::TypeSig::Void => { |
|
| 7384 | 9496 | return Type::Void; |
|
| 7385 | 9497 | } |
|
| 7386 | 9498 | case ast::TypeSig::Never => { |
|
| 7387 | 9499 | return Type::Never; |
| 7411 | 9523 | return Type::Array(ArrayType { item: allocType(self, item), length }); |
|
| 7412 | 9524 | } |
|
| 7413 | 9525 | case ast::TypeSig::Slice { class, itemType, mutable } => { |
|
| 7414 | 9526 | let item = try infer(self, itemType); |
|
| 7415 | 9527 | return Type::Slice { |
|
| 7416 | - | class, |
|
| 9528 | + | class: resolvePointerClass(class), |
|
| 7417 | 9529 | item: allocType(self, item), |
|
| 7418 | 9530 | mutable, |
|
| 7419 | 9531 | }; |
|
| 7420 | 9532 | } |
|
| 7421 | 9533 | case ast::TypeSig::Pointer { class, valueType, mutable } => { |
|
| 7422 | 9534 | let target = try infer(self, valueType); |
|
| 7423 | 9535 | return Type::Pointer { |
|
| 7424 | - | class, |
|
| 9536 | + | class: resolvePointerClass(class), |
|
| 7425 | 9537 | target: allocType(self, target), |
|
| 7426 | 9538 | mutable, |
|
| 7427 | 9539 | }; |
|
| 7428 | 9540 | } |
|
| 7429 | 9541 | case ast::TypeSig::Optional { valueType } => { |
|
| 7430 | 9542 | let payload = try infer(self, valueType); |
|
| 7431 | 9543 | return Type::Optional(allocType(self, payload)); |
|
| 7432 | 9544 | } |
|
| 7433 | 9545 | case ast::TypeSig::Nominal(name) => { |
|
| 7434 | 9546 | let ty = try resolveTypeName(self, name); |
|
| 9547 | + | try requireNominalArguments(self, ty, node); |
|
| 7435 | 9548 | return Type::Nominal(ty); |
|
| 7436 | 9549 | } |
|
| 7437 | 9550 | case ast::TypeSig::Record { fields, labeled } => { |
|
| 7438 | 9551 | let mut recordType = try resolveRecordFields(self, node, fields, labeled); |
|
| 7439 | 9552 | set recordType.declaredCopy = true; |
|
| 7440 | 9553 | for field in recordType.fields { |
|
| 7441 | 9554 | if not isCopy(field.fieldType) { |
|
| 7442 | 9555 | set recordType.declaredCopy = false; |
|
| 7443 | 9556 | } |
|
| 7444 | 9557 | } |
|
| 9558 | + | set recordType.regions = self.regionScope; |
|
| 7445 | 9559 | let nominalTy = allocNominalType(self, NominalType::Record(recordType)); |
|
| 9560 | + | if let scope = self.regionScope { |
|
| 9561 | + | let map = regionSubstitution(self, scope); |
|
| 9562 | + | for parameter, i in scope.entries { |
|
| 9563 | + | set map.arguments[i] = parameter; |
|
| 9564 | + | } |
|
| 9565 | + | return Type::Nominal(internNominalApplication(self, nominalTy, &map)); |
|
| 9566 | + | } |
|
| 7446 | 9567 | return Type::Nominal(nominalTy); |
|
| 7447 | 9568 | } |
|
| 7448 | 9569 | case ast::TypeSig::Fn { sig: t, isUnsafe } => { |
|
| 7449 | - | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 9570 | + | let a = alloc::arenaAllocator(self.arena); |
|
| 7450 | 9571 | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 7451 | 9572 | let mut throwList: *mut [*Type] = &mut []; |
|
| 7452 | 9573 | ||
| 7453 | 9574 | if t.params.len > MAX_FN_PARAMS { |
|
| 7454 | 9575 | throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch { |
| 7468 | 9589 | paramTypes.append(allocType(self, paramTy), a); |
|
| 7469 | 9590 | } |
|
| 7470 | 9591 | for tyNode in t.throwList { |
|
| 7471 | 9592 | let throwTy = try resolveValueType(self, tyNode); |
|
| 7472 | 9593 | try ensureStorableType(self, tyNode, throwTy); |
|
| 9594 | + | try validateErrorTag(self, tyNode, throwTy, &throwList[..]); |
|
| 7473 | 9595 | throwList.append(allocType(self, throwTy), a); |
|
| 7474 | 9596 | } |
|
| 7475 | 9597 | let mut retType = allocType(self, Type::Void); |
|
| 7476 | 9598 | if let ret = t.returnType { |
|
| 7477 | 9599 | let resolvedRet = try resolveValueType(self, ret); |
|
| 7478 | 9600 | try ensureStorableType(self, ret, resolvedRet); |
|
| 7479 | 9601 | set retType = allocType(self, resolvedRet); |
|
| 7480 | 9602 | } |
|
| 7481 | 9603 | let fnType = FnType { |
|
| 9604 | + | regions: nil, |
|
| 7482 | 9605 | paramTypes: ¶mTypes[..], |
|
| 7483 | 9606 | returnType: retType, |
|
| 7484 | 9607 | throwList: &throwList[..], |
|
| 7485 | 9608 | isUnsafe, |
|
| 7486 | 9609 | }; |
| 7491 | 9614 | let sym = try resolveNamePath(self, traitName); |
|
| 7492 | 9615 | let case SymbolData::Trait(traitInfo) = sym.data |
|
| 7493 | 9616 | else throw emitError(self, traitName, ErrorKind::Internal); |
|
| 7494 | 9617 | setNodeSymbol(self, traitName, sym); |
|
| 7495 | 9618 | ||
| 7496 | - | return Type::TraitObject { class, traitInfo, mutable }; |
|
| 9619 | + | return Type::TraitObject { class: resolvePointerClass(class), traitInfo, mutable }; |
|
| 7497 | 9620 | } |
|
| 7498 | 9621 | } |
|
| 7499 | 9622 | } |
|
| 7500 | 9623 | ||
| 7501 | 9624 | /// Check if a type can be used for inferrence. |
| 7510 | 9633 | else => return true, |
|
| 7511 | 9634 | } |
|
| 7512 | 9635 | } |
|
| 7513 | 9636 | ||
| 7514 | 9637 | /// Analyze a standalone expression by wrapping it in a synthetic function. |
|
| 7515 | - | export unsafe fn resolveExpr( |
|
| 7516 | - | self: &mut Resolver, expr: *ast::Node, arena: &mut ast::NodeArena |
|
| 9638 | + | export unsafe fn resolveExpr 'arena ( |
|
| 9639 | + | self: &mut Resolver 'arena, expr: *ast::Node, arena: &mut ast::NodeArena |
|
| 7517 | 9640 | ) -> Diagnostics throws (ResolveError) { |
|
| 7518 | 9641 | let a = alloc::arenaAllocator(&mut arena.arena); |
|
| 7519 | 9642 | let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr)); |
|
| 7520 | 9643 | let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a); |
|
| 7521 | 9644 | let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts); |
| 7533 | 9656 | ||
| 7534 | 9657 | return diagnostics(self); |
|
| 7535 | 9658 | } |
|
| 7536 | 9659 | ||
| 7537 | 9660 | /// Analyze a parsed module root, ie. a block of top-level statements. |
|
| 7538 | - | export unsafe fn resolveModuleRoot(self: &mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 9661 | + | export unsafe fn resolveModuleRoot 'arena (self: &mut Resolver 'arena, root: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 7539 | 9662 | let case ast::NodeValue::Block(block) = root.value |
|
| 7540 | 9663 | else panic "resolveModuleRoot: expected block for module root"; |
|
| 7541 | 9664 | ||
| 7542 | 9665 | enterScope(self, root); |
|
| 7543 | 9666 | try resolveModuleDecls(self, &block) catch { |
| 7553 | 9676 | } |
|
| 7554 | 9677 | ||
| 7555 | 9678 | /// Analyze the module graph. This pass processes `mod` statements, creating symbols |
|
| 7556 | 9679 | /// and scopes for them, and also binds type names in each module so that cross-module |
|
| 7557 | 9680 | /// type references work regardless of declaration order. |
|
| 7558 | - | unsafe fn resolveModuleGraph(self: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 9681 | + | unsafe fn resolveModuleGraph 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
|
| 7559 | 9682 | try bindTypeNames(self, block); |
|
| 7560 | 9683 | ||
| 7561 | 9684 | for node in block.statements { |
|
| 7562 | 9685 | if let case ast::NodeValue::Mod(decl) = node.value { |
|
| 7563 | 9686 | try resolveModGraph(self, node, decl); |
| 7565 | 9688 | } |
|
| 7566 | 9689 | } |
|
| 7567 | 9690 | ||
| 7568 | 9691 | /// Bind all type names in a module. |
|
| 7569 | 9692 | /// Skips declarations that have already been bound. |
|
| 7570 | - | unsafe fn bindTypeNames(self: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 9693 | + | unsafe fn bindTypeNames 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
|
| 7571 | 9694 | for node in block.statements { |
|
| 7572 | 9695 | match node.value { |
|
| 7573 | 9696 | case ast::NodeValue::RecordDecl(decl) => { |
|
| 7574 | 9697 | if symbolFor(self, node) == nil { |
|
| 7575 | 9698 | try bindTypeName(self, node, decl.name, decl.attrs) catch {}; |
| 7589 | 9712 | } |
|
| 7590 | 9713 | } |
|
| 7591 | 9714 | } |
|
| 7592 | 9715 | ||
| 7593 | 9716 | /// Resolve all type bodies in a module. |
|
| 7594 | - | unsafe fn resolveTypeBodies(self: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 9717 | + | unsafe fn resolveTypeBodies 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
|
| 7595 | 9718 | for node in block.statements { |
|
| 7596 | 9719 | match node.value { |
|
| 7597 | 9720 | case ast::NodeValue::RecordDecl(decl) => { |
|
| 7598 | 9721 | try resolveRecordBody(self, node, decl) catch { |
|
| 7599 | 9722 | // Continue resolving other types even if one fails. |
| 7622 | 9745 | /// previous pass. |
|
| 7623 | 9746 | /// |
|
| 7624 | 9747 | /// This function uses a two-phase approach: |
|
| 7625 | 9748 | /// Phase 1: Bind all type names to allow forward references and mutual recursion. |
|
| 7626 | 9749 | /// Phase 2: Resolve type bodies, ie. field types, variant types, etc. |
|
| 7627 | - | unsafe fn resolveModuleDecls(res: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 9750 | + | unsafe fn resolveModuleDecls 'arena (res: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
|
| 7628 | 9751 | // Phase 1: Bind all type names as placeholders. |
|
| 7629 | 9752 | try bindTypeNames(res, block); |
|
| 7630 | 9753 | // Phase 2: Process imports so names available from the module graph can |
|
| 7631 | 9754 | // be used in function signatures. |
|
| 7632 | 9755 | for node in block.statements { |
| 7688 | 9811 | fn linearBindingAvailable(env: &LinearEnv, index: u32) -> bool { |
|
| 7689 | 9812 | return (env.available & ((1 as u64) << (index as u64))) <> 0; |
|
| 7690 | 9813 | } |
|
| 7691 | 9814 | ||
| 7692 | 9815 | /// Add a local binding when its resolved type moves by value. |
|
| 7693 | - | unsafe fn addLinearBinding(checker: &mut LinearChecker, env: &mut LinearEnv, node: *ast::Node) |
|
| 7694 | - | throws (ResolveError) |
|
| 9816 | + | unsafe fn addLinearBinding 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node) |
|
| 9817 | + | throws (ResolveError) where 'arena: 'checking |
|
| 7695 | 9818 | { |
|
| 7696 | 9819 | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7697 | 9820 | let case SymbolData::Value { type: ty, .. } = sym.data else return; |
|
| 7698 | 9821 | if not isMoveOnly(ty) { |
|
| 7699 | 9822 | return; |
| 7705 | 9828 | set env.available |= (1 as u64) << (env.len as u64); |
|
| 7706 | 9829 | set env.len += 1; |
|
| 7707 | 9830 | } |
|
| 7708 | 9831 | ||
| 7709 | 9832 | /// Mark a tracked binding as uninitialized. |
|
| 7710 | - | unsafe fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) { |
|
| 9833 | + | unsafe fn markLinearBindingUnavailable 'arena (self: &mut Resolver 'arena, env: &mut LinearEnv, node: *ast::Node) { |
|
| 7711 | 9834 | let sym = symbolFor(self, node) else return; |
|
| 7712 | 9835 | let index = findLinearBinding(env, sym) else return; |
|
| 7713 | 9836 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7714 | 9837 | } |
|
| 7715 | 9838 | ||
| 7716 | 9839 | /// Require exact-use bindings introduced after `start` to be consumed. |
|
| 7717 | - | unsafe fn finishLinearScope( |
|
| 7718 | - | checker: &mut LinearChecker, |
|
| 9840 | + | unsafe fn finishLinearScope 'arena 'checking ( |
|
| 9841 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 7719 | 9842 | env: &mut LinearEnv, |
|
| 7720 | 9843 | start: u32, |
|
| 7721 | - | ) throws (ResolveError) { |
|
| 9844 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 7722 | 9845 | if not env.terminated { |
|
| 7723 | 9846 | for i in start..env.len { |
|
| 7724 | 9847 | if linearBindingAvailable(env, i) { |
|
| 7725 | 9848 | let sym = env.symbols[i]; |
|
| 7726 | 9849 | let case SymbolData::Value { type: ty, .. } = sym.data |
| 7737 | 9860 | } |
|
| 7738 | 9861 | set env.len = start; |
|
| 7739 | 9862 | } |
|
| 7740 | 9863 | ||
| 7741 | 9864 | /// Require a tracked identifier to remain available for any access. |
|
| 7742 | - | unsafe fn checkLinearIdent( |
|
| 7743 | - | checker: &mut LinearChecker, |
|
| 9865 | + | unsafe fn checkLinearIdent 'arena 'checking ( |
|
| 9866 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 7744 | 9867 | env: &mut LinearEnv, |
|
| 7745 | 9868 | node: *ast::Node, |
|
| 7746 | - | ) throws (ResolveError) { |
|
| 9869 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 7747 | 9870 | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7748 | 9871 | let index = findLinearBinding(env, sym) else return; |
|
| 7749 | 9872 | if not linearBindingAvailable(env, index) { |
|
| 7750 | 9873 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7751 | 9874 | else panic "consumeLinearIdent: expected value symbol"; |
| 7754 | 9877 | throw emitError(checker.resolver, node, kind); |
|
| 7755 | 9878 | } |
|
| 7756 | 9879 | } |
|
| 7757 | 9880 | ||
| 7758 | 9881 | /// Move or consume a tracked identifier once. |
|
| 7759 | - | unsafe fn consumeLinearIdent( |
|
| 7760 | - | checker: &mut LinearChecker, |
|
| 9882 | + | unsafe fn consumeLinearIdent 'arena 'checking ( |
|
| 9883 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 7761 | 9884 | env: &mut LinearEnv, |
|
| 7762 | 9885 | node: *ast::Node, |
|
| 7763 | - | ) throws (ResolveError) { |
|
| 9886 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 7764 | 9887 | try checkLinearIdent(checker, env, node); |
|
| 7765 | 9888 | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7766 | 9889 | let index = findLinearBinding(env, sym) else return; |
|
| 7767 | 9890 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7768 | 9891 | } |
|
| 7769 | 9892 | ||
| 7770 | 9893 | /// Merge ownership availability across two live branches. |
|
| 7771 | 9894 | /// Validate both inputs before writing to an output that can alias either input. |
|
| 7772 | - | unsafe fn joinLinearBranches( |
|
| 7773 | - | checker: &mut LinearChecker, |
|
| 9895 | + | unsafe fn joinLinearBranches 'arena 'checking ( |
|
| 9896 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 7774 | 9897 | env: &mut LinearEnv, |
|
| 7775 | 9898 | left: &LinearEnv, |
|
| 7776 | 9899 | right: &LinearEnv, |
|
| 7777 | 9900 | node: *ast::Node, |
|
| 7778 | - | ) throws (ResolveError) { |
|
| 9901 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 7779 | 9902 | if left.terminated and right.terminated { |
|
| 7780 | 9903 | set *env = *left; |
|
| 7781 | 9904 | set env.terminated = true; |
|
| 7782 | 9905 | return; |
|
| 7783 | 9906 | } |
| 7804 | 9927 | ); |
|
| 7805 | 9928 | } |
|
| 7806 | 9929 | set available &= ~((1 as u64) << (i as u64)); |
|
| 7807 | 9930 | } |
|
| 7808 | 9931 | } |
|
| 9932 | + | let regionalLoans = left.regionalLoans | right.regionalLoans; |
|
| 7809 | 9933 | set *env = *left; |
|
| 7810 | 9934 | set env.available = available; |
|
| 9935 | + | set env.regionalLoans = regionalLoans; |
|
| 7811 | 9936 | } |
|
| 7812 | 9937 | ||
| 7813 | 9938 | /// Require all available exact-use bindings to be consumed at a function exit. |
|
| 7814 | - | unsafe fn finishLinearExit( |
|
| 7815 | - | checker: &mut LinearChecker, |
|
| 9939 | + | unsafe fn finishLinearExit 'arena 'checking ( |
|
| 9940 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 7816 | 9941 | env: &mut LinearEnv, |
|
| 7817 | - | ) throws (ResolveError) { |
|
| 9942 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 7818 | 9943 | if env.terminated { |
|
| 7819 | 9944 | return; |
|
| 7820 | 9945 | } |
|
| 7821 | 9946 | for i in 0..env.len { |
|
| 7822 | 9947 | if linearBindingAvailable(env, i) { |
| 7834 | 9959 | } |
|
| 7835 | 9960 | set env.terminated = true; |
|
| 7836 | 9961 | } |
|
| 7837 | 9962 | ||
| 7838 | 9963 | /// Find the local root borrowed or consumed by an argument expression. |
|
| 7839 | - | fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Symbol { |
|
| 9964 | + | fn linearRootSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol { |
|
| 7840 | 9965 | match node.value { |
|
| 7841 | 9966 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => |
|
| 7842 | 9967 | return symbolFor(self, node), |
|
| 7843 | 9968 | case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value), |
|
| 7844 | 9969 | case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target), |
| 7849 | 9974 | case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target), |
|
| 7850 | 9975 | else => return nil, |
|
| 7851 | 9976 | } |
|
| 7852 | 9977 | } |
|
| 7853 | 9978 | ||
| 9979 | + | /// A projection loan that remains active until its named region ends. |
|
| 9980 | + | record RegionalLoan: Copy { |
|
| 9981 | + | /// Address expression that supplies access to the loan. |
|
| 9982 | + | source: *ast::Node, |
|
| 9983 | + | /// Declared lifetime of the projection. |
|
| 9984 | + | region: *unsafe types::Region, |
|
| 9985 | + | /// Storage protected by the projection. |
|
| 9986 | + | place: BorrowPlace, |
|
| 9987 | + | /// Whether accesses through other references are excluded. |
|
| 9988 | + | exclusive: bool, |
|
| 9989 | + | } |
|
| 9990 | + | ||
| 9991 | + | /// Return whether a region identity is visible in a lexical environment. |
|
| 9992 | + | unsafe fn regionInScope(scope: ?*RegionScope, region: *unsafe types::Region) -> bool { |
|
| 9993 | + | let mut cursor = scope; |
|
| 9994 | + | while let current = cursor { |
|
| 9995 | + | for entry in current.entries { |
|
| 9996 | + | if entry.id == region.id { |
|
| 9997 | + | return true; |
|
| 9998 | + | } |
|
| 9999 | + | } |
|
| 10000 | + | set cursor = current.parent; |
|
| 10001 | + | } |
|
| 10002 | + | return false; |
|
| 10003 | + | } |
|
| 10004 | + | ||
| 10005 | + | /// Retain only loans whose regions remain active at a control-flow destination. |
|
| 10006 | + | unsafe fn regionalLoansInScope 'arena 'checking (checker: &LinearChecker 'arena 'checking, mask: u64, scope: ?*RegionScope) -> u64 where 'arena: 'checking { |
|
| 10007 | + | let mut result: u64 = 0; |
|
| 10008 | + | for i in 0..checker.regionalLen { |
|
| 10009 | + | let bit = (1 as u64) << (i as u64); |
|
| 10010 | + | if (mask & bit) <> 0 and regionInScope(scope, checker.regional[i].region) { |
|
| 10011 | + | set result |= bit; |
|
| 10012 | + | } |
|
| 10013 | + | } |
|
| 10014 | + | return result; |
|
| 10015 | + | } |
|
| 10016 | + | ||
| 10017 | + | /// Remap one loan mask after the regional loan table is compacted. |
|
| 10018 | + | fn remapRegionalLoans(mask: u64, mapping: &[u64]) -> u64 { |
|
| 10019 | + | let mut result: u64 = 0; |
|
| 10020 | + | for replacement, i in mapping { |
|
| 10021 | + | if (mask & ((1 as u64) << (i as u64))) <> 0 { |
|
| 10022 | + | set result |= replacement; |
|
| 10023 | + | } |
|
| 10024 | + | } |
|
| 10025 | + | return result; |
|
| 10026 | + | } |
|
| 10027 | + | ||
| 10028 | + | /// Reclaim ended-region entries and preserve loans for enclosing regions. |
|
| 10029 | + | unsafe fn compactRegionalLoans 'arena 'checking ( |
|
| 10030 | + | checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, |
|
| 10031 | + | scope: ?*RegionScope |
|
| 10032 | + | ) where 'arena: 'checking { |
|
| 10033 | + | let oldLen = checker.regionalLen; |
|
| 10034 | + | let mut mapping: [u64; MAX_REGIONAL_LOANS] = [0; MAX_REGIONAL_LOANS]; |
|
| 10035 | + | let mut next: u32 = 0; |
|
| 10036 | + | for i in 0..oldLen { |
|
| 10037 | + | let loan = checker.regional[i]; |
|
| 10038 | + | if regionInScope(scope, loan.region) { |
|
| 10039 | + | set checker.regional[next] = loan; |
|
| 10040 | + | set mapping[i] = (1 as u64) << (next as u64); |
|
| 10041 | + | set next += 1; |
|
| 10042 | + | } |
|
| 10043 | + | } |
|
| 10044 | + | set env.regionalLoans = remapRegionalLoans(env.regionalLoans, &mapping[..oldLen]); |
|
| 10045 | + | for i in 0..checker.loopDepth { |
|
| 10046 | + | set checker.loopBackLoans[i] = remapRegionalLoans(checker.loopBackLoans[i], &mapping[..oldLen]); |
|
| 10047 | + | set checker.loopExitLoans[i] = remapRegionalLoans(checker.loopExitLoans[i], &mapping[..oldLen]); |
|
| 10048 | + | } |
|
| 10049 | + | set checker.regionalLen = next; |
|
| 10050 | + | } |
|
| 10051 | + | ||
| 10052 | + | /// Check whether an access comes from the reference created by a projection. |
|
| 10053 | + | unsafe fn usesRegionalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: *ast::Node) -> bool { |
|
| 10054 | + | if node.id == source.id { |
|
| 10055 | + | return true; |
|
| 10056 | + | } |
|
| 10057 | + | let root = linearRootSymbol(self, node) else return false; |
|
| 10058 | + | let origin = localReferenceSource(root) else return false; |
|
| 10059 | + | return usesRegionalLoan(self, origin, source); |
|
| 10060 | + | } |
|
| 10061 | + | ||
| 10062 | + | /// Retain a full-region projection independently of its local binding scope. |
|
| 10063 | + | unsafe fn addRegionalLoan 'arena 'checking ( |
|
| 10064 | + | checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node, address: ast::AddressOf |
|
| 10065 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 10066 | + | let ty = typeFor(checker.resolver, node) else return; |
|
| 10067 | + | let mut class = types::PointerClass::Ref; |
|
| 10068 | + | match ty { |
|
| 10069 | + | case Type::Cell { class: cellClass, .. } => set class = cellClass, |
|
| 10070 | + | case Type::Pointer { class: pointerClass, .. } => set class = pointerClass, |
|
| 10071 | + | case Type::Slice { class: sliceClass, .. } => set class = sliceClass, |
|
| 10072 | + | else => return, |
|
| 10073 | + | } |
|
| 10074 | + | let case types::PointerClass::Region(region) = class else return; |
|
| 10075 | + | let storage = addressStorageClass(checker.resolver, address.target); |
|
| 10076 | + | let case types::PointerClass::Region(parent) = storage else return; |
|
| 10077 | + | if parent.id <> region.id { |
|
| 10078 | + | return; |
|
| 10079 | + | } |
|
| 10080 | + | let place = borrowPlace(checker.resolver, address.target); |
|
| 10081 | + | if place.root == nil { |
|
| 10082 | + | return; |
|
| 10083 | + | } |
|
| 10084 | + | for loan, i in &checker.regional[..checker.regionalLen] { |
|
| 10085 | + | if loan.source.id == node.id { |
|
| 10086 | + | set env.regionalLoans |= (1 as u64) << (i as u64); |
|
| 10087 | + | return; |
|
| 10088 | + | } |
|
| 10089 | + | } |
|
| 10090 | + | if checker.regionalLen >= MAX_REGIONAL_LOANS { |
|
| 10091 | + | throw emitError(checker.resolver, node, ErrorKind::RegionalLoanOverflow); |
|
| 10092 | + | } |
|
| 10093 | + | let index = checker.regionalLen; |
|
| 10094 | + | set checker.regional[index] = RegionalLoan { source: node, region, place, exclusive: address.mutable }; |
|
| 10095 | + | set checker.regionalLen += 1; |
|
| 10096 | + | set env.regionalLoans |= (1 as u64) << (index as u64); |
|
| 10097 | + | } |
|
| 10098 | + | ||
| 10099 | + | /// Return whether an initializer copies a shared reference with a named region. |
|
| 10100 | + | /// Address expressions and casts retain a loan on their source storage. |
|
| 10101 | + | unsafe fn copiesRegionalReference(ty: Type, value: *ast::Node) -> bool { |
|
| 10102 | + | if referenceRegion(ty) == nil or not isCopy(ty) { |
|
| 10103 | + | return false; |
|
| 10104 | + | } |
|
| 10105 | + | match value.value { |
|
| 10106 | + | case ast::NodeValue::AddressOf(_), ast::NodeValue::As(_) => return false, |
|
| 10107 | + | else => return true, |
|
| 10108 | + | } |
|
| 10109 | + | } |
|
| 10110 | + | ||
| 7854 | 10111 | /// Return the initializer that supplies a local reference's storage. |
|
| 7855 | 10112 | unsafe fn localReferenceSource(sym: *unsafe mut Symbol) -> ?*ast::Node { |
|
| 7856 | 10113 | let case SymbolData::Value { type: ty, .. } = sym.data else return nil; |
|
| 10114 | + | if let case Type::Session(_) = ty { |
|
| 10115 | + | if let case ast::NodeValue::RegionBinding(binding) = sym.node.value { |
|
| 10116 | + | return binding.value; |
|
| 10117 | + | } |
|
| 10118 | + | } |
|
| 7857 | 10119 | if isRefType(ty) { |
|
| 7858 | 10120 | if let case ast::NodeValue::Let(binding) = sym.node.value { |
|
| 10121 | + | if copiesRegionalReference(ty, binding.value) { |
|
| 10122 | + | return nil; |
|
| 10123 | + | } |
|
| 10124 | + | return binding.value; |
|
| 10125 | + | } |
|
| 10126 | + | if let case ast::NodeValue::RegionBinding(binding) = sym.node.value { |
|
| 7859 | 10127 | return binding.value; |
|
| 7860 | 10128 | } |
|
| 7861 | 10129 | } |
|
| 7862 | 10130 | return nil; |
|
| 7863 | 10131 | } |
|
| 7864 | 10132 | ||
| 7865 | 10133 | /// Resolve a place through reference locals without extending its storage lifetime. |
|
| 7866 | - | unsafe fn borrowPlace(self: &mut Resolver, node: *ast::Node) -> BorrowPlace { |
|
| 10134 | + | unsafe fn borrowPlace 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> BorrowPlace { |
|
| 7867 | 10135 | let mut place = BorrowPlace { root: nil, fields: undefined, len: 0, precise: true }; |
|
| 7868 | 10136 | match node.value { |
|
| 7869 | 10137 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => { |
|
| 7870 | 10138 | let sym = symbolFor(self, node) else return place; |
|
| 7871 | 10139 | if let source = localReferenceSource(sym) { |
|
| 7872 | - | return borrowPlace(self, source); |
|
| 10140 | + | let origin = borrowPlace(self, source); |
|
| 10141 | + | if origin.root <> nil { |
|
| 10142 | + | return origin; |
|
| 10143 | + | } |
|
| 7873 | 10144 | } |
|
| 7874 | 10145 | set place.root = sym; |
|
| 7875 | 10146 | } |
|
| 7876 | 10147 | case ast::NodeValue::AddressOf(addr) => return borrowPlace(self, addr.target), |
|
| 7877 | 10148 | case ast::NodeValue::As(expr) => return borrowPlace(self, expr.value), |
| 7895 | 10166 | set place.precise = false; |
|
| 7896 | 10167 | } |
|
| 7897 | 10168 | case ast::NodeValue::Subscript { container, .. } => { |
|
| 7898 | 10169 | set place = borrowPlace(self, container); |
|
| 7899 | 10170 | if let ty = typeFor(self, container) { |
|
| 7900 | - | if let case Type::Slice { class, .. } = autoDeref(ty); class <> types::PointerClass::Ref { |
|
| 10171 | + | if let case Type::Slice { class, .. } = autoDeref(ty); not types::isReference(class) { |
|
| 7901 | 10172 | set place.len = 0; |
|
| 7902 | 10173 | } |
|
| 7903 | 10174 | } |
|
| 7904 | 10175 | set place.precise = false; |
|
| 7905 | 10176 | } |
| 7928 | 10199 | } |
|
| 7929 | 10200 | return true; |
|
| 7930 | 10201 | } |
|
| 7931 | 10202 | ||
| 7932 | 10203 | /// Check whether access uses a reference or one of its lexical reborrows. |
|
| 7933 | - | unsafe fn usesLocalLoan(self: &mut Resolver, node: *ast::Node, binding: *unsafe mut Symbol) -> bool { |
|
| 10204 | + | unsafe fn usesLocalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, binding: *unsafe mut Symbol) -> bool { |
|
| 7934 | 10205 | let root = linearRootSymbol(self, node) else return false; |
|
| 7935 | 10206 | if root == binding { |
|
| 7936 | 10207 | return true; |
|
| 7937 | 10208 | } |
|
| 7938 | 10209 | let source = localReferenceSource(root) else return false; |
|
| 7939 | 10210 | return usesLocalLoan(self, source, binding); |
|
| 7940 | 10211 | } |
|
| 7941 | 10212 | ||
| 7942 | 10213 | /// Reject accesses that conflict with a reference in an active lexical scope. |
|
| 7943 | - | unsafe fn checkLocalLoans(checker: &mut LinearChecker, node: *ast::Node, exclusive: bool) |
|
| 7944 | - | throws (ResolveError) |
|
| 10214 | + | unsafe fn checkLocalLoans 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, exclusive: bool) |
|
| 10215 | + | throws (ResolveError) where 'arena: 'checking |
|
| 7945 | 10216 | { |
|
| 7946 | 10217 | let place = borrowPlace(checker.resolver, node); |
|
| 7947 | 10218 | let root = place.root else return; |
|
| 10219 | + | for i in 0..checker.regionalLen { |
|
| 10220 | + | if (env.regionalLoans & ((1 as u64) << (i as u64))) == 0 { |
|
| 10221 | + | continue; |
|
| 10222 | + | } |
|
| 10223 | + | let loan = checker.regional[i]; |
|
| 10224 | + | if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place) |
|
| 10225 | + | and not usesRegionalLoan(checker.resolver, node, loan.source) |
|
| 10226 | + | { |
|
| 10227 | + | throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name)); |
|
| 10228 | + | } |
|
| 10229 | + | } |
|
| 7948 | 10230 | for i in 0..checker.localLen { |
|
| 7949 | 10231 | let loan = checker.locals[i]; |
|
| 7950 | 10232 | if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place) |
|
| 7951 | 10233 | and not usesLocalLoan(checker.resolver, node, loan.binding) |
|
| 7952 | 10234 | { |
|
| 7953 | 10235 | throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name)); |
|
| 7954 | 10236 | } |
|
| 7955 | 10237 | } |
|
| 7956 | 10238 | } |
|
| 7957 | 10239 | ||
| 7958 | - | /// Retain existing storage until its immutable reference binding leaves scope. |
|
| 7959 | - | unsafe fn addLocalLoan(checker: &mut LinearChecker, node: *ast::Node, binding: ast::Let) |
|
| 7960 | - | throws (ResolveError) |
|
| 10240 | + | /// Retain source storage for local borrows and region headers. |
|
| 10241 | + | unsafe fn addLocalLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, binding: ast::Let) |
|
| 10242 | + | throws (ResolveError) where 'arena: 'checking |
|
| 7961 | 10243 | { |
|
| 7962 | 10244 | let ty = typeFor(checker.resolver, binding.ident) else return; |
|
| 7963 | 10245 | if not isRefType(ty) { |
|
| 10246 | + | let case Type::Session(_) = ty else return; |
|
| 10247 | + | let case ast::NodeValue::RegionBinding(_) = node.value else return; |
|
| 10248 | + | } |
|
| 10249 | + | if let case ast::NodeValue::Let(_) = node.value; |
|
| 10250 | + | copiesRegionalReference(ty, binding.value) |
|
| 10251 | + | { |
|
| 7964 | 10252 | return; |
|
| 7965 | 10253 | } |
|
| 7966 | 10254 | let place = borrowPlace(checker.resolver, binding.value); |
|
| 7967 | 10255 | if place.root == nil { |
|
| 10256 | + | if referenceRegion(ty) <> nil { |
|
| 10257 | + | return; |
|
| 10258 | + | } |
|
| 7968 | 10259 | throw emitError(checker.resolver, node, ErrorKind::RefBinding); |
|
| 7969 | 10260 | } |
|
| 7970 | 10261 | if checker.localLen >= MAX_LINEAR_BINDINGS { |
|
| 7971 | 10262 | throw emitError(checker.resolver, node, ErrorKind::Internal); |
|
| 7972 | 10263 | } |
|
| 7973 | 10264 | let sym = symbolFor(checker.resolver, node) else panic "reference without binding"; |
|
| 7974 | - | let exclusive = isExclusiveArgument(ty); |
|
| 7975 | - | try checkLocalLoans(checker, binding.value, exclusive); |
|
| 10265 | + | let mut exclusive = isExclusiveArgument(ty); |
|
| 10266 | + | if let case Type::Cell { .. } = ty { |
|
| 10267 | + | if let case ast::NodeValue::As(expr) = binding.value.value { |
|
| 10268 | + | if let source = typeFor(checker.resolver, expr.value) { |
|
| 10269 | + | if let case Type::Pointer { mutable: true, .. } = source { |
|
| 10270 | + | set exclusive = true; |
|
| 10271 | + | } |
|
| 10272 | + | } |
|
| 10273 | + | } |
|
| 10274 | + | } |
|
| 10275 | + | try checkLocalLoans(checker, env, binding.value, exclusive); |
|
| 7976 | 10276 | set checker.locals[checker.localLen] = LocalLoan { binding: sym, place, exclusive }; |
|
| 7977 | 10277 | set checker.localLen += 1; |
|
| 7978 | 10278 | } |
|
| 7979 | 10279 | ||
| 7980 | - | /// Protect a pattern source until its reference bindings leave scope. |
|
| 7981 | - | unsafe fn addPatternLoan(checker: &mut LinearChecker, subject: *ast::Node) |
|
| 7982 | - | throws (ResolveError) |
|
| 10280 | + | /// Protect storage borrowed by a pointer pattern until its bindings leave scope. |
|
| 10281 | + | unsafe fn addPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, subject: *ast::Node) |
|
| 10282 | + | throws (ResolveError) where 'arena: 'checking |
|
| 7983 | 10283 | { |
|
| 10284 | + | let ty = typeFor(checker.resolver, subject) else return; |
|
| 10285 | + | if unwrapMatchSubject(ty).by == MatchBy::Value { |
|
| 10286 | + | return; |
|
| 10287 | + | } |
|
| 7984 | 10288 | let place = borrowPlace(checker.resolver, subject); |
|
| 7985 | 10289 | if place.root == nil { |
|
| 7986 | 10290 | return; |
|
| 7987 | 10291 | } |
|
| 7988 | 10292 | if checker.loanLen >= MAX_LINEAR_BINDINGS { |
| 7991 | 10295 | set checker.loans[checker.loanLen] = place; |
|
| 7992 | 10296 | set checker.loanLen += 1; |
|
| 7993 | 10297 | } |
|
| 7994 | 10298 | ||
| 7995 | 10299 | /// Reject a write, mutable loan, or ownership transfer of a pattern source. |
|
| 7996 | - | unsafe fn checkPatternLoan(checker: &mut LinearChecker, node: *ast::Node) |
|
| 7997 | - | throws (ResolveError) |
|
| 10300 | + | unsafe fn checkPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, node: *ast::Node) |
|
| 10301 | + | throws (ResolveError) where 'arena: 'checking |
|
| 7998 | 10302 | { |
|
| 7999 | 10303 | let place = borrowPlace(checker.resolver, node); |
|
| 8000 | 10304 | let root = place.root else return; |
|
| 8001 | 10305 | for i in 0..checker.loanLen { |
|
| 8002 | 10306 | if placesOverlap(&checker.loans[i], &place) { |
|
| 8003 | 10307 | throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name)); |
|
| 8004 | 10308 | } |
|
| 8005 | 10309 | } |
|
| 8006 | 10310 | } |
|
| 8007 | 10311 | ||
| 10312 | + | /// Return whether a parameter borrows its argument only for the call. |
|
| 10313 | + | unsafe fn isBorrowedReferenceParameter(ty: Type) -> bool { |
|
| 10314 | + | if not isRefType(ty) { |
|
| 10315 | + | return false; |
|
| 10316 | + | } |
|
| 10317 | + | match ty { |
|
| 10318 | + | case Type::Cell { class, .. } => return class == types::PointerClass::Ref, |
|
| 10319 | + | case Type::Pointer { class, .. } => return class == types::PointerClass::Ref, |
|
| 10320 | + | case Type::Slice { class, .. } => return class == types::PointerClass::Ref, |
|
| 10321 | + | case Type::TraitObject { class, .. } => return class == types::PointerClass::Ref, |
|
| 10322 | + | else => return false, |
|
| 10323 | + | } |
|
| 10324 | + | } |
|
| 10325 | + | ||
| 8008 | 10326 | /// Return whether a parameter can mutate or consume its argument's storage. |
|
| 8009 | 10327 | unsafe fn isExclusiveArgument(ty: Type) -> bool { |
|
| 8010 | 10328 | match ty { |
|
| 8011 | 10329 | case Type::Pointer { mutable, .. } => return mutable, |
|
| 8012 | 10330 | case Type::Slice { mutable, .. } => return mutable, |
| 8015 | 10333 | } |
|
| 8016 | 10334 | } |
|
| 8017 | 10335 | ||
| 8018 | 10336 | /// Add the value identifiers introduced by a pattern. |
|
| 8019 | 10337 | /// Return whether the pattern introduces references to its source storage. |
|
| 8020 | - | unsafe fn addLinearPatternBindings( |
|
| 8021 | - | checker: &mut LinearChecker, |
|
| 10338 | + | unsafe fn addLinearPatternBindings 'arena 'checking ( |
|
| 10339 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8022 | 10340 | env: &mut LinearEnv, |
|
| 8023 | 10341 | pattern: *ast::Node, |
|
| 8024 | - | ) -> bool throws (ResolveError) { |
|
| 10342 | + | ) -> bool throws (ResolveError) where 'arena: 'checking { |
|
| 8025 | 10343 | let mut hasReferences = false; |
|
| 8026 | 10344 | match pattern.value { |
|
| 8027 | 10345 | case ast::NodeValue::Ident(_) => { |
|
| 8028 | 10346 | try addLinearBinding(checker, env, pattern); |
|
| 8029 | 10347 | if let ty = typeFor(checker.resolver, pattern) { |
| 8057 | 10375 | } |
|
| 8058 | 10376 | return hasReferences; |
|
| 8059 | 10377 | } |
|
| 8060 | 10378 | ||
| 8061 | 10379 | /// Check a lexical block and exact-use of locals introduced in it. |
|
| 8062 | - | unsafe fn checkLinearBlock( |
|
| 8063 | - | checker: &mut LinearChecker, |
|
| 10380 | + | unsafe fn checkLinearBlock 'arena 'checking ( |
|
| 10381 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8064 | 10382 | env: &mut LinearEnv, |
|
| 8065 | 10383 | node: *ast::Node, |
|
| 8066 | - | ) throws (ResolveError) { |
|
| 10384 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8067 | 10385 | let start = env.len; |
|
| 8068 | 10386 | let localStart = checker.localLen; |
|
| 8069 | 10387 | let case ast::NodeValue::Block(block) = node.value |
|
| 8070 | 10388 | else panic "checkLinearBlock: expected block"; |
|
| 8071 | 10389 | for stmt in block.statements { |
| 8078 | 10396 | set checker.localLen = localStart; |
|
| 8079 | 10397 | } |
|
| 8080 | 10398 | ||
| 8081 | 10399 | /// Push a repeated-control-flow boundary. |
|
| 8082 | 10400 | /// Initialize all loop state at this depth before increasing `loopDepth`. |
|
| 8083 | - | fn enterLinearLoop(checker: &mut LinearChecker, env: &LinearEnv) { |
|
| 10401 | + | fn enterLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking { |
|
| 8084 | 10402 | assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow"; |
|
| 8085 | 10403 | let depth = checker.loopDepth; |
|
| 10404 | + | set checker.loopBackLoans[depth] = 0; |
|
| 10405 | + | set checker.loopExitLoans[depth] = 0; |
|
| 10406 | + | set checker.loopRegions[depth] = checker.regions; |
|
| 8086 | 10407 | set checker.loopMarks[depth] = env.len; |
|
| 8087 | 10408 | set checker.loopAvailable[depth] = env.available; |
|
| 8088 | 10409 | set checker.loopExitAvailable[depth] = env.available; |
|
| 8089 | 10410 | set checker.loopHasNaturalExit[depth] = false; |
|
| 8090 | 10411 | set checker.loopBreakSeen[depth] = false; |
|
| 8091 | 10412 | set checker.loopDepth += 1; |
|
| 8092 | 10413 | } |
|
| 8093 | 10414 | ||
| 8094 | 10415 | /// Require a repeated body's outer bindings to match its entry state. |
|
| 8095 | - | unsafe fn checkLinearLoopBackEdge( |
|
| 8096 | - | checker: &mut LinearChecker, |
|
| 10416 | + | unsafe fn checkLinearLoopBackEdge 'arena 'checking ( |
|
| 10417 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8097 | 10418 | env: &LinearEnv, |
|
| 8098 | 10419 | node: *ast::Node, |
|
| 8099 | - | ) throws (ResolveError) { |
|
| 10420 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8100 | 10421 | if env.terminated { |
|
| 8101 | 10422 | return; |
|
| 8102 | 10423 | } |
|
| 8103 | 10424 | assert checker.loopDepth > 0, "linear loop back edge outside loop"; |
|
| 8104 | 10425 | let depth = checker.loopDepth - 1; |
|
| 10426 | + | set checker.loopBackLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]); |
|
| 8105 | 10427 | let mark = checker.loopMarks[depth]; |
|
| 8106 | 10428 | let entryAvailable = checker.loopAvailable[depth]; |
|
| 8107 | 10429 | for i in 0..mark { |
|
| 8108 | 10430 | let bit = (1 as u64) << (i as u64); |
|
| 8109 | 10431 | if (env.available & bit) <> (entryAvailable & bit) { |
| 8116 | 10438 | } |
|
| 8117 | 10439 | } |
|
| 8118 | 10440 | } |
|
| 8119 | 10441 | ||
| 8120 | 10442 | /// Record the ownership state of a loop's condition-false exit. |
|
| 8121 | - | fn setLinearLoopNaturalExit(checker: &mut LinearChecker, env: &LinearEnv) { |
|
| 10443 | + | unsafe fn setLinearLoopNaturalExit 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking { |
|
| 8122 | 10444 | assert checker.loopDepth > 0, "linear loop exit outside loop"; |
|
| 8123 | 10445 | let depth = checker.loopDepth - 1; |
|
| 10446 | + | set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]); |
|
| 8124 | 10447 | set checker.loopExitAvailable[depth] = env.available; |
|
| 8125 | 10448 | set checker.loopHasNaturalExit[depth] = true; |
|
| 8126 | 10449 | } |
|
| 8127 | 10450 | ||
| 8128 | 10451 | /// Require a break exit to agree with every other exit from this loop. |
|
| 8129 | - | unsafe fn checkLinearLoopBreak( |
|
| 8130 | - | checker: &mut LinearChecker, |
|
| 10452 | + | unsafe fn checkLinearLoopBreak 'arena 'checking ( |
|
| 10453 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8131 | 10454 | env: &LinearEnv, |
|
| 8132 | 10455 | node: *ast::Node, |
|
| 8133 | - | ) throws (ResolveError) { |
|
| 10456 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8134 | 10457 | assert checker.loopDepth > 0, "linear loop break outside loop"; |
|
| 8135 | 10458 | let depth = checker.loopDepth - 1; |
|
| 10459 | + | set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]); |
|
| 8136 | 10460 | let mark = checker.loopMarks[depth]; |
|
| 8137 | 10461 | if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] { |
|
| 8138 | 10462 | let expected = checker.loopExitAvailable[depth]; |
|
| 8139 | 10463 | for i in 0..mark { |
|
| 8140 | 10464 | let bit = (1 as u64) << (i as u64); |
| 8152 | 10476 | } |
|
| 8153 | 10477 | set checker.loopBreakSeen[depth] = true; |
|
| 8154 | 10478 | } |
|
| 8155 | 10479 | ||
| 8156 | 10480 | /// Pop a repeated-control-flow boundary. |
|
| 8157 | - | fn exitLinearLoop(checker: &mut LinearChecker) { |
|
| 10481 | + | fn exitLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking) where 'arena: 'checking { |
|
| 8158 | 10482 | assert checker.loopDepth > 0, "exitLinearLoop: not in loop"; |
|
| 8159 | 10483 | set checker.loopDepth -= 1; |
|
| 8160 | 10484 | } |
|
| 8161 | 10485 | ||
| 8162 | 10486 | /// Check a conditional and merge its ownership states. |
|
| 8163 | - | unsafe fn checkLinearIf( |
|
| 8164 | - | checker: &mut LinearChecker, |
|
| 10487 | + | unsafe fn checkLinearIf 'arena 'checking ( |
|
| 10488 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8165 | 10489 | env: &mut LinearEnv, |
|
| 8166 | 10490 | node: *ast::Node, |
|
| 8167 | 10491 | conditional: ast::If, |
|
| 8168 | - | ) throws (ResolveError) { |
|
| 10492 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8169 | 10493 | try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume); |
|
| 8170 | 10494 | let base = *env; |
|
| 8171 | 10495 | let mut thenEnv = base; |
|
| 8172 | 10496 | try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard); |
|
| 8173 | 10497 | let mut elseEnv = base; |
| 8176 | 10500 | } |
|
| 8177 | 10501 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
|
| 8178 | 10502 | } |
|
| 8179 | 10503 | ||
| 8180 | 10504 | /// Check an expression conditional and merge its ownership states. |
|
| 8181 | - | unsafe fn checkLinearCondExpr( |
|
| 8182 | - | checker: &mut LinearChecker, |
|
| 10505 | + | unsafe fn checkLinearCondExpr 'arena 'checking ( |
|
| 10506 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8183 | 10507 | env: &mut LinearEnv, |
|
| 8184 | 10508 | node: *ast::Node, |
|
| 8185 | 10509 | conditional: ast::CondExpr, |
|
| 8186 | 10510 | usage: LinearUse, |
|
| 8187 | - | ) throws (ResolveError) { |
|
| 10511 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8188 | 10512 | try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume); |
|
| 8189 | 10513 | let base = *env; |
|
| 8190 | 10514 | let mut thenEnv = base; |
|
| 8191 | 10515 | try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage); |
|
| 8192 | 10516 | let mut elseEnv = base; |
|
| 8193 | 10517 | try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage); |
|
| 8194 | 10518 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
|
| 8195 | 10519 | } |
|
| 8196 | 10520 | ||
| 8197 | 10521 | /// Pointer patterns borrow their subject; value patterns consume it. |
|
| 8198 | - | unsafe fn patternSubjectUse(self: &Resolver, subject: *ast::Node) -> LinearUse { |
|
| 10522 | + | unsafe fn patternSubjectUse 'arena (self: &Resolver 'arena, subject: *ast::Node) -> LinearUse { |
|
| 8199 | 10523 | if let ty = typeFor(self, subject) { |
|
| 8200 | 10524 | if let case Type::Pointer { .. } = ty { |
|
| 8201 | 10525 | return LinearUse::Borrow; |
|
| 8202 | 10526 | } |
|
| 8203 | 10527 | } |
|
| 8204 | 10528 | return LinearUse::Consume; |
|
| 8205 | 10529 | } |
|
| 8206 | 10530 | ||
| 8207 | 10531 | /// Check a match expression, including ownership transferred into patterns. |
|
| 8208 | - | unsafe fn checkLinearMatch( |
|
| 8209 | - | checker: &mut LinearChecker, |
|
| 10532 | + | unsafe fn checkLinearMatch 'arena 'checking ( |
|
| 10533 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8210 | 10534 | env: &mut LinearEnv, |
|
| 8211 | 10535 | node: *ast::Node, |
|
| 8212 | 10536 | matchExpr: ast::Match, |
|
| 8213 | - | ) throws (ResolveError) { |
|
| 10537 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8214 | 10538 | try checkLinearNode(checker, env, matchExpr.subject, patternSubjectUse(checker.resolver, matchExpr.subject)); |
|
| 8215 | 10539 | let base = *env; |
|
| 8216 | 10540 | let mut haveResult = false; |
|
| 8217 | 10541 | let mut result = base; |
|
| 8218 | 10542 | for prongNode in matchExpr.prongs { |
| 8264 | 10588 | set *env = result; |
|
| 8265 | 10589 | } |
|
| 8266 | 10590 | } |
|
| 8267 | 10591 | ||
| 8268 | 10592 | /// Check call-scoped loans and argument ownership transfers. |
|
| 8269 | - | unsafe fn checkLinearCall( |
|
| 8270 | - | checker: &mut LinearChecker, |
|
| 10593 | + | unsafe fn checkLinearCall 'arena 'checking ( |
|
| 10594 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8271 | 10595 | env: &mut LinearEnv, |
|
| 8272 | 10596 | node: *ast::Node, |
|
| 8273 | 10597 | call: ast::Call, |
|
| 8274 | - | ) throws (ResolveError) { |
|
| 10598 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8275 | 10599 | match checker.resolver.nodeData.entries[node.id].extra { |
|
| 8276 | 10600 | case NodeExtra::SliceAppend { .. }, NodeExtra::SliceDelete { .. } => { |
|
| 8277 | 10601 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 8278 | 10602 | else panic "slice mutation without receiver"; |
|
| 8279 | 10603 | try checkPatternLoan(checker, access.parent); |
|
| 8280 | - | try checkLocalLoans(checker, access.parent, true); |
|
| 10604 | + | try checkLocalLoans(checker, env, access.parent, true); |
|
| 8281 | 10605 | } |
|
| 8282 | 10606 | else => {} |
|
| 8283 | 10607 | } |
|
| 8284 | 10608 | try checkLinearNode(checker, env, call.callee, LinearUse::Observe); |
|
| 8285 | 10609 | let mut fnInfo: ?*FnType = nil; |
| 8325 | 10649 | set haveReceiver = true; |
|
| 8326 | 10650 | } |
|
| 8327 | 10651 | else => {} |
|
| 8328 | 10652 | } |
|
| 8329 | 10653 | if haveReceiver { |
|
| 8330 | - | try checkLocalLoans(checker, access.parent, |
|
| 10654 | + | try checkLocalLoans(checker, env, access.parent, |
|
| 8331 | 10655 | receiverMutable or receiverClass == types::PointerClass::Owned); |
|
| 8332 | 10656 | if receiverMutable or receiverClass == types::PointerClass::Owned { |
|
| 8333 | 10657 | try checkPatternLoan(checker, access.parent); |
|
| 8334 | 10658 | } |
|
| 8335 | 10659 | if receiverClass <> types::PointerClass::Unsafe { |
| 8339 | 10663 | set exclusive[placesLen] = |
|
| 8340 | 10664 | receiverClass == types::PointerClass::Owned or receiverMutable; |
|
| 8341 | 10665 | set placesLen += 1; |
|
| 8342 | 10666 | } |
|
| 8343 | 10667 | } |
|
| 8344 | - | if receiverClass == types::PointerClass::Ref { |
|
| 10668 | + | if types::isReference(receiverClass) { |
|
| 8345 | 10669 | try checkLinearNode(checker, env, access.parent, LinearUse::Borrow); |
|
| 8346 | 10670 | } else if receiverClass == types::PointerClass::Owned { |
|
| 8347 | 10671 | try checkLinearNode(checker, env, access.parent, LinearUse::Consume); |
|
| 8348 | 10672 | } |
|
| 8349 | 10673 | } |
| 8366 | 10690 | set places[placesLen] = place; |
|
| 8367 | 10691 | set exclusive[placesLen] = argExclusive; |
|
| 8368 | 10692 | set placesLen += 1; |
|
| 8369 | 10693 | } |
|
| 8370 | 10694 | } |
|
| 8371 | - | try checkLocalLoans(checker, arg, argExclusive); |
|
| 8372 | - | if isRefType(expected) { |
|
| 10695 | + | try checkLocalLoans(checker, env, arg, argExclusive); |
|
| 10696 | + | if isBorrowedReferenceParameter(expected) { |
|
| 8373 | 10697 | try checkLinearNode(checker, env, arg, LinearUse::Borrow); |
|
| 8374 | 10698 | } else { |
|
| 8375 | 10699 | try checkLinearNode(checker, env, arg, LinearUse::Consume); |
|
| 8376 | 10700 | } |
|
| 8377 | 10701 | } |
| 8379 | 10703 | set env.terminated = true; |
|
| 8380 | 10704 | } |
|
| 8381 | 10705 | } |
|
| 8382 | 10706 | ||
| 8383 | 10707 | /// Check a pattern conditional. Linear scrutinees require an exhaustive match. |
|
| 8384 | - | unsafe fn checkLinearIfLet( |
|
| 8385 | - | checker: &mut LinearChecker, |
|
| 10708 | + | unsafe fn checkLinearIfLet 'arena 'checking ( |
|
| 10709 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 8386 | 10710 | env: &mut LinearEnv, |
|
| 8387 | 10711 | node: *ast::Node, |
|
| 8388 | 10712 | conditional: ast::IfLet, |
|
| 8389 | - | ) throws (ResolveError) { |
|
| 10713 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8390 | 10714 | if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee); |
|
| 8391 | 10715 | isLinear(subjectTy) |
|
| 8392 | 10716 | { |
|
| 8393 | 10717 | throw emitError( |
|
| 8394 | 10718 | checker.resolver, |
| 8420 | 10744 | try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard); |
|
| 8421 | 10745 | } |
|
| 8422 | 10746 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
|
| 8423 | 10747 | } |
|
| 8424 | 10748 | ||
| 8425 | - | /// Check one expression or statement under an ownership-use context. |
|
| 8426 | - | unsafe fn checkLinearNode( |
|
| 8427 | - | checker: &mut LinearChecker, |
|
| 8428 | - | env: &mut LinearEnv, |
|
| 10749 | + | /// Check a repeated region-loan flow until its loop-entry mask is stable. |
|
| 10750 | + | /// Each additional pass must add a bit from the bounded regional loan table. |
|
| 10751 | + | unsafe fn checkLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node) |
|
| 10752 | + | throws (ResolveError) where 'arena: 'checking |
|
| 10753 | + | { |
|
| 10754 | + | if let case ast::NodeValue::For(forStmt) = node.value { |
|
| 10755 | + | try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume); |
|
| 10756 | + | } |
|
| 10757 | + | let base = *env; |
|
| 10758 | + | let depth = checker.loopDepth; |
|
| 10759 | + | let mut entryLoans = base.regionalLoans; |
|
| 10760 | + | loop { |
|
| 10761 | + | let mut pass = base; |
|
| 10762 | + | set pass.regionalLoans = entryLoans; |
|
| 10763 | + | try checkLinearLoopPass(checker, &mut pass, node); |
|
| 10764 | + | let next = entryLoans | checker.loopBackLoans[depth]; |
|
| 10765 | + | if next == entryLoans { |
|
| 10766 | + | set *env = pass; |
|
| 10767 | + | return; |
|
| 10768 | + | } |
|
| 10769 | + | set entryLoans = next; |
|
| 10770 | + | } |
|
| 10771 | + | } |
|
| 10772 | + | ||
| 10773 | + | /// Check one pass through a loop with the current loop-entry loan state. |
|
| 10774 | + | unsafe fn checkLinearLoopPass 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node) |
|
| 10775 | + | throws (ResolveError) where 'arena: 'checking |
|
| 10776 | + | { |
|
| 10777 | + | match node.value { |
|
| 10778 | + | case ast::NodeValue::While(whileStmt) => { |
|
| 10779 | + | enterLinearLoop(checker, env); |
|
| 10780 | + | try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume); |
|
| 10781 | + | let conditionExit = *env; |
|
| 10782 | + | setLinearLoopNaturalExit(checker, &conditionExit); |
|
| 10783 | + | let mut bodyEnv = conditionExit; |
|
| 10784 | + | try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard); |
|
| 10785 | + | try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body); |
|
| 10786 | + | exitLinearLoop(checker); |
|
| 10787 | + | set *env = conditionExit; |
|
| 10788 | + | set env.regionalLoans = checker.loopExitLoans[checker.loopDepth]; |
|
| 10789 | + | if let elseBranch = whileStmt.elseBranch { |
|
| 10790 | + | let mut elseEnv = conditionExit; |
|
| 10791 | + | try checkLinearNode( |
|
| 10792 | + | checker, |
|
| 10793 | + | &mut elseEnv, |
|
| 10794 | + | elseBranch, |
|
| 10795 | + | LinearUse::Discard, |
|
| 10796 | + | ); |
|
| 10797 | + | let exits = *env; |
|
| 10798 | + | try joinLinearBranches(checker, env, &exits, &elseEnv, node); |
|
| 10799 | + | } |
|
| 10800 | + | } |
|
| 10801 | + | case ast::NodeValue::WhileLet(whileStmt) => { |
|
| 10802 | + | if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee); |
|
| 10803 | + | isLinear(subjectTy) |
|
| 10804 | + | { |
|
| 10805 | + | throw emitError( |
|
| 10806 | + | checker.resolver, |
|
| 10807 | + | whileStmt.pattern.scrutinee, |
|
| 10808 | + | ErrorKind::LinearPartialMove, |
|
| 10809 | + | ); |
|
| 10810 | + | } |
|
| 10811 | + | let base = *env; |
|
| 10812 | + | enterLinearLoop(checker, env); |
|
| 10813 | + | let mut bodyEnv = base; |
|
| 10814 | + | try checkLinearNode( |
|
| 10815 | + | checker, |
|
| 10816 | + | &mut bodyEnv, |
|
| 10817 | + | whileStmt.pattern.scrutinee, |
|
| 10818 | + | patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee), |
|
| 10819 | + | ); |
|
| 10820 | + | let mut conditionExit = bodyEnv; |
|
| 10821 | + | let start = bodyEnv.len; |
|
| 10822 | + | let loanStart = checker.loanLen; |
|
| 10823 | + | if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) { |
|
| 10824 | + | try addPatternLoan(checker, whileStmt.pattern.scrutinee); |
|
| 10825 | + | } |
|
| 10826 | + | if let guard = whileStmt.pattern.guard { |
|
| 10827 | + | try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume); |
|
| 10828 | + | let mut guardExit = bodyEnv; |
|
| 10829 | + | try finishLinearScope(checker, &mut guardExit, start); |
|
| 10830 | + | let previous = conditionExit; |
|
| 10831 | + | try joinLinearBranches( |
|
| 10832 | + | checker, |
|
| 10833 | + | &mut conditionExit, |
|
| 10834 | + | &previous, |
|
| 10835 | + | &guardExit, |
|
| 10836 | + | guard, |
|
| 10837 | + | ); |
|
| 10838 | + | } |
|
| 10839 | + | setLinearLoopNaturalExit(checker, &conditionExit); |
|
| 10840 | + | try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard); |
|
| 10841 | + | try finishLinearScope(checker, &mut bodyEnv, start); |
|
| 10842 | + | set checker.loanLen = loanStart; |
|
| 10843 | + | try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body); |
|
| 10844 | + | exitLinearLoop(checker); |
|
| 10845 | + | set *env = conditionExit; |
|
| 10846 | + | set env.regionalLoans = checker.loopExitLoans[checker.loopDepth]; |
|
| 10847 | + | if let elseBranch = whileStmt.elseBranch { |
|
| 10848 | + | let mut elseEnv = conditionExit; |
|
| 10849 | + | try checkLinearNode( |
|
| 10850 | + | checker, |
|
| 10851 | + | &mut elseEnv, |
|
| 10852 | + | elseBranch, |
|
| 10853 | + | LinearUse::Discard, |
|
| 10854 | + | ); |
|
| 10855 | + | let exits = *env; |
|
| 10856 | + | try joinLinearBranches(checker, env, &exits, &elseEnv, node); |
|
| 10857 | + | } |
|
| 10858 | + | } |
|
| 10859 | + | case ast::NodeValue::For(forStmt) => { |
|
| 10860 | + | if let iterableTy = typeFor(checker.resolver, forStmt.iterable) { |
|
| 10861 | + | if isLinear(iterableTy) { |
|
| 10862 | + | throw emitError( |
|
| 10863 | + | checker.resolver, |
|
| 10864 | + | forStmt.iterable, |
|
| 10865 | + | ErrorKind::LinearPartialMove, |
|
| 10866 | + | ); |
|
| 10867 | + | } |
|
| 10868 | + | } |
|
| 10869 | + | let base = *env; |
|
| 10870 | + | enterLinearLoop(checker, env); |
|
| 10871 | + | setLinearLoopNaturalExit(checker, &base); |
|
| 10872 | + | let mut bodyEnv = base; |
|
| 10873 | + | let start = bodyEnv.len; |
|
| 10874 | + | try addLinearBinding(checker, &mut bodyEnv, forStmt.binding); |
|
| 10875 | + | if let index = forStmt.index { |
|
| 10876 | + | try addLinearBinding(checker, &mut bodyEnv, index); |
|
| 10877 | + | } |
|
| 10878 | + | try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard); |
|
| 10879 | + | try finishLinearScope(checker, &mut bodyEnv, start); |
|
| 10880 | + | try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body); |
|
| 10881 | + | exitLinearLoop(checker); |
|
| 10882 | + | set *env = base; |
|
| 10883 | + | set env.regionalLoans = checker.loopExitLoans[checker.loopDepth]; |
|
| 10884 | + | if let elseBranch = forStmt.elseBranch { |
|
| 10885 | + | let mut elseEnv = base; |
|
| 10886 | + | try checkLinearNode( |
|
| 10887 | + | checker, |
|
| 10888 | + | &mut elseEnv, |
|
| 10889 | + | elseBranch, |
|
| 10890 | + | LinearUse::Discard, |
|
| 10891 | + | ); |
|
| 10892 | + | let exits = *env; |
|
| 10893 | + | try joinLinearBranches(checker, env, &exits, &elseEnv, node); |
|
| 10894 | + | } |
|
| 10895 | + | } |
|
| 10896 | + | case ast::NodeValue::Loop { body } => { |
|
| 10897 | + | let base = *env; |
|
| 10898 | + | enterLinearLoop(checker, env); |
|
| 10899 | + | let mut bodyEnv = base; |
|
| 10900 | + | try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard); |
|
| 10901 | + | try checkLinearLoopBackEdge(checker, &bodyEnv, body); |
|
| 10902 | + | let depth = checker.loopDepth - 1; |
|
| 10903 | + | let breakSeen = checker.loopBreakSeen[depth]; |
|
| 10904 | + | let exitAvailable = checker.loopExitAvailable[depth]; |
|
| 10905 | + | exitLinearLoop(checker); |
|
| 10906 | + | set *env = base; |
|
| 10907 | + | set env.regionalLoans = checker.loopExitLoans[checker.loopDepth]; |
|
| 10908 | + | if breakSeen { |
|
| 10909 | + | set env.available = exitAvailable; |
|
| 10910 | + | } else { |
|
| 10911 | + | set env.terminated = true; |
|
| 10912 | + | } |
|
| 10913 | + | } |
|
| 10914 | + | else => panic "checkLinearLoopPass: expected loop", |
|
| 10915 | + | } |
|
| 10916 | + | } |
|
| 10917 | + | ||
| 10918 | + | /// Check one expression or statement under an ownership-use context. |
|
| 10919 | + | unsafe fn checkLinearNode 'arena 'checking ( |
|
| 10920 | + | checker: &mut LinearChecker 'arena 'checking, |
|
| 10921 | + | env: &mut LinearEnv, |
|
| 8429 | 10922 | node: *ast::Node, |
|
| 8430 | 10923 | usage: LinearUse, |
|
| 8431 | - | ) throws (ResolveError) { |
|
| 10924 | + | ) throws (ResolveError) where 'arena: 'checking { |
|
| 8432 | 10925 | if env.terminated { |
|
| 8433 | 10926 | return; |
|
| 8434 | 10927 | } |
|
| 8435 | 10928 | if usage <> LinearUse::Locate { |
|
| 8436 | 10929 | match node.value { |
|
| 8437 | 10930 | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_), |
|
| 8438 | 10931 | ast::NodeValue::FieldAccess(_), ast::NodeValue::Subscript { .. }, |
|
| 8439 | 10932 | ast::NodeValue::Deref(_) => { |
|
| 8440 | - | let mut exclusive = usage == LinearUse::Place; |
|
| 10933 | + | let mut exclusive = usage == LinearUse::Place and not isCellDeref(checker.resolver, node); |
|
| 8441 | 10934 | if usage == LinearUse::Consume { |
|
| 8442 | 10935 | if let ty = typeFor(checker.resolver, node) { |
|
| 8443 | 10936 | set exclusive = isExclusiveArgument(ty); |
|
| 8444 | 10937 | } |
|
| 8445 | 10938 | } |
|
| 8446 | - | try checkLocalLoans(checker, node, exclusive); |
|
| 10939 | + | try checkLocalLoans(checker, env, node, exclusive); |
|
| 8447 | 10940 | } |
|
| 8448 | 10941 | else => {} |
|
| 8449 | 10942 | } |
|
| 8450 | 10943 | } |
|
| 8451 | 10944 | match node.value { |
| 8466 | 10959 | throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard); |
|
| 8467 | 10960 | } |
|
| 8468 | 10961 | } |
|
| 8469 | 10962 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
|
| 8470 | 10963 | } |
|
| 10964 | + | case ast::NodeValue::RegionBlock { bindings, body, .. } => { |
|
| 10965 | + | let previousRegions = checker.regions; |
|
| 10966 | + | let case NodeExtra::Regions(scope) = checker.resolver.nodeData.entries[node.id].extra else { |
|
| 10967 | + | if checker.resolver.errors.len > 0 { |
|
| 10968 | + | return; |
|
| 10969 | + | } |
|
| 10970 | + | panic "checkLinearNode: missing region scope"; |
|
| 10971 | + | }; |
|
| 10972 | + | set checker.regions = scope; |
|
| 10973 | + | let start = env.len; |
|
| 10974 | + | let localStart = checker.localLen; |
|
| 10975 | + | for bindingNode in bindings { |
|
| 10976 | + | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
|
| 10977 | + | else panic "checkLinearNode: invalid borrow binding"; |
|
| 10978 | + | try checkLinearNode(checker, env, binding.value, LinearUse::Consume); |
|
| 10979 | + | if env.terminated { |
|
| 10980 | + | break; |
|
| 10981 | + | } |
|
| 10982 | + | try addLinearBinding(checker, env, bindingNode); |
|
| 10983 | + | try addLocalLoan(checker, env, bindingNode, ast::borrowBinding(binding)); |
|
| 10984 | + | } |
|
| 10985 | + | try checkLinearBlock(checker, env, body); |
|
| 10986 | + | try finishLinearScope(checker, env, start); |
|
| 10987 | + | set checker.localLen = localStart; |
|
| 10988 | + | set checker.regions = previousRegions; |
|
| 10989 | + | compactRegionalLoans(checker, env, previousRegions); |
|
| 10990 | + | } |
|
| 8471 | 10991 | case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node), |
|
| 8472 | 10992 | case ast::NodeValue::Let(binding) => { |
|
| 8473 | 10993 | let mut isUndefined = false; |
|
| 8474 | 10994 | if let case ast::NodeValue::Undef = binding.value.value { |
|
| 8475 | 10995 | set isUndefined = true; |
| 8488 | 11008 | try checkLinearNode(checker, env, binding.value, LinearUse::Consume); |
|
| 8489 | 11009 | if env.terminated { |
|
| 8490 | 11010 | return; |
|
| 8491 | 11011 | } |
|
| 8492 | 11012 | try addLinearBinding(checker, env, node); |
|
| 8493 | - | try addLocalLoan(checker, node, binding); |
|
| 11013 | + | try addLocalLoan(checker, env, node, binding); |
|
| 8494 | 11014 | if isUndefined { |
|
| 8495 | 11015 | markLinearBindingUnavailable(checker.resolver, env, node); |
|
| 8496 | 11016 | } |
|
| 8497 | 11017 | } |
|
| 8498 | 11018 | case ast::NodeValue::Assign(assign) => { |
|
| 8499 | - | try checkPatternLoan(checker, assign.left); |
|
| 11019 | + | if not isCellDeref(checker.resolver, assign.left) { |
|
| 11020 | + | try checkPatternLoan(checker, assign.left); |
|
| 11021 | + | } |
|
| 8500 | 11022 | let mut target: ?u32 = nil; |
|
| 8501 | 11023 | let mut targetLinear = false; |
|
| 8502 | 11024 | if let leftTy = typeFor(checker.resolver, assign.left) { |
|
| 8503 | 11025 | if isMoveOnly(leftTy) { |
|
| 8504 | 11026 | set targetLinear = isLinear(leftTy); |
| 8527 | 11049 | ); |
|
| 8528 | 11050 | } |
|
| 8529 | 11051 | set env.available |= (1 as u64) << (index as u64); |
|
| 8530 | 11052 | } |
|
| 8531 | 11053 | } |
|
| 11054 | + | case ast::NodeValue::RegionApply { value, .. } => |
|
| 11055 | + | try checkLinearNode(checker, env, value, usage), |
|
| 8532 | 11056 | case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call), |
|
| 8533 | 11057 | case ast::NodeValue::AddressOf(addr) => { |
|
| 8534 | - | try checkLocalLoans(checker, addr.target, addr.mutable); |
|
| 11058 | + | try checkLocalLoans(checker, env, addr.target, addr.mutable); |
|
| 8535 | 11059 | if addr.mutable { |
|
| 8536 | 11060 | try checkPatternLoan(checker, addr.target); |
|
| 8537 | 11061 | } |
|
| 8538 | 11062 | try checkLinearNode(checker, env, addr.target, LinearUse::Locate); |
|
| 11063 | + | try addRegionalLoan(checker, env, node, addr); |
|
| 8539 | 11064 | } |
|
| 8540 | 11065 | case ast::NodeValue::Deref(target) => { |
|
| 8541 | 11066 | if let resultTy = typeFor(checker.resolver, node) { |
|
| 8542 | 11067 | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 8543 | 11068 | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
| 8603 | 11128 | case ast::NodeValue::UnOp(op) => { |
|
| 8604 | 11129 | try checkLinearNode(checker, env, op.value, LinearUse::Consume); |
|
| 8605 | 11130 | } |
|
| 8606 | 11131 | case ast::NodeValue::As(expr) => { |
|
| 8607 | 11132 | let mut castUse = usage; |
|
| 11133 | + | if let targetTy = typeFor(checker.resolver, node) { |
|
| 11134 | + | if let case Type::Cell { .. } = targetTy { |
|
| 11135 | + | set castUse = LinearUse::Consume; |
|
| 11136 | + | } |
|
| 11137 | + | } |
|
| 8608 | 11138 | if let targetTy = typeFor(checker.resolver, node); isNumericType(targetTy) { |
|
| 8609 | 11139 | set castUse = LinearUse::Observe; |
|
| 8610 | 11140 | } |
|
| 8611 | 11141 | try checkLinearNode(checker, env, expr.value, castUse); |
|
| 8612 | 11142 | } |
| 8715 | 11245 | try finishLinearScope(checker, &mut branch, start); |
|
| 8716 | 11246 | let previous = *env; |
|
| 8717 | 11247 | try joinLinearBranches(checker, env, &previous, &branch, node); |
|
| 8718 | 11248 | } |
|
| 8719 | 11249 | } |
|
| 8720 | - | case ast::NodeValue::While(whileStmt) => { |
|
| 8721 | - | enterLinearLoop(checker, env); |
|
| 8722 | - | try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume); |
|
| 8723 | - | let conditionExit = *env; |
|
| 8724 | - | setLinearLoopNaturalExit(checker, &conditionExit); |
|
| 8725 | - | let mut bodyEnv = conditionExit; |
|
| 8726 | - | try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard); |
|
| 8727 | - | try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body); |
|
| 8728 | - | exitLinearLoop(checker); |
|
| 8729 | - | set *env = conditionExit; |
|
| 8730 | - | if let elseBranch = whileStmt.elseBranch { |
|
| 8731 | - | let mut elseEnv = conditionExit; |
|
| 8732 | - | try checkLinearNode( |
|
| 8733 | - | checker, |
|
| 8734 | - | &mut elseEnv, |
|
| 8735 | - | elseBranch, |
|
| 8736 | - | LinearUse::Discard, |
|
| 8737 | - | ); |
|
| 8738 | - | try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node); |
|
| 8739 | - | } |
|
| 8740 | - | } |
|
| 8741 | - | case ast::NodeValue::WhileLet(whileStmt) => { |
|
| 8742 | - | if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee); |
|
| 8743 | - | isLinear(subjectTy) |
|
| 8744 | - | { |
|
| 8745 | - | throw emitError( |
|
| 8746 | - | checker.resolver, |
|
| 8747 | - | whileStmt.pattern.scrutinee, |
|
| 8748 | - | ErrorKind::LinearPartialMove, |
|
| 8749 | - | ); |
|
| 8750 | - | } |
|
| 8751 | - | let base = *env; |
|
| 8752 | - | enterLinearLoop(checker, env); |
|
| 8753 | - | let mut bodyEnv = base; |
|
| 8754 | - | try checkLinearNode( |
|
| 8755 | - | checker, |
|
| 8756 | - | &mut bodyEnv, |
|
| 8757 | - | whileStmt.pattern.scrutinee, |
|
| 8758 | - | patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee), |
|
| 8759 | - | ); |
|
| 8760 | - | let mut conditionExit = bodyEnv; |
|
| 8761 | - | let start = bodyEnv.len; |
|
| 8762 | - | let loanStart = checker.loanLen; |
|
| 8763 | - | if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) { |
|
| 8764 | - | try addPatternLoan(checker, whileStmt.pattern.scrutinee); |
|
| 8765 | - | } |
|
| 8766 | - | if let guard = whileStmt.pattern.guard { |
|
| 8767 | - | try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume); |
|
| 8768 | - | let mut guardExit = bodyEnv; |
|
| 8769 | - | try finishLinearScope(checker, &mut guardExit, start); |
|
| 8770 | - | let previous = conditionExit; |
|
| 8771 | - | try joinLinearBranches( |
|
| 8772 | - | checker, |
|
| 8773 | - | &mut conditionExit, |
|
| 8774 | - | &previous, |
|
| 8775 | - | &guardExit, |
|
| 8776 | - | guard, |
|
| 8777 | - | ); |
|
| 8778 | - | } |
|
| 8779 | - | setLinearLoopNaturalExit(checker, &conditionExit); |
|
| 8780 | - | try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard); |
|
| 8781 | - | try finishLinearScope(checker, &mut bodyEnv, start); |
|
| 8782 | - | set checker.loanLen = loanStart; |
|
| 8783 | - | try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body); |
|
| 8784 | - | exitLinearLoop(checker); |
|
| 8785 | - | set *env = conditionExit; |
|
| 8786 | - | if let elseBranch = whileStmt.elseBranch { |
|
| 8787 | - | let mut elseEnv = conditionExit; |
|
| 8788 | - | try checkLinearNode( |
|
| 8789 | - | checker, |
|
| 8790 | - | &mut elseEnv, |
|
| 8791 | - | elseBranch, |
|
| 8792 | - | LinearUse::Discard, |
|
| 8793 | - | ); |
|
| 8794 | - | try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node); |
|
| 8795 | - | } |
|
| 8796 | - | } |
|
| 8797 | - | case ast::NodeValue::For(forStmt) => { |
|
| 8798 | - | if let iterableTy = typeFor(checker.resolver, forStmt.iterable) { |
|
| 8799 | - | if isLinear(iterableTy) { |
|
| 8800 | - | throw emitError( |
|
| 8801 | - | checker.resolver, |
|
| 8802 | - | forStmt.iterable, |
|
| 8803 | - | ErrorKind::LinearPartialMove, |
|
| 8804 | - | ); |
|
| 8805 | - | } |
|
| 8806 | - | } |
|
| 8807 | - | try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume); |
|
| 8808 | - | let base = *env; |
|
| 8809 | - | enterLinearLoop(checker, env); |
|
| 8810 | - | setLinearLoopNaturalExit(checker, &base); |
|
| 8811 | - | let mut bodyEnv = base; |
|
| 8812 | - | let start = bodyEnv.len; |
|
| 8813 | - | try addLinearBinding(checker, &mut bodyEnv, forStmt.binding); |
|
| 8814 | - | if let index = forStmt.index { |
|
| 8815 | - | try addLinearBinding(checker, &mut bodyEnv, index); |
|
| 8816 | - | } |
|
| 8817 | - | try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard); |
|
| 8818 | - | try finishLinearScope(checker, &mut bodyEnv, start); |
|
| 8819 | - | try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body); |
|
| 8820 | - | exitLinearLoop(checker); |
|
| 8821 | - | set *env = base; |
|
| 8822 | - | if let elseBranch = forStmt.elseBranch { |
|
| 8823 | - | let mut elseEnv = base; |
|
| 8824 | - | try checkLinearNode( |
|
| 8825 | - | checker, |
|
| 8826 | - | &mut elseEnv, |
|
| 8827 | - | elseBranch, |
|
| 8828 | - | LinearUse::Discard, |
|
| 8829 | - | ); |
|
| 8830 | - | try joinLinearBranches(checker, env, &base, &elseEnv, node); |
|
| 8831 | - | } |
|
| 8832 | - | } |
|
| 8833 | - | case ast::NodeValue::Loop { body } => { |
|
| 8834 | - | let base = *env; |
|
| 8835 | - | enterLinearLoop(checker, env); |
|
| 8836 | - | let mut bodyEnv = base; |
|
| 8837 | - | try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard); |
|
| 8838 | - | try checkLinearLoopBackEdge(checker, &bodyEnv, body); |
|
| 8839 | - | let depth = checker.loopDepth - 1; |
|
| 8840 | - | let breakSeen = checker.loopBreakSeen[depth]; |
|
| 8841 | - | let exitAvailable = checker.loopExitAvailable[depth]; |
|
| 8842 | - | exitLinearLoop(checker); |
|
| 8843 | - | set *env = base; |
|
| 8844 | - | if breakSeen { |
|
| 8845 | - | set env.available = exitAvailable; |
|
| 8846 | - | } else { |
|
| 8847 | - | set env.terminated = true; |
|
| 8848 | - | } |
|
| 8849 | - | } |
|
| 11250 | + | case ast::NodeValue::While(_), ast::NodeValue::WhileLet(_), |
|
| 11251 | + | ast::NodeValue::For(_), ast::NodeValue::Loop { .. } => |
|
| 11252 | + | try checkLinearLoop(checker, env, node), |
|
| 8850 | 11253 | case ast::NodeValue::Break => { |
|
| 8851 | 11254 | assert checker.loopDepth > 0, "linear loop control outside loop"; |
|
| 8852 | 11255 | let start = checker.loopMarks[checker.loopDepth - 1]; |
|
| 8853 | 11256 | try finishLinearScope(checker, env, start); |
|
| 8854 | 11257 | try checkLinearLoopBreak(checker, env, node); |
| 8886 | 11289 | else => {} |
|
| 8887 | 11290 | } |
|
| 8888 | 11291 | } |
|
| 8889 | 11292 | ||
| 8890 | 11293 | /// Check exact-use ownership for one resolved function. |
|
| 8891 | - | unsafe fn checkLinearFn( |
|
| 8892 | - | self: &mut Resolver, |
|
| 11294 | + | unsafe fn checkLinearFn 'arena ( |
|
| 11295 | + | self: &mut Resolver 'arena, |
|
| 8893 | 11296 | receiver: ?*ast::Node, |
|
| 8894 | 11297 | params: *[*ast::Node], |
|
| 8895 | 11298 | body: *ast::Node, |
|
| 8896 | 11299 | ) throws (ResolveError) { |
|
| 8897 | - | let mut checker = LinearChecker { |
|
| 8898 | - | resolver: self as *unsafe mut Resolver, |
|
| 8899 | - | loans: undefined, |
|
| 8900 | - | loanLen: 0, |
|
| 8901 | - | locals: undefined, |
|
| 8902 | - | localLen: 0, |
|
| 8903 | - | loopMarks: undefined, |
|
| 8904 | - | loopAvailable: undefined, |
|
| 8905 | - | loopExitAvailable: undefined, |
|
| 8906 | - | loopHasNaturalExit: undefined, |
|
| 8907 | - | loopBreakSeen: undefined, |
|
| 8908 | - | loopDepth: 0, |
|
| 8909 | - | }; |
|
| 8910 | - | let mut env = LinearEnv { |
|
| 8911 | - | symbols: undefined, |
|
| 8912 | - | available: 0, |
|
| 8913 | - | len: 0, |
|
| 8914 | - | terminated: false, |
|
| 8915 | - | }; |
|
| 8916 | - | if let receiverNode = receiver { |
|
| 8917 | - | try addLinearBinding(&mut checker, &mut env, receiverNode); |
|
| 8918 | - | } |
|
| 8919 | - | for paramNode in params { |
|
| 8920 | - | let case ast::NodeValue::FnParam(_) = paramNode.value |
|
| 8921 | - | else panic "checkLinearFn: expected parameter"; |
|
| 8922 | - | try addLinearBinding(&mut checker, &mut env, paramNode); |
|
| 11300 | + | try resolveNominalApplications(self, body); |
|
| 11301 | + | let regions = self.regionScope; |
|
| 11302 | + | let resolved: 'checking = &mut *self where 'arena: 'checking in { |
|
| 11303 | + | let mut checker = LinearChecker 'arena 'checking { |
|
| 11304 | + | resolver: resolved, |
|
| 11305 | + | regional: undefined, |
|
| 11306 | + | regionalLen: 0, |
|
| 11307 | + | regions, |
|
| 11308 | + | loopBackLoans: undefined, |
|
| 11309 | + | loopExitLoans: undefined, |
|
| 11310 | + | loopRegions: undefined, |
|
| 11311 | + | loans: undefined, |
|
| 11312 | + | loanLen: 0, |
|
| 11313 | + | locals: undefined, |
|
| 11314 | + | localLen: 0, |
|
| 11315 | + | loopMarks: undefined, |
|
| 11316 | + | loopAvailable: undefined, |
|
| 11317 | + | loopExitAvailable: undefined, |
|
| 11318 | + | loopHasNaturalExit: undefined, |
|
| 11319 | + | loopBreakSeen: undefined, |
|
| 11320 | + | loopDepth: 0, |
|
| 11321 | + | }; |
|
| 11322 | + | let mut env = LinearEnv { |
|
| 11323 | + | regionalLoans: 0, |
|
| 11324 | + | symbols: undefined, |
|
| 11325 | + | available: 0, |
|
| 11326 | + | len: 0, |
|
| 11327 | + | terminated: false, |
|
| 11328 | + | }; |
|
| 11329 | + | if let receiverNode = receiver { |
|
| 11330 | + | try addLinearBinding(&mut checker, &mut env, receiverNode); |
|
| 11331 | + | } |
|
| 11332 | + | for paramNode in params { |
|
| 11333 | + | let case ast::NodeValue::FnParam(_) = paramNode.value |
|
| 11334 | + | else panic "checkLinearFn: expected parameter"; |
|
| 11335 | + | try addLinearBinding(&mut checker, &mut env, paramNode); |
|
| 11336 | + | } |
|
| 11337 | + | try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard); |
|
| 11338 | + | try finishLinearScope(&mut checker, &mut env, 0); |
|
| 8923 | 11339 | } |
|
| 8924 | - | try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard); |
|
| 8925 | - | try finishLinearScope(&mut checker, &mut env, 0); |
|
| 8926 | 11340 | } |
|
| 8927 | 11341 | ||
| 8928 | 11342 | /// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules. |
|
| 8929 | - | unsafe fn resolveModuleDefs(self: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 11343 | + | unsafe fn resolveModuleDefs 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) { |
|
| 8930 | 11344 | for stmt in block.statements { |
|
| 11345 | + | try resolveNominalApplications(self, stmt); |
|
| 8931 | 11346 | try visitDef(self, stmt); |
|
| 11347 | + | try resolveNominalApplications(self, stmt); |
|
| 8932 | 11348 | } |
|
| 8933 | 11349 | } |
|
| 8934 | 11350 | ||
| 8935 | 11351 | /// Resolve all packages. |
|
| 8936 | 11352 | /// The graph must outlive later uses of the resolver. |
|
| 8937 | - | export unsafe fn resolve(self: &mut Resolver, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 11353 | + | export unsafe fn resolve 'arena (self: &mut Resolver 'arena, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 8938 | 11354 | set self.moduleGraph = graph as *unsafe module::ModuleGraph; |
|
| 8939 | 11355 | ||
| 8940 | 11356 | // 1. Bind all package roots to enable cross-package references. |
|
| 8941 | 11357 | for i in 0..packages.len { |
|
| 8942 | 11358 | let pkg = packages[i]; |
| 8958 | 11374 | } |
|
| 8959 | 11375 | return diagnostics(self); |
|
| 8960 | 11376 | } |
|
| 8961 | 11377 | ||
| 8962 | 11378 | /// Resolve a package. |
|
| 8963 | - | unsafe fn resolvePackage(self: &mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 11379 | + | unsafe fn resolvePackage 'arena (self: &mut Resolver 'arena, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 8964 | 11380 | let rootId = rootEntry.id; |
|
| 8965 | 11381 | let scope = self.moduleScopes[rootId as u32] |
|
| 8966 | 11382 | else panic "resolvePackage: module scope not found"; |
|
| 8967 | 11383 | ||
| 8968 | 11384 | // Set up the module scope for this package. |
lib/std/lang/resolver/printer.rad
+91 -6
| 30 | 30 | io::print(name); |
|
| 31 | 31 | io::print("'"); |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | /// Print a pointer-like type prefix. |
|
| 35 | - | fn printPtrPrefix(class: types::PointerClass, mutable: bool) { |
|
| 35 | + | unsafe fn printPtrPrefix(class: types::PointerClass, mutable: bool) { |
|
| 36 | 36 | match class { |
|
| 37 | 37 | case types::PointerClass::Owned => io::print("*"), |
|
| 38 | 38 | case types::PointerClass::Ref => io::print("&"), |
|
| 39 | + | case types::PointerClass::Region(region) => { |
|
| 40 | + | io::print("&"); |
|
| 41 | + | io::print(region.name); |
|
| 42 | + | io::print(" "); |
|
| 43 | + | } |
|
| 39 | 44 | case types::PointerClass::Unsafe => io::print("*unsafe "), |
|
| 40 | 45 | } |
|
| 41 | 46 | if mutable { |
|
| 42 | 47 | io::print("mut "); |
|
| 43 | 48 | } |
| 102 | 107 | io::print("i32"); |
|
| 103 | 108 | } |
|
| 104 | 109 | case super::Type::I64 => { |
|
| 105 | 110 | io::print("i64"); |
|
| 106 | 111 | } |
|
| 112 | + | case super::Type::Cell { class, payload } => { |
|
| 113 | + | printPtrPrefix(class, false); |
|
| 114 | + | io::print("cell "); |
|
| 115 | + | printTypeBody(*payload, brief); |
|
| 116 | + | } |
|
| 117 | + | case super::Type::Session(region) => { |
|
| 118 | + | io::print("Session "); |
|
| 119 | + | io::print(region.name); |
|
| 120 | + | } |
|
| 107 | 121 | case super::Type::Pointer { class, target, mutable } => { |
|
| 108 | 122 | printPtrPrefix(class, mutable); |
|
| 109 | 123 | printTypeBody(*target, brief); |
|
| 110 | 124 | } |
|
| 111 | 125 | case super::Type::Slice { class, item, mutable } => { |
| 174 | 188 | } |
|
| 175 | 189 | ||
| 176 | 190 | /// Print detailed information about a nominal type (record or union). |
|
| 177 | 191 | unsafe fn printNominalType(info: *unsafe super::NominalType) { |
|
| 178 | 192 | match *info { |
|
| 179 | - | case super::NominalType::Placeholder(_) => { |
|
| 193 | + | case super::NominalType::Placeholder(_), super::NominalType::Resolving(_) => { |
|
| 180 | 194 | io::print("<placeholder>"); |
|
| 181 | 195 | } |
|
| 196 | + | case super::NominalType::Application(applied) => { |
|
| 197 | + | printNominalTypeName(applied.base); |
|
| 198 | + | printNominalArguments(applied); |
|
| 199 | + | } |
|
| 182 | 200 | case super::NominalType::Record(recordType) => { |
|
| 183 | 201 | io::print("record {"); |
|
| 184 | 202 | if recordType.fields.len > 0 { |
|
| 185 | 203 | io::print(" "); |
|
| 186 | 204 | for i in 0..recordType.fields.len { |
| 224 | 242 | io::print(" "); |
|
| 225 | 243 | } |
|
| 226 | 244 | io::print("}"); |
|
| 227 | 245 | } |
|
| 228 | 246 | } |
|
| 247 | + | if let applied = super::nominalApplication(info) { |
|
| 248 | + | if let case super::NominalType::Application(_) = *info { |
|
| 249 | + | return; |
|
| 250 | + | } |
|
| 251 | + | printNominalArguments(applied); |
|
| 252 | + | } |
|
| 229 | 253 | } |
|
| 230 | 254 | ||
| 231 | 255 | /// Print just the type kind for a nominal type, without detailed info. |
|
| 232 | 256 | unsafe fn printNominalTypeName(info: *unsafe super::NominalType) { |
|
| 233 | 257 | match *info { |
|
| 234 | - | case super::NominalType::Placeholder(_) => { |
|
| 258 | + | case super::NominalType::Placeholder(_), super::NominalType::Resolving(_) => { |
|
| 235 | 259 | io::print("<placeholder>"); |
|
| 236 | 260 | } |
|
| 261 | + | case super::NominalType::Application(applied) => { |
|
| 262 | + | printNominalTypeName(applied.base); |
|
| 263 | + | printNominalArguments(applied); |
|
| 264 | + | } |
|
| 237 | 265 | case super::NominalType::Record(_) => { |
|
| 238 | 266 | io::print("<record>"); |
|
| 239 | 267 | } |
|
| 240 | 268 | case super::NominalType::Union(_) => { |
|
| 241 | 269 | io::print("<union>"); |
|
| 242 | 270 | } |
|
| 243 | 271 | } |
|
| 272 | + | if let applied = super::nominalApplication(info) { |
|
| 273 | + | if let case super::NominalType::Application(_) = *info { |
|
| 274 | + | return; |
|
| 275 | + | } |
|
| 276 | + | printNominalArguments(applied); |
|
| 277 | + | } |
|
| 244 | 278 | } |
|
| 245 | 279 | ||
| 246 | 280 | /// Print a single diagnostic entry. |
|
| 247 | - | unsafe fn printError(err: &super::Error, res: &super::Resolver) { |
|
| 281 | + | unsafe fn printError 'arena (err: &super::Error, res: &super::Resolver 'arena) { |
|
| 248 | 282 | if let node = err.node { |
|
| 249 | 283 | // Find the module containing this error. |
|
| 250 | 284 | if let moduleEntry = module::get(res.moduleGraph, err.moduleId) { |
|
| 251 | 285 | // Get the source text if available. |
|
| 252 | - | if let source = moduleEntry.source { |
|
| 286 | + | if let source = module::sourceFor(moduleEntry) { |
|
| 253 | 287 | // Convert offset to location. |
|
| 254 | 288 | if let loc = scanner::getLocation(scanner::SourceLoc::File(moduleEntry.filePath), source, node.span.offset) { |
|
| 255 | 289 | // Print: filename:line:col: error: message |
|
| 256 | 290 | if let case scanner::SourceLoc::File(path) = loc.source { |
|
| 257 | 291 | io::print(path); |
| 445 | 479 | io::print("catch with inferred binding requires single error type; use typed catches for multiple error types"); |
|
| 446 | 480 | } |
|
| 447 | 481 | case super::ErrorKind::TryCatchDuplicateType => { |
|
| 448 | 482 | io::print("duplicate error type in catch clauses"); |
|
| 449 | 483 | } |
|
| 484 | + | case super::ErrorKind::AmbiguousRegionalError => { |
|
| 485 | + | io::print("error types must remain distinct after region erasure"); |
|
| 486 | + | } |
|
| 450 | 487 | case super::ErrorKind::TryCatchNonExhaustive => { |
|
| 451 | 488 | io::print("catch clauses do not cover all error types"); |
|
| 452 | 489 | } |
|
| 453 | 490 | case super::ErrorKind::MissingTry => { |
|
| 454 | 491 | io::print("called fallible function without using try"); |
| 530 | 567 | io::print("`Copy` composite contains a non-copy value"); |
|
| 531 | 568 | } |
|
| 532 | 569 | case super::ErrorKind::ConflictingOwnershipMarkers => { |
|
| 533 | 570 | io::print("a composite cannot be both `Copy` and `Once`"); |
|
| 534 | 571 | } |
|
| 572 | + | case super::ErrorKind::UnknownRegion(name) => { |
|
| 573 | + | io::print("unknown region "); |
|
| 574 | + | io::print(name); |
|
| 575 | + | } |
|
| 576 | + | case super::ErrorKind::RegionArgumentCount(count) => { |
|
| 577 | + | printMismatch("region argument count", "expected", count); |
|
| 578 | + | } |
|
| 579 | + | case super::ErrorKind::RegionInference(name) => { |
|
| 580 | + | io::print("cannot infer a consistent argument for region "); |
|
| 581 | + | io::print(name); |
|
| 582 | + | } |
|
| 583 | + | case super::ErrorKind::RegionParent(name) => { |
|
| 584 | + | io::print("region argument does not satisfy the parent relation for "); |
|
| 585 | + | io::print(name); |
|
| 586 | + | } |
|
| 587 | + | case super::ErrorKind::RecursiveType => io::print("recursive value type has no finite layout"), |
|
| 588 | + | case super::ErrorKind::RegionCycle(name) => { |
|
| 589 | + | io::print("cyclic region parent relation for "); |
|
| 590 | + | io::print(name); |
|
| 591 | + | } |
|
| 535 | 592 | case super::ErrorKind::InvalidRefPosition => { |
|
| 536 | 593 | io::print("reference type is only allowed as a parameter or local binding"); |
|
| 537 | 594 | } |
|
| 538 | 595 | case super::ErrorKind::RefBinding => { |
|
| 539 | 596 | io::print("reference local requires an immutable binding to existing storage"); |
| 545 | 602 | io::print("unsafe operation requires an unsafe function or block"); |
|
| 546 | 603 | } |
|
| 547 | 604 | case super::ErrorKind::UnsafeCall => { |
|
| 548 | 605 | io::print("calling an unsafe function requires an unsafe function or block"); |
|
| 549 | 606 | } |
|
| 607 | + | case super::ErrorKind::InvalidAllocationValue => { |
|
| 608 | + | io::print("session allocation requires a value safe for bulk reclamation and plain Copy elements for slices"); |
|
| 609 | + | } |
|
| 610 | + | case super::ErrorKind::InvalidAllocationLayout => { |
|
| 611 | + | io::print("invalid or overflowing allocation layout"); |
|
| 612 | + | } |
|
| 613 | + | case super::ErrorKind::InvalidAllocationRuntime => { |
|
| 614 | + | io::print("invalid standard allocation trait signature"); |
|
| 615 | + | } |
|
| 616 | + | case super::ErrorKind::InvalidCellPayload => { |
|
| 617 | + | io::print("cell payload must be a storable plain Copy value"); |
|
| 618 | + | } |
|
| 619 | + | case super::ErrorKind::InvalidSessionSource => { |
|
| 620 | + | io::print("session requires one exclusive borrow of an std::lang::alloc::Alloc implementer"); |
|
| 621 | + | } |
|
| 622 | + | case super::ErrorKind::RegionalLoanOverflow => io::print("too many simultaneous regional projections"), |
|
| 623 | + | case super::ErrorKind::RegionEscape(name) => { |
|
| 624 | + | io::print("value outlives region "); |
|
| 625 | + | io::print(name); |
|
| 626 | + | } |
|
| 550 | 627 | case super::ErrorKind::Internal => { |
|
| 551 | 628 | io::print("internal compiler error"); |
|
| 552 | 629 | } |
|
| 553 | 630 | case super::ErrorKind::RecordFieldOutOfOrder { .. } => { |
|
| 554 | 631 | io::print("record field out of order"); |
| 568 | 645 | } |
|
| 569 | 646 | io::print("\n"); |
|
| 570 | 647 | } |
|
| 571 | 648 | ||
| 572 | 649 | /// Entry point for printing resolver diagnostics in vim quickfix format. |
|
| 573 | - | export unsafe fn printDiagnostics(diag: &super::Diagnostics, res: &super::Resolver) { |
|
| 650 | + | export unsafe fn printDiagnostics 'arena (diag: &super::Diagnostics, res: &super::Resolver 'arena) { |
|
| 574 | 651 | for i in 0..diag.errors.len { |
|
| 575 | 652 | printError(&diag.errors[i], res); |
|
| 576 | 653 | } |
|
| 577 | 654 | } |
|
| 655 | + | ||
| 656 | + | /// Print an applied nominal type's explicit region arguments. |
|
| 657 | + | unsafe fn printNominalArguments(applied: *unsafe super::NominalApplication) { |
|
| 658 | + | for region in applied.arguments { |
|
| 659 | + | io::print(" "); |
|
| 660 | + | io::print(region.name); |
|
| 661 | + | } |
|
| 662 | + | } |
lib/std/lang/resolver/tests.rad
+4954 -3120
| 1 | 1 | //! Resolver tests. |
|
| 2 | 2 | ||
| 3 | + | /// Region identity, borrowing, and nominal application tests. |
|
| 4 | + | @test mod regions; |
|
| 5 | + | ||
| 3 | 6 | use std::mem; |
|
| 4 | 7 | use std::testing; |
|
| 5 | 8 | use std::lang::alloc; |
|
| 6 | 9 | use std::lang::ast; |
|
| 7 | 10 | use std::lang::types; |
| 27 | 30 | ||
| 28 | 31 | /// Package scope used by resolver tests. |
|
| 29 | 32 | unsafe static PKG_SCOPE: super::Scope = undefined; |
|
| 30 | 33 | ||
| 31 | 34 | /// Module entries used by resolver tests. |
|
| 32 | - | unsafe static MODULE_ENTRIES: [module::ModuleEntry; 8] = undefined; |
|
| 35 | + | unsafe static MODULE_ENTRIES: [?*module::ModuleEntry; 8] = [nil; 8]; |
|
| 33 | 36 | ||
| 34 | 37 | /// Module graph used by resolver tests. |
|
| 35 | 38 | unsafe static MODULE_GRAPH: module::ModuleGraph = undefined; |
|
| 36 | 39 | ||
| 37 | 40 | /// Module AST arena storage used by resolver tests. |
|
| 38 | - | static MODULE_ARENA_STORAGE: [u8; 4096] = [0; 4096]; |
|
| 41 | + | static MODULE_ARENA_STORAGE: [u8; 16384] = [0; 16384]; |
|
| 39 | 42 | ||
| 40 | 43 | /// Module AST arena used by resolver tests. |
|
| 41 | 44 | unsafe static MODULE_ARENA: ast::NodeArena = undefined; |
|
| 42 | 45 | ||
| 43 | 46 | /// Interned string pool used by resolver tests. |
| 51 | 54 | "Second", "Opt", "x", |
|
| 52 | 55 | "value", "idx" |
|
| 53 | 56 | ]; |
|
| 54 | 57 | ||
| 55 | 58 | /// Resolver result with AST, used by test helpers. |
|
| 56 | - | record TestResult: Copy { |
|
| 59 | + | export record TestResult: Copy { |
|
| 60 | + | /// Diagnostics from semantic analysis. |
|
| 57 | 61 | diagnostics: super::Diagnostics, |
|
| 62 | + | /// Root of the parsed test program. |
|
| 58 | 63 | root: *ast::Node, |
|
| 59 | 64 | } |
|
| 60 | 65 | ||
| 61 | 66 | /// Create isolated storage for tests to avoid conflicts with global resolver storage. |
|
| 62 | 67 | unsafe fn testStorage() -> super::ResolverStorage { |
|
| 63 | 68 | return super::ResolverStorage { |
|
| 64 | - | arena: alloc::new(&mut ARENA_STORAGE[..]), |
|
| 65 | 69 | nodeData: &mut NODE_DATA_STORAGE[..], |
|
| 66 | 70 | pkgScope: &mut PKG_SCOPE, |
|
| 67 | 71 | errors: &mut ERROR_STORAGE[..], |
|
| 68 | 72 | }; |
|
| 69 | 73 | } |
|
| 70 | 74 | ||
| 75 | + | /// Create an arena owner for one resolver test scope. |
|
| 76 | + | export unsafe fn testArena() -> alloc::Arena { |
|
| 77 | + | return alloc::new(&mut ARENA_STORAGE[..]); |
|
| 78 | + | } |
|
| 79 | + | ||
| 71 | 80 | /// Construct a resolver backed by test storage and a synthetic module graph. |
|
| 72 | - | unsafe fn testResolver() -> super::Resolver { |
|
| 81 | + | export unsafe fn testResolver 'arena (arena: &'arena mut alloc::Arena) -> super::Resolver 'arena { |
|
| 73 | 82 | // TODO: This should be initialized only once. |
|
| 74 | 83 | for i in 0..LITERALS.len { |
|
| 75 | 84 | strings::intern(&mut STRING_POOL, LITERALS[i]); |
|
| 76 | 85 | } |
|
| 77 | 86 | // TODO: Use local static for this. |
|
| 78 | 87 | // Reset the module graph for each test. |
|
| 79 | 88 | set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]); |
|
| 80 | 89 | set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut MODULE_ARENA); |
|
| 81 | 90 | let config = super::Config { buildTest: true }; |
|
| 82 | - | let res = super::resolver(testStorage(), config); |
|
| 91 | + | let res = super::resolver(arena, testStorage(), config); |
|
| 83 | 92 | ||
| 84 | 93 | return res; |
|
| 85 | 94 | } |
|
| 86 | 95 | ||
| 87 | 96 | /// Resolve a block of statements by wrapping them in a synthetic function. |
|
| 88 | - | unsafe fn resolveStatements( |
|
| 89 | - | self: &mut super::Resolver, block: ast::Block, arena: &mut ast::NodeArena |
|
| 97 | + | unsafe fn resolveStatements 'arena ( |
|
| 98 | + | self: &mut super::Resolver 'arena, block: ast::Block, arena: &mut ast::NodeArena |
|
| 90 | 99 | ) -> TestResult throws (super::ResolveError) { |
|
| 91 | 100 | let module = ast::synthFnModule(arena, super::ANALYZE_BLOCK_FN_NAME, block.statements); |
|
| 92 | 101 | let diagnostics = try super::resolveModuleRoot(self, module.modBody) catch { |
|
| 93 | 102 | return TestResult { diagnostics: super::diagnostics(self), root: module.modBody }; |
|
| 94 | 103 | }; |
|
| 95 | 104 | return TestResult { diagnostics, root: module.fnBody }; |
|
| 96 | 105 | } |
|
| 97 | 106 | ||
| 98 | 107 | /// Parse and analyze an expression string for testing. |
|
| 99 | - | unsafe fn resolveExprStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 108 | + | unsafe fn resolveExprStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 100 | 109 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 101 | 110 | let mut p = parser::mkParser(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL); |
|
| 102 | 111 | parser::advance(&mut p); |
|
| 103 | 112 | ||
| 104 | 113 | let expr = try parser::parseExpr(&mut p) catch { |
| 110 | 119 | return TestResult { diagnostics, root: expr }; |
|
| 111 | 120 | } |
|
| 112 | 121 | ||
| 113 | 122 | /// Parse and analyze a module string for testing. |
|
| 114 | 123 | /// Use this for code with `fn`, `record`, `union`, etc. at the top level. |
|
| 115 | - | unsafe fn resolveProgramStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 124 | + | export unsafe fn resolveProgramStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 116 | 125 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 117 | 126 | let stmt: *ast::Node = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch { |
|
| 118 | 127 | panic "resolveProgramStr: parsing failed"; |
|
| 119 | 128 | }; |
|
| 120 | 129 | let diagnostics = try super::resolveModuleRoot(self, stmt) catch { |
|
| 121 | 130 | throw testing::TestError::Failed; |
|
| 122 | 131 | }; |
|
| 123 | 132 | return TestResult { diagnostics, root: stmt }; |
|
| 124 | 133 | } |
|
| 125 | 134 | ||
| 135 | + | /// Resolve session diagnostics against the standard allocator declaration contract. |
|
| 136 | + | export unsafe fn resolveSessionProgramStr 'arena (self: &mut super::Resolver 'arena, program: *[u8]) -> TestResult |
|
| 137 | + | throws (testing::TestError) |
|
| 138 | + | { |
|
| 139 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 140 | + | let root = try registerModule(&mut MODULE_GRAPH, nil, "std", "export mod lang; mod app;", &mut arena); |
|
| 141 | + | let lang = try registerModule(&mut MODULE_GRAPH, root, "lang", "export mod alloc;", &mut arena); |
|
| 142 | + | let allocator = try registerModule(&mut MODULE_GRAPH, lang, "alloc", |
|
| 143 | + | "export record Arena { data: *mut [u8], offset: u32 } export fn reset(arena: &mut Arena) { set arena.offset = 0; } export union AllocError: Copy { OutOfMemory } export trait Alloc { unsafe fn (&mut Alloc) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError); unsafe fn (&mut Alloc) reserveSlice(itemSize: u32, itemAlignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError); } instance Alloc for Arena { unsafe fn (arena: &mut Arena) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { panic; } unsafe fn (arena: &mut Arena) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { panic; } }", |
|
| 144 | + | &mut arena); |
|
| 145 | + | let app = try registerModule(&mut MODULE_GRAPH, root, "app", program, &mut arena); |
|
| 146 | + | return try resolveModuleTree(self, root); |
|
| 147 | + | } |
|
| 148 | + | ||
| 126 | 149 | /// Parse and analyze a block of statements (eg. inside a function body) for testing. |
|
| 127 | 150 | /// Use this for code with `let` bindings and expressions, not module-level declarations. |
|
| 128 | - | unsafe fn resolveBlockStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 151 | + | unsafe fn resolveBlockStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 129 | 152 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 130 | 153 | let parsed = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch { |
|
| 131 | 154 | panic "resolveBlockStr: parsing failed"; |
|
| 132 | 155 | }; |
|
| 133 | 156 | let case ast::NodeValue::Block(block) = parsed.value |
| 141 | 164 | root: analysis.root, |
|
| 142 | 165 | }; |
|
| 143 | 166 | } |
|
| 144 | 167 | ||
| 145 | 168 | /// Resolve a module with the full resolution process. |
|
| 146 | - | unsafe fn resolveModuleTree( |
|
| 147 | - | res: &mut super::Resolver, |
|
| 169 | + | unsafe fn resolveModuleTree 'arena ( |
|
| 170 | + | res: &mut super::Resolver 'arena, |
|
| 148 | 171 | rootId: u16 |
|
| 149 | 172 | ) -> TestResult throws (testing::TestError) { |
|
| 150 | 173 | let root = module::get(&MODULE_GRAPH, rootId) |
|
| 151 | 174 | else throw testing::TestError::Failed; |
|
| 152 | - | let rootAst = root.ast |
|
| 175 | + | let rootAst = module::astFor(root) |
|
| 153 | 176 | else throw testing::TestError::Failed; |
|
| 154 | 177 | let packages = [super::Pkg { |
|
| 155 | 178 | rootEntry: root, |
|
| 156 | 179 | rootAst, |
|
| 157 | 180 | }]; |
| 189 | 212 | }; |
|
| 190 | 213 | return modId; |
|
| 191 | 214 | } |
|
| 192 | 215 | ||
| 193 | 216 | /// Ensure an expression statement produces the expected type and return the expression node. |
|
| 194 | - | fn expectExprStmtType(self: &super::Resolver, node: *ast::Node, expected: super::Type) -> *ast::Node |
|
| 217 | + | fn expectExprStmtType 'arena (self: &super::Resolver 'arena, node: *ast::Node, expected: super::Type) -> *ast::Node |
|
| 195 | 218 | throws (testing::TestError) |
|
| 196 | 219 | { |
|
| 197 | 220 | let case ast::NodeValue::ExprStmt(expr) = node.value |
|
| 198 | 221 | else throw testing::TestError::Failed; |
|
| 199 | 222 | try expectType(self, expr, expected); |
|
| 200 | 223 | ||
| 201 | 224 | return expr; |
|
| 202 | 225 | } |
|
| 203 | 226 | ||
| 204 | 227 | /// Assert that the test result contains no diagnostic errors. |
|
| 205 | - | fn expectNoErrors(r: &TestResult) throws (testing::TestError) { |
|
| 228 | + | export fn expectNoErrors(r: &TestResult) throws (testing::TestError) { |
|
| 206 | 229 | try testing::expect(super::success(&r.diagnostics)); |
|
| 207 | 230 | } |
|
| 208 | 231 | ||
| 209 | 232 | /// Extract the first error from a test result, failing if none exists. |
|
| 210 | - | fn expectError(result: &TestResult) -> super::Error throws (testing::TestError) { |
|
| 233 | + | export fn expectError(result: &TestResult) -> super::Error throws (testing::TestError) { |
|
| 211 | 234 | let err = super::errorAt(&result.diagnostics.errors[..], 0) |
|
| 212 | 235 | else throw testing::TestError::Failed; |
|
| 213 | 236 | return err; |
|
| 214 | 237 | } |
|
| 215 | 238 |
| 313 | 336 | } |
|
| 314 | 337 | return *actual == expected; |
|
| 315 | 338 | } |
|
| 316 | 339 | ||
| 317 | 340 | /// Extract the first error and ensure it has the expected kind. |
|
| 318 | - | fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> super::Error |
|
| 341 | + | export fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> super::Error |
|
| 319 | 342 | throws (testing::TestError) |
|
| 320 | 343 | { |
|
| 321 | 344 | let err = try expectError(result); |
|
| 322 | 345 | try testing::expect(errorKindMatches(&err.kind, kind)); |
|
| 323 | 346 | return err; |
|
| 324 | 347 | } |
|
| 325 | 348 | ||
| 326 | 349 | /// Ensure an expression resolves to the expected type annotation. |
|
| 327 | - | fn expectType(self: &super::Resolver, expr: *ast::Node, expected: super::Type) |
|
| 350 | + | fn expectType 'arena (self: &super::Resolver 'arena, expr: *ast::Node, expected: super::Type) |
|
| 328 | 351 | throws (testing::TestError) |
|
| 329 | 352 | { |
|
| 330 | 353 | let actual = super::typeFor(self, expr) |
|
| 331 | 354 | else throw testing::TestError::Failed; |
|
| 332 | 355 |
| 345 | 368 | try testing::expect(mismatch.actual == actual); |
|
| 346 | 369 | } |
|
| 347 | 370 | ||
| 348 | 371 | /// Resolve a program and require successful analysis. |
|
| 349 | 372 | unsafe fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) { |
|
| 350 | - | let mut a = testResolver(); |
|
| 351 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 352 | - | try expectNoErrors(&result); |
|
| 373 | + | let mut testArena1 = testArena(); |
|
| 374 | + | let testStorage1: 'test1 = &mut testArena1 in { |
|
| 375 | + | let mut a = testResolver(testStorage1); |
|
| 376 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 377 | + | try expectNoErrors(&result); |
|
| 378 | + | } |
|
| 353 | 379 | } |
|
| 354 | 380 | ||
| 355 | 381 | /// Require an inferred integer type mismatch. |
|
| 356 | 382 | unsafe fn expectIntMismatch(program: *[u8], expected: super::Type) |
|
| 357 | 383 | throws (testing::TestError) |
|
| 358 | 384 | { |
|
| 359 | - | let mut a = testResolver(); |
|
| 360 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 361 | - | let err = try expectError(&result); |
|
| 362 | - | try expectTypeMismatch(err, expected, super::Type::Int); |
|
| 385 | + | let mut testArena2 = testArena(); |
|
| 386 | + | let testStorage2: 'test2 = &mut testArena2 in { |
|
| 387 | + | let mut a = testResolver(testStorage2); |
|
| 388 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 389 | + | let err = try expectError(&result); |
|
| 390 | + | try expectTypeMismatch(err, expected, super::Type::Int); |
|
| 391 | + | } |
|
| 363 | 392 | } |
|
| 364 | 393 | ||
| 365 | 394 | /// Retrieve the nth statement from a block node. |
|
| 366 | - | fn getBlockStmt(block: *ast::Node, index: u32) -> *ast::Node |
|
| 395 | + | export fn getBlockStmt(block: *ast::Node, index: u32) -> *ast::Node |
|
| 367 | 396 | throws (testing::TestError) |
|
| 368 | 397 | { |
|
| 369 | 398 | let case ast::NodeValue::Block(body) = block.value |
|
| 370 | 399 | else throw testing::TestError::Failed; |
|
| 371 | 400 |
| 374 | 403 | } |
|
| 375 | 404 | return body.statements[index]; |
|
| 376 | 405 | } |
|
| 377 | 406 | ||
| 378 | 407 | /// Retrieve a function body block by function name from the program scope. |
|
| 379 | - | unsafe fn getFnBody(a: &super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block |
|
| 408 | + | unsafe fn getFnBody 'arena (a: &super::Resolver 'arena, root: *ast::Node, name: *[u8]) -> ast::Block |
|
| 380 | 409 | throws (testing::TestError) |
|
| 381 | 410 | { |
|
| 382 | 411 | let scope = super::scopeFor(a, root) |
|
| 383 | 412 | else throw testing::TestError::Failed; |
|
| 384 | 413 | let sym = super::findSymbolInScope(scope, name) |
| 417 | 446 | } |
|
| 418 | 447 | panic "getUnionVariantPayload: variant not found"; |
|
| 419 | 448 | } |
|
| 420 | 449 | ||
| 421 | 450 | /// Get a nominal type by name, in the scope of the given block node. |
|
| 422 | - | unsafe fn getTypeInScopeOf(a: &super::Resolver, blk: *ast::Node, name: *[u8]) -> *unsafe super::NominalType |
|
| 451 | + | unsafe fn getTypeInScopeOf 'arena (a: &super::Resolver 'arena, blk: *ast::Node, name: *[u8]) -> *unsafe super::NominalType |
|
| 423 | 452 | throws (testing::TestError) |
|
| 424 | 453 | { |
|
| 425 | 454 | let scope = super::scopeFor(a, blk) |
|
| 426 | 455 | else throw testing::TestError::Failed; |
|
| 427 | 456 | let sym = super::findSymbolInScope(scope, name) |
| 430 | 459 | else throw testing::TestError::Failed; |
|
| 431 | 460 | return ty; |
|
| 432 | 461 | } |
|
| 433 | 462 | ||
| 434 | 463 | /// Return the resolved type of a syntax node. |
|
| 435 | - | fn typeOf(a: &super::Resolver, node: *ast::Node) -> super::Type |
|
| 464 | + | fn typeOf 'arena (a: &super::Resolver 'arena, node: *ast::Node) -> super::Type |
|
| 436 | 465 | throws (testing::TestError) |
|
| 437 | 466 | { |
|
| 438 | 467 | let ty = super::typeFor(a, node) |
|
| 439 | 468 | else throw testing::TestError::Failed; |
|
| 440 | 469 | return ty; |
| 472 | 501 | ||
| 473 | 502 | return *target; |
|
| 474 | 503 | } |
|
| 475 | 504 | ||
| 476 | 505 | /// Verify that a node has a constant integer value with the expected magnitude. |
|
| 477 | - | fn expectConstInt(a: &super::Resolver, node: *ast::Node, expected: u32) |
|
| 506 | + | fn expectConstInt 'arena (a: &super::Resolver 'arena, node: *ast::Node, expected: u32) |
|
| 478 | 507 | throws (testing::TestError) |
|
| 479 | 508 | { |
|
| 480 | 509 | let constVal = super::constValueEntry(a, node) |
|
| 481 | 510 | else throw testing::TestError::Failed; |
|
| 482 | 511 |
| 488 | 517 | ||
| 489 | 518 | /// Resolve an expression that should evaluate to a constant, and verify it equals the expected value. |
|
| 490 | 519 | unsafe fn resolveAndExpectConstExpr(expr: *[u8], expected: u32) |
|
| 491 | 520 | throws (testing::TestError) |
|
| 492 | 521 | { |
|
| 493 | - | let mut a = testResolver(); |
|
| 494 | - | let result = try resolveExprStr(&mut a, expr); |
|
| 495 | - | try expectNoErrors(&result); |
|
| 496 | - | try expectType(&a, result.root, super::Type::U32); |
|
| 497 | - | try expectConstInt(&a, result.root, expected); |
|
| 522 | + | let mut testArena3 = testArena(); |
|
| 523 | + | let testStorage3: 'test3 = &mut testArena3 in { |
|
| 524 | + | let mut a = testResolver(testStorage3); |
|
| 525 | + | let result = try resolveExprStr(&mut a, expr); |
|
| 526 | + | try expectNoErrors(&result); |
|
| 527 | + | try expectType(&a, result.root, super::Type::U32); |
|
| 528 | + | try expectConstInt(&a, result.root, expected); |
|
| 529 | + | } |
|
| 498 | 530 | } |
|
| 499 | 531 | ||
| 500 | 532 | /// Resolve a statement that should evaluate to a constant, and verify it equals the expected value. |
|
| 501 | 533 | unsafe fn resolveAndExpectConstStmt(expr: *[u8], expected: u32) |
|
| 502 | 534 | throws (testing::TestError) |
|
| 503 | 535 | { |
|
| 504 | - | let mut a = testResolver(); |
|
| 505 | - | let result = try resolveProgramStr(&mut a, expr); |
|
| 506 | - | try expectNoErrors(&result); |
|
| 507 | - | let stmt = try getBlockStmt(result.root, 1); |
|
| 508 | - | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 509 | - | try expectConstInt(&a, expr, expected); |
|
| 536 | + | let mut testArena4 = testArena(); |
|
| 537 | + | let testStorage4: 'test4 = &mut testArena4 in { |
|
| 538 | + | let mut a = testResolver(testStorage4); |
|
| 539 | + | let result = try resolveProgramStr(&mut a, expr); |
|
| 540 | + | try expectNoErrors(&result); |
|
| 541 | + | let stmt = try getBlockStmt(result.root, 1); |
|
| 542 | + | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 543 | + | try expectConstInt(&a, expr, expected); |
|
| 544 | + | } |
|
| 510 | 545 | } |
|
| 511 | 546 | ||
| 512 | 547 | // Tests /////////////////////////////////////////////////////////////////////// |
|
| 513 | 548 | ||
| 514 | 549 | @test unsafe fn testResolveLit() throws (testing::TestError) { |
|
| 515 | - | let mut a = testResolver(); |
|
| 516 | - | let result = try resolveExprStr(&mut a, "true"); |
|
| 550 | + | let mut testArena5 = testArena(); |
|
| 551 | + | let testStorage5: 'test5 = &mut testArena5 in { |
|
| 552 | + | let mut a = testResolver(testStorage5); |
|
| 553 | + | let result = try resolveExprStr(&mut a, "true"); |
|
| 517 | 554 | ||
| 518 | - | try expectNoErrors(&result); |
|
| 519 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 555 | + | try expectNoErrors(&result); |
|
| 556 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 557 | + | } |
|
| 520 | 558 | } |
|
| 521 | 559 | ||
| 522 | 560 | @test unsafe fn testResolveStringLiteralType() throws (testing::TestError) { |
|
| 523 | - | let mut a = testResolver(); |
|
| 524 | - | let result = try resolveExprStr(&mut a, "\"hello\""); |
|
| 561 | + | let mut testArena6 = testArena(); |
|
| 562 | + | let testStorage6: 'test6 = &mut testArena6 in { |
|
| 563 | + | let mut a = testResolver(testStorage6); |
|
| 564 | + | let result = try resolveExprStr(&mut a, "\"hello\""); |
|
| 525 | 565 | ||
| 526 | - | try expectNoErrors(&result); |
|
| 527 | - | let ty = try typeOf(&a, result.root); |
|
| 528 | - | let elemTy = try expectSliceType(ty, false); |
|
| 529 | - | try testing::expect(elemTy == super::Type::U8); |
|
| 566 | + | try expectNoErrors(&result); |
|
| 567 | + | let ty = try typeOf(&a, result.root); |
|
| 568 | + | let elemTy = try expectSliceType(ty, false); |
|
| 569 | + | try testing::expect(elemTy == super::Type::U8); |
|
| 570 | + | } |
|
| 530 | 571 | } |
|
| 531 | 572 | ||
| 532 | 573 | @test unsafe fn testResolveAsNumeric() throws (testing::TestError) { |
|
| 533 | 574 | { |
|
| 534 | - | let mut a = testResolver(); |
|
| 535 | - | let result = try resolveExprStr(&mut a, "1 as u32"); |
|
| 536 | - | try expectNoErrors(&result); |
|
| 537 | - | try expectType(&a, result.root, super::Type::U32); |
|
| 575 | + | let mut testArena7 = testArena(); |
|
| 576 | + | let testStorage7: 'test7 = &mut testArena7 in { |
|
| 577 | + | let mut a = testResolver(testStorage7); |
|
| 578 | + | let result = try resolveExprStr(&mut a, "1 as u32"); |
|
| 579 | + | try expectNoErrors(&result); |
|
| 580 | + | try expectType(&a, result.root, super::Type::U32); |
|
| 581 | + | } |
|
| 538 | 582 | } { |
|
| 539 | - | let mut a = testResolver(); |
|
| 540 | - | let result = try resolveBlockStr(&mut a, "let x: u32 = 913; x as u8;"); |
|
| 541 | - | try expectNoErrors(&result); |
|
| 583 | + | let mut testArena8 = testArena(); |
|
| 584 | + | let testStorage8: 'test8 = &mut testArena8 in { |
|
| 585 | + | let mut a = testResolver(testStorage8); |
|
| 586 | + | let result = try resolveBlockStr(&mut a, "let x: u32 = 913; x as u8;"); |
|
| 587 | + | try expectNoErrors(&result); |
|
| 542 | 588 | ||
| 543 | - | let x = try getBlockStmt(result.root, 1); |
|
| 544 | - | try expectExprStmtType(&a, x, super::Type::U8); |
|
| 589 | + | let x = try getBlockStmt(result.root, 1); |
|
| 590 | + | try expectExprStmtType(&a, x, super::Type::U8); |
|
| 591 | + | } |
|
| 545 | 592 | } |
|
| 546 | 593 | } |
|
| 547 | 594 | ||
| 548 | 595 | @test unsafe fn testResolveAsInvalid() throws (testing::TestError) { |
|
| 549 | - | let mut a = testResolver(); |
|
| 550 | - | let result = try resolveProgramStr(&mut a, "true as u32"); |
|
| 551 | - | ||
| 552 | - | try expectErrorKind( |
|
| 553 | - | &result, |
|
| 554 | - | super::ErrorKind::InvalidAsCast(super::InvalidAsCast { |
|
| 555 | - | from: super::Type::Bool, |
|
| 556 | - | to: super::Type::U32, |
|
| 596 | + | let mut testArena9 = testArena(); |
|
| 597 | + | let testStorage9: 'test9 = &mut testArena9 in { |
|
| 598 | + | let mut a = testResolver(testStorage9); |
|
| 599 | + | let result = try resolveProgramStr(&mut a, "true as u32"); |
|
| 600 | + | ||
| 601 | + | try expectErrorKind( |
|
| 602 | + | &result, |
|
| 603 | + | super::ErrorKind::InvalidAsCast(super::InvalidAsCast { |
|
| 604 | + | from: super::Type::Bool, |
|
| 605 | + | to: super::Type::U32, |
|
| 557 | 606 | }) |
|
| 558 | - | ); |
|
| 607 | + | ); |
|
| 608 | + | } |
|
| 559 | 609 | } |
|
| 560 | 610 | ||
| 561 | 611 | @test unsafe fn testResolveAsUnionToInt() throws (testing::TestError) { |
|
| 562 | - | let mut a = testResolver(); |
|
| 563 | - | let program = "union Color { Red } Color::Red as u32;"; |
|
| 564 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 565 | - | try expectNoErrors(&result); |
|
| 612 | + | let mut testArena10 = testArena(); |
|
| 613 | + | let testStorage10: 'test10 = &mut testArena10 in { |
|
| 614 | + | let mut a = testResolver(testStorage10); |
|
| 615 | + | let program = "union Color { Red } Color::Red as u32;"; |
|
| 616 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 617 | + | try expectNoErrors(&result); |
|
| 566 | 618 | ||
| 567 | - | let red = try getBlockStmt(result.root, 1); |
|
| 568 | - | try expectExprStmtType(&a, red, super::Type::U32); |
|
| 619 | + | let red = try getBlockStmt(result.root, 1); |
|
| 620 | + | try expectExprStmtType(&a, red, super::Type::U32); |
|
| 621 | + | } |
|
| 569 | 622 | } |
|
| 570 | 623 | ||
| 571 | 624 | @test unsafe fn testResolveBinding() throws (testing::TestError) { |
|
| 572 | - | let mut a = testResolver(); |
|
| 573 | - | let result = try resolveBlockStr(&mut a, "let x: bool = true; x;"); |
|
| 574 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 625 | + | let mut testArena11 = testArena(); |
|
| 626 | + | let testStorage11: 'test11 = &mut testArena11 in { |
|
| 627 | + | let mut a = testResolver(testStorage11); |
|
| 628 | + | let result = try resolveBlockStr(&mut a, "let x: bool = true; x;"); |
|
| 629 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 575 | 630 | ||
| 576 | - | try expectNoErrors(&result); |
|
| 577 | - | try expectType(&a, stmt, super::Type::Void); |
|
| 578 | - | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 631 | + | try expectNoErrors(&result); |
|
| 632 | + | try expectType(&a, stmt, super::Type::Void); |
|
| 633 | + | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 579 | 634 | ||
| 580 | - | let case ast::NodeValue::ExprStmt(x) = stmt.value |
|
| 581 | - | else throw testing::TestError::Failed; |
|
| 635 | + | let case ast::NodeValue::ExprStmt(x) = stmt.value |
|
| 636 | + | else throw testing::TestError::Failed; |
|
| 582 | 637 | ||
| 583 | - | let sym = super::symbolFor(&a, x) |
|
| 584 | - | else throw testing::TestError::Failed; |
|
| 585 | - | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 586 | - | else throw testing::TestError::Failed; |
|
| 587 | - | try testing::expect(valType == super::Type::Bool); |
|
| 638 | + | let sym = super::symbolFor(&a, x) |
|
| 639 | + | else throw testing::TestError::Failed; |
|
| 640 | + | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 641 | + | else throw testing::TestError::Failed; |
|
| 642 | + | try testing::expect(valType == super::Type::Bool); |
|
| 643 | + | } |
|
| 588 | 644 | } |
|
| 589 | 645 | ||
| 590 | 646 | @test unsafe fn testResolveBindingInvalid() throws (testing::TestError) { |
|
| 591 | - | let mut a = testResolver(); |
|
| 592 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = true;"); |
|
| 593 | - | let err = try expectError(&result); |
|
| 594 | - | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 647 | + | let mut testArena12 = testArena(); |
|
| 648 | + | let testStorage12: 'test12 = &mut testArena12 in { |
|
| 649 | + | let mut a = testResolver(testStorage12); |
|
| 650 | + | let result = try resolveBlockStr(&mut a, "let x: i32 = true;"); |
|
| 651 | + | let err = try expectError(&result); |
|
| 652 | + | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 653 | + | } |
|
| 595 | 654 | } |
|
| 596 | 655 | ||
| 597 | 656 | @test unsafe fn testResolveDuplicateBinding() throws (testing::TestError) { |
|
| 598 | - | let mut a = testResolver(); |
|
| 599 | - | let result = try resolveBlockStr(&mut a, "let x: bool = true; let x: u8 = 1;"); |
|
| 600 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 601 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("x")); |
|
| 657 | + | let mut testArena13 = testArena(); |
|
| 658 | + | let testStorage13: 'test13 = &mut testArena13 in { |
|
| 659 | + | let mut a = testResolver(testStorage13); |
|
| 660 | + | let result = try resolveBlockStr(&mut a, "let x: bool = true; let x: u8 = 1;"); |
|
| 661 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 662 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("x")); |
|
| 663 | + | } |
|
| 602 | 664 | } |
|
| 603 | 665 | ||
| 604 | 666 | @test unsafe fn testResolveConstLiteralValue() throws (testing::TestError) { |
|
| 605 | - | let mut a = testResolver(); |
|
| 606 | - | let program = "constant ANSWER: i32 = 42;"; |
|
| 607 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 608 | - | try expectNoErrors(&result); |
|
| 667 | + | let mut testArena14 = testArena(); |
|
| 668 | + | let testStorage14: 'test14 = &mut testArena14 in { |
|
| 669 | + | let mut a = testResolver(testStorage14); |
|
| 670 | + | let program = "constant ANSWER: i32 = 42;"; |
|
| 671 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 672 | + | try expectNoErrors(&result); |
|
| 609 | 673 | ||
| 610 | - | let constNode = try getBlockStmt(result.root, 0); |
|
| 611 | - | let sym = super::symbolFor(&a, constNode) |
|
| 612 | - | else throw testing::TestError::Failed; |
|
| 613 | - | let case super::SymbolData::Constant { type: constType, .. } = sym.data |
|
| 614 | - | else throw testing::TestError::Failed; |
|
| 615 | - | try testing::expect(constType == super::Type::I32); |
|
| 674 | + | let constNode = try getBlockStmt(result.root, 0); |
|
| 675 | + | let sym = super::symbolFor(&a, constNode) |
|
| 676 | + | else throw testing::TestError::Failed; |
|
| 677 | + | let case super::SymbolData::Constant { type: constType, .. } = sym.data |
|
| 678 | + | else throw testing::TestError::Failed; |
|
| 679 | + | try testing::expect(constType == super::Type::I32); |
|
| 680 | + | } |
|
| 616 | 681 | } |
|
| 617 | 682 | ||
| 618 | 683 | @test unsafe fn testResolveConstRequiresConstantExpr() throws (testing::TestError) { |
|
| 619 | - | let mut a = testResolver(); |
|
| 620 | - | let program = "fn value() -> i32 { return 1 } fn main() { constant ANSWER: i32 = value(); }"; |
|
| 621 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 622 | - | let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 684 | + | let mut testArena15 = testArena(); |
|
| 685 | + | let testStorage15: 'test15 = &mut testArena15 in { |
|
| 686 | + | let mut a = testResolver(testStorage15); |
|
| 687 | + | let program = "fn value() -> i32 { return 1 } fn main() { constant ANSWER: i32 = value(); }"; |
|
| 688 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 689 | + | let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 623 | 690 | ||
| 624 | - | let errNode = err.node |
|
| 625 | - | else throw testing::TestError::Failed; |
|
| 626 | - | let case ast::NodeValue::Call(_) = errNode.value |
|
| 627 | - | else throw testing::TestError::Failed; |
|
| 691 | + | let errNode = err.node |
|
| 692 | + | else throw testing::TestError::Failed; |
|
| 693 | + | let case ast::NodeValue::Call(_) = errNode.value |
|
| 694 | + | else throw testing::TestError::Failed; |
|
| 695 | + | } |
|
| 628 | 696 | } |
|
| 629 | 697 | ||
| 630 | 698 | @test unsafe fn testResolveStaticLiteralValue() throws (testing::TestError) { |
|
| 631 | - | let mut a = testResolver(); |
|
| 632 | - | let program = "static COUNTER: i32 = 0;"; |
|
| 633 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 634 | - | try expectNoErrors(&result); |
|
| 699 | + | let mut testArena16 = testArena(); |
|
| 700 | + | let testStorage16: 'test16 = &mut testArena16 in { |
|
| 701 | + | let mut a = testResolver(testStorage16); |
|
| 702 | + | let program = "static COUNTER: i32 = 0;"; |
|
| 703 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 704 | + | try expectNoErrors(&result); |
|
| 635 | 705 | ||
| 636 | - | let staticNode = try getBlockStmt(result.root, 0); |
|
| 637 | - | let sym = super::symbolFor(&a, staticNode) |
|
| 638 | - | else throw testing::TestError::Failed; |
|
| 639 | - | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 640 | - | else throw testing::TestError::Failed; |
|
| 641 | - | try testing::expect(valType == super::Type::I32); |
|
| 706 | + | let staticNode = try getBlockStmt(result.root, 0); |
|
| 707 | + | let sym = super::symbolFor(&a, staticNode) |
|
| 708 | + | else throw testing::TestError::Failed; |
|
| 709 | + | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 710 | + | else throw testing::TestError::Failed; |
|
| 711 | + | try testing::expect(valType == super::Type::I32); |
|
| 712 | + | } |
|
| 642 | 713 | } |
|
| 643 | 714 | ||
| 644 | 715 | @test unsafe fn testResolveStaticRequiresConstantExpr() throws (testing::TestError) { |
|
| 645 | - | let mut a = testResolver(); |
|
| 646 | - | let program = "fn seed() -> i32 { return 1; } static COUNTER: i32 = seed();"; |
|
| 647 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 648 | - | let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 716 | + | let mut testArena17 = testArena(); |
|
| 717 | + | let testStorage17: 'test17 = &mut testArena17 in { |
|
| 718 | + | let mut a = testResolver(testStorage17); |
|
| 719 | + | let program = "fn seed() -> i32 { return 1; } static COUNTER: i32 = seed();"; |
|
| 720 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 721 | + | let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 649 | 722 | ||
| 650 | - | let errNode = err.node |
|
| 651 | - | else throw testing::TestError::Failed; |
|
| 652 | - | let case ast::NodeValue::Call(_) = errNode.value |
|
| 653 | - | else throw testing::TestError::Failed; |
|
| 723 | + | let errNode = err.node |
|
| 724 | + | else throw testing::TestError::Failed; |
|
| 725 | + | let case ast::NodeValue::Call(_) = errNode.value |
|
| 726 | + | else throw testing::TestError::Failed; |
|
| 727 | + | } |
|
| 654 | 728 | } |
|
| 655 | 729 | ||
| 656 | 730 | @test unsafe fn testSymbolStoresFnAttributes() throws (testing::TestError) { |
|
| 657 | - | let mut a = testResolver(); |
|
| 658 | - | let program = "@default export fn f() { return; }"; |
|
| 659 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 660 | - | try expectNoErrors(&result); |
|
| 731 | + | let mut testArena18 = testArena(); |
|
| 732 | + | let testStorage18: 'test18 = &mut testArena18 in { |
|
| 733 | + | let mut a = testResolver(testStorage18); |
|
| 734 | + | let program = "@default export fn f() { return; }"; |
|
| 735 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 736 | + | try expectNoErrors(&result); |
|
| 661 | 737 | ||
| 662 | - | let scope = super::scopeFor(&a, result.root) |
|
| 663 | - | else throw testing::TestError::Failed; |
|
| 664 | - | let sym = super::findSymbolInScope(scope, "f") |
|
| 665 | - | else throw testing::TestError::Failed; |
|
| 738 | + | let scope = super::scopeFor(&a, result.root) |
|
| 739 | + | else throw testing::TestError::Failed; |
|
| 740 | + | let sym = super::findSymbolInScope(scope, "f") |
|
| 741 | + | else throw testing::TestError::Failed; |
|
| 666 | 742 | ||
| 667 | - | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 668 | - | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 669 | - | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern)); |
|
| 743 | + | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 744 | + | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 745 | + | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern)); |
|
| 746 | + | } |
|
| 670 | 747 | } |
|
| 671 | 748 | ||
| 672 | 749 | @test unsafe fn testSymbolStoresRecordAttributes() throws (testing::TestError) { |
|
| 673 | - | let mut a = testResolver(); |
|
| 674 | - | let program = "export record S { value: i32 }"; |
|
| 675 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 676 | - | try expectNoErrors(&result); |
|
| 750 | + | let mut testArena19 = testArena(); |
|
| 751 | + | let testStorage19: 'test19 = &mut testArena19 in { |
|
| 752 | + | let mut a = testResolver(testStorage19); |
|
| 753 | + | let program = "export record S { value: i32 }"; |
|
| 754 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 755 | + | try expectNoErrors(&result); |
|
| 677 | 756 | ||
| 678 | - | let scope = super::scopeFor(&a, result.root) |
|
| 679 | - | else throw testing::TestError::Failed; |
|
| 680 | - | let sym = super::findSymbolInScope(scope, "S") |
|
| 681 | - | else throw testing::TestError::Failed; |
|
| 757 | + | let scope = super::scopeFor(&a, result.root) |
|
| 758 | + | else throw testing::TestError::Failed; |
|
| 759 | + | let sym = super::findSymbolInScope(scope, "S") |
|
| 760 | + | else throw testing::TestError::Failed; |
|
| 682 | 761 | ||
| 683 | - | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 684 | - | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 762 | + | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 763 | + | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 764 | + | } |
|
| 685 | 765 | } |
|
| 686 | 766 | ||
| 687 | 767 | @test unsafe fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) { |
|
| 688 | - | let mut a = testResolver(); |
|
| 689 | - | let program = "@default record T { value: i32 }"; |
|
| 690 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 691 | - | try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn); |
|
| 768 | + | let mut testArena20 = testArena(); |
|
| 769 | + | let testStorage20: 'test20 = &mut testArena20 in { |
|
| 770 | + | let mut a = testResolver(testStorage20); |
|
| 771 | + | let program = "@default record T { value: i32 }"; |
|
| 772 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 773 | + | try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn); |
|
| 774 | + | } |
|
| 692 | 775 | } |
|
| 693 | 776 | ||
| 694 | 777 | @test unsafe fn testDefaultAttributeRejectedOnUnion() throws (testing::TestError) { |
|
| 695 | - | let mut a = testResolver(); |
|
| 696 | - | let program = "@default union Result { Ok, Err }"; |
|
| 697 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 698 | - | try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn); |
|
| 778 | + | let mut testArena21 = testArena(); |
|
| 779 | + | let testStorage21: 'test21 = &mut testArena21 in { |
|
| 780 | + | let mut a = testResolver(testStorage21); |
|
| 781 | + | let program = "@default union Result { Ok, Err }"; |
|
| 782 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 783 | + | try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn); |
|
| 784 | + | } |
|
| 699 | 785 | } |
|
| 700 | 786 | ||
| 701 | 787 | @test unsafe fn testResolveArrayLiteralTyped() throws (testing::TestError) { |
|
| 702 | - | let mut a = testResolver(); |
|
| 703 | - | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2];"); |
|
| 704 | - | try expectNoErrors(&result); |
|
| 788 | + | let mut testArena22 = testArena(); |
|
| 789 | + | let testStorage22: 'test22 = &mut testArena22 in { |
|
| 790 | + | let mut a = testResolver(testStorage22); |
|
| 791 | + | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2];"); |
|
| 792 | + | try expectNoErrors(&result); |
|
| 705 | 793 | ||
| 706 | - | let stmt = try getBlockStmt(result.root, 0); |
|
| 707 | - | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 708 | - | else throw testing::TestError::Failed; |
|
| 709 | - | let arrayTy = try typeOf(&a, decl.value); |
|
| 710 | - | let elemTy = try expectArrayType(arrayTy, 2); |
|
| 711 | - | try testing::expect(elemTy == super::Type::I32); |
|
| 794 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 795 | + | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 796 | + | else throw testing::TestError::Failed; |
|
| 797 | + | let arrayTy = try typeOf(&a, decl.value); |
|
| 798 | + | let elemTy = try expectArrayType(arrayTy, 2); |
|
| 799 | + | try testing::expect(elemTy == super::Type::I32); |
|
| 800 | + | } |
|
| 712 | 801 | } |
|
| 713 | 802 | ||
| 714 | 803 | @test unsafe fn testResolveArrayLiteralElementMismatch() throws (testing::TestError) { |
|
| 715 | - | let mut a = testResolver(); |
|
| 716 | - | let result = try resolveProgramStr(&mut a, "let xs: [bool; 2] = [true, 1];"); |
|
| 717 | - | let err = try expectError(&result); |
|
| 718 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 804 | + | let mut testArena23 = testArena(); |
|
| 805 | + | let testStorage23: 'test23 = &mut testArena23 in { |
|
| 806 | + | let mut a = testResolver(testStorage23); |
|
| 807 | + | let result = try resolveProgramStr(&mut a, "let xs: [bool; 2] = [true, 1];"); |
|
| 808 | + | let err = try expectError(&result); |
|
| 809 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 810 | + | } |
|
| 719 | 811 | } |
|
| 720 | 812 | ||
| 721 | 813 | @test unsafe fn testResolveArrayLiteralCannotInfer() throws (testing::TestError) { |
|
| 722 | - | let mut a = testResolver(); |
|
| 723 | - | let result = try resolveProgramStr(&mut a, "let xs = [1, 2];"); |
|
| 724 | - | try expectErrorKind(&result, super::ErrorKind::CannotInferType); |
|
| 814 | + | let mut testArena24 = testArena(); |
|
| 815 | + | let testStorage24: 'test24 = &mut testArena24 in { |
|
| 816 | + | let mut a = testResolver(testStorage24); |
|
| 817 | + | let result = try resolveProgramStr(&mut a, "let xs = [1, 2];"); |
|
| 818 | + | try expectErrorKind(&result, super::ErrorKind::CannotInferType); |
|
| 819 | + | } |
|
| 725 | 820 | } |
|
| 726 | 821 | ||
| 727 | 822 | @test unsafe fn testResolveArrayLiteralOverflow() throws (testing::TestError) { |
|
| 728 | - | let mut a = testResolver(); |
|
| 729 | - | let result = try resolveProgramStr(&mut a, "let xs: [u8; 2] = [1, 256];"); |
|
| 730 | - | let err = try expectError(&result); |
|
| 731 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 732 | - | else throw testing::TestError::Failed; |
|
| 823 | + | let mut testArena25 = testArena(); |
|
| 824 | + | let testStorage25: 'test25 = &mut testArena25 in { |
|
| 825 | + | let mut a = testResolver(testStorage25); |
|
| 826 | + | let result = try resolveProgramStr(&mut a, "let xs: [u8; 2] = [1, 256];"); |
|
| 827 | + | let err = try expectError(&result); |
|
| 828 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 829 | + | else throw testing::TestError::Failed; |
|
| 830 | + | } |
|
| 733 | 831 | } |
|
| 734 | 832 | ||
| 735 | 833 | @test unsafe fn testResolveArrayLiteralTooFewElements() throws (testing::TestError) { |
|
| 736 | - | let mut a = testResolver(); |
|
| 737 | - | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1];"); |
|
| 738 | - | let err = try expectError(&result); |
|
| 739 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 740 | - | else throw testing::TestError::Failed; |
|
| 834 | + | let mut testArena26 = testArena(); |
|
| 835 | + | let testStorage26: 'test26 = &mut testArena26 in { |
|
| 836 | + | let mut a = testResolver(testStorage26); |
|
| 837 | + | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1];"); |
|
| 838 | + | let err = try expectError(&result); |
|
| 839 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 840 | + | else throw testing::TestError::Failed; |
|
| 841 | + | } |
|
| 741 | 842 | } |
|
| 742 | 843 | ||
| 743 | 844 | @test unsafe fn testResolveArrayLiteralTooManyElements() throws (testing::TestError) { |
|
| 744 | - | let mut a = testResolver(); |
|
| 745 | - | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2, 3];"); |
|
| 746 | - | let err = try expectError(&result); |
|
| 747 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 748 | - | else throw testing::TestError::Failed; |
|
| 845 | + | let mut testArena27 = testArena(); |
|
| 846 | + | let testStorage27: 'test27 = &mut testArena27 in { |
|
| 847 | + | let mut a = testResolver(testStorage27); |
|
| 848 | + | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2, 3];"); |
|
| 849 | + | let err = try expectError(&result); |
|
| 850 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 851 | + | else throw testing::TestError::Failed; |
|
| 852 | + | } |
|
| 749 | 853 | } |
|
| 750 | 854 | ||
| 751 | 855 | @test unsafe fn testResolveArrayLiteralEmptyWithAnnotation() throws (testing::TestError) { |
|
| 752 | - | let mut a = testResolver(); |
|
| 753 | - | let result = try resolveProgramStr(&mut a, "let xs: [i32; 0] = [];"); |
|
| 754 | - | try expectNoErrors(&result); |
|
| 856 | + | let mut testArena28 = testArena(); |
|
| 857 | + | let testStorage28: 'test28 = &mut testArena28 in { |
|
| 858 | + | let mut a = testResolver(testStorage28); |
|
| 859 | + | let result = try resolveProgramStr(&mut a, "let xs: [i32; 0] = [];"); |
|
| 860 | + | try expectNoErrors(&result); |
|
| 861 | + | } |
|
| 755 | 862 | } |
|
| 756 | 863 | ||
| 757 | 864 | @test unsafe fn testResolveNestedArrayLiteralTyped() throws (testing::TestError) { |
|
| 758 | - | let mut a = testResolver(); |
|
| 759 | - | let result = try resolveProgramStr(&mut a, "let grid: [[i32; 2]; 2] = [[1, 2], [3, 4]];"); |
|
| 760 | - | try expectNoErrors(&result); |
|
| 865 | + | let mut testArena29 = testArena(); |
|
| 866 | + | let testStorage29: 'test29 = &mut testArena29 in { |
|
| 867 | + | let mut a = testResolver(testStorage29); |
|
| 868 | + | let result = try resolveProgramStr(&mut a, "let grid: [[i32; 2]; 2] = [[1, 2], [3, 4]];"); |
|
| 869 | + | try expectNoErrors(&result); |
|
| 761 | 870 | ||
| 762 | - | let stmt = try getBlockStmt(result.root, 0); |
|
| 763 | - | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 764 | - | else throw testing::TestError::Failed; |
|
| 765 | - | let gridTy = try typeOf(&a, decl.value); |
|
| 766 | - | let rowTy = try expectArrayType(gridTy, 2); |
|
| 767 | - | let elemTy = try expectArrayType(rowTy, 2); |
|
| 768 | - | try testing::expect(elemTy == super::Type::I32); |
|
| 871 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 872 | + | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 873 | + | else throw testing::TestError::Failed; |
|
| 874 | + | let gridTy = try typeOf(&a, decl.value); |
|
| 875 | + | let rowTy = try expectArrayType(gridTy, 2); |
|
| 876 | + | let elemTy = try expectArrayType(rowTy, 2); |
|
| 877 | + | try testing::expect(elemTy == super::Type::I32); |
|
| 878 | + | } |
|
| 769 | 879 | } |
|
| 770 | 880 | ||
| 771 | 881 | @test unsafe fn testResolveArrayLiteralWithOptionalElems() throws (testing::TestError) { |
|
| 772 | - | let mut a = testResolver(); |
|
| 773 | - | let result = try resolveProgramStr(&mut a, "let xs: [?i32; 2] = [1, 2];"); |
|
| 774 | - | try expectNoErrors(&result); |
|
| 882 | + | let mut testArena30 = testArena(); |
|
| 883 | + | let testStorage30: 'test30 = &mut testArena30 in { |
|
| 884 | + | let mut a = testResolver(testStorage30); |
|
| 885 | + | let result = try resolveProgramStr(&mut a, "let xs: [?i32; 2] = [1, 2];"); |
|
| 886 | + | try expectNoErrors(&result); |
|
| 775 | 887 | ||
| 776 | - | let stmt = try getBlockStmt(result.root, 0); |
|
| 777 | - | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 778 | - | else throw testing::TestError::Failed; |
|
| 779 | - | let arrayTy = try typeOf(&a, decl.value); |
|
| 780 | - | let elemTy = try expectArrayType(arrayTy, 2); |
|
| 781 | - | let case super::Type::Optional(inner) = elemTy |
|
| 782 | - | else throw testing::TestError::Failed; |
|
| 783 | - | try testing::expect(*inner == super::Type::I32); |
|
| 888 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 889 | + | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 890 | + | else throw testing::TestError::Failed; |
|
| 891 | + | let arrayTy = try typeOf(&a, decl.value); |
|
| 892 | + | let elemTy = try expectArrayType(arrayTy, 2); |
|
| 893 | + | let case super::Type::Optional(inner) = elemTy |
|
| 894 | + | else throw testing::TestError::Failed; |
|
| 895 | + | try testing::expect(*inner == super::Type::I32); |
|
| 896 | + | } |
|
| 784 | 897 | } |
|
| 785 | 898 | ||
| 786 | 899 | @test unsafe fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) { |
|
| 787 | - | let mut a = testResolver(); |
|
| 788 | - | let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];"); |
|
| 789 | - | let err = try expectError(&result); |
|
| 790 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 791 | - | else throw testing::TestError::Failed; |
|
| 900 | + | let mut testArena31 = testArena(); |
|
| 901 | + | let testStorage31: 'test31 = &mut testArena31 in { |
|
| 902 | + | let mut a = testResolver(testStorage31); |
|
| 903 | + | let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];"); |
|
| 904 | + | let err = try expectError(&result); |
|
| 905 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 906 | + | else throw testing::TestError::Failed; |
|
| 907 | + | } |
|
| 792 | 908 | } |
|
| 793 | 909 | ||
| 794 | 910 | @test unsafe fn testResolveArrayRepeatBasic() throws (testing::TestError) { |
|
| 795 | - | let mut a = testResolver(); |
|
| 796 | - | let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3];"); |
|
| 797 | - | try expectNoErrors(&result); |
|
| 911 | + | let mut testArena32 = testArena(); |
|
| 912 | + | let testStorage32: 'test32 = &mut testArena32 in { |
|
| 913 | + | let mut a = testResolver(testStorage32); |
|
| 914 | + | let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3];"); |
|
| 915 | + | try expectNoErrors(&result); |
|
| 798 | 916 | ||
| 799 | - | let stmt = try getBlockStmt(result.root, 0); |
|
| 800 | - | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 801 | - | else throw testing::TestError::Failed; |
|
| 802 | - | let arrayTy = try typeOf(&a, decl.value); |
|
| 803 | - | let elemTy = try expectArrayType(arrayTy, 3); |
|
| 804 | - | try testing::expect(elemTy == super::Type::I32); |
|
| 917 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 918 | + | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 919 | + | else throw testing::TestError::Failed; |
|
| 920 | + | let arrayTy = try typeOf(&a, decl.value); |
|
| 921 | + | let elemTy = try expectArrayType(arrayTy, 3); |
|
| 922 | + | try testing::expect(elemTy == super::Type::I32); |
|
| 923 | + | } |
|
| 805 | 924 | } |
|
| 806 | 925 | ||
| 807 | 926 | @test unsafe fn testResolveArrayRepeatWithExpression() throws (testing::TestError) { |
|
| 808 | - | let mut a = testResolver(); |
|
| 809 | - | let result = try resolveProgramStr(&mut a, "let xs: [i32; 5] = [3 + 2; 5];"); |
|
| 810 | - | try expectNoErrors(&result); |
|
| 927 | + | let mut testArena33 = testArena(); |
|
| 928 | + | let testStorage33: 'test33 = &mut testArena33 in { |
|
| 929 | + | let mut a = testResolver(testStorage33); |
|
| 930 | + | let result = try resolveProgramStr(&mut a, "let xs: [i32; 5] = [3 + 2; 5];"); |
|
| 931 | + | try expectNoErrors(&result); |
|
| 811 | 932 | ||
| 812 | - | let stmt = try getBlockStmt(result.root, 0); |
|
| 813 | - | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 814 | - | else throw testing::TestError::Failed; |
|
| 815 | - | let arrayTy = try typeOf(&a, decl.value); |
|
| 816 | - | let elemTy = try expectArrayType(arrayTy, 5); |
|
| 817 | - | try testing::expect(elemTy == super::Type::I32); |
|
| 933 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 934 | + | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 935 | + | else throw testing::TestError::Failed; |
|
| 936 | + | let arrayTy = try typeOf(&a, decl.value); |
|
| 937 | + | let elemTy = try expectArrayType(arrayTy, 5); |
|
| 938 | + | try testing::expect(elemTy == super::Type::I32); |
|
| 939 | + | } |
|
| 818 | 940 | } |
|
| 819 | 941 | ||
| 820 | 942 | @test unsafe fn testResolveArrayRepeatLiteralArithmetic() throws (testing::TestError) { |
|
| 821 | - | let mut a = testResolver(); |
|
| 822 | - | // `3 * 1` folds to a compile-time constant, so the repeat count is valid. |
|
| 823 | - | let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3 * 1];"); |
|
| 824 | - | try expectNoErrors(&result); |
|
| 943 | + | let mut testArena34 = testArena(); |
|
| 944 | + | let testStorage34: 'test34 = &mut testArena34 in { |
|
| 945 | + | let mut a = testResolver(testStorage34); |
|
| 946 | + | // `3 * 1` folds to a compile-time constant, so the repeat count is valid. |
|
| 947 | + | let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3 * 1];"); |
|
| 948 | + | try expectNoErrors(&result); |
|
| 949 | + | } |
|
| 825 | 950 | } |
|
| 826 | 951 | ||
| 827 | 952 | @test unsafe fn testResolveArrayRepeatNonConstCount() throws (testing::TestError) { |
|
| 828 | - | let mut a = testResolver(); |
|
| 829 | - | // A function call is not a constant expression. |
|
| 830 | - | let result = try resolveProgramStr(&mut a, "fn f() -> u32 { return 3; } let xs: [i32; 3] = [42; f()];"); |
|
| 831 | - | try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 953 | + | let mut testArena35 = testArena(); |
|
| 954 | + | let testStorage35: 'test35 = &mut testArena35 in { |
|
| 955 | + | let mut a = testResolver(testStorage35); |
|
| 956 | + | // A function call is not a constant expression. |
|
| 957 | + | let result = try resolveProgramStr(&mut a, "fn f() -> u32 { return 3; } let xs: [i32; 3] = [42; f()];"); |
|
| 958 | + | try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 959 | + | } |
|
| 832 | 960 | } |
|
| 833 | 961 | ||
| 834 | 962 | @test unsafe fn testResolveArrayRepeatCountMismatch() throws (testing::TestError) { |
|
| 835 | - | let mut a = testResolver(); |
|
| 836 | - | let result = try resolveProgramStr(&mut a, "let xs: [i32; 4] = [1; 3];"); |
|
| 837 | - | let err = try expectError(&result); |
|
| 838 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 839 | - | else throw testing::TestError::Failed; |
|
| 963 | + | let mut testArena36 = testArena(); |
|
| 964 | + | let testStorage36: 'test36 = &mut testArena36 in { |
|
| 965 | + | let mut a = testResolver(testStorage36); |
|
| 966 | + | let result = try resolveProgramStr(&mut a, "let xs: [i32; 4] = [1; 3];"); |
|
| 967 | + | let err = try expectError(&result); |
|
| 968 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 969 | + | else throw testing::TestError::Failed; |
|
| 970 | + | } |
|
| 840 | 971 | } |
|
| 841 | 972 | ||
| 842 | 973 | @test unsafe fn testResolveArrayIndex() throws (testing::TestError) { |
|
| 843 | - | let mut a = testResolver(); |
|
| 844 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; xs[1];"; |
|
| 845 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 846 | - | try expectNoErrors(&result); |
|
| 974 | + | let mut testArena37 = testArena(); |
|
| 975 | + | let testStorage37: 'test37 = &mut testArena37 in { |
|
| 976 | + | let mut a = testResolver(testStorage37); |
|
| 977 | + | let program = "let xs: [i32; 3] = [1, 2, 3]; xs[1];"; |
|
| 978 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 979 | + | try expectNoErrors(&result); |
|
| 847 | 980 | ||
| 848 | - | let stmt = try getBlockStmt(result.root, 1); |
|
| 849 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 981 | + | let stmt = try getBlockStmt(result.root, 1); |
|
| 982 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 983 | + | } |
|
| 850 | 984 | } |
|
| 851 | 985 | ||
| 852 | 986 | @test unsafe fn testResolveSliceIndex() throws (testing::TestError) { |
|
| 853 | - | let mut a = testResolver(); |
|
| 854 | - | let program = "static xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];"; |
|
| 855 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 856 | - | try expectNoErrors(&result); |
|
| 987 | + | let mut testArena38 = testArena(); |
|
| 988 | + | let testStorage38: 'test38 = &mut testArena38 in { |
|
| 989 | + | let mut a = testResolver(testStorage38); |
|
| 990 | + | let program = "static xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];"; |
|
| 991 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 992 | + | try expectNoErrors(&result); |
|
| 857 | 993 | ||
| 858 | - | let sliceStmt = try getBlockStmt(result.root, 1); |
|
| 859 | - | let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value |
|
| 860 | - | else throw testing::TestError::Failed; |
|
| 861 | - | let sliceTy = try typeOf(&a, sliceDecl.value); |
|
| 862 | - | let elemTy = try expectSliceType(sliceTy, false); |
|
| 863 | - | try testing::expect(elemTy == super::Type::I32); |
|
| 994 | + | let sliceStmt = try getBlockStmt(result.root, 1); |
|
| 995 | + | let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value |
|
| 996 | + | else throw testing::TestError::Failed; |
|
| 997 | + | let sliceTy = try typeOf(&a, sliceDecl.value); |
|
| 998 | + | let elemTy = try expectSliceType(sliceTy, false); |
|
| 999 | + | try testing::expect(elemTy == super::Type::I32); |
|
| 864 | 1000 | ||
| 865 | - | let indexStmt = try getBlockStmt(result.root, 2); |
|
| 866 | - | try expectExprStmtType(&a, indexStmt, super::Type::I32); |
|
| 1001 | + | let indexStmt = try getBlockStmt(result.root, 2); |
|
| 1002 | + | try expectExprStmtType(&a, indexStmt, super::Type::I32); |
|
| 1003 | + | } |
|
| 867 | 1004 | } |
|
| 868 | 1005 | ||
| 869 | 1006 | @test unsafe fn testResolveSliceFields() throws (testing::TestError) { |
|
| 870 | - | let mut a = testResolver(); |
|
| 871 | - | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; unsafe { slice.ptr; }"; |
|
| 872 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 873 | - | try expectNoErrors(&result); |
|
| 1007 | + | let mut testArena39 = testArena(); |
|
| 1008 | + | let testStorage39: 'test39 = &mut testArena39 in { |
|
| 1009 | + | let mut a = testResolver(testStorage39); |
|
| 1010 | + | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; unsafe { slice.ptr; }"; |
|
| 1011 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1012 | + | try expectNoErrors(&result); |
|
| 874 | 1013 | ||
| 875 | - | let lenStmt = try getBlockStmt(result.root, 2); |
|
| 876 | - | let case ast::NodeValue::ExprStmt(lenExpr) = lenStmt.value |
|
| 877 | - | else throw testing::TestError::Failed; |
|
| 878 | - | let lenTy = try typeOf(&a, lenExpr); |
|
| 879 | - | try testing::expect(lenTy == super::Type::U32); |
|
| 1014 | + | let lenStmt = try getBlockStmt(result.root, 2); |
|
| 1015 | + | let case ast::NodeValue::ExprStmt(lenExpr) = lenStmt.value |
|
| 1016 | + | else throw testing::TestError::Failed; |
|
| 1017 | + | let lenTy = try typeOf(&a, lenExpr); |
|
| 1018 | + | try testing::expect(lenTy == super::Type::U32); |
|
| 880 | 1019 | ||
| 881 | - | let ptrStmt = try getBlockStmt(result.root, 3); |
|
| 882 | - | let case ast::NodeValue::Block(ptrBlock) = ptrStmt.value |
|
| 883 | - | else throw testing::TestError::Failed; |
|
| 884 | - | let case ast::NodeValue::ExprStmt(ptrExpr) = ptrBlock.statements[0].value |
|
| 885 | - | else throw testing::TestError::Failed; |
|
| 886 | - | let ptrTy = try typeOf(&a, ptrExpr); |
|
| 887 | - | let targetTy = try expectPointerType(ptrTy, false); |
|
| 888 | - | try testing::expect(targetTy == super::Type::I32); |
|
| 1020 | + | let ptrStmt = try getBlockStmt(result.root, 3); |
|
| 1021 | + | let case ast::NodeValue::Block(ptrBlock) = ptrStmt.value |
|
| 1022 | + | else throw testing::TestError::Failed; |
|
| 1023 | + | let case ast::NodeValue::ExprStmt(ptrExpr) = ptrBlock.statements[0].value |
|
| 1024 | + | else throw testing::TestError::Failed; |
|
| 1025 | + | let ptrTy = try typeOf(&a, ptrExpr); |
|
| 1026 | + | let targetTy = try expectPointerType(ptrTy, false); |
|
| 1027 | + | try testing::expect(targetTy == super::Type::I32); |
|
| 1028 | + | } |
|
| 889 | 1029 | } |
|
| 890 | 1030 | ||
| 891 | 1031 | @test unsafe fn testResolveSliceLiteralImmutable() throws (testing::TestError) { |
|
| 892 | - | let mut a = testResolver(); |
|
| 893 | - | let program = "let slice: *[i32] = &[1, 2, 3];"; |
|
| 894 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 895 | - | try expectNoErrors(&result); |
|
| 1032 | + | let mut testArena40 = testArena(); |
|
| 1033 | + | let testStorage40: 'test40 = &mut testArena40 in { |
|
| 1034 | + | let mut a = testResolver(testStorage40); |
|
| 1035 | + | let program = "let slice: *[i32] = &[1, 2, 3];"; |
|
| 1036 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1037 | + | try expectNoErrors(&result); |
|
| 1038 | + | } |
|
| 896 | 1039 | } |
|
| 897 | 1040 | ||
| 898 | 1041 | /// Empty array literal infers element type from slice annotation. |
|
| 899 | 1042 | @test unsafe fn testResolveSliceLiteralEmpty() throws (testing::TestError) { |
|
| 900 | - | let mut a = testResolver(); |
|
| 901 | - | let program = "let slice: *[i32] = &[];"; |
|
| 902 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 903 | - | try expectNoErrors(&result); |
|
| 1043 | + | let mut testArena41 = testArena(); |
|
| 1044 | + | let testStorage41: 'test41 = &mut testArena41 in { |
|
| 1045 | + | let mut a = testResolver(testStorage41); |
|
| 1046 | + | let program = "let slice: *[i32] = &[];"; |
|
| 1047 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1048 | + | try expectNoErrors(&result); |
|
| 1049 | + | } |
|
| 904 | 1050 | } |
|
| 905 | 1051 | ||
| 906 | 1052 | /// Nested array literal should infer inner element type from slice annotation. |
|
| 907 | 1053 | @test unsafe fn testResolveSliceLiteralNestedArray() throws (testing::TestError) { |
|
| 908 | - | let mut a = testResolver(); |
|
| 909 | - | let program = "let slice: *[[i32; 2]] = &[[1, 2], [3, 4]];"; |
|
| 910 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 911 | - | try expectNoErrors(&result); |
|
| 1054 | + | let mut testArena42 = testArena(); |
|
| 1055 | + | let testStorage42: 'test42 = &mut testArena42 in { |
|
| 1056 | + | let mut a = testResolver(testStorage42); |
|
| 1057 | + | let program = "let slice: *[[i32; 2]] = &[[1, 2], [3, 4]];"; |
|
| 1058 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1059 | + | try expectNoErrors(&result); |
|
| 1060 | + | } |
|
| 912 | 1061 | } |
|
| 913 | 1062 | ||
| 914 | 1063 | @test unsafe fn testResolveSliceFromArray() throws (testing::TestError) { |
|
| 915 | 1064 | { |
|
| 916 | - | let mut a = testResolver(); |
|
| 917 | - | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];"; |
|
| 918 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 919 | - | try expectNoErrors(&result); |
|
| 1065 | + | let mut testArena43 = testArena(); |
|
| 1066 | + | let testStorage43: 'test43 = &mut testArena43 in { |
|
| 1067 | + | let mut a = testResolver(testStorage43); |
|
| 1068 | + | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];"; |
|
| 1069 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1070 | + | try expectNoErrors(&result); |
|
| 1071 | + | } |
|
| 920 | 1072 | } { |
|
| 921 | - | let mut a = testResolver(); |
|
| 922 | - | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];"; |
|
| 923 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 924 | - | try expectNoErrors(&result); |
|
| 1073 | + | let mut testArena44 = testArena(); |
|
| 1074 | + | let testStorage44: 'test44 = &mut testArena44 in { |
|
| 1075 | + | let mut a = testResolver(testStorage44); |
|
| 1076 | + | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];"; |
|
| 1077 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1078 | + | try expectNoErrors(&result); |
|
| 1079 | + | } |
|
| 925 | 1080 | } { |
|
| 926 | - | let mut a = testResolver(); |
|
| 927 | - | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];"; |
|
| 928 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 929 | - | try expectNoErrors(&result); |
|
| 1081 | + | let mut testArena45 = testArena(); |
|
| 1082 | + | let testStorage45: 'test45 = &mut testArena45 in { |
|
| 1083 | + | let mut a = testResolver(testStorage45); |
|
| 1084 | + | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];"; |
|
| 1085 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1086 | + | try expectNoErrors(&result); |
|
| 1087 | + | } |
|
| 930 | 1088 | } { |
|
| 931 | - | let mut a = testResolver(); |
|
| 932 | - | let program = "static xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];"; |
|
| 933 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 934 | - | try expectNoErrors(&result); |
|
| 1089 | + | let mut testArena46 = testArena(); |
|
| 1090 | + | let testStorage46: 'test46 = &mut testArena46 in { |
|
| 1091 | + | let mut a = testResolver(testStorage46); |
|
| 1092 | + | let program = "static xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];"; |
|
| 1093 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1094 | + | try expectNoErrors(&result); |
|
| 1095 | + | } |
|
| 935 | 1096 | } |
|
| 936 | 1097 | } |
|
| 937 | 1098 | ||
| 938 | 1099 | @test unsafe fn testResolveSliceLiteralMutableRequiresMut() throws (testing::TestError) { |
|
| 939 | - | let mut a = testResolver(); |
|
| 940 | - | let program = "let slice: *mut [i32] = &[1, 2, 3];"; |
|
| 941 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 942 | - | let err = try expectError(&result); |
|
| 943 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 944 | - | else throw testing::TestError::Failed; |
|
| 1100 | + | let mut testArena47 = testArena(); |
|
| 1101 | + | let testStorage47: 'test47 = &mut testArena47 in { |
|
| 1102 | + | let mut a = testResolver(testStorage47); |
|
| 1103 | + | let program = "let slice: *mut [i32] = &[1, 2, 3];"; |
|
| 1104 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1105 | + | let err = try expectError(&result); |
|
| 1106 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 1107 | + | else throw testing::TestError::Failed; |
|
| 1108 | + | } |
|
| 945 | 1109 | } |
|
| 946 | 1110 | ||
| 947 | 1111 | @test unsafe fn testResolveSliceLiteralMutable() throws (testing::TestError) { |
|
| 948 | - | let mut a = testResolver(); |
|
| 949 | - | let program = "let slice: *mut [i32] = &mut [1, 2, 3];"; |
|
| 950 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 951 | - | try expectNoErrors(&result); |
|
| 1112 | + | let mut testArena48 = testArena(); |
|
| 1113 | + | let testStorage48: 'test48 = &mut testArena48 in { |
|
| 1114 | + | let mut a = testResolver(testStorage48); |
|
| 1115 | + | let program = "let slice: *mut [i32] = &mut [1, 2, 3];"; |
|
| 1116 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1117 | + | try expectNoErrors(&result); |
|
| 1118 | + | } |
|
| 952 | 1119 | } |
|
| 953 | 1120 | ||
| 954 | 1121 | @test unsafe fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) { |
|
| 955 | - | let mut a = testResolver(); |
|
| 956 | - | let program = "let x: i32 = 0; let ptr: *mut i32 = &x;"; |
|
| 957 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 958 | - | let err = try expectError(&result); |
|
| 959 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 960 | - | else throw testing::TestError::Failed; |
|
| 1122 | + | let mut testArena49 = testArena(); |
|
| 1123 | + | let testStorage49: 'test49 = &mut testArena49 in { |
|
| 1124 | + | let mut a = testResolver(testStorage49); |
|
| 1125 | + | let program = "let x: i32 = 0; let ptr: *mut i32 = &x;"; |
|
| 1126 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1127 | + | let err = try expectError(&result); |
|
| 1128 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 1129 | + | else throw testing::TestError::Failed; |
|
| 1130 | + | } |
|
| 961 | 1131 | } |
|
| 962 | 1132 | ||
| 963 | 1133 | @test unsafe fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) { |
|
| 964 | - | let mut a = testResolver(); |
|
| 965 | - | let program = "static x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;"; |
|
| 966 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 967 | - | try expectNoErrors(&result); |
|
| 1134 | + | let mut testArena50 = testArena(); |
|
| 1135 | + | let testStorage50: 'test50 = &mut testArena50 in { |
|
| 1136 | + | let mut a = testResolver(testStorage50); |
|
| 1137 | + | let program = "static x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;"; |
|
| 1138 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1139 | + | try expectNoErrors(&result); |
|
| 1140 | + | } |
|
| 968 | 1141 | } |
|
| 969 | 1142 | ||
| 970 | 1143 | @test unsafe fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) { |
|
| 971 | 1144 | { |
|
| 972 | - | let mut a = testResolver(); |
|
| 973 | - | let program = "let x: i32 = 0; let ptr = &mut x;"; |
|
| 974 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 975 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1145 | + | let mut testArena51 = testArena(); |
|
| 1146 | + | let testStorage51: 'test51 = &mut testArena51 in { |
|
| 1147 | + | let mut a = testResolver(testStorage51); |
|
| 1148 | + | let program = "let x: i32 = 0; let ptr = &mut x;"; |
|
| 1149 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1150 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1151 | + | } |
|
| 976 | 1152 | } { |
|
| 977 | - | let mut a = testResolver(); |
|
| 978 | - | let program = "let mut x: i32 = 0; &mut x;"; |
|
| 979 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 980 | - | try expectNoErrors(&result); |
|
| 1153 | + | let mut testArena52 = testArena(); |
|
| 1154 | + | let testStorage52: 'test52 = &mut testArena52 in { |
|
| 1155 | + | let mut a = testResolver(testStorage52); |
|
| 1156 | + | let program = "let mut x: i32 = 0; &mut x;"; |
|
| 1157 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1158 | + | try expectNoErrors(&result); |
|
| 1159 | + | } |
|
| 981 | 1160 | } |
|
| 982 | 1161 | } |
|
| 983 | 1162 | ||
| 984 | 1163 | @test unsafe fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) { |
|
| 985 | 1164 | { |
|
| 986 | - | let mut a = testResolver(); |
|
| 987 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];"; |
|
| 988 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 989 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1165 | + | let mut testArena53 = testArena(); |
|
| 1166 | + | let testStorage53: 'test53 = &mut testArena53 in { |
|
| 1167 | + | let mut a = testResolver(testStorage53); |
|
| 1168 | + | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];"; |
|
| 1169 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1170 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1171 | + | } |
|
| 990 | 1172 | } { |
|
| 991 | - | let mut a = testResolver(); |
|
| 992 | - | let program = "let mut xs: [i32; 3] = [1, 2, 3]; &mut xs[..];"; |
|
| 993 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 994 | - | try expectNoErrors(&result); |
|
| 1173 | + | let mut testArena54 = testArena(); |
|
| 1174 | + | let testStorage54: 'test54 = &mut testArena54 in { |
|
| 1175 | + | let mut a = testResolver(testStorage54); |
|
| 1176 | + | let program = "let mut xs: [i32; 3] = [1, 2, 3]; &mut xs[..];"; |
|
| 1177 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1178 | + | try expectNoErrors(&result); |
|
| 1179 | + | } |
|
| 995 | 1180 | } |
|
| 996 | 1181 | } |
|
| 997 | 1182 | ||
| 998 | 1183 | @test unsafe fn testResolveSliceCannotAssignToArray() throws (testing::TestError) { |
|
| 999 | - | let mut a = testResolver(); |
|
| 1000 | - | let program = "let xs: *[u8] = &[1, 2]; let ys: [u8; 2] = xs;"; |
|
| 1001 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1002 | - | let err = try expectError(&result); |
|
| 1003 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 1004 | - | else throw testing::TestError::Failed; |
|
| 1184 | + | let mut testArena55 = testArena(); |
|
| 1185 | + | let testStorage55: 'test55 = &mut testArena55 in { |
|
| 1186 | + | let mut a = testResolver(testStorage55); |
|
| 1187 | + | let program = "let xs: *[u8] = &[1, 2]; let ys: [u8; 2] = xs;"; |
|
| 1188 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1189 | + | let err = try expectError(&result); |
|
| 1190 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 1191 | + | else throw testing::TestError::Failed; |
|
| 1192 | + | } |
|
| 1005 | 1193 | } |
|
| 1006 | 1194 | ||
| 1007 | 1195 | @test unsafe fn testResolveSliceSyntaxRequiresAddressOf() throws (testing::TestError) { |
|
| 1008 | - | let mut a = testResolver(); |
|
| 1009 | - | let program = "let xs: [u8; 2] = [1, 2]; xs[..];"; |
|
| 1010 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1011 | - | try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress); |
|
| 1196 | + | let mut testArena56 = testArena(); |
|
| 1197 | + | let testStorage56: 'test56 = &mut testArena56 in { |
|
| 1198 | + | let mut a = testResolver(testStorage56); |
|
| 1199 | + | let program = "let xs: [u8; 2] = [1, 2]; xs[..];"; |
|
| 1200 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1201 | + | try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress); |
|
| 1202 | + | } |
|
| 1012 | 1203 | } |
|
| 1013 | 1204 | ||
| 1014 | 1205 | @test unsafe fn testResolveSliceResliceRequiresAddressOf() throws (testing::TestError) { |
|
| 1015 | - | let mut a = testResolver(); |
|
| 1016 | - | let program = "fn f(s: *[u8]) -> *[u8] { return s[..]; }"; |
|
| 1017 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1018 | - | try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress); |
|
| 1206 | + | let mut testArena57 = testArena(); |
|
| 1207 | + | let testStorage57: 'test57 = &mut testArena57 in { |
|
| 1208 | + | let mut a = testResolver(testStorage57); |
|
| 1209 | + | let program = "fn f(s: *[u8]) -> *[u8] { return s[..]; }"; |
|
| 1210 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1211 | + | try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress); |
|
| 1212 | + | } |
|
| 1019 | 1213 | } |
|
| 1020 | 1214 | ||
| 1021 | 1215 | @test unsafe fn testResolveSliceRangeOutOfBounds() throws (testing::TestError) { |
|
| 1022 | 1216 | { |
|
| 1023 | - | let mut a = testResolver(); |
|
| 1024 | - | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[..3];"; |
|
| 1025 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1026 | - | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
|
| 1217 | + | let mut testArena58 = testArena(); |
|
| 1218 | + | let testStorage58: 'test58 = &mut testArena58 in { |
|
| 1219 | + | let mut a = testResolver(testStorage58); |
|
| 1220 | + | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[..3];"; |
|
| 1221 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1222 | + | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
|
| 1223 | + | } |
|
| 1027 | 1224 | } { |
|
| 1028 | - | let mut a = testResolver(); |
|
| 1029 | - | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[3..];"; |
|
| 1030 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1031 | - | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
|
| 1225 | + | let mut testArena59 = testArena(); |
|
| 1226 | + | let testStorage59: 'test59 = &mut testArena59 in { |
|
| 1227 | + | let mut a = testResolver(testStorage59); |
|
| 1228 | + | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[3..];"; |
|
| 1229 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1230 | + | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
|
| 1231 | + | } |
|
| 1032 | 1232 | } { |
|
| 1033 | - | let mut a = testResolver(); |
|
| 1034 | - | let program = "let xs: [u8; 4] = [1, 2, 3, 4]; let slice = &xs[3..2];"; |
|
| 1035 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1036 | - | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
|
| 1233 | + | let mut testArena60 = testArena(); |
|
| 1234 | + | let testStorage60: 'test60 = &mut testArena60 in { |
|
| 1235 | + | let mut a = testResolver(testStorage60); |
|
| 1236 | + | let program = "let xs: [u8; 4] = [1, 2, 3, 4]; let slice = &xs[3..2];"; |
|
| 1237 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1238 | + | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
|
| 1239 | + | } |
|
| 1037 | 1240 | } |
|
| 1038 | 1241 | } |
|
| 1039 | 1242 | ||
| 1040 | 1243 | @test unsafe fn testResolveArrayLenConstValue() throws (testing::TestError) { |
|
| 1041 | - | let mut a = testResolver(); |
|
| 1042 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; constant LEN: u32 = xs.len;"; |
|
| 1043 | - | let result = try resolveBlockStr(&mut a, program); |
|
| 1044 | - | try expectNoErrors(&result); |
|
| 1244 | + | let mut testArena61 = testArena(); |
|
| 1245 | + | let testStorage61: 'test61 = &mut testArena61 in { |
|
| 1246 | + | let mut a = testResolver(testStorage61); |
|
| 1247 | + | let program = "let xs: [i32; 3] = [1, 2, 3]; constant LEN: u32 = xs.len;"; |
|
| 1248 | + | let result = try resolveBlockStr(&mut a, program); |
|
| 1249 | + | try expectNoErrors(&result); |
|
| 1045 | 1250 | ||
| 1046 | - | let constStmt = try getBlockStmt(result.root, 1); |
|
| 1047 | - | let case ast::NodeValue::ConstDecl(decl) = constStmt.value |
|
| 1048 | - | else throw testing::TestError::Failed; |
|
| 1049 | - | let valueConst = super::constValueEntry(&a, decl.value) |
|
| 1050 | - | else throw testing::TestError::Failed; |
|
| 1051 | - | let case super::ConstValue::Int(lenVal) = valueConst |
|
| 1052 | - | else throw testing::TestError::Failed; |
|
| 1053 | - | try testing::expect(lenVal.magnitude == 3); |
|
| 1054 | - | try testing::expect(not lenVal.negative); |
|
| 1251 | + | let constStmt = try getBlockStmt(result.root, 1); |
|
| 1252 | + | let case ast::NodeValue::ConstDecl(decl) = constStmt.value |
|
| 1253 | + | else throw testing::TestError::Failed; |
|
| 1254 | + | let valueConst = super::constValueEntry(&a, decl.value) |
|
| 1255 | + | else throw testing::TestError::Failed; |
|
| 1256 | + | let case super::ConstValue::Int(lenVal) = valueConst |
|
| 1257 | + | else throw testing::TestError::Failed; |
|
| 1258 | + | try testing::expect(lenVal.magnitude == 3); |
|
| 1259 | + | try testing::expect(not lenVal.negative); |
|
| 1260 | + | } |
|
| 1055 | 1261 | } |
|
| 1056 | 1262 | ||
| 1057 | 1263 | @test unsafe fn testResolveIndexNonIndexable() throws (testing::TestError) { |
|
| 1058 | - | let mut a = testResolver(); |
|
| 1059 | - | let program = "let flag: bool = true; flag[0];"; |
|
| 1060 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1061 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedIndexable); |
|
| 1264 | + | let mut testArena62 = testArena(); |
|
| 1265 | + | let testStorage62: 'test62 = &mut testArena62 in { |
|
| 1266 | + | let mut a = testResolver(testStorage62); |
|
| 1267 | + | let program = "let flag: bool = true; flag[0];"; |
|
| 1268 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1269 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedIndexable); |
|
| 1270 | + | } |
|
| 1062 | 1271 | } |
|
| 1063 | 1272 | ||
| 1064 | 1273 | @test unsafe fn testResolveSliceFieldUnknown() throws (testing::TestError) { |
|
| 1065 | - | let mut a = testResolver(); |
|
| 1066 | - | let program = "let xs: [i32; 2] = [1, 2]; (&xs[0..]).unknown;"; |
|
| 1067 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1068 | - | try expectErrorKind(&result, super::ErrorKind::SliceFieldUnknown("unknown")); |
|
| 1274 | + | let mut testArena63 = testArena(); |
|
| 1275 | + | let testStorage63: 'test63 = &mut testArena63 in { |
|
| 1276 | + | let mut a = testResolver(testStorage63); |
|
| 1277 | + | let program = "let xs: [i32; 2] = [1, 2]; (&xs[0..]).unknown;"; |
|
| 1278 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1279 | + | try expectErrorKind(&result, super::ErrorKind::SliceFieldUnknown("unknown")); |
|
| 1280 | + | } |
|
| 1069 | 1281 | } |
|
| 1070 | 1282 | ||
| 1071 | 1283 | @test unsafe fn testResolveArrayFieldUnknown() throws (testing::TestError) { |
|
| 1072 | - | let mut a = testResolver(); |
|
| 1073 | - | let program = "let xs: [i32; 2] = [1, 2]; xs.field;"; |
|
| 1074 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1075 | - | try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field")); |
|
| 1284 | + | let mut testArena64 = testArena(); |
|
| 1285 | + | let testStorage64: 'test64 = &mut testArena64 in { |
|
| 1286 | + | let mut a = testResolver(testStorage64); |
|
| 1287 | + | let program = "let xs: [i32; 2] = [1, 2]; xs.field;"; |
|
| 1288 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1289 | + | try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field")); |
|
| 1290 | + | } |
|
| 1076 | 1291 | } |
|
| 1077 | 1292 | ||
| 1078 | 1293 | @test unsafe fn testResolveIfConditionRequiresBool() throws (testing::TestError) { |
|
| 1079 | 1294 | { |
|
| 1080 | - | let mut a = testResolver(); |
|
| 1081 | - | let result = try resolveProgramStr(&mut a, "if 42 {}"); |
|
| 1082 | - | let err = try expectError(&result); |
|
| 1083 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 1295 | + | let mut testArena65 = testArena(); |
|
| 1296 | + | let testStorage65: 'test65 = &mut testArena65 in { |
|
| 1297 | + | let mut a = testResolver(testStorage65); |
|
| 1298 | + | let result = try resolveProgramStr(&mut a, "if 42 {}"); |
|
| 1299 | + | let err = try expectError(&result); |
|
| 1300 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 1301 | + | } |
|
| 1084 | 1302 | } { |
|
| 1085 | - | let mut a = testResolver(); |
|
| 1086 | - | let result = try resolveProgramStr(&mut a, "if true {}"); |
|
| 1087 | - | try expectNoErrors(&result); |
|
| 1303 | + | let mut testArena66 = testArena(); |
|
| 1304 | + | let testStorage66: 'test66 = &mut testArena66 in { |
|
| 1305 | + | let mut a = testResolver(testStorage66); |
|
| 1306 | + | let result = try resolveProgramStr(&mut a, "if true {}"); |
|
| 1307 | + | try expectNoErrors(&result); |
|
| 1308 | + | } |
|
| 1088 | 1309 | } |
|
| 1089 | 1310 | } |
|
| 1090 | 1311 | ||
| 1091 | 1312 | @test unsafe fn testResolveIfLetScopeBinding() throws (testing::TestError) { |
|
| 1092 | - | let mut a = testResolver(); |
|
| 1093 | - | let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x }"); |
|
| 1094 | - | try expectNoErrors(&result); |
|
| 1313 | + | let mut testArena67 = testArena(); |
|
| 1314 | + | let testStorage67: 'test67 = &mut testArena67 in { |
|
| 1315 | + | let mut a = testResolver(testStorage67); |
|
| 1316 | + | let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x }"); |
|
| 1317 | + | try expectNoErrors(&result); |
|
| 1095 | 1318 | ||
| 1096 | - | // Get the if-let statement and verify `x` has type `i32`. |
|
| 1097 | - | let ifLetStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1098 | - | let case ast::NodeValue::IfLet(ifLet) = ifLetStmt.value |
|
| 1099 | - | else throw testing::TestError::Failed; |
|
| 1319 | + | // Get the if-let statement and verify `x` has type `i32`. |
|
| 1320 | + | let ifLetStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1321 | + | let case ast::NodeValue::IfLet(ifLet) = ifLetStmt.value |
|
| 1322 | + | else throw testing::TestError::Failed; |
|
| 1100 | 1323 | ||
| 1101 | - | let thenStmt = try parser::tests::getBlockLastStmt(ifLet.thenBranch); |
|
| 1102 | - | let case ast::NodeValue::ExprStmt(xExpr) = thenStmt.value |
|
| 1103 | - | else throw testing::TestError::Failed; |
|
| 1324 | + | let thenStmt = try parser::tests::getBlockLastStmt(ifLet.thenBranch); |
|
| 1325 | + | let case ast::NodeValue::ExprStmt(xExpr) = thenStmt.value |
|
| 1326 | + | else throw testing::TestError::Failed; |
|
| 1104 | 1327 | ||
| 1105 | - | try expectType(&a, xExpr, super::Type::I32); |
|
| 1328 | + | try expectType(&a, xExpr, super::Type::I32); |
|
| 1106 | 1329 | ||
| 1107 | - | let scope = super::scopeFor(&a, ifLetStmt) |
|
| 1108 | - | else throw testing::TestError::Failed; |
|
| 1109 | - | let xSym = super::findSymbolInScope(scope, "x") |
|
| 1110 | - | else throw testing::TestError::Failed; |
|
| 1111 | - | let case super::SymbolData::Value { type: valType, .. } = xSym.data |
|
| 1112 | - | else throw testing::TestError::Failed; |
|
| 1330 | + | let scope = super::scopeFor(&a, ifLetStmt) |
|
| 1331 | + | else throw testing::TestError::Failed; |
|
| 1332 | + | let xSym = super::findSymbolInScope(scope, "x") |
|
| 1333 | + | else throw testing::TestError::Failed; |
|
| 1334 | + | let case super::SymbolData::Value { type: valType, .. } = xSym.data |
|
| 1335 | + | else throw testing::TestError::Failed; |
|
| 1113 | 1336 | ||
| 1114 | - | try testing::expect(valType == super::Type::I32); |
|
| 1337 | + | try testing::expect(valType == super::Type::I32); |
|
| 1338 | + | } |
|
| 1115 | 1339 | } |
|
| 1116 | 1340 | ||
| 1117 | 1341 | @test unsafe fn testResolveIfLetScopeBindingError() throws (testing::TestError) { |
|
| 1118 | - | let mut a = testResolver(); |
|
| 1119 | - | let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x } else { x }"); |
|
| 1120 | - | let err = try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1121 | - | ||
| 1122 | - | // Verify the error comes from the else branch (offset 48). |
|
| 1123 | - | let errNode = err.node |
|
| 1124 | - | else throw testing::TestError::Failed; |
|
| 1125 | - | try testing::expect(errNode.span.offset == 48); |
|
| 1342 | + | let mut testArena68 = testArena(); |
|
| 1343 | + | let testStorage68: 'test68 = &mut testArena68 in { |
|
| 1344 | + | let mut a = testResolver(testStorage68); |
|
| 1345 | + | let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x } else { x }"); |
|
| 1346 | + | let err = try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1347 | + | ||
| 1348 | + | // Verify the error comes from the else branch (offset 48). |
|
| 1349 | + | let errNode = err.node |
|
| 1350 | + | else throw testing::TestError::Failed; |
|
| 1351 | + | try testing::expect(errNode.span.offset == 48); |
|
| 1352 | + | } |
|
| 1126 | 1353 | } |
|
| 1127 | 1354 | ||
| 1128 | 1355 | /// Tests that `if let` with a condition expression binds the variable in scope. |
|
| 1129 | 1356 | @test unsafe fn testResolveIfLetConditionBindsVariable() throws (testing::TestError) { |
|
| 1130 | - | let mut a = testResolver(); |
|
| 1131 | - | let program = "let opt: ?i32 = 42; if let x = opt; x == 1 { x }"; |
|
| 1132 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1133 | - | try expectNoErrors(&result); |
|
| 1357 | + | let mut testArena69 = testArena(); |
|
| 1358 | + | let testStorage69: 'test69 = &mut testArena69 in { |
|
| 1359 | + | let mut a = testResolver(testStorage69); |
|
| 1360 | + | let program = "let opt: ?i32 = 42; if let x = opt; x == 1 { x }"; |
|
| 1361 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1362 | + | try expectNoErrors(&result); |
|
| 1363 | + | } |
|
| 1134 | 1364 | } |
|
| 1135 | 1365 | ||
| 1136 | 1366 | @test unsafe fn testResolveWhileConditionRequiresBool() throws (testing::TestError) { |
|
| 1137 | 1367 | { |
|
| 1138 | - | let mut a = testResolver(); |
|
| 1139 | - | let result = try resolveProgramStr(&mut a, "while 1 {}"); |
|
| 1140 | - | let err = try expectError(&result); |
|
| 1141 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 1368 | + | let mut testArena70 = testArena(); |
|
| 1369 | + | let testStorage70: 'test70 = &mut testArena70 in { |
|
| 1370 | + | let mut a = testResolver(testStorage70); |
|
| 1371 | + | let result = try resolveProgramStr(&mut a, "while 1 {}"); |
|
| 1372 | + | let err = try expectError(&result); |
|
| 1373 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 1374 | + | } |
|
| 1142 | 1375 | } { |
|
| 1143 | - | let mut a = testResolver(); |
|
| 1144 | - | let result = try resolveProgramStr(&mut a, "while true {}"); |
|
| 1145 | - | try expectNoErrors(&result); |
|
| 1376 | + | let mut testArena71 = testArena(); |
|
| 1377 | + | let testStorage71: 'test71 = &mut testArena71 in { |
|
| 1378 | + | let mut a = testResolver(testStorage71); |
|
| 1379 | + | let result = try resolveProgramStr(&mut a, "while true {}"); |
|
| 1380 | + | try expectNoErrors(&result); |
|
| 1381 | + | } |
|
| 1146 | 1382 | } |
|
| 1147 | 1383 | } |
|
| 1148 | 1384 | ||
| 1149 | 1385 | @test unsafe fn testResolveWhileLetBindingScope() throws (testing::TestError) { |
|
| 1150 | 1386 | { |
|
| 1151 | - | let mut a = testResolver(); |
|
| 1152 | - | let program = "let mut opt: ?i32 = 42; while let x = opt; x > 0 { x; opt; }"; |
|
| 1387 | + | let mut testArena72 = testArena(); |
|
| 1388 | + | let testStorage72: 'test72 = &mut testArena72 in { |
|
| 1389 | + | let mut a = testResolver(testStorage72); |
|
| 1390 | + | let program = "let mut opt: ?i32 = 42; while let x = opt; x > 0 { x; opt; }"; |
|
| 1391 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1392 | + | try expectNoErrors(&result); |
|
| 1393 | + | ||
| 1394 | + | let whileStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1395 | + | let case ast::NodeValue::WhileLet(loopNode) = whileStmt.value |
|
| 1396 | + | else throw testing::TestError::Failed; |
|
| 1397 | + | ||
| 1398 | + | let bodyStmt = try parser::tests::getBlockFirstStmt(loopNode.body); |
|
| 1399 | + | try expectExprStmtType(&a, bodyStmt, super::Type::I32); |
|
| 1400 | + | ||
| 1401 | + | let scope = super::scopeFor(&a, whileStmt) |
|
| 1402 | + | else throw testing::TestError::Failed; |
|
| 1403 | + | let xSym = super::findSymbolInScope(scope, "x") |
|
| 1404 | + | else throw testing::TestError::Failed; |
|
| 1405 | + | let case super::SymbolData::Value { type: valType, .. } = xSym.data |
|
| 1406 | + | else throw testing::TestError::Failed; |
|
| 1407 | + | try testing::expect(valType == super::Type::I32); |
|
| 1408 | + | } |
|
| 1409 | + | } { |
|
| 1410 | + | let mut testArena73 = testArena(); |
|
| 1411 | + | let testStorage73: 'test73 = &mut testArena73 in { |
|
| 1412 | + | let mut a = testResolver(testStorage73); |
|
| 1413 | + | let program = "let opt: ?i32 = nil; while let x = opt; true { break } else { x }"; |
|
| 1414 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1415 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1416 | + | } |
|
| 1417 | + | } |
|
| 1418 | + | } |
|
| 1419 | + | ||
| 1420 | + | @test unsafe fn testResolveForArrayBindsElementType() throws (testing::TestError) { |
|
| 1421 | + | let mut testArena74 = testArena(); |
|
| 1422 | + | let testStorage74: 'test74 = &mut testArena74 in { |
|
| 1423 | + | let mut a = testResolver(testStorage74); |
|
| 1424 | + | let program = "let xs: [i32; 2] = [1, 2]; for x in xs { x; }"; |
|
| 1153 | 1425 | let result = try resolveProgramStr(&mut a, program); |
|
| 1154 | 1426 | try expectNoErrors(&result); |
|
| 1155 | 1427 | ||
| 1156 | - | let whileStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1157 | - | let case ast::NodeValue::WhileLet(loopNode) = whileStmt.value |
|
| 1428 | + | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1429 | + | let case ast::NodeValue::For(loopNode) = forStmt.value |
|
| 1158 | 1430 | else throw testing::TestError::Failed; |
|
| 1159 | 1431 | ||
| 1160 | - | let bodyStmt = try parser::tests::getBlockFirstStmt(loopNode.body); |
|
| 1161 | - | try expectExprStmtType(&a, bodyStmt, super::Type::I32); |
|
| 1162 | - | ||
| 1163 | - | let scope = super::scopeFor(&a, whileStmt) |
|
| 1432 | + | let scope = super::scopeFor(&a, forStmt) |
|
| 1164 | 1433 | else throw testing::TestError::Failed; |
|
| 1165 | - | let xSym = super::findSymbolInScope(scope, "x") |
|
| 1434 | + | let sym = super::findSymbolInScope(scope, "x") |
|
| 1166 | 1435 | else throw testing::TestError::Failed; |
|
| 1167 | - | let case super::SymbolData::Value { type: valType, .. } = xSym.data |
|
| 1436 | + | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 1168 | 1437 | else throw testing::TestError::Failed; |
|
| 1169 | 1438 | try testing::expect(valType == super::Type::I32); |
|
| 1170 | - | } { |
|
| 1171 | - | let mut a = testResolver(); |
|
| 1172 | - | let program = "let opt: ?i32 = nil; while let x = opt; true { break } else { x }"; |
|
| 1173 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1174 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1175 | - | } |
|
| 1176 | - | } |
|
| 1177 | - | ||
| 1178 | - | @test unsafe fn testResolveForArrayBindsElementType() throws (testing::TestError) { |
|
| 1179 | - | let mut a = testResolver(); |
|
| 1180 | - | let program = "let xs: [i32; 2] = [1, 2]; for x in xs { x; }"; |
|
| 1181 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1182 | - | try expectNoErrors(&result); |
|
| 1183 | - | ||
| 1184 | - | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1185 | - | let case ast::NodeValue::For(loopNode) = forStmt.value |
|
| 1186 | - | else throw testing::TestError::Failed; |
|
| 1187 | 1439 | ||
| 1188 | - | let scope = super::scopeFor(&a, forStmt) |
|
| 1189 | - | else throw testing::TestError::Failed; |
|
| 1190 | - | let sym = super::findSymbolInScope(scope, "x") |
|
| 1191 | - | else throw testing::TestError::Failed; |
|
| 1192 | - | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 1193 | - | else throw testing::TestError::Failed; |
|
| 1194 | - | try testing::expect(valType == super::Type::I32); |
|
| 1195 | - | ||
| 1196 | - | let bindingTy = super::typeFor(&a, loopNode.binding) |
|
| 1197 | - | else throw testing::TestError::Failed; |
|
| 1198 | - | try testing::expect(bindingTy == super::Type::I32); |
|
| 1440 | + | let bindingTy = super::typeFor(&a, loopNode.binding) |
|
| 1441 | + | else throw testing::TestError::Failed; |
|
| 1442 | + | try testing::expect(bindingTy == super::Type::I32); |
|
| 1443 | + | } |
|
| 1199 | 1444 | } |
|
| 1200 | 1445 | ||
| 1201 | 1446 | @test unsafe fn testResolveForIndexedLoopBindsIndex() throws (testing::TestError) { |
|
| 1202 | - | let mut a = testResolver(); |
|
| 1203 | - | let program = "let xs: [bool; 3] = [true; 3]; for value, idx in xs { value; idx; }"; |
|
| 1204 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1205 | - | try expectNoErrors(&result); |
|
| 1447 | + | let mut testArena75 = testArena(); |
|
| 1448 | + | let testStorage75: 'test75 = &mut testArena75 in { |
|
| 1449 | + | let mut a = testResolver(testStorage75); |
|
| 1450 | + | let program = "let xs: [bool; 3] = [true; 3]; for value, idx in xs { value; idx; }"; |
|
| 1451 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1452 | + | try expectNoErrors(&result); |
|
| 1206 | 1453 | ||
| 1207 | - | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1208 | - | let case ast::NodeValue::For(loopNode) = forStmt.value |
|
| 1209 | - | else throw testing::TestError::Failed; |
|
| 1454 | + | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1455 | + | let case ast::NodeValue::For(loopNode) = forStmt.value |
|
| 1456 | + | else throw testing::TestError::Failed; |
|
| 1210 | 1457 | ||
| 1211 | - | let scope = super::scopeFor(&a, forStmt) |
|
| 1212 | - | else throw testing::TestError::Failed; |
|
| 1213 | - | let valueSym = super::findSymbolInScope(scope, "value") |
|
| 1214 | - | else throw testing::TestError::Failed; |
|
| 1215 | - | let case super::SymbolData::Value { type: valueValType, .. } = valueSym.data |
|
| 1216 | - | else throw testing::TestError::Failed; |
|
| 1217 | - | try testing::expect(valueValType == super::Type::Bool); |
|
| 1218 | - | let indexSym = super::findSymbolInScope(scope, "idx") |
|
| 1219 | - | else throw testing::TestError::Failed; |
|
| 1220 | - | let case super::SymbolData::Value { type: indexValType, .. } = indexSym.data |
|
| 1221 | - | else throw testing::TestError::Failed; |
|
| 1222 | - | try testing::expect(indexValType == super::Type::U32); |
|
| 1458 | + | let scope = super::scopeFor(&a, forStmt) |
|
| 1459 | + | else throw testing::TestError::Failed; |
|
| 1460 | + | let valueSym = super::findSymbolInScope(scope, "value") |
|
| 1461 | + | else throw testing::TestError::Failed; |
|
| 1462 | + | let case super::SymbolData::Value { type: valueValType, .. } = valueSym.data |
|
| 1463 | + | else throw testing::TestError::Failed; |
|
| 1464 | + | try testing::expect(valueValType == super::Type::Bool); |
|
| 1465 | + | let indexSym = super::findSymbolInScope(scope, "idx") |
|
| 1466 | + | else throw testing::TestError::Failed; |
|
| 1467 | + | let case super::SymbolData::Value { type: indexValType, .. } = indexSym.data |
|
| 1468 | + | else throw testing::TestError::Failed; |
|
| 1469 | + | try testing::expect(indexValType == super::Type::U32); |
|
| 1223 | 1470 | ||
| 1224 | - | let indexNode = loopNode.index |
|
| 1225 | - | else throw testing::TestError::Failed; |
|
| 1226 | - | let indexTy = super::typeFor(&a, indexNode) |
|
| 1227 | - | else throw testing::TestError::Failed; |
|
| 1228 | - | try testing::expect(indexTy == super::Type::U32); |
|
| 1471 | + | let indexNode = loopNode.index |
|
| 1472 | + | else throw testing::TestError::Failed; |
|
| 1473 | + | let indexTy = super::typeFor(&a, indexNode) |
|
| 1474 | + | else throw testing::TestError::Failed; |
|
| 1475 | + | try testing::expect(indexTy == super::Type::U32); |
|
| 1476 | + | } |
|
| 1229 | 1477 | } |
|
| 1230 | 1478 | ||
| 1231 | 1479 | @test unsafe fn testResolveForSliceIterable() throws (testing::TestError) { |
|
| 1232 | - | let mut a = testResolver(); |
|
| 1233 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; for x in &xs[..] { x; }"; |
|
| 1234 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1235 | - | try expectNoErrors(&result); |
|
| 1480 | + | let mut testArena76 = testArena(); |
|
| 1481 | + | let testStorage76: 'test76 = &mut testArena76 in { |
|
| 1482 | + | let mut a = testResolver(testStorage76); |
|
| 1483 | + | let program = "let xs: [i32; 3] = [1, 2, 3]; for x in &xs[..] { x; }"; |
|
| 1484 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1485 | + | try expectNoErrors(&result); |
|
| 1236 | 1486 | ||
| 1237 | - | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1238 | - | let case ast::NodeValue::For(loopNode) = forStmt.value |
|
| 1239 | - | else throw testing::TestError::Failed; |
|
| 1487 | + | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 1488 | + | let case ast::NodeValue::For(loopNode) = forStmt.value |
|
| 1489 | + | else throw testing::TestError::Failed; |
|
| 1240 | 1490 | ||
| 1241 | - | let bindingTy = super::typeFor(&a, loopNode.binding) |
|
| 1242 | - | else throw testing::TestError::Failed; |
|
| 1243 | - | try testing::expect(bindingTy == super::Type::I32); |
|
| 1491 | + | let bindingTy = super::typeFor(&a, loopNode.binding) |
|
| 1492 | + | else throw testing::TestError::Failed; |
|
| 1493 | + | try testing::expect(bindingTy == super::Type::I32); |
|
| 1494 | + | } |
|
| 1244 | 1495 | } |
|
| 1245 | 1496 | ||
| 1246 | 1497 | @test unsafe fn testResolveForRequiresIterable() throws (testing::TestError) { |
|
| 1247 | - | let mut a = testResolver(); |
|
| 1248 | - | let result = try resolveProgramStr(&mut a, "for x in true { x; }"); |
|
| 1249 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedIterable); |
|
| 1498 | + | let mut testArena77 = testArena(); |
|
| 1499 | + | let testStorage77: 'test77 = &mut testArena77 in { |
|
| 1500 | + | let mut a = testResolver(testStorage77); |
|
| 1501 | + | let result = try resolveProgramStr(&mut a, "for x in true { x; }"); |
|
| 1502 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedIterable); |
|
| 1503 | + | } |
|
| 1250 | 1504 | } |
|
| 1251 | 1505 | ||
| 1252 | 1506 | @test unsafe fn testResolveForRangeBoundsMustNumeric() throws (testing::TestError) { |
|
| 1253 | - | let mut a = testResolver(); |
|
| 1254 | - | let result = try resolveBlockStr(&mut a, "for i in 0..true { i; }"); |
|
| 1255 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 1507 | + | let mut testArena78 = testArena(); |
|
| 1508 | + | let testStorage78: 'test78 = &mut testArena78 in { |
|
| 1509 | + | let mut a = testResolver(testStorage78); |
|
| 1510 | + | let result = try resolveBlockStr(&mut a, "for i in 0..true { i; }"); |
|
| 1511 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 1512 | + | } |
|
| 1256 | 1513 | } |
|
| 1257 | 1514 | ||
| 1258 | 1515 | @test unsafe fn testResolveMatchPatternTypeMismatch() throws (testing::TestError) { |
|
| 1259 | - | let mut a = testResolver(); |
|
| 1260 | - | let program = "let val: i32 = 0; match val { case true => {} }"; |
|
| 1261 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1262 | - | let err = try expectError(&result); |
|
| 1263 | - | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 1516 | + | let mut testArena79 = testArena(); |
|
| 1517 | + | let testStorage79: 'test79 = &mut testArena79 in { |
|
| 1518 | + | let mut a = testResolver(testStorage79); |
|
| 1519 | + | let program = "let val: i32 = 0; match val { case true => {} }"; |
|
| 1520 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1521 | + | let err = try expectError(&result); |
|
| 1522 | + | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 1523 | + | } |
|
| 1264 | 1524 | } |
|
| 1265 | 1525 | ||
| 1266 | 1526 | @test unsafe fn testResolveMatchUnionVariantTypeMismatch() throws (testing::TestError) { |
|
| 1267 | - | let mut a = testResolver(); |
|
| 1268 | - | let program = "union First { A } union Second { B } fn run(val: First) { match val { case Second::B => {} } }"; |
|
| 1269 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1270 | - | let err = try expectError(&result); |
|
| 1527 | + | let mut testArena80 = testArena(); |
|
| 1528 | + | let testStorage80: 'test80 = &mut testArena80 in { |
|
| 1529 | + | let mut a = testResolver(testStorage80); |
|
| 1530 | + | let program = "union First { A } union Second { B } fn run(val: First) { match val { case Second::B => {} } }"; |
|
| 1531 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1532 | + | let err = try expectError(&result); |
|
| 1271 | 1533 | ||
| 1272 | - | let firstTy = try getTypeInScopeOf(&a, result.root, "First"); |
|
| 1273 | - | let secondTy = try getTypeInScopeOf(&a, result.root, "Second"); |
|
| 1274 | - | try expectTypeMismatch(err, super::Type::Nominal(firstTy), super::Type::Nominal(secondTy)); |
|
| 1534 | + | let firstTy = try getTypeInScopeOf(&a, result.root, "First"); |
|
| 1535 | + | let secondTy = try getTypeInScopeOf(&a, result.root, "Second"); |
|
| 1536 | + | try expectTypeMismatch(err, super::Type::Nominal(firstTy), super::Type::Nominal(secondTy)); |
|
| 1537 | + | } |
|
| 1275 | 1538 | } |
|
| 1276 | 1539 | ||
| 1277 | 1540 | @test unsafe fn testResolveMatchUnionPayloadMissing() throws (testing::TestError) { |
|
| 1278 | - | let mut a = testResolver(); |
|
| 1279 | - | let program = "union Opt { Some(i32) } fn run(val: Opt) { match val { case Opt::Some => {} } }"; |
|
| 1280 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1281 | - | try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadMissing("Some")); |
|
| 1541 | + | let mut testArena81 = testArena(); |
|
| 1542 | + | let testStorage81: 'test81 = &mut testArena81 in { |
|
| 1543 | + | let mut a = testResolver(testStorage81); |
|
| 1544 | + | let program = "union Opt { Some(i32) } fn run(val: Opt) { match val { case Opt::Some => {} } }"; |
|
| 1545 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1546 | + | try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadMissing("Some")); |
|
| 1547 | + | } |
|
| 1282 | 1548 | } |
|
| 1283 | 1549 | ||
| 1284 | 1550 | @test unsafe fn testResolveMatchUnionVoidVariantExplicitDiscriminant() throws (testing::TestError) { |
|
| 1285 | - | let mut a = testResolver(); |
|
| 1286 | - | let program = "union Opt { Some = 5 } fn run(val: Opt) { match val { case Opt::Some => {} } }"; |
|
| 1287 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1288 | - | try expectNoErrors(&result); |
|
| 1551 | + | let mut testArena82 = testArena(); |
|
| 1552 | + | let testStorage82: 'test82 = &mut testArena82 in { |
|
| 1553 | + | let mut a = testResolver(testStorage82); |
|
| 1554 | + | let program = "union Opt { Some = 5 } fn run(val: Opt) { match val { case Opt::Some => {} } }"; |
|
| 1555 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1556 | + | try expectNoErrors(&result); |
|
| 1557 | + | } |
|
| 1289 | 1558 | } |
|
| 1290 | 1559 | ||
| 1291 | 1560 | @test unsafe fn testResolveMatchUnionPayloadUnexpected() throws (testing::TestError) { |
|
| 1292 | - | let mut a = testResolver(); |
|
| 1293 | - | let program = "union Opt { None } fn run(val: Opt) { match val { case Opt::None(x) => {} } }"; |
|
| 1294 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1295 | - | try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadUnexpected("None")); |
|
| 1561 | + | let mut testArena83 = testArena(); |
|
| 1562 | + | let testStorage83: 'test83 = &mut testArena83 in { |
|
| 1563 | + | let mut a = testResolver(testStorage83); |
|
| 1564 | + | let program = "union Opt { None } fn run(val: Opt) { match val { case Opt::None(x) => {} } }"; |
|
| 1565 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1566 | + | try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadUnexpected("None")); |
|
| 1567 | + | } |
|
| 1296 | 1568 | } |
|
| 1297 | 1569 | ||
| 1298 | 1570 | @test unsafe fn testResolveMatchUnionUnknownVariant() throws (testing::TestError) { |
|
| 1299 | - | let mut a = testResolver(); |
|
| 1300 | - | let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Unknown => {} } }"; |
|
| 1301 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1302 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 1571 | + | let mut testArena84 = testArena(); |
|
| 1572 | + | let testStorage84: 'test84 = &mut testArena84 in { |
|
| 1573 | + | let mut a = testResolver(testStorage84); |
|
| 1574 | + | let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Unknown => {} } }"; |
|
| 1575 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1576 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 1577 | + | } |
|
| 1303 | 1578 | } |
|
| 1304 | 1579 | ||
| 1305 | 1580 | @test unsafe fn testResolveMatchUnionNonExhaustive() throws (testing::TestError) { |
|
| 1306 | 1581 | { |
|
| 1307 | - | let mut a = testResolver(); |
|
| 1308 | - | let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Some => {} } }"; |
|
| 1309 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1310 | - | try expectErrorKind(&result, super::ErrorKind::UnionMatchNonExhaustive("None")); |
|
| 1582 | + | let mut testArena85 = testArena(); |
|
| 1583 | + | let testStorage85: 'test85 = &mut testArena85 in { |
|
| 1584 | + | let mut a = testResolver(testStorage85); |
|
| 1585 | + | let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Some => {} } }"; |
|
| 1586 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1587 | + | try expectErrorKind(&result, super::ErrorKind::UnionMatchNonExhaustive("None")); |
|
| 1588 | + | } |
|
| 1311 | 1589 | } { |
|
| 1312 | - | let mut a = testResolver(); |
|
| 1313 | - | let program = "union Opt { Some, None } fn run(value: Opt) { match value { else => {} } }"; |
|
| 1314 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1315 | - | try expectNoErrors(&result); |
|
| 1590 | + | let mut testArena86 = testArena(); |
|
| 1591 | + | let testStorage86: 'test86 = &mut testArena86 in { |
|
| 1592 | + | let mut a = testResolver(testStorage86); |
|
| 1593 | + | let program = "union Opt { Some, None } fn run(value: Opt) { match value { else => {} } }"; |
|
| 1594 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1595 | + | try expectNoErrors(&result); |
|
| 1596 | + | } |
|
| 1316 | 1597 | } |
|
| 1317 | 1598 | } |
|
| 1318 | 1599 | ||
| 1319 | 1600 | @test unsafe fn testResolveMatchUnionNonExhaustiveExplicitDiscriminants() throws (testing::TestError) { |
|
| 1320 | - | let mut a = testResolver(); |
|
| 1321 | - | let program = "union U { A = 3, B = 9 } fn run(value: U) { match value { case U::A => {}, case U::B => {} } }"; |
|
| 1322 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1323 | - | try expectNoErrors(&result); |
|
| 1601 | + | let mut testArena87 = testArena(); |
|
| 1602 | + | let testStorage87: 'test87 = &mut testArena87 in { |
|
| 1603 | + | let mut a = testResolver(testStorage87); |
|
| 1604 | + | let program = "union U { A = 3, B = 9 } fn run(value: U) { match value { case U::A => {}, case U::B => {} } }"; |
|
| 1605 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1606 | + | try expectNoErrors(&result); |
|
| 1607 | + | } |
|
| 1324 | 1608 | } |
|
| 1325 | 1609 | ||
| 1326 | 1610 | @test unsafe fn testResolveMatchUnionBindingScope() throws (testing::TestError) { |
|
| 1327 | - | let mut a = testResolver(); |
|
| 1328 | - | let program = "union Opt { Some(i32), None } fn f(value: Opt) { match value { case Opt::Some(x) if x > 0 => { x; } else => {} } }"; |
|
| 1329 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1330 | - | try expectNoErrors(&result); |
|
| 1611 | + | let mut testArena88 = testArena(); |
|
| 1612 | + | let testStorage88: 'test88 = &mut testArena88 in { |
|
| 1613 | + | let mut a = testResolver(testStorage88); |
|
| 1614 | + | let program = "union Opt { Some(i32), None } fn f(value: Opt) { match value { case Opt::Some(x) if x > 0 => { x; } else => {} } }"; |
|
| 1615 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1616 | + | try expectNoErrors(&result); |
|
| 1331 | 1617 | ||
| 1332 | - | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 1333 | - | try testing::expect(fnBlock.statements.len > 0); |
|
| 1618 | + | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 1619 | + | try testing::expect(fnBlock.statements.len > 0); |
|
| 1334 | 1620 | ||
| 1335 | - | let matchNode = fnBlock.statements[0]; |
|
| 1336 | - | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 1337 | - | else throw testing::TestError::Failed; |
|
| 1338 | - | let caseNode = sw.prongs[0]; |
|
| 1621 | + | let matchNode = fnBlock.statements[0]; |
|
| 1622 | + | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 1623 | + | else throw testing::TestError::Failed; |
|
| 1624 | + | let caseNode = sw.prongs[0]; |
|
| 1339 | 1625 | ||
| 1340 | - | let scope = super::scopeFor(&a, caseNode) |
|
| 1341 | - | else throw testing::TestError::Failed; |
|
| 1342 | - | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 1343 | - | else throw testing::TestError::Failed; |
|
| 1344 | - | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 1345 | - | else throw testing::TestError::Failed; |
|
| 1346 | - | try testing::expect(payloadValType == super::Type::I32); |
|
| 1626 | + | let scope = super::scopeFor(&a, caseNode) |
|
| 1627 | + | else throw testing::TestError::Failed; |
|
| 1628 | + | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 1629 | + | else throw testing::TestError::Failed; |
|
| 1630 | + | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 1631 | + | else throw testing::TestError::Failed; |
|
| 1632 | + | try testing::expect(payloadValType == super::Type::I32); |
|
| 1633 | + | } |
|
| 1347 | 1634 | } |
|
| 1348 | 1635 | ||
| 1349 | 1636 | @test unsafe fn testResolveMatchUnionPatternNonUnionType() throws (testing::TestError) { |
|
| 1350 | - | let mut a = testResolver(); |
|
| 1351 | - | let program = "union Opt { Some, None } fn f(value: Opt) { match value { case true => {} } }"; |
|
| 1352 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1353 | - | let err = try expectError(&result); |
|
| 1354 | - | let optionTy = try getTypeInScopeOf(&a, result.root, "Opt"); |
|
| 1355 | - | try expectTypeMismatch(err, super::Type::Nominal(optionTy), super::Type::Bool); |
|
| 1637 | + | let mut testArena89 = testArena(); |
|
| 1638 | + | let testStorage89: 'test89 = &mut testArena89 in { |
|
| 1639 | + | let mut a = testResolver(testStorage89); |
|
| 1640 | + | let program = "union Opt { Some, None } fn f(value: Opt) { match value { case true => {} } }"; |
|
| 1641 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1642 | + | let err = try expectError(&result); |
|
| 1643 | + | let optionTy = try getTypeInScopeOf(&a, result.root, "Opt"); |
|
| 1644 | + | try expectTypeMismatch(err, super::Type::Nominal(optionTy), super::Type::Bool); |
|
| 1645 | + | } |
|
| 1356 | 1646 | } |
|
| 1357 | 1647 | ||
| 1358 | 1648 | @test unsafe fn testResolveMatchGuardForms() throws (testing::TestError) { |
|
| 1359 | - | let mut a = testResolver(); |
|
| 1360 | - | let program = "fn first(value: i32) { match value { case _ if true => {}, else => {} } }"; |
|
| 1361 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1362 | - | try expectNoErrors(&result); |
|
| 1649 | + | let mut testArena90 = testArena(); |
|
| 1650 | + | let testStorage90: 'test90 = &mut testArena90 in { |
|
| 1651 | + | let mut a = testResolver(testStorage90); |
|
| 1652 | + | let program = "fn first(value: i32) { match value { case _ if true => {}, else => {} } }"; |
|
| 1653 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1654 | + | try expectNoErrors(&result); |
|
| 1655 | + | } |
|
| 1363 | 1656 | } |
|
| 1364 | 1657 | ||
| 1365 | 1658 | /// Test that a binding prong binds the subject to the identifier. |
|
| 1366 | 1659 | @test unsafe fn testResolveMatchBindingProng() throws (testing::TestError) { |
|
| 1367 | - | let mut a = testResolver(); |
|
| 1368 | - | let program = "fn f(value: i32) -> i32 { match value { x => return x } }"; |
|
| 1369 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1370 | - | try expectNoErrors(&result); |
|
| 1660 | + | let mut testArena91 = testArena(); |
|
| 1661 | + | let testStorage91: 'test91 = &mut testArena91 in { |
|
| 1662 | + | let mut a = testResolver(testStorage91); |
|
| 1663 | + | let program = "fn f(value: i32) -> i32 { match value { x => return x } }"; |
|
| 1664 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1665 | + | try expectNoErrors(&result); |
|
| 1666 | + | } |
|
| 1371 | 1667 | } |
|
| 1372 | 1668 | ||
| 1373 | 1669 | /// Test that a binding prong with guard can use the bound variable. |
|
| 1374 | 1670 | @test unsafe fn testResolveMatchBindingProngGuard() throws (testing::TestError) { |
|
| 1375 | - | let mut a = testResolver(); |
|
| 1376 | - | let program = "fn f(value: i32) -> i32 { match value { x if x > 0 => return x, _ => return 0 } }"; |
|
| 1377 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1378 | - | try expectNoErrors(&result); |
|
| 1671 | + | let mut testArena92 = testArena(); |
|
| 1672 | + | let testStorage92: 'test92 = &mut testArena92 in { |
|
| 1673 | + | let mut a = testResolver(testStorage92); |
|
| 1674 | + | let program = "fn f(value: i32) -> i32 { match value { x if x > 0 => return x, _ => return 0 } }"; |
|
| 1675 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1676 | + | try expectNoErrors(&result); |
|
| 1677 | + | } |
|
| 1379 | 1678 | } |
|
| 1380 | 1679 | ||
| 1381 | 1680 | /// Test that a binding prong covers all union variants for exhaustiveness. |
|
| 1382 | 1681 | @test unsafe fn testResolveMatchBindingProngExhaustive() throws (testing::TestError) { |
|
| 1383 | - | let mut a = testResolver(); |
|
| 1384 | - | let program = "union U { A, B, C } fn f(u: U) -> i32 { match u { x => return 0 } }"; |
|
| 1385 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1386 | - | try expectNoErrors(&result); |
|
| 1682 | + | let mut testArena93 = testArena(); |
|
| 1683 | + | let testStorage93: 'test93 = &mut testArena93 in { |
|
| 1684 | + | let mut a = testResolver(testStorage93); |
|
| 1685 | + | let program = "union U { A, B, C } fn f(u: U) -> i32 { match u { x => return 0 } }"; |
|
| 1686 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1687 | + | try expectNoErrors(&result); |
|
| 1688 | + | } |
|
| 1387 | 1689 | } |
|
| 1388 | 1690 | ||
| 1389 | 1691 | /// Test that `case x =>` fails if `x` is not in scope, since bare identifiers |
|
| 1390 | 1692 | /// in case patterns are values to compare against, not bindings. |
|
| 1391 | 1693 | @test unsafe fn testResolveMatchCaseUndefinedIdent() throws (testing::TestError) { |
|
| 1392 | - | let mut a = testResolver(); |
|
| 1393 | - | let program = "fn f(n: i32) -> i32 { match n { case x => return 0 } }"; |
|
| 1394 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1395 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1694 | + | let mut testArena94 = testArena(); |
|
| 1695 | + | let testStorage94: 'test94 = &mut testArena94 in { |
|
| 1696 | + | let mut a = testResolver(testStorage94); |
|
| 1697 | + | let program = "fn f(n: i32) -> i32 { match n { case x => return 0 } }"; |
|
| 1698 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1699 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1700 | + | } |
|
| 1396 | 1701 | } |
|
| 1397 | 1702 | ||
| 1398 | 1703 | /// Test matching on optionals: exhaustiveness and type unwrapping. |
|
| 1399 | 1704 | @test unsafe fn testResolveMatchOptional() throws (testing::TestError) { |
|
| 1400 | 1705 | { |
|
| 1401 | 1706 | // Exhaustive: binding + nil case. |
|
| 1402 | - | let mut a = testResolver(); |
|
| 1403 | - | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {} } }"; |
|
| 1404 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1405 | - | try expectNoErrors(&result); |
|
| 1707 | + | let mut testArena95 = testArena(); |
|
| 1708 | + | let testStorage95: 'test95 = &mut testArena95 in { |
|
| 1709 | + | let mut a = testResolver(testStorage95); |
|
| 1710 | + | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {} } }"; |
|
| 1711 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1712 | + | try expectNoErrors(&result); |
|
| 1713 | + | } |
|
| 1406 | 1714 | } { |
|
| 1407 | 1715 | // Missing nil case. |
|
| 1408 | - | let mut a = testResolver(); |
|
| 1409 | - | let program = "fn f(opt: ?i32) { match opt { v => {} } }"; |
|
| 1410 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1411 | - | try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingNil); |
|
| 1716 | + | let mut testArena96 = testArena(); |
|
| 1717 | + | let testStorage96: 'test96 = &mut testArena96 in { |
|
| 1718 | + | let mut a = testResolver(testStorage96); |
|
| 1719 | + | let program = "fn f(opt: ?i32) { match opt { v => {} } }"; |
|
| 1720 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1721 | + | try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingNil); |
|
| 1722 | + | } |
|
| 1412 | 1723 | } { |
|
| 1413 | 1724 | // Missing value case. |
|
| 1414 | - | let mut a = testResolver(); |
|
| 1415 | - | let program = "fn f(opt: ?i32) { match opt { case nil => {} } }"; |
|
| 1416 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1417 | - | try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingValue); |
|
| 1725 | + | let mut testArena97 = testArena(); |
|
| 1726 | + | let testStorage97: 'test97 = &mut testArena97 in { |
|
| 1727 | + | let mut a = testResolver(testStorage97); |
|
| 1728 | + | let program = "fn f(opt: ?i32) { match opt { case nil => {} } }"; |
|
| 1729 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1730 | + | try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingValue); |
|
| 1731 | + | } |
|
| 1418 | 1732 | } { |
|
| 1419 | 1733 | // Else covers both cases. |
|
| 1420 | - | let mut a = testResolver(); |
|
| 1421 | - | let program = "fn f(opt: ?i32) { match opt { else => {} } }"; |
|
| 1422 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1423 | - | try expectNoErrors(&result); |
|
| 1734 | + | let mut testArena98 = testArena(); |
|
| 1735 | + | let testStorage98: 'test98 = &mut testArena98 in { |
|
| 1736 | + | let mut a = testResolver(testStorage98); |
|
| 1737 | + | let program = "fn f(opt: ?i32) { match opt { else => {} } }"; |
|
| 1738 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1739 | + | try expectNoErrors(&result); |
|
| 1740 | + | } |
|
| 1424 | 1741 | } { |
|
| 1425 | 1742 | // Binding unwraps the inner type. |
|
| 1426 | - | let mut a = testResolver(); |
|
| 1427 | - | let program = "fn f(opt: ?i32) -> i32 { match opt { v => return v + 1, case nil => return 0 } }"; |
|
| 1428 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1429 | - | try expectNoErrors(&result); |
|
| 1743 | + | let mut testArena99 = testArena(); |
|
| 1744 | + | let testStorage99: 'test99 = &mut testArena99 in { |
|
| 1745 | + | let mut a = testResolver(testStorage99); |
|
| 1746 | + | let program = "fn f(opt: ?i32) -> i32 { match opt { v => return v + 1, case nil => return 0 } }"; |
|
| 1747 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1748 | + | try expectNoErrors(&result); |
|
| 1749 | + | } |
|
| 1430 | 1750 | } |
|
| 1431 | 1751 | } |
|
| 1432 | 1752 | ||
| 1433 | 1753 | /// Test that match on non-union types requires exhaustiveness. |
|
| 1434 | 1754 | @test unsafe fn testResolveMatchGenericExhaustive() throws (testing::TestError) { |
|
| 1435 | 1755 | { |
|
| 1436 | 1756 | // Match on i32 without catch-all should error. |
|
| 1437 | - | let mut a = testResolver(); |
|
| 1438 | - | let program = "fn f(x: i32) { match x { case 1 => {} } }"; |
|
| 1439 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1440 | - | try expectErrorKind(&result, super::ErrorKind::MatchNonExhaustive); |
|
| 1757 | + | let mut testArena100 = testArena(); |
|
| 1758 | + | let testStorage100: 'test100 = &mut testArena100 in { |
|
| 1759 | + | let mut a = testResolver(testStorage100); |
|
| 1760 | + | let program = "fn f(x: i32) { match x { case 1 => {} } }"; |
|
| 1761 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1762 | + | try expectErrorKind(&result, super::ErrorKind::MatchNonExhaustive); |
|
| 1763 | + | } |
|
| 1441 | 1764 | } { |
|
| 1442 | 1765 | // Match on i32 with else is fine. |
|
| 1443 | - | let mut a = testResolver(); |
|
| 1444 | - | let program = "fn f(x: i32) { match x { case 1 => {}, else => {} } }"; |
|
| 1445 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1446 | - | try expectNoErrors(&result); |
|
| 1766 | + | let mut testArena101 = testArena(); |
|
| 1767 | + | let testStorage101: 'test101 = &mut testArena101 in { |
|
| 1768 | + | let mut a = testResolver(testStorage101); |
|
| 1769 | + | let program = "fn f(x: i32) { match x { case 1 => {}, else => {} } }"; |
|
| 1770 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1771 | + | try expectNoErrors(&result); |
|
| 1772 | + | } |
|
| 1447 | 1773 | } { |
|
| 1448 | 1774 | // Match on i32 with binding catch-all is fine. |
|
| 1449 | - | let mut a = testResolver(); |
|
| 1450 | - | let program = "fn f(x: i32) { match x { y => {} } }"; |
|
| 1451 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1452 | - | try expectNoErrors(&result); |
|
| 1775 | + | let mut testArena102 = testArena(); |
|
| 1776 | + | let testStorage102: 'test102 = &mut testArena102 in { |
|
| 1777 | + | let mut a = testResolver(testStorage102); |
|
| 1778 | + | let program = "fn f(x: i32) { match x { y => {} } }"; |
|
| 1779 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1780 | + | try expectNoErrors(&result); |
|
| 1781 | + | } |
|
| 1453 | 1782 | } { |
|
| 1454 | 1783 | // Match on i32 with wildcard catch-all is fine. |
|
| 1455 | - | let mut a = testResolver(); |
|
| 1456 | - | let program = "fn f(x: i32) { match x { case _ => {} } }"; |
|
| 1457 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1458 | - | try expectNoErrors(&result); |
|
| 1784 | + | let mut testArena103 = testArena(); |
|
| 1785 | + | let testStorage103: 'test103 = &mut testArena103 in { |
|
| 1786 | + | let mut a = testResolver(testStorage103); |
|
| 1787 | + | let program = "fn f(x: i32) { match x { case _ => {} } }"; |
|
| 1788 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1789 | + | try expectNoErrors(&result); |
|
| 1790 | + | } |
|
| 1459 | 1791 | } |
|
| 1460 | 1792 | } |
|
| 1461 | 1793 | ||
| 1462 | 1794 | /// Test that match on bool requires both true and false cases. |
|
| 1463 | 1795 | @test unsafe fn testResolveMatchBoolExhaustive() throws (testing::TestError) { |
|
| 1464 | 1796 | { |
|
| 1465 | 1797 | // Match on bool with both cases is fine. |
|
| 1466 | - | let mut a = testResolver(); |
|
| 1467 | - | let program = "fn f(x: bool) { match x { case true => {}, case false => {} } }"; |
|
| 1468 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1469 | - | try expectNoErrors(&result); |
|
| 1798 | + | let mut testArena104 = testArena(); |
|
| 1799 | + | let testStorage104: 'test104 = &mut testArena104 in { |
|
| 1800 | + | let mut a = testResolver(testStorage104); |
|
| 1801 | + | let program = "fn f(x: bool) { match x { case true => {}, case false => {} } }"; |
|
| 1802 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1803 | + | try expectNoErrors(&result); |
|
| 1804 | + | } |
|
| 1470 | 1805 | } { |
|
| 1471 | 1806 | // Match on bool missing true should error. |
|
| 1472 | - | let mut a = testResolver(); |
|
| 1473 | - | let program = "fn f(x: bool) { match x { case false => {} } }"; |
|
| 1474 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1475 | - | try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(true)); |
|
| 1807 | + | let mut testArena105 = testArena(); |
|
| 1808 | + | let testStorage105: 'test105 = &mut testArena105 in { |
|
| 1809 | + | let mut a = testResolver(testStorage105); |
|
| 1810 | + | let program = "fn f(x: bool) { match x { case false => {} } }"; |
|
| 1811 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1812 | + | try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(true)); |
|
| 1813 | + | } |
|
| 1476 | 1814 | } { |
|
| 1477 | 1815 | // Match on bool missing false should error. |
|
| 1478 | - | let mut a = testResolver(); |
|
| 1479 | - | let program = "fn f(x: bool) { match x { case true => {} } }"; |
|
| 1480 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1481 | - | try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(false)); |
|
| 1816 | + | let mut testArena106 = testArena(); |
|
| 1817 | + | let testStorage106: 'test106 = &mut testArena106 in { |
|
| 1818 | + | let mut a = testResolver(testStorage106); |
|
| 1819 | + | let program = "fn f(x: bool) { match x { case true => {} } }"; |
|
| 1820 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1821 | + | try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(false)); |
|
| 1822 | + | } |
|
| 1482 | 1823 | } { |
|
| 1483 | 1824 | // Match on bool with else is fine. |
|
| 1484 | - | let mut a = testResolver(); |
|
| 1485 | - | let program = "fn f(x: bool) { match x { else => {} } }"; |
|
| 1486 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1487 | - | try expectNoErrors(&result); |
|
| 1825 | + | let mut testArena107 = testArena(); |
|
| 1826 | + | let testStorage107: 'test107 = &mut testArena107 in { |
|
| 1827 | + | let mut a = testResolver(testStorage107); |
|
| 1828 | + | let program = "fn f(x: bool) { match x { else => {} } }"; |
|
| 1829 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1830 | + | try expectNoErrors(&result); |
|
| 1831 | + | } |
|
| 1488 | 1832 | } { |
|
| 1489 | 1833 | // Match on bool with binding catch-all is fine. |
|
| 1490 | - | let mut a = testResolver(); |
|
| 1491 | - | let program = "fn f(x: bool) { match x { b => {} } }"; |
|
| 1492 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1493 | - | try expectNoErrors(&result); |
|
| 1834 | + | let mut testArena108 = testArena(); |
|
| 1835 | + | let testStorage108: 'test108 = &mut testArena108 in { |
|
| 1836 | + | let mut a = testResolver(testStorage108); |
|
| 1837 | + | let program = "fn f(x: bool) { match x { b => {} } }"; |
|
| 1838 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1839 | + | try expectNoErrors(&result); |
|
| 1840 | + | } |
|
| 1494 | 1841 | } |
|
| 1495 | 1842 | } |
|
| 1496 | 1843 | ||
| 1497 | 1844 | @test unsafe fn testResolveBreakRequiresLoop() throws (testing::TestError) { |
|
| 1498 | 1845 | { |
|
| 1499 | - | let mut a = testResolver(); |
|
| 1500 | - | let result = try resolveProgramStr(&mut a, "break;"); |
|
| 1501 | - | try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl); |
|
| 1846 | + | let mut testArena109 = testArena(); |
|
| 1847 | + | let testStorage109: 'test109 = &mut testArena109 in { |
|
| 1848 | + | let mut a = testResolver(testStorage109); |
|
| 1849 | + | let result = try resolveProgramStr(&mut a, "break;"); |
|
| 1850 | + | try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl); |
|
| 1851 | + | } |
|
| 1502 | 1852 | } { |
|
| 1503 | - | let mut a = testResolver(); |
|
| 1504 | - | let result = try resolveProgramStr(&mut a, "loop { break }"); |
|
| 1505 | - | try expectNoErrors(&result); |
|
| 1853 | + | let mut testArena110 = testArena(); |
|
| 1854 | + | let testStorage110: 'test110 = &mut testArena110 in { |
|
| 1855 | + | let mut a = testResolver(testStorage110); |
|
| 1856 | + | let result = try resolveProgramStr(&mut a, "loop { break }"); |
|
| 1857 | + | try expectNoErrors(&result); |
|
| 1858 | + | } |
|
| 1506 | 1859 | } |
|
| 1507 | 1860 | } |
|
| 1508 | 1861 | ||
| 1509 | 1862 | @test unsafe fn testResolveContinueRequiresLoop() throws (testing::TestError) { |
|
| 1510 | 1863 | { |
|
| 1511 | - | let mut a = testResolver(); |
|
| 1512 | - | let result = try resolveProgramStr(&mut a, "continue;"); |
|
| 1513 | - | try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl); |
|
| 1864 | + | let mut testArena111 = testArena(); |
|
| 1865 | + | let testStorage111: 'test111 = &mut testArena111 in { |
|
| 1866 | + | let mut a = testResolver(testStorage111); |
|
| 1867 | + | let result = try resolveProgramStr(&mut a, "continue;"); |
|
| 1868 | + | try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl); |
|
| 1869 | + | } |
|
| 1514 | 1870 | } { |
|
| 1515 | - | let mut a = testResolver(); |
|
| 1516 | - | let result = try resolveProgramStr(&mut a, "while true { continue }"); |
|
| 1517 | - | try expectNoErrors(&result); |
|
| 1871 | + | let mut testArena112 = testArena(); |
|
| 1872 | + | let testStorage112: 'test112 = &mut testArena112 in { |
|
| 1873 | + | let mut a = testResolver(testStorage112); |
|
| 1874 | + | let result = try resolveProgramStr(&mut a, "while true { continue }"); |
|
| 1875 | + | try expectNoErrors(&result); |
|
| 1876 | + | } |
|
| 1518 | 1877 | } |
|
| 1519 | 1878 | } |
|
| 1520 | 1879 | ||
| 1521 | 1880 | @test unsafe fn testResolveFnTypeVoidNoParams() throws (testing::TestError) { |
|
| 1522 | - | let mut a = testResolver(); |
|
| 1523 | - | let result = try resolveProgramStr(&mut a, "fn f() {} f();"); |
|
| 1524 | - | try expectNoErrors(&result); |
|
| 1525 | - | ||
| 1526 | - | let blockNode = result.root; |
|
| 1527 | - | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1528 | - | else throw testing::TestError::Failed; |
|
| 1529 | - | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 1530 | - | let callStmt = try getBlockStmt(blockNode, 1); |
|
| 1531 | - | ||
| 1532 | - | { // Verify the function symbol captures an empty parameter list and void return. |
|
| 1533 | - | let sym = super::symbolFor(&a, fnNode) |
|
| 1534 | - | else throw testing::TestError::Failed; |
|
| 1535 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 1536 | - | else throw testing::TestError::Failed; |
|
| 1537 | - | try testing::expect(fnTy.paramTypes.len == 0); |
|
| 1538 | - | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 1539 | - | } |
|
| 1540 | - | { // Checking that the type of the call matches the function return type. |
|
| 1541 | - | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
|
| 1881 | + | let mut testArena113 = testArena(); |
|
| 1882 | + | let testStorage113: 'test113 = &mut testArena113 in { |
|
| 1883 | + | let mut a = testResolver(testStorage113); |
|
| 1884 | + | let result = try resolveProgramStr(&mut a, "fn f() {} f();"); |
|
| 1885 | + | try expectNoErrors(&result); |
|
| 1542 | 1886 | ||
| 1543 | - | let fnSym = super::symbolFor(&a, fnNode) |
|
| 1544 | - | else throw testing::TestError::Failed; |
|
| 1545 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
|
| 1887 | + | let blockNode = result.root; |
|
| 1888 | + | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1546 | 1889 | else throw testing::TestError::Failed; |
|
| 1547 | - | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1890 | + | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 1891 | + | let callStmt = try getBlockStmt(blockNode, 1); |
|
| 1892 | + | ||
| 1893 | + | { // Verify the function symbol captures an empty parameter list and void return. |
|
| 1894 | + | let sym = super::symbolFor(&a, fnNode) |
|
| 1895 | + | else throw testing::TestError::Failed; |
|
| 1896 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 1897 | + | else throw testing::TestError::Failed; |
|
| 1898 | + | try testing::expect(fnTy.paramTypes.len == 0); |
|
| 1899 | + | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 1900 | + | } |
|
| 1901 | + | { // Checking that the type of the call matches the function return type. |
|
| 1902 | + | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
|
| 1903 | + | ||
| 1904 | + | let fnSym = super::symbolFor(&a, fnNode) |
|
| 1905 | + | else throw testing::TestError::Failed; |
|
| 1906 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
|
| 1907 | + | else throw testing::TestError::Failed; |
|
| 1908 | + | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1909 | + | } |
|
| 1548 | 1910 | } |
|
| 1549 | 1911 | } |
|
| 1550 | 1912 | ||
| 1551 | 1913 | @test unsafe fn testResolveFnTypeReturnsValue() throws (testing::TestError) { |
|
| 1552 | - | let mut a = testResolver(); |
|
| 1553 | - | let program = "fn f() -> i32 { return 1; } f();"; |
|
| 1554 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1555 | - | try expectNoErrors(&result); |
|
| 1556 | - | ||
| 1557 | - | let blockNode = result.root; |
|
| 1558 | - | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1559 | - | else throw testing::TestError::Failed; |
|
| 1560 | - | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 1561 | - | let callStmt = try getBlockStmt(blockNode, 1); |
|
| 1562 | - | ||
| 1563 | - | { // Function returns i32 with no parameters. |
|
| 1564 | - | let sym = super::symbolFor(&a, fnNode) |
|
| 1565 | - | else throw testing::TestError::Failed; |
|
| 1566 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 1567 | - | else throw testing::TestError::Failed; |
|
| 1568 | - | try testing::expect(fnTy.paramTypes.len == 0); |
|
| 1569 | - | try testing::expect(*fnTy.returnType == super::Type::I32); |
|
| 1570 | - | } |
|
| 1571 | - | { // Call expression should inherit the function's return type. |
|
| 1572 | - | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::I32); |
|
| 1914 | + | let mut testArena114 = testArena(); |
|
| 1915 | + | let testStorage114: 'test114 = &mut testArena114 in { |
|
| 1916 | + | let mut a = testResolver(testStorage114); |
|
| 1917 | + | let program = "fn f() -> i32 { return 1; } f();"; |
|
| 1918 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1919 | + | try expectNoErrors(&result); |
|
| 1573 | 1920 | ||
| 1574 | - | let fnSym = super::symbolFor(&a, fnNode) |
|
| 1921 | + | let blockNode = result.root; |
|
| 1922 | + | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1575 | 1923 | else throw testing::TestError::Failed; |
|
| 1576 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
|
| 1577 | - | else throw testing::TestError::Failed; |
|
| 1578 | - | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1924 | + | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 1925 | + | let callStmt = try getBlockStmt(blockNode, 1); |
|
| 1926 | + | ||
| 1927 | + | { // Function returns i32 with no parameters. |
|
| 1928 | + | let sym = super::symbolFor(&a, fnNode) |
|
| 1929 | + | else throw testing::TestError::Failed; |
|
| 1930 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 1931 | + | else throw testing::TestError::Failed; |
|
| 1932 | + | try testing::expect(fnTy.paramTypes.len == 0); |
|
| 1933 | + | try testing::expect(*fnTy.returnType == super::Type::I32); |
|
| 1934 | + | } |
|
| 1935 | + | { // Call expression should inherit the function's return type. |
|
| 1936 | + | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::I32); |
|
| 1937 | + | ||
| 1938 | + | let fnSym = super::symbolFor(&a, fnNode) |
|
| 1939 | + | else throw testing::TestError::Failed; |
|
| 1940 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
|
| 1941 | + | else throw testing::TestError::Failed; |
|
| 1942 | + | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1943 | + | } |
|
| 1579 | 1944 | } |
|
| 1580 | 1945 | } |
|
| 1581 | 1946 | ||
| 1582 | 1947 | @test unsafe fn testResolveFnTypeSingleParam() throws (testing::TestError) { |
|
| 1583 | - | let mut a = testResolver(); |
|
| 1584 | - | let program = "fn f(x: i8) {} let x: i8 = 1; f(x);"; |
|
| 1585 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1586 | - | try expectNoErrors(&result); |
|
| 1587 | - | ||
| 1588 | - | let blockNode = result.root; |
|
| 1589 | - | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1590 | - | else throw testing::TestError::Failed; |
|
| 1591 | - | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 1592 | - | let callStmt = try getBlockStmt(blockNode, 2); |
|
| 1593 | - | ||
| 1594 | - | { // Single parameter propagates nominal type onto the symbol and parameter node. |
|
| 1595 | - | let sym = super::symbolFor(&a, fnNode) |
|
| 1596 | - | else throw testing::TestError::Failed; |
|
| 1597 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 1598 | - | else throw testing::TestError::Failed; |
|
| 1599 | - | try testing::expect(fnTy.paramTypes.len == 1); |
|
| 1600 | - | try testing::expect(*fnTy.paramTypes[0] == super::Type::I8); |
|
| 1601 | - | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 1948 | + | let mut testArena115 = testArena(); |
|
| 1949 | + | let testStorage115: 'test115 = &mut testArena115 in { |
|
| 1950 | + | let mut a = testResolver(testStorage115); |
|
| 1951 | + | let program = "fn f(x: i8) {} let x: i8 = 1; f(x);"; |
|
| 1952 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1953 | + | try expectNoErrors(&result); |
|
| 1602 | 1954 | ||
| 1603 | - | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value |
|
| 1955 | + | let blockNode = result.root; |
|
| 1956 | + | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1604 | 1957 | else throw testing::TestError::Failed; |
|
| 1605 | - | try testing::expect(fnDecl.sig.params.len == 1); |
|
| 1606 | - | ||
| 1607 | - | let paramNode = fnDecl.sig.params[0]; |
|
| 1608 | - | try expectType(&a, paramNode, super::Type::I8); |
|
| 1609 | - | } |
|
| 1610 | - | { // Call should resolve to void, matching the function's return type. |
|
| 1611 | - | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
|
| 1612 | - | let fnSym = super::symbolFor(&a, fnNode) |
|
| 1613 | - | else throw testing::TestError::Failed; |
|
| 1614 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
|
| 1615 | - | else throw testing::TestError::Failed; |
|
| 1616 | - | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1958 | + | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 1959 | + | let callStmt = try getBlockStmt(blockNode, 2); |
|
| 1960 | + | ||
| 1961 | + | { // Single parameter propagates nominal type onto the symbol and parameter node. |
|
| 1962 | + | let sym = super::symbolFor(&a, fnNode) |
|
| 1963 | + | else throw testing::TestError::Failed; |
|
| 1964 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 1965 | + | else throw testing::TestError::Failed; |
|
| 1966 | + | try testing::expect(fnTy.paramTypes.len == 1); |
|
| 1967 | + | try testing::expect(*fnTy.paramTypes[0] == super::Type::I8); |
|
| 1968 | + | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 1969 | + | ||
| 1970 | + | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value |
|
| 1971 | + | else throw testing::TestError::Failed; |
|
| 1972 | + | try testing::expect(fnDecl.sig.params.len == 1); |
|
| 1973 | + | ||
| 1974 | + | let paramNode = fnDecl.sig.params[0]; |
|
| 1975 | + | try expectType(&a, paramNode, super::Type::I8); |
|
| 1976 | + | } |
|
| 1977 | + | { // Call should resolve to void, matching the function's return type. |
|
| 1978 | + | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
|
| 1979 | + | let fnSym = super::symbolFor(&a, fnNode) |
|
| 1980 | + | else throw testing::TestError::Failed; |
|
| 1981 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
|
| 1982 | + | else throw testing::TestError::Failed; |
|
| 1983 | + | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1984 | + | } |
|
| 1617 | 1985 | } |
|
| 1618 | 1986 | } |
|
| 1619 | 1987 | ||
| 1620 | 1988 | @test unsafe fn testResolveFnTypeMultipleParams() throws (testing::TestError) { |
|
| 1621 | - | let mut a = testResolver(); |
|
| 1622 | - | let program = "fn f(x: i8, y: i32) {} let x: i8 = 1; let y: i32 = 2; f(x, y);"; |
|
| 1623 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1624 | - | try expectNoErrors(&result); |
|
| 1625 | - | ||
| 1626 | - | let blockNode = result.root; |
|
| 1627 | - | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1628 | - | else throw testing::TestError::Failed; |
|
| 1629 | - | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 1630 | - | let callStmt = try getBlockStmt(blockNode, 3); |
|
| 1631 | - | ||
| 1632 | - | { // Ensure multi-parameter signatures record both argument types. |
|
| 1633 | - | let sym = super::symbolFor(&a, fnNode) |
|
| 1634 | - | else throw testing::TestError::Failed; |
|
| 1635 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 1636 | - | else throw testing::TestError::Failed; |
|
| 1637 | - | try testing::expect(fnTy.paramTypes.len == 2); |
|
| 1638 | - | try testing::expect(*fnTy.paramTypes[0] == super::Type::I8); |
|
| 1639 | - | try testing::expect(*fnTy.paramTypes[1] == super::Type::I32); |
|
| 1640 | - | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 1641 | - | ||
| 1642 | - | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value |
|
| 1643 | - | else throw testing::TestError::Failed; |
|
| 1644 | - | try testing::expect(fnDecl.sig.params.len == 2); |
|
| 1989 | + | let mut testArena116 = testArena(); |
|
| 1990 | + | let testStorage116: 'test116 = &mut testArena116 in { |
|
| 1991 | + | let mut a = testResolver(testStorage116); |
|
| 1992 | + | let program = "fn f(x: i8, y: i32) {} let x: i8 = 1; let y: i32 = 2; f(x, y);"; |
|
| 1993 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 1994 | + | try expectNoErrors(&result); |
|
| 1645 | 1995 | ||
| 1646 | - | let firstParam = fnDecl.sig.params[0]; |
|
| 1647 | - | let secondParam = fnDecl.sig.params[1]; |
|
| 1648 | - | try expectType(&a, firstParam, super::Type::I8); |
|
| 1649 | - | try expectType(&a, secondParam, super::Type::I32); |
|
| 1650 | - | } |
|
| 1651 | - | { // Call expression should again mirror the function return type. |
|
| 1652 | - | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
|
| 1653 | - | let fnSym = super::symbolFor(&a, fnNode) |
|
| 1654 | - | else throw testing::TestError::Failed; |
|
| 1655 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
|
| 1996 | + | let blockNode = result.root; |
|
| 1997 | + | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1656 | 1998 | else throw testing::TestError::Failed; |
|
| 1657 | - | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1999 | + | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 2000 | + | let callStmt = try getBlockStmt(blockNode, 3); |
|
| 2001 | + | ||
| 2002 | + | { // Ensure multi-parameter signatures record both argument types. |
|
| 2003 | + | let sym = super::symbolFor(&a, fnNode) |
|
| 2004 | + | else throw testing::TestError::Failed; |
|
| 2005 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 2006 | + | else throw testing::TestError::Failed; |
|
| 2007 | + | try testing::expect(fnTy.paramTypes.len == 2); |
|
| 2008 | + | try testing::expect(*fnTy.paramTypes[0] == super::Type::I8); |
|
| 2009 | + | try testing::expect(*fnTy.paramTypes[1] == super::Type::I32); |
|
| 2010 | + | try testing::expect(*fnTy.returnType == super::Type::Void); |
|
| 2011 | + | ||
| 2012 | + | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value |
|
| 2013 | + | else throw testing::TestError::Failed; |
|
| 2014 | + | try testing::expect(fnDecl.sig.params.len == 2); |
|
| 2015 | + | ||
| 2016 | + | let firstParam = fnDecl.sig.params[0]; |
|
| 2017 | + | let secondParam = fnDecl.sig.params[1]; |
|
| 2018 | + | try expectType(&a, firstParam, super::Type::I8); |
|
| 2019 | + | try expectType(&a, secondParam, super::Type::I32); |
|
| 2020 | + | } |
|
| 2021 | + | { // Call expression should again mirror the function return type. |
|
| 2022 | + | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
|
| 2023 | + | let fnSym = super::symbolFor(&a, fnNode) |
|
| 2024 | + | else throw testing::TestError::Failed; |
|
| 2025 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
|
| 2026 | + | else throw testing::TestError::Failed; |
|
| 2027 | + | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 2028 | + | } |
|
| 1658 | 2029 | } |
|
| 1659 | 2030 | } |
|
| 1660 | 2031 | ||
| 1661 | 2032 | @test unsafe fn testResolveFnRecursiveCall() throws (testing::TestError) { |
|
| 1662 | - | let mut a = testResolver(); |
|
| 1663 | - | let program = "fn flip(b: bool) -> bool { if b { return false; } return flip(false); }"; |
|
| 1664 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1665 | - | try expectNoErrors(&result); |
|
| 1666 | - | ||
| 1667 | - | let blockNode = result.root; |
|
| 1668 | - | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1669 | - | else throw testing::TestError::Failed; |
|
| 1670 | - | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 2033 | + | let mut testArena117 = testArena(); |
|
| 2034 | + | let testStorage117: 'test117 = &mut testArena117 in { |
|
| 2035 | + | let mut a = testResolver(testStorage117); |
|
| 2036 | + | let program = "fn flip(b: bool) -> bool { if b { return false; } return flip(false); }"; |
|
| 2037 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2038 | + | try expectNoErrors(&result); |
|
| 1671 | 2039 | ||
| 1672 | - | { // Function symbol should be visible for recursive calls within its own body. |
|
| 1673 | - | let sym = super::symbolFor(&a, fnNode) |
|
| 1674 | - | else throw testing::TestError::Failed; |
|
| 1675 | - | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 2040 | + | let blockNode = result.root; |
|
| 2041 | + | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 1676 | 2042 | else throw testing::TestError::Failed; |
|
| 1677 | - | try testing::expect(fnTy.paramTypes.len == 1); |
|
| 1678 | - | try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool); |
|
| 1679 | - | try testing::expect(*fnTy.returnType == super::Type::Bool); |
|
| 2043 | + | let fnNode = try getBlockStmt(blockNode, 0); |
|
| 2044 | + | ||
| 2045 | + | { // Function symbol should be visible for recursive calls within its own body. |
|
| 2046 | + | let sym = super::symbolFor(&a, fnNode) |
|
| 2047 | + | else throw testing::TestError::Failed; |
|
| 2048 | + | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
|
| 2049 | + | else throw testing::TestError::Failed; |
|
| 2050 | + | try testing::expect(fnTy.paramTypes.len == 1); |
|
| 2051 | + | try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool); |
|
| 2052 | + | try testing::expect(*fnTy.returnType == super::Type::Bool); |
|
| 2053 | + | } |
|
| 1680 | 2054 | } |
|
| 1681 | 2055 | } |
|
| 1682 | 2056 | ||
| 1683 | 2057 | @test unsafe fn testResolveFnCallMissingArgument() throws (testing::TestError) { |
|
| 1684 | - | let mut a = testResolver(); |
|
| 1685 | - | let program = "fn f(x: i8) {} f();"; |
|
| 1686 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1687 | - | // Expect an error when a required parameter is omitted. |
|
| 1688 | - | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
|
| 1689 | - | expected: 1, |
|
| 1690 | - | actual: 0, |
|
| 1691 | - | })); |
|
| 2058 | + | let mut testArena118 = testArena(); |
|
| 2059 | + | let testStorage118: 'test118 = &mut testArena118 in { |
|
| 2060 | + | let mut a = testResolver(testStorage118); |
|
| 2061 | + | let program = "fn f(x: i8) {} f();"; |
|
| 2062 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2063 | + | // Expect an error when a required parameter is omitted. |
|
| 2064 | + | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
|
| 2065 | + | expected: 1, |
|
| 2066 | + | actual: 0, |
|
| 2067 | + | })); |
|
| 2068 | + | } |
|
| 1692 | 2069 | } |
|
| 1693 | 2070 | ||
| 1694 | 2071 | @test unsafe fn testResolveFnCallExtraArgument() throws (testing::TestError) { |
|
| 1695 | - | let mut a = testResolver(); |
|
| 1696 | - | let program = "fn f() {} f(1);"; |
|
| 1697 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1698 | - | // Passing more arguments than declared should fail. |
|
| 1699 | - | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
|
| 1700 | - | expected: 0, |
|
| 1701 | - | actual: 1, |
|
| 1702 | - | })); |
|
| 2072 | + | let mut testArena119 = testArena(); |
|
| 2073 | + | let testStorage119: 'test119 = &mut testArena119 in { |
|
| 2074 | + | let mut a = testResolver(testStorage119); |
|
| 2075 | + | let program = "fn f() {} f(1);"; |
|
| 2076 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2077 | + | // Passing more arguments than declared should fail. |
|
| 2078 | + | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
|
| 2079 | + | expected: 0, |
|
| 2080 | + | actual: 1, |
|
| 2081 | + | })); |
|
| 2082 | + | } |
|
| 1703 | 2083 | } |
|
| 1704 | 2084 | ||
| 1705 | 2085 | @test unsafe fn testResolveFnCallArgumentTypeMismatch() throws (testing::TestError) { |
|
| 1706 | - | let mut a = testResolver(); |
|
| 1707 | - | let program = "fn f(x: i8) {} f(true);"; |
|
| 1708 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1709 | - | let err = try expectError(&result); |
|
| 1710 | - | // The argument type (bool) should not match the parameter type (i8). |
|
| 1711 | - | try expectTypeMismatch(err, super::Type::I8, super::Type::Bool); |
|
| 2086 | + | let mut testArena120 = testArena(); |
|
| 2087 | + | let testStorage120: 'test120 = &mut testArena120 in { |
|
| 2088 | + | let mut a = testResolver(testStorage120); |
|
| 2089 | + | let program = "fn f(x: i8) {} f(true);"; |
|
| 2090 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2091 | + | let err = try expectError(&result); |
|
| 2092 | + | // The argument type (bool) should not match the parameter type (i8). |
|
| 2093 | + | try expectTypeMismatch(err, super::Type::I8, super::Type::Bool); |
|
| 2094 | + | } |
|
| 1712 | 2095 | } |
|
| 1713 | 2096 | ||
| 1714 | 2097 | @test unsafe fn testResolveFnReturnTypeMismatch() throws (testing::TestError) { |
|
| 1715 | - | let mut a = testResolver(); |
|
| 1716 | - | let program = "fn f() -> i32 { return true; }"; |
|
| 1717 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1718 | - | let err = try expectError(&result); |
|
| 1719 | - | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 2098 | + | let mut testArena121 = testArena(); |
|
| 2099 | + | let testStorage121: 'test121 = &mut testArena121 in { |
|
| 2100 | + | let mut a = testResolver(testStorage121); |
|
| 2101 | + | let program = "fn f() -> i32 { return true; }"; |
|
| 2102 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2103 | + | let err = try expectError(&result); |
|
| 2104 | + | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 2105 | + | } |
|
| 1720 | 2106 | } |
|
| 1721 | 2107 | ||
| 1722 | 2108 | @test unsafe fn testResolveFnReturnVoid() throws (testing::TestError) { |
|
| 1723 | 2109 | { |
|
| 1724 | - | let mut a = testResolver(); |
|
| 1725 | - | let result = try resolveProgramStr(&mut a, "fn f() { return; }"); |
|
| 1726 | - | try expectNoErrors(&result); |
|
| 2110 | + | let mut testArena122 = testArena(); |
|
| 2111 | + | let testStorage122: 'test122 = &mut testArena122 in { |
|
| 2112 | + | let mut a = testResolver(testStorage122); |
|
| 2113 | + | let result = try resolveProgramStr(&mut a, "fn f() { return; }"); |
|
| 2114 | + | try expectNoErrors(&result); |
|
| 2115 | + | } |
|
| 1727 | 2116 | } { |
|
| 1728 | - | let mut a = testResolver(); |
|
| 1729 | - | let result = try resolveProgramStr(&mut a, "fn g() -> i32 { return; }"); |
|
| 1730 | - | let err = try expectError(&result); |
|
| 1731 | - | try expectTypeMismatch(err, super::Type::I32, super::Type::Void); |
|
| 2117 | + | let mut testArena123 = testArena(); |
|
| 2118 | + | let testStorage123: 'test123 = &mut testArena123 in { |
|
| 2119 | + | let mut a = testResolver(testStorage123); |
|
| 2120 | + | let result = try resolveProgramStr(&mut a, "fn g() -> i32 { return; }"); |
|
| 2121 | + | let err = try expectError(&result); |
|
| 2122 | + | try expectTypeMismatch(err, super::Type::I32, super::Type::Void); |
|
| 2123 | + | } |
|
| 1732 | 2124 | } |
|
| 1733 | 2125 | } |
|
| 1734 | 2126 | ||
| 1735 | 2127 | @test unsafe fn testResolveFnMissingReturn() throws (testing::TestError) { |
|
| 1736 | 2128 | { |
|
| 1737 | - | let mut a = testResolver(); |
|
| 1738 | - | let result = try resolveProgramStr(&mut a, "fn f() -> i32 {}"); |
|
| 1739 | - | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 2129 | + | let mut testArena124 = testArena(); |
|
| 2130 | + | let testStorage124: 'test124 = &mut testArena124 in { |
|
| 2131 | + | let mut a = testResolver(testStorage124); |
|
| 2132 | + | let result = try resolveProgramStr(&mut a, "fn f() -> i32 {}"); |
|
| 2133 | + | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 2134 | + | } |
|
| 1740 | 2135 | } { |
|
| 1741 | - | let mut a = testResolver(); |
|
| 1742 | - | let program = "fn g(flag: bool) -> i32 { if flag { return 1; } 2; }"; |
|
| 1743 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1744 | - | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 2136 | + | let mut testArena125 = testArena(); |
|
| 2137 | + | let testStorage125: 'test125 = &mut testArena125 in { |
|
| 2138 | + | let mut a = testResolver(testStorage125); |
|
| 2139 | + | let program = "fn g(flag: bool) -> i32 { if flag { return 1; } 2; }"; |
|
| 2140 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2141 | + | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 2142 | + | } |
|
| 1745 | 2143 | } |
|
| 1746 | 2144 | } |
|
| 1747 | 2145 | ||
| 1748 | 2146 | /// Never-returning functions require divergence on every path. |
|
| 1749 | 2147 | @test unsafe fn testResolveNeverReturn() throws (testing::TestError) { |
| 1759 | 2157 | "fn f() -> ! { while true {} }", |
|
| 1760 | 2158 | "fn f(flag: bool) -> ! { if flag { panic; } else { while true {} } }", |
|
| 1761 | 2159 | "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! throws (i32) { try f(); }", |
|
| 1762 | 2160 | "fn f() -> ! throws (i32) { throw 1; } fn g() -> i32 { try f() catch { return 2; }; }", |
|
| 1763 | 2161 | ] { |
|
| 1764 | - | let mut a = testResolver(); |
|
| 1765 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1766 | - | try expectNoErrors(&result); |
|
| 2162 | + | let mut testArena126 = testArena(); |
|
| 2163 | + | let testStorage126: 'test126 = &mut testArena126 in { |
|
| 2164 | + | let mut a = testResolver(testStorage126); |
|
| 2165 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2166 | + | try expectNoErrors(&result); |
|
| 2167 | + | } |
|
| 1767 | 2168 | } |
|
| 1768 | 2169 | } |
|
| 1769 | 2170 | ||
| 1770 | 2171 | /// A never-returning signature rejects any normal completion path. |
|
| 1771 | 2172 | @test unsafe fn testResolveNeverFallthrough() throws (testing::TestError) { |
| 1774 | 2175 | "fn f(flag: bool) -> ! { if flag { panic; } }", |
|
| 1775 | 2176 | "fn f() -> ! { while true { break; } }", |
|
| 1776 | 2177 | "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! { try f() catch {}; }", |
|
| 1777 | 2178 | "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! { try f() catch e as i32 {}; }", |
|
| 1778 | 2179 | ] { |
|
| 1779 | - | let mut a = testResolver(); |
|
| 1780 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1781 | - | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 2180 | + | let mut testArena127 = testArena(); |
|
| 2181 | + | let testStorage127: 'test127 = &mut testArena127 in { |
|
| 2182 | + | let mut a = testResolver(testStorage127); |
|
| 2183 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2184 | + | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 2185 | + | } |
|
| 1782 | 2186 | } |
|
| 1783 | 2187 | } |
|
| 1784 | 2188 | ||
| 1785 | 2189 | /// No value, including undefined, can construct a never return value. |
|
| 1786 | 2190 | @test unsafe fn testResolveNeverValue() throws (testing::TestError) { |
| 1788 | 2192 | "fn f() -> ! { return; }", |
|
| 1789 | 2193 | "fn f() -> ! { return 1; }", |
|
| 1790 | 2194 | "unsafe fn f() -> ! { return undefined; }", |
|
| 1791 | 2195 | "fn ordinary() {} fn f() { let callback: fn() -> ! = ordinary; }", |
|
| 1792 | 2196 | ] { |
|
| 1793 | - | let mut a = testResolver(); |
|
| 1794 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1795 | - | let error = try expectError(&result); |
|
| 2197 | + | let mut testArena128 = testArena(); |
|
| 2198 | + | let testStorage128: 'test128 = &mut testArena128 in { |
|
| 2199 | + | let mut a = testResolver(testStorage128); |
|
| 2200 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2201 | + | let error = try expectError(&result); |
|
| 2202 | + | } |
|
| 1796 | 2203 | } |
|
| 1797 | 2204 | } |
|
| 1798 | 2205 | ||
| 1799 | 2206 | /// Caught errors preserve the caller's exact-use ownership obligations. |
|
| 1800 | 2207 | @test unsafe fn testResolveNeverCaughtOwnership() throws (testing::TestError) { |
|
| 1801 | 2208 | for program in &[ |
|
| 1802 | 2209 | "union Token: Once { Held(u32) } fn fail() -> ! throws (i32) { throw 1; } fn f(token: Token) { try fail() catch {}; }", |
|
| 1803 | 2210 | "union Token: Once { Held(u32) } fn fail() -> ! throws (i32) { throw 1; } fn f(token: Token) { let absent = try? fail(); }", |
|
| 1804 | 2211 | ] { |
|
| 1805 | - | let mut a = testResolver(); |
|
| 1806 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1807 | - | let error = try expectError(&result); |
|
| 1808 | - | let case super::ErrorKind::LinearNotConsumed(_) = error.kind |
|
| 1809 | - | else throw testing::TestError::Failed; |
|
| 2212 | + | let mut testArena129 = testArena(); |
|
| 2213 | + | let testStorage129: 'test129 = &mut testArena129 in { |
|
| 2214 | + | let mut a = testResolver(testStorage129); |
|
| 2215 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2216 | + | let error = try expectError(&result); |
|
| 2217 | + | let case super::ErrorKind::LinearNotConsumed(_) = error.kind |
|
| 2218 | + | else throw testing::TestError::Failed; |
|
| 2219 | + | } |
|
| 1810 | 2220 | } |
|
| 1811 | 2221 | } |
|
| 1812 | 2222 | ||
| 1813 | 2223 | @test unsafe fn testResolveFnAllPathsReturn() throws (testing::TestError) { |
|
| 1814 | - | let mut a = testResolver(); |
|
| 1815 | - | let program = "fn h(flag: bool) -> i32 { if flag { return 1; } else { return 2; } }"; |
|
| 1816 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1817 | - | try expectNoErrors(&result); |
|
| 2224 | + | let mut testArena130 = testArena(); |
|
| 2225 | + | let testStorage130: 'test130 = &mut testArena130 in { |
|
| 2226 | + | let mut a = testResolver(testStorage130); |
|
| 2227 | + | let program = "fn h(flag: bool) -> i32 { if flag { return 1; } else { return 2; } }"; |
|
| 2228 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2229 | + | try expectNoErrors(&result); |
|
| 2230 | + | } |
|
| 1818 | 2231 | } |
|
| 1819 | 2232 | ||
| 1820 | 2233 | /// Test that match statements with returns in all branches don't require a |
|
| 1821 | 2234 | /// return at the end of the function. |
|
| 1822 | 2235 | @test unsafe fn testResolveFnMatchAllPathsReturn() throws (testing::TestError) { |
|
| 1823 | 2236 | { |
|
| 1824 | 2237 | // Union match with all variants returning. |
|
| 1825 | - | let mut a = testResolver(); |
|
| 1826 | - | let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => return 2 } }"; |
|
| 1827 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1828 | - | try expectNoErrors(&result); |
|
| 2238 | + | let mut testArena131 = testArena(); |
|
| 2239 | + | let testStorage131: 'test131 = &mut testArena131 in { |
|
| 2240 | + | let mut a = testResolver(testStorage131); |
|
| 2241 | + | let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => return 2 } }"; |
|
| 2242 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2243 | + | try expectNoErrors(&result); |
|
| 2244 | + | } |
|
| 1829 | 2245 | } { |
|
| 1830 | 2246 | // Match with default case where all branches return. |
|
| 1831 | - | let mut a = testResolver(); |
|
| 1832 | - | let program = "fn f(x: i32) -> i32 { match x { case 1 => return 1, else => return 0, } }"; |
|
| 1833 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1834 | - | try expectNoErrors(&result); |
|
| 2247 | + | let mut testArena132 = testArena(); |
|
| 2248 | + | let testStorage132: 'test132 = &mut testArena132 in { |
|
| 2249 | + | let mut a = testResolver(testStorage132); |
|
| 2250 | + | let program = "fn f(x: i32) -> i32 { match x { case 1 => return 1, else => return 0, } }"; |
|
| 2251 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2252 | + | try expectNoErrors(&result); |
|
| 2253 | + | } |
|
| 1835 | 2254 | } { |
|
| 1836 | 2255 | // Match where not all branches return should error. |
|
| 1837 | - | let mut a = testResolver(); |
|
| 1838 | - | let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => {} } }"; |
|
| 1839 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1840 | - | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 2256 | + | let mut testArena133 = testArena(); |
|
| 2257 | + | let testStorage133: 'test133 = &mut testArena133 in { |
|
| 2258 | + | let mut a = testResolver(testStorage133); |
|
| 2259 | + | let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => {} } }"; |
|
| 2260 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2261 | + | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 2262 | + | } |
|
| 1841 | 2263 | } |
|
| 1842 | 2264 | } |
|
| 1843 | 2265 | ||
| 1844 | 2266 | @test unsafe fn testResolveAssign() throws (testing::TestError) { |
|
| 1845 | 2267 | { |
|
| 1846 | - | let mut a = testResolver(); |
|
| 1847 | - | let result = try resolveProgramStr(&mut a, "let mut x: i32 = 0; set x = 1;"); |
|
| 1848 | - | try expectNoErrors(&result); |
|
| 2268 | + | let mut testArena134 = testArena(); |
|
| 2269 | + | let testStorage134: 'test134 = &mut testArena134 in { |
|
| 2270 | + | let mut a = testResolver(testStorage134); |
|
| 2271 | + | let result = try resolveProgramStr(&mut a, "let mut x: i32 = 0; set x = 1;"); |
|
| 2272 | + | try expectNoErrors(&result); |
|
| 2273 | + | } |
|
| 1849 | 2274 | } { |
|
| 1850 | - | let mut a = testResolver(); |
|
| 1851 | - | let result = try resolveProgramStr(&mut a, "let x: i32 = 0; set x = 1;"); |
|
| 1852 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2275 | + | let mut testArena135 = testArena(); |
|
| 2276 | + | let testStorage135: 'test135 = &mut testArena135 in { |
|
| 2277 | + | let mut a = testResolver(testStorage135); |
|
| 2278 | + | let result = try resolveProgramStr(&mut a, "let x: i32 = 0; set x = 1;"); |
|
| 2279 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2280 | + | } |
|
| 1853 | 2281 | } { |
|
| 1854 | - | let mut a = testResolver(); |
|
| 1855 | - | let result = try resolveProgramStr(&mut a, "let mut x: bool = false; set x = 1;"); |
|
| 1856 | - | let err = try expectError(&result); |
|
| 1857 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 2282 | + | let mut testArena136 = testArena(); |
|
| 2283 | + | let testStorage136: 'test136 = &mut testArena136 in { |
|
| 2284 | + | let mut a = testResolver(testStorage136); |
|
| 2285 | + | let result = try resolveProgramStr(&mut a, "let mut x: bool = false; set x = 1;"); |
|
| 2286 | + | let err = try expectError(&result); |
|
| 2287 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 2288 | + | } |
|
| 1858 | 2289 | } { |
|
| 1859 | - | let mut a = testResolver(); |
|
| 1860 | - | let result = try resolveProgramStr(&mut a, "set x = 1;"); |
|
| 1861 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 2290 | + | let mut testArena137 = testArena(); |
|
| 2291 | + | let testStorage137: 'test137 = &mut testArena137 in { |
|
| 2292 | + | let mut a = testResolver(testStorage137); |
|
| 2293 | + | let result = try resolveProgramStr(&mut a, "set x = 1;"); |
|
| 2294 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 2295 | + | } |
|
| 1862 | 2296 | } { |
|
| 1863 | - | let mut a = testResolver(); |
|
| 1864 | - | let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = 1;"); |
|
| 1865 | - | try expectNoErrors(&result); |
|
| 2297 | + | let mut testArena138 = testArena(); |
|
| 2298 | + | let testStorage138: 'test138 = &mut testArena138 in { |
|
| 2299 | + | let mut a = testResolver(testStorage138); |
|
| 2300 | + | let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = 1;"); |
|
| 2301 | + | try expectNoErrors(&result); |
|
| 2302 | + | } |
|
| 1866 | 2303 | } { |
|
| 1867 | - | let mut a = testResolver(); |
|
| 1868 | - | let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = nil;"); |
|
| 1869 | - | try expectNoErrors(&result); |
|
| 2304 | + | let mut testArena139 = testArena(); |
|
| 2305 | + | let testStorage139: 'test139 = &mut testArena139 in { |
|
| 2306 | + | let mut a = testResolver(testStorage139); |
|
| 2307 | + | let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = nil;"); |
|
| 2308 | + | try expectNoErrors(&result); |
|
| 2309 | + | } |
|
| 1870 | 2310 | } |
|
| 1871 | 2311 | } |
|
| 1872 | 2312 | ||
| 1873 | 2313 | @test unsafe fn testResolveAssignSubscript() throws (testing::TestError) { |
|
| 1874 | 2314 | { |
|
| 1875 | - | let mut a = testResolver(); |
|
| 1876 | - | let program = "let mut xs: [u8; 2] = [0, 1]; set xs[0] = 9;"; |
|
| 1877 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1878 | - | try expectNoErrors(&result); |
|
| 2315 | + | let mut testArena140 = testArena(); |
|
| 2316 | + | let testStorage140: 'test140 = &mut testArena140 in { |
|
| 2317 | + | let mut a = testResolver(testStorage140); |
|
| 2318 | + | let program = "let mut xs: [u8; 2] = [0, 1]; set xs[0] = 9;"; |
|
| 2319 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2320 | + | try expectNoErrors(&result); |
|
| 2321 | + | } |
|
| 1879 | 2322 | } |
|
| 1880 | 2323 | { |
|
| 1881 | - | let mut a = testResolver(); |
|
| 1882 | - | let program = "static xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;"; |
|
| 1883 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1884 | - | try expectNoErrors(&result); |
|
| 2324 | + | let mut testArena141 = testArena(); |
|
| 2325 | + | let testStorage141: 'test141 = &mut testArena141 in { |
|
| 2326 | + | let mut a = testResolver(testStorage141); |
|
| 2327 | + | let program = "static xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;"; |
|
| 2328 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2329 | + | try expectNoErrors(&result); |
|
| 2330 | + | } |
|
| 1885 | 2331 | } |
|
| 1886 | 2332 | { |
|
| 1887 | - | let mut a = testResolver(); |
|
| 1888 | - | let program = "static xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 1889 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1890 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2333 | + | let mut testArena142 = testArena(); |
|
| 2334 | + | let testStorage142: 'test142 = &mut testArena142 in { |
|
| 2335 | + | let mut a = testResolver(testStorage142); |
|
| 2336 | + | let program = "static xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 2337 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2338 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2339 | + | } |
|
| 1891 | 2340 | } |
|
| 1892 | 2341 | { |
|
| 1893 | - | let mut a = testResolver(); |
|
| 1894 | - | let program = "let xs: [u8; 2] = [0, 1]; set xs[0] = 9;"; |
|
| 1895 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1896 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2342 | + | let mut testArena143 = testArena(); |
|
| 2343 | + | let testStorage143: 'test143 = &mut testArena143 in { |
|
| 2344 | + | let mut a = testResolver(testStorage143); |
|
| 2345 | + | let program = "let xs: [u8; 2] = [0, 1]; set xs[0] = 9;"; |
|
| 2346 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2347 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2348 | + | } |
|
| 1897 | 2349 | } |
|
| 1898 | 2350 | { |
|
| 1899 | - | let mut a = testResolver(); |
|
| 1900 | - | let program = "static xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 1901 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1902 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2351 | + | let mut testArena144 = testArena(); |
|
| 2352 | + | let testStorage144: 'test144 = &mut testArena144 in { |
|
| 2353 | + | let mut a = testResolver(testStorage144); |
|
| 2354 | + | let program = "static xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 2355 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2356 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2357 | + | } |
|
| 1903 | 2358 | } |
|
| 1904 | 2359 | } |
|
| 1905 | 2360 | ||
| 1906 | 2361 | @test unsafe fn testResolveAssignIntegerLits() throws (testing::TestError) { |
|
| 1907 | 2362 | try expectAnalyzeOk("let x: i8 = 127;"); |
| 1942 | 2397 | try expectIntMismatch("constant LIMIT: u8 = -5;", super::Type::U8); |
|
| 1943 | 2398 | } |
|
| 1944 | 2399 | ||
| 1945 | 2400 | @test unsafe fn testNilCoercions() throws (testing::TestError) { |
|
| 1946 | 2401 | { |
|
| 1947 | - | let mut a = testResolver(); |
|
| 1948 | - | let result = try resolveBlockStr(&mut a, "let opt: ?i32 = nil;"); |
|
| 1949 | - | try expectNoErrors(&result); |
|
| 2402 | + | let mut testArena145 = testArena(); |
|
| 2403 | + | let testStorage145: 'test145 = &mut testArena145 in { |
|
| 2404 | + | let mut a = testResolver(testStorage145); |
|
| 2405 | + | let result = try resolveBlockStr(&mut a, "let opt: ?i32 = nil;"); |
|
| 2406 | + | try expectNoErrors(&result); |
|
| 2407 | + | } |
|
| 1950 | 2408 | } { |
|
| 1951 | - | let mut a = testResolver(); |
|
| 1952 | - | let program = "fn g(opt: ?i32) {} fn f() { g(nil); }"; |
|
| 1953 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1954 | - | try expectNoErrors(&result); |
|
| 2409 | + | let mut testArena146 = testArena(); |
|
| 2410 | + | let testStorage146: 'test146 = &mut testArena146 in { |
|
| 2411 | + | let mut a = testResolver(testStorage146); |
|
| 2412 | + | let program = "fn g(opt: ?i32) {} fn f() { g(nil); }"; |
|
| 2413 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2414 | + | try expectNoErrors(&result); |
|
| 2415 | + | } |
|
| 1955 | 2416 | } { |
|
| 1956 | - | let mut a = testResolver(); |
|
| 1957 | - | let program = "fn make(flag: bool) -> ?i32 { if flag { return 1; } return nil; }"; |
|
| 1958 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1959 | - | try expectNoErrors(&result); |
|
| 2417 | + | let mut testArena147 = testArena(); |
|
| 2418 | + | let testStorage147: 'test147 = &mut testArena147 in { |
|
| 2419 | + | let mut a = testResolver(testStorage147); |
|
| 2420 | + | let program = "fn make(flag: bool) -> ?i32 { if flag { return 1; } return nil; }"; |
|
| 2421 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2422 | + | try expectNoErrors(&result); |
|
| 2423 | + | } |
|
| 1960 | 2424 | } |
|
| 1961 | 2425 | } |
|
| 1962 | 2426 | ||
| 1963 | 2427 | @test unsafe fn testOptionalComparedWithNil() throws (testing::TestError) { |
|
| 1964 | - | let mut a = testResolver(); |
|
| 1965 | - | let program = "let opt: ?i32 = nil; opt == nil; nil == opt; opt == 1; 1 == opt; opt == opt; nil == nil;"; |
|
| 1966 | - | let result = try resolveBlockStr(&mut a, program); |
|
| 1967 | - | try expectNoErrors(&result); |
|
| 2428 | + | let mut testArena148 = testArena(); |
|
| 2429 | + | let testStorage148: 'test148 = &mut testArena148 in { |
|
| 2430 | + | let mut a = testResolver(testStorage148); |
|
| 2431 | + | let program = "let opt: ?i32 = nil; opt == nil; nil == opt; opt == 1; 1 == opt; opt == opt; nil == nil;"; |
|
| 2432 | + | let result = try resolveBlockStr(&mut a, program); |
|
| 2433 | + | try expectNoErrors(&result); |
|
| 1968 | 2434 | ||
| 1969 | - | for i in 1..7 { |
|
| 1970 | - | let stmt = try getBlockStmt(result.root, i); |
|
| 1971 | - | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 2435 | + | for i in 1..7 { |
|
| 2436 | + | let stmt = try getBlockStmt(result.root, i); |
|
| 2437 | + | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 2438 | + | } |
|
| 1972 | 2439 | } |
|
| 1973 | 2440 | } |
|
| 1974 | 2441 | ||
| 1975 | 2442 | @test unsafe fn testResolveRecordLiteralAllFieldsSet() throws (testing::TestError) { |
|
| 1976 | - | let mut a = testResolver(); |
|
| 1977 | - | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, y: 2 };"; |
|
| 1978 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1979 | - | try expectNoErrors(&result); |
|
| 2443 | + | let mut testArena149 = testArena(); |
|
| 2444 | + | let testStorage149: 'test149 = &mut testArena149 in { |
|
| 2445 | + | let mut a = testResolver(testStorage149); |
|
| 2446 | + | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, y: 2 };"; |
|
| 2447 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2448 | + | try expectNoErrors(&result); |
|
| 2449 | + | } |
|
| 1980 | 2450 | } |
|
| 1981 | 2451 | ||
| 1982 | 2452 | @test unsafe fn testResolveRecordLiteralMissingField() throws (testing::TestError) { |
|
| 1983 | - | let mut a = testResolver(); |
|
| 1984 | - | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1 };"; |
|
| 1985 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1986 | - | try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y")); |
|
| 2453 | + | let mut testArena150 = testArena(); |
|
| 2454 | + | let testStorage150: 'test150 = &mut testArena150 in { |
|
| 2455 | + | let mut a = testResolver(testStorage150); |
|
| 2456 | + | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1 };"; |
|
| 2457 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2458 | + | try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y")); |
|
| 2459 | + | } |
|
| 1987 | 2460 | } |
|
| 1988 | 2461 | ||
| 1989 | 2462 | @test unsafe fn testResolveRecordLiteralFieldTypeMismatch() throws (testing::TestError) { |
|
| 1990 | - | let mut a = testResolver(); |
|
| 1991 | - | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: true, y: 2 };"; |
|
| 1992 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 1993 | - | let err = try expectError(&result); |
|
| 1994 | - | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 2463 | + | let mut testArena151 = testArena(); |
|
| 2464 | + | let testStorage151: 'test151 = &mut testArena151 in { |
|
| 2465 | + | let mut a = testResolver(testStorage151); |
|
| 2466 | + | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: true, y: 2 };"; |
|
| 2467 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2468 | + | let err = try expectError(&result); |
|
| 2469 | + | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 1995 | 2470 | ||
| 1996 | - | let errNode = err.node |
|
| 1997 | - | else throw testing::TestError::Failed; |
|
| 1998 | - | let case ast::NodeValue::Bool(_) = errNode.value |
|
| 1999 | - | else throw testing::TestError::Failed; |
|
| 2471 | + | let errNode = err.node |
|
| 2472 | + | else throw testing::TestError::Failed; |
|
| 2473 | + | let case ast::NodeValue::Bool(_) = errNode.value |
|
| 2474 | + | else throw testing::TestError::Failed; |
|
| 2475 | + | } |
|
| 2000 | 2476 | } |
|
| 2001 | 2477 | ||
| 2002 | 2478 | @test unsafe fn testResolveRecordLiteralExtraField() throws (testing::TestError) { |
|
| 2003 | - | let mut a = testResolver(); |
|
| 2004 | - | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, z: 3, y: 2 };"; |
|
| 2005 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2006 | - | let err = try expectError(&result); |
|
| 2007 | - | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 2008 | - | else throw testing::TestError::Failed; |
|
| 2479 | + | let mut testArena152 = testArena(); |
|
| 2480 | + | let testStorage152: 'test152 = &mut testArena152 in { |
|
| 2481 | + | let mut a = testResolver(testStorage152); |
|
| 2482 | + | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, z: 3, y: 2 };"; |
|
| 2483 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2484 | + | let err = try expectError(&result); |
|
| 2485 | + | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 2486 | + | else throw testing::TestError::Failed; |
|
| 2487 | + | } |
|
| 2009 | 2488 | } |
|
| 2010 | 2489 | ||
| 2011 | 2490 | /// Test that anonymous record literals with labels can be passed to functions expecting named records. |
|
| 2012 | 2491 | @test unsafe fn testResolveAnonRecordLabeledToNamedRecord() throws (testing::TestError) { |
|
| 2013 | - | let mut a = testResolver(); |
|
| 2014 | - | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) -> i32 { return p.x; } foo({ x: 1, y: 2 });"; |
|
| 2015 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2016 | - | try expectNoErrors(&result); |
|
| 2492 | + | let mut testArena153 = testArena(); |
|
| 2493 | + | let testStorage153: 'test153 = &mut testArena153 in { |
|
| 2494 | + | let mut a = testResolver(testStorage153); |
|
| 2495 | + | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) -> i32 { return p.x; } foo({ x: 1, y: 2 });"; |
|
| 2496 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2497 | + | try expectNoErrors(&result); |
|
| 2498 | + | } |
|
| 2017 | 2499 | } |
|
| 2018 | 2500 | ||
| 2019 | 2501 | /// Test that anonymous record with wrong field name causes out of order error. |
|
| 2020 | 2502 | @test unsafe fn testResolveAnonRecordWrongFieldName() throws (testing::TestError) { |
|
| 2021 | - | let mut a = testResolver(); |
|
| 2022 | - | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, z: 2 });"; |
|
| 2023 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2024 | - | let err = try expectError(&result); |
|
| 2025 | - | let case super::ErrorKind::RecordFieldOutOfOrder { field: _, prev: _ } = err.kind |
|
| 2026 | - | else throw testing::TestError::Failed; |
|
| 2503 | + | let mut testArena154 = testArena(); |
|
| 2504 | + | let testStorage154: 'test154 = &mut testArena154 in { |
|
| 2505 | + | let mut a = testResolver(testStorage154); |
|
| 2506 | + | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, z: 2 });"; |
|
| 2507 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2508 | + | let err = try expectError(&result); |
|
| 2509 | + | let case super::ErrorKind::RecordFieldOutOfOrder { field: _, prev: _ } = err.kind |
|
| 2510 | + | else throw testing::TestError::Failed; |
|
| 2511 | + | } |
|
| 2027 | 2512 | } |
|
| 2028 | 2513 | ||
| 2029 | 2514 | /// Test that anonymous record with wrong field type causes type mismatch. |
|
| 2030 | 2515 | @test unsafe fn testResolveAnonRecordWrongFieldType() throws (testing::TestError) { |
|
| 2031 | - | let mut a = testResolver(); |
|
| 2032 | - | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: true, y: 2 });"; |
|
| 2033 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2034 | - | let err = try expectError(&result); |
|
| 2035 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 2036 | - | else throw testing::TestError::Failed; |
|
| 2516 | + | let mut testArena155 = testArena(); |
|
| 2517 | + | let testStorage155: 'test155 = &mut testArena155 in { |
|
| 2518 | + | let mut a = testResolver(testStorage155); |
|
| 2519 | + | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: true, y: 2 });"; |
|
| 2520 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2521 | + | let err = try expectError(&result); |
|
| 2522 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 2523 | + | else throw testing::TestError::Failed; |
|
| 2524 | + | } |
|
| 2037 | 2525 | } |
|
| 2038 | 2526 | ||
| 2039 | 2527 | /// Test that anonymous record with missing field causes a missing field error. |
|
| 2040 | 2528 | @test unsafe fn testResolveAnonRecordMissingField() throws (testing::TestError) { |
|
| 2041 | - | let mut a = testResolver(); |
|
| 2042 | - | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1 });"; |
|
| 2043 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2044 | - | try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y")); |
|
| 2529 | + | let mut testArena156 = testArena(); |
|
| 2530 | + | let testStorage156: 'test156 = &mut testArena156 in { |
|
| 2531 | + | let mut a = testResolver(testStorage156); |
|
| 2532 | + | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1 });"; |
|
| 2533 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2534 | + | try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y")); |
|
| 2535 | + | } |
|
| 2045 | 2536 | } |
|
| 2046 | 2537 | ||
| 2047 | 2538 | /// Test that anonymous record with extra field causes a count mismatch error. |
|
| 2048 | 2539 | @test unsafe fn testResolveAnonRecordExtraField() throws (testing::TestError) { |
|
| 2049 | - | let mut a = testResolver(); |
|
| 2050 | - | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, y: 2, z: 3 });"; |
|
| 2051 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2052 | - | let err = try expectError(&result); |
|
| 2053 | - | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 2054 | - | else throw testing::TestError::Failed; |
|
| 2540 | + | let mut testArena157 = testArena(); |
|
| 2541 | + | let testStorage157: 'test157 = &mut testArena157 in { |
|
| 2542 | + | let mut a = testResolver(testStorage157); |
|
| 2543 | + | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, y: 2, z: 3 });"; |
|
| 2544 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2545 | + | let err = try expectError(&result); |
|
| 2546 | + | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 2547 | + | else throw testing::TestError::Failed; |
|
| 2548 | + | } |
|
| 2055 | 2549 | } |
|
| 2056 | 2550 | ||
| 2057 | 2551 | /// Test that anonymous record fields can be coerced (e.g., i32 to optional). |
|
| 2058 | 2552 | @test unsafe fn testResolveAnonRecordFieldCoercion() throws (testing::TestError) { |
|
| 2059 | - | let mut a = testResolver(); |
|
| 2060 | - | let program = "record Opt { x: ?i32 } fn foo(p: Opt) {} foo({ x: 42 });"; |
|
| 2061 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2062 | - | try expectNoErrors(&result); |
|
| 2553 | + | let mut testArena158 = testArena(); |
|
| 2554 | + | let testStorage158: 'test158 = &mut testArena158 in { |
|
| 2555 | + | let mut a = testResolver(testStorage158); |
|
| 2556 | + | let program = "record Opt { x: ?i32 } fn foo(p: Opt) {} foo({ x: 42 });"; |
|
| 2557 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2558 | + | try expectNoErrors(&result); |
|
| 2559 | + | } |
|
| 2063 | 2560 | } |
|
| 2064 | 2561 | ||
| 2065 | 2562 | /// Test that arrays of anonymous records with labeled fields are allowed. |
|
| 2066 | 2563 | @test unsafe fn testResolveAnonRecordArray() throws (testing::TestError) { |
|
| 2067 | - | let mut a = testResolver(); |
|
| 2068 | - | let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4 }];"; |
|
| 2069 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2070 | - | try expectNoErrors(&result); |
|
| 2564 | + | let mut testArena159 = testArena(); |
|
| 2565 | + | let testStorage159: 'test159 = &mut testArena159 in { |
|
| 2566 | + | let mut a = testResolver(testStorage159); |
|
| 2567 | + | let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4 }];"; |
|
| 2568 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2569 | + | try expectNoErrors(&result); |
|
| 2570 | + | } |
|
| 2071 | 2571 | } |
|
| 2072 | 2572 | ||
| 2073 | 2573 | /// Test that arrays of anonymous records with extra fields cause count mismatch. |
|
| 2074 | 2574 | @test unsafe fn testResolveAnonRecordArrayMismatch() throws (testing::TestError) { |
|
| 2075 | - | let mut a = testResolver(); |
|
| 2076 | - | let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4, z: 5 }];"; |
|
| 2077 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2078 | - | let err = try expectError(&result); |
|
| 2079 | - | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 2080 | - | else throw testing::TestError::Failed; |
|
| 2575 | + | let mut testArena160 = testArena(); |
|
| 2576 | + | let testStorage160: 'test160 = &mut testArena160 in { |
|
| 2577 | + | let mut a = testResolver(testStorage160); |
|
| 2578 | + | let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4, z: 5 }];"; |
|
| 2579 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2580 | + | let err = try expectError(&result); |
|
| 2581 | + | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 2582 | + | else throw testing::TestError::Failed; |
|
| 2583 | + | } |
|
| 2081 | 2584 | } |
|
| 2082 | 2585 | ||
| 2083 | 2586 | /// Test that unlabeled record declarations are analyzed correctly. |
|
| 2084 | 2587 | @test unsafe fn testResolveUnlabeledRecordDecl() throws (testing::TestError) { |
|
| 2085 | - | let mut a = testResolver(); |
|
| 2086 | - | let program = "record R(i32, bool);"; |
|
| 2087 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2088 | - | try expectNoErrors(&result); |
|
| 2089 | - | ||
| 2090 | - | // Verify the type symbol was created with labeled=false. |
|
| 2091 | - | let nominalTy = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2092 | - | let case super::NominalType::Record(recordType) = *nominalTy |
|
| 2093 | - | else throw testing::TestError::Failed; |
|
| 2094 | - | try testing::expect(not recordType.labeled); |
|
| 2095 | - | try testing::expect(recordType.fields.len == 2); |
|
| 2096 | - | try testing::expect(recordType.fields[0].name == nil); |
|
| 2097 | - | try testing::expect(recordType.fields[1].name == nil); |
|
| 2588 | + | let mut testArena161 = testArena(); |
|
| 2589 | + | let testStorage161: 'test161 = &mut testArena161 in { |
|
| 2590 | + | let mut a = testResolver(testStorage161); |
|
| 2591 | + | let program = "record R(i32, bool);"; |
|
| 2592 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2593 | + | try expectNoErrors(&result); |
|
| 2594 | + | ||
| 2595 | + | // Verify the type symbol was created with labeled=false. |
|
| 2596 | + | let nominalTy = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2597 | + | let case super::NominalType::Record(recordType) = *nominalTy |
|
| 2598 | + | else throw testing::TestError::Failed; |
|
| 2599 | + | try testing::expect(not recordType.labeled); |
|
| 2600 | + | try testing::expect(recordType.fields.len == 2); |
|
| 2601 | + | try testing::expect(recordType.fields[0].name == nil); |
|
| 2602 | + | try testing::expect(recordType.fields[1].name == nil); |
|
| 2603 | + | } |
|
| 2098 | 2604 | } |
|
| 2099 | 2605 | ||
| 2100 | 2606 | @test unsafe fn testResolveLabeledRecordDecl() throws (testing::TestError) { |
|
| 2101 | - | let mut a = testResolver(); |
|
| 2102 | - | let program = "record R { x: i32, y: i32 }"; |
|
| 2103 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2104 | - | try expectNoErrors(&result); |
|
| 2607 | + | let mut testArena162 = testArena(); |
|
| 2608 | + | let testStorage162: 'test162 = &mut testArena162 in { |
|
| 2609 | + | let mut a = testResolver(testStorage162); |
|
| 2610 | + | let program = "record R { x: i32, y: i32 }"; |
|
| 2611 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2612 | + | try expectNoErrors(&result); |
|
| 2105 | 2613 | ||
| 2106 | - | let nominalTy = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2107 | - | let case super::NominalType::Record(recordType) = *nominalTy |
|
| 2108 | - | else throw testing::TestError::Failed; |
|
| 2109 | - | try testing::expect(recordType.labeled); |
|
| 2110 | - | try testing::expect(recordType.fields.len == 2); |
|
| 2111 | - | try testing::expect(recordType.fields[0].name <> nil); |
|
| 2112 | - | try testing::expect(recordType.fields[1].name <> nil); |
|
| 2614 | + | let nominalTy = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2615 | + | let case super::NominalType::Record(recordType) = *nominalTy |
|
| 2616 | + | else throw testing::TestError::Failed; |
|
| 2617 | + | try testing::expect(recordType.labeled); |
|
| 2618 | + | try testing::expect(recordType.fields.len == 2); |
|
| 2619 | + | try testing::expect(recordType.fields[0].name <> nil); |
|
| 2620 | + | try testing::expect(recordType.fields[1].name <> nil); |
|
| 2621 | + | } |
|
| 2113 | 2622 | } |
|
| 2114 | 2623 | ||
| 2115 | 2624 | @test unsafe fn testResolveRecordFieldAccessValid() throws (testing::TestError) { |
|
| 2116 | - | let mut a = testResolver(); |
|
| 2117 | - | let program = "record Pt { x: i32, y: u8 } let p = Pt { x: 1, y: 2 }; p.y;"; |
|
| 2118 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2119 | - | try expectNoErrors(&result); |
|
| 2625 | + | let mut testArena163 = testArena(); |
|
| 2626 | + | let testStorage163: 'test163 = &mut testArena163 in { |
|
| 2627 | + | let mut a = testResolver(testStorage163); |
|
| 2628 | + | let program = "record Pt { x: i32, y: u8 } let p = Pt { x: 1, y: 2 }; p.y;"; |
|
| 2629 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2630 | + | try expectNoErrors(&result); |
|
| 2120 | 2631 | ||
| 2121 | - | let fieldStmt = try getBlockStmt(result.root, 2); |
|
| 2122 | - | try expectExprStmtType(&a, fieldStmt, super::Type::U8); |
|
| 2632 | + | let fieldStmt = try getBlockStmt(result.root, 2); |
|
| 2633 | + | try expectExprStmtType(&a, fieldStmt, super::Type::U8); |
|
| 2634 | + | } |
|
| 2123 | 2635 | } |
|
| 2124 | 2636 | ||
| 2125 | 2637 | @test unsafe fn testResolveRecordFieldAccessUnknownField() throws (testing::TestError) { |
|
| 2126 | - | let mut a = testResolver(); |
|
| 2127 | - | let program = "record Pt { x: i32 } let p = Pt { x: 1 }; p.y;"; |
|
| 2128 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2129 | - | try expectErrorKind(&result, super::ErrorKind::RecordFieldUnknown("y")); |
|
| 2638 | + | let mut testArena164 = testArena(); |
|
| 2639 | + | let testStorage164: 'test164 = &mut testArena164 in { |
|
| 2640 | + | let mut a = testResolver(testStorage164); |
|
| 2641 | + | let program = "record Pt { x: i32 } let p = Pt { x: 1 }; p.y;"; |
|
| 2642 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2643 | + | try expectErrorKind(&result, super::ErrorKind::RecordFieldUnknown("y")); |
|
| 2644 | + | } |
|
| 2130 | 2645 | } |
|
| 2131 | 2646 | ||
| 2132 | 2647 | @test unsafe fn testResolveRecordFieldAccessOnFunctionReturn() throws (testing::TestError) { |
|
| 2133 | - | let mut a = testResolver(); |
|
| 2134 | - | let program = "record Pt { x: i32, y: i32 } fn make() -> Pt { return Pt { x: 5, y: 10 }; } make().x;"; |
|
| 2135 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2136 | - | try expectNoErrors(&result); |
|
| 2648 | + | let mut testArena165 = testArena(); |
|
| 2649 | + | let testStorage165: 'test165 = &mut testArena165 in { |
|
| 2650 | + | let mut a = testResolver(testStorage165); |
|
| 2651 | + | let program = "record Pt { x: i32, y: i32 } fn make() -> Pt { return Pt { x: 5, y: 10 }; } make().x;"; |
|
| 2652 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2653 | + | try expectNoErrors(&result); |
|
| 2137 | 2654 | ||
| 2138 | - | let stmt = try getBlockStmt(result.root, 2); |
|
| 2139 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2655 | + | let stmt = try getBlockStmt(result.root, 2); |
|
| 2656 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2657 | + | } |
|
| 2140 | 2658 | } |
|
| 2141 | 2659 | ||
| 2142 | 2660 | @test unsafe fn testResolveRecordFieldAccessChained() throws (testing::TestError) { |
|
| 2143 | - | let mut a = testResolver(); |
|
| 2144 | - | let program = "record C { value: i32 } record B { c: C } record A { b: B } let a = A { b: B { c: C { value: 100 } } }; a.b.c.value;"; |
|
| 2145 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2146 | - | try expectNoErrors(&result); |
|
| 2661 | + | let mut testArena166 = testArena(); |
|
| 2662 | + | let testStorage166: 'test166 = &mut testArena166 in { |
|
| 2663 | + | let mut a = testResolver(testStorage166); |
|
| 2664 | + | let program = "record C { value: i32 } record B { c: C } record A { b: B } let a = A { b: B { c: C { value: 100 } } }; a.b.c.value;"; |
|
| 2665 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2666 | + | try expectNoErrors(&result); |
|
| 2147 | 2667 | ||
| 2148 | - | let stmt = try getBlockStmt(result.root, 4); |
|
| 2149 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2668 | + | let stmt = try getBlockStmt(result.root, 4); |
|
| 2669 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2670 | + | } |
|
| 2150 | 2671 | } |
|
| 2151 | 2672 | ||
| 2152 | 2673 | @test unsafe fn testResolveRecordFieldAccessOnInteger() throws (testing::TestError) { |
|
| 2153 | - | let mut a = testResolver(); |
|
| 2154 | - | let program = "let x: i32 = 42; x.field;"; |
|
| 2155 | - | let result = try resolveBlockStr(&mut a, program); |
|
| 2156 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2674 | + | let mut testArena167 = testArena(); |
|
| 2675 | + | let testStorage167: 'test167 = &mut testArena167 in { |
|
| 2676 | + | let mut a = testResolver(testStorage167); |
|
| 2677 | + | let program = "let x: i32 = 42; x.field;"; |
|
| 2678 | + | let result = try resolveBlockStr(&mut a, program); |
|
| 2679 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2680 | + | } |
|
| 2157 | 2681 | } |
|
| 2158 | 2682 | ||
| 2159 | 2683 | @test unsafe fn testResolveRecordFieldAccessOnArray() throws (testing::TestError) { |
|
| 2160 | - | let mut a = testResolver(); |
|
| 2161 | - | let program = "let arr: [i32; 3] = [1, 2, 3]; arr.field;"; |
|
| 2162 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2163 | - | try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field")); |
|
| 2684 | + | let mut testArena168 = testArena(); |
|
| 2685 | + | let testStorage168: 'test168 = &mut testArena168 in { |
|
| 2686 | + | let mut a = testResolver(testStorage168); |
|
| 2687 | + | let program = "let arr: [i32; 3] = [1, 2, 3]; arr.field;"; |
|
| 2688 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2689 | + | try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field")); |
|
| 2690 | + | } |
|
| 2164 | 2691 | } |
|
| 2165 | 2692 | ||
| 2166 | 2693 | @test unsafe fn testResolveRecordFieldAccessOnBool() throws (testing::TestError) { |
|
| 2167 | - | let mut a = testResolver(); |
|
| 2168 | - | let program = "let b: bool = true; b.field;"; |
|
| 2169 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2170 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2694 | + | let mut testArena169 = testArena(); |
|
| 2695 | + | let testStorage169: 'test169 = &mut testArena169 in { |
|
| 2696 | + | let mut a = testResolver(testStorage169); |
|
| 2697 | + | let program = "let b: bool = true; b.field;"; |
|
| 2698 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2699 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2700 | + | } |
|
| 2171 | 2701 | } |
|
| 2172 | 2702 | ||
| 2173 | 2703 | @test unsafe fn testResolveRecordFieldAccessOnOptional() throws (testing::TestError) { |
|
| 2174 | - | let mut a = testResolver(); |
|
| 2175 | - | let program = "record Pt { x: i32 } let opt: ?Pt = Pt { x: 5 }; opt.x;"; |
|
| 2176 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2177 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2704 | + | let mut testArena170 = testArena(); |
|
| 2705 | + | let testStorage170: 'test170 = &mut testArena170 in { |
|
| 2706 | + | let mut a = testResolver(testStorage170); |
|
| 2707 | + | let program = "record Pt { x: i32 } let opt: ?Pt = Pt { x: 5 }; opt.x;"; |
|
| 2708 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2709 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2710 | + | } |
|
| 2178 | 2711 | } |
|
| 2179 | 2712 | ||
| 2180 | 2713 | /// Records may reference themselves through pointers without causing resolution errors. |
|
| 2181 | 2714 | @test unsafe fn testResolveRecordSelfReferentialPointer() throws (testing::TestError) { |
|
| 2182 | - | let mut a = testResolver(); |
|
| 2183 | - | let program = "record A { next: *A }"; |
|
| 2184 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2185 | - | try expectNoErrors(&result); |
|
| 2715 | + | let mut testArena171 = testArena(); |
|
| 2716 | + | let testStorage171: 'test171 = &mut testArena171 in { |
|
| 2717 | + | let mut a = testResolver(testStorage171); |
|
| 2718 | + | let program = "record A { next: *A }"; |
|
| 2719 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2720 | + | try expectNoErrors(&result); |
|
| 2721 | + | } |
|
| 2186 | 2722 | } |
|
| 2187 | 2723 | ||
| 2188 | 2724 | /// Mutually recursive records should resolve without infinite loops. |
|
| 2189 | 2725 | @test unsafe fn testResolveRecordMutuallyRecursive() throws (testing::TestError) { |
|
| 2190 | - | let mut a = testResolver(); |
|
| 2191 | - | let program = "record A { b: *B } record B { a: *A }"; |
|
| 2192 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2193 | - | try expectNoErrors(&result); |
|
| 2726 | + | let mut testArena172 = testArena(); |
|
| 2727 | + | let testStorage172: 'test172 = &mut testArena172 in { |
|
| 2728 | + | let mut a = testResolver(testStorage172); |
|
| 2729 | + | let program = "record A { b: *B } record B { a: *A }"; |
|
| 2730 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2731 | + | try expectNoErrors(&result); |
|
| 2732 | + | } |
|
| 2194 | 2733 | } |
|
| 2195 | 2734 | ||
| 2196 | 2735 | /// Unions may reference themselves through pointers without causing resolution errors. |
|
| 2197 | 2736 | @test unsafe fn testResolveUnionSelfReferentialPointerAllowed() throws (testing::TestError) { |
|
| 2198 | - | let mut a = testResolver(); |
|
| 2199 | - | let program = "union List { Cons(*List), Nil }"; |
|
| 2200 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2201 | - | try expectNoErrors(&result); |
|
| 2737 | + | let mut testArena173 = testArena(); |
|
| 2738 | + | let testStorage173: 'test173 = &mut testArena173 in { |
|
| 2739 | + | let mut a = testResolver(testStorage173); |
|
| 2740 | + | let program = "union List { Cons(*List), Nil }"; |
|
| 2741 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2742 | + | try expectNoErrors(&result); |
|
| 2743 | + | } |
|
| 2202 | 2744 | } |
|
| 2203 | 2745 | ||
| 2204 | 2746 | /// Mutually recursive unions should resolve without infinite loops. |
|
| 2205 | 2747 | @test unsafe fn testResolveUnionMutuallyRecursive() throws (testing::TestError) { |
|
| 2206 | - | let mut a = testResolver(); |
|
| 2207 | - | let program = "union A { HasB(*B), None } union B { HasA(*A), None }"; |
|
| 2208 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2209 | - | try expectNoErrors(&result); |
|
| 2748 | + | let mut testArena174 = testArena(); |
|
| 2749 | + | let testStorage174: 'test174 = &mut testArena174 in { |
|
| 2750 | + | let mut a = testResolver(testStorage174); |
|
| 2751 | + | let program = "union A { HasB(*B), None } union B { HasA(*A), None }"; |
|
| 2752 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2753 | + | try expectNoErrors(&result); |
|
| 2754 | + | } |
|
| 2210 | 2755 | } |
|
| 2211 | 2756 | ||
| 2212 | 2757 | /// Unions with record payloads containing slice references to self should resolve. |
|
| 2213 | 2758 | /// This matches the pattern in sexpr.rad: `List { tail: *[Expr] }`. |
|
| 2214 | 2759 | @test unsafe fn testResolveUnionRecordPayloadWithSliceSelfRef() throws (testing::TestError) { |
|
| 2215 | - | let mut a = testResolver(); |
|
| 2216 | - | let program = "union Expr { Null, List { head: *[u8], tail: *[Expr] } }"; |
|
| 2217 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2218 | - | try expectNoErrors(&result); |
|
| 2760 | + | let mut testArena175 = testArena(); |
|
| 2761 | + | let testStorage175: 'test175 = &mut testArena175 in { |
|
| 2762 | + | let mut a = testResolver(testStorage175); |
|
| 2763 | + | let program = "union Expr { Null, List { head: *[u8], tail: *[Expr] } }"; |
|
| 2764 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2765 | + | try expectNoErrors(&result); |
|
| 2766 | + | } |
|
| 2219 | 2767 | } |
|
| 2220 | 2768 | ||
| 2221 | 2769 | @test unsafe fn testUndefinedCoercions() throws (testing::TestError) { |
|
| 2222 | 2770 | { |
|
| 2223 | - | let mut a = testResolver(); |
|
| 2224 | - | let result = try resolveBlockStr(&mut a, "unsafe { let count: i32 = undefined; }"); |
|
| 2225 | - | try expectNoErrors(&result); |
|
| 2771 | + | let mut testArena176 = testArena(); |
|
| 2772 | + | let testStorage176: 'test176 = &mut testArena176 in { |
|
| 2773 | + | let mut a = testResolver(testStorage176); |
|
| 2774 | + | let result = try resolveBlockStr(&mut a, "unsafe { let count: i32 = undefined; }"); |
|
| 2775 | + | try expectNoErrors(&result); |
|
| 2776 | + | } |
|
| 2226 | 2777 | } { |
|
| 2227 | - | let mut a = testResolver(); |
|
| 2228 | - | let program = "unsafe { let mut value: i32 = 0; set value = undefined; }"; |
|
| 2229 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2230 | - | try expectNoErrors(&result); |
|
| 2778 | + | let mut testArena177 = testArena(); |
|
| 2779 | + | let testStorage177: 'test177 = &mut testArena177 in { |
|
| 2780 | + | let mut a = testResolver(testStorage177); |
|
| 2781 | + | let program = "unsafe { let mut value: i32 = 0; set value = undefined; }"; |
|
| 2782 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2783 | + | try expectNoErrors(&result); |
|
| 2784 | + | } |
|
| 2231 | 2785 | } { |
|
| 2232 | - | let mut a = testResolver(); |
|
| 2233 | - | let program = "fn f(x: i32) {} unsafe fn g() { f(undefined); }"; |
|
| 2234 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2235 | - | try expectNoErrors(&result); |
|
| 2786 | + | let mut testArena178 = testArena(); |
|
| 2787 | + | let testStorage178: 'test178 = &mut testArena178 in { |
|
| 2788 | + | let mut a = testResolver(testStorage178); |
|
| 2789 | + | let program = "fn f(x: i32) {} unsafe fn g() { f(undefined); }"; |
|
| 2790 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2791 | + | try expectNoErrors(&result); |
|
| 2792 | + | } |
|
| 2236 | 2793 | } { |
|
| 2237 | - | let mut a = testResolver(); |
|
| 2238 | - | let program = "unsafe fn fetch() -> i32 { return undefined; }"; |
|
| 2239 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2240 | - | try expectNoErrors(&result); |
|
| 2794 | + | let mut testArena179 = testArena(); |
|
| 2795 | + | let testStorage179: 'test179 = &mut testArena179 in { |
|
| 2796 | + | let mut a = testResolver(testStorage179); |
|
| 2797 | + | let program = "unsafe fn fetch() -> i32 { return undefined; }"; |
|
| 2798 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2799 | + | try expectNoErrors(&result); |
|
| 2800 | + | } |
|
| 2241 | 2801 | } |
|
| 2242 | 2802 | } |
|
| 2243 | 2803 | ||
| 2244 | 2804 | @test unsafe fn testResolveBlockVoid() throws (testing::TestError) { |
|
| 2245 | - | let mut a = testResolver(); |
|
| 2246 | - | let result = try resolveProgramStr(&mut a, "{ 42; }"); |
|
| 2247 | - | try expectNoErrors(&result); |
|
| 2805 | + | let mut testArena180 = testArena(); |
|
| 2806 | + | let testStorage180: 'test180 = &mut testArena180 in { |
|
| 2807 | + | let mut a = testResolver(testStorage180); |
|
| 2808 | + | let result = try resolveProgramStr(&mut a, "{ 42; }"); |
|
| 2809 | + | try expectNoErrors(&result); |
|
| 2248 | 2810 | ||
| 2249 | - | let block = try getBlockStmt(result.root, 0); |
|
| 2250 | - | try expectType(&a, block, super::Type::Void); |
|
| 2811 | + | let block = try getBlockStmt(result.root, 0); |
|
| 2812 | + | try expectType(&a, block, super::Type::Void); |
|
| 2813 | + | } |
|
| 2251 | 2814 | } |
|
| 2252 | 2815 | ||
| 2253 | 2816 | @test unsafe fn testResolveBlockNever() throws (testing::TestError) { |
|
| 2254 | - | let mut a = testResolver(); |
|
| 2255 | - | let result = try resolveProgramStr(&mut a, "{ panic; }"); |
|
| 2256 | - | try expectNoErrors(&result); |
|
| 2817 | + | let mut testArena181 = testArena(); |
|
| 2818 | + | let testStorage181: 'test181 = &mut testArena181 in { |
|
| 2819 | + | let mut a = testResolver(testStorage181); |
|
| 2820 | + | let result = try resolveProgramStr(&mut a, "{ panic; }"); |
|
| 2821 | + | try expectNoErrors(&result); |
|
| 2257 | 2822 | ||
| 2258 | - | let block = try getBlockStmt(result.root, 0); |
|
| 2259 | - | try expectType(&a, block, super::Type::Never); |
|
| 2823 | + | let block = try getBlockStmt(result.root, 0); |
|
| 2824 | + | try expectType(&a, block, super::Type::Never); |
|
| 2825 | + | } |
|
| 2260 | 2826 | } |
|
| 2261 | 2827 | ||
| 2262 | 2828 | @test unsafe fn testResolveIfAllBranchesNever() throws (testing::TestError) { |
|
| 2263 | - | let mut a = testResolver(); |
|
| 2264 | - | let program = "if true { panic; } else { panic; }"; |
|
| 2265 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2266 | - | try expectNoErrors(&result); |
|
| 2829 | + | let mut testArena182 = testArena(); |
|
| 2830 | + | let testStorage182: 'test182 = &mut testArena182 in { |
|
| 2831 | + | let mut a = testResolver(testStorage182); |
|
| 2832 | + | let program = "if true { panic; } else { panic; }"; |
|
| 2833 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2834 | + | try expectNoErrors(&result); |
|
| 2267 | 2835 | ||
| 2268 | - | let stmt = try getBlockStmt(result.root, 0); |
|
| 2269 | - | try expectType(&a, stmt, super::Type::Never); |
|
| 2836 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 2837 | + | try expectType(&a, stmt, super::Type::Never); |
|
| 2838 | + | } |
|
| 2270 | 2839 | } |
|
| 2271 | 2840 | ||
| 2272 | 2841 | @test unsafe fn testResolveIfMixedBranchesNotNever() throws (testing::TestError) { |
|
| 2273 | - | let mut a = testResolver(); |
|
| 2274 | - | let program = "if true { panic; } else {}"; |
|
| 2275 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2276 | - | try expectNoErrors(&result); |
|
| 2842 | + | let mut testArena183 = testArena(); |
|
| 2843 | + | let testStorage183: 'test183 = &mut testArena183 in { |
|
| 2844 | + | let mut a = testResolver(testStorage183); |
|
| 2845 | + | let program = "if true { panic; } else {}"; |
|
| 2846 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2847 | + | try expectNoErrors(&result); |
|
| 2277 | 2848 | ||
| 2278 | - | let stmt = try getBlockStmt(result.root, 0); |
|
| 2279 | - | try expectType(&a, stmt, super::Type::Void); |
|
| 2849 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 2850 | + | try expectType(&a, stmt, super::Type::Void); |
|
| 2851 | + | } |
|
| 2280 | 2852 | } |
|
| 2281 | 2853 | ||
| 2282 | 2854 | @test unsafe fn testResolveLetElse() throws (testing::TestError) { |
|
| 2283 | - | let mut a = testResolver(); |
|
| 2284 | - | let program = "let opt: ?i32 = 42; let value = opt else panic; value;"; |
|
| 2285 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2286 | - | try expectNoErrors(&result); |
|
| 2855 | + | let mut testArena184 = testArena(); |
|
| 2856 | + | let testStorage184: 'test184 = &mut testArena184 in { |
|
| 2857 | + | let mut a = testResolver(testStorage184); |
|
| 2858 | + | let program = "let opt: ?i32 = 42; let value = opt else panic; value;"; |
|
| 2859 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2860 | + | try expectNoErrors(&result); |
|
| 2287 | 2861 | ||
| 2288 | - | let blockNode = result.root; |
|
| 2289 | - | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 2290 | - | else throw testing::TestError::Failed; |
|
| 2291 | - | let letElseNode = try getBlockStmt(blockNode, 1); |
|
| 2292 | - | let valueStmt = try getBlockStmt(blockNode, 2); |
|
| 2862 | + | let blockNode = result.root; |
|
| 2863 | + | let case ast::NodeValue::Block(block) = blockNode.value |
|
| 2864 | + | else throw testing::TestError::Failed; |
|
| 2865 | + | let letElseNode = try getBlockStmt(blockNode, 1); |
|
| 2866 | + | let valueStmt = try getBlockStmt(blockNode, 2); |
|
| 2293 | 2867 | ||
| 2294 | - | { // Ensure the bound identifier receives the inner optional type. |
|
| 2295 | - | let valueExpr = try expectExprStmtType(&a, valueStmt, super::Type::I32); |
|
| 2868 | + | { // Ensure the bound identifier receives the inner optional type. |
|
| 2869 | + | let valueExpr = try expectExprStmtType(&a, valueStmt, super::Type::I32); |
|
| 2296 | 2870 | ||
| 2297 | - | let sym = super::symbolFor(&a, valueExpr) |
|
| 2298 | - | else throw testing::TestError::Failed; |
|
| 2299 | - | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 2300 | - | else throw testing::TestError::Failed; |
|
| 2301 | - | try testing::expect(valType == super::Type::I32); |
|
| 2871 | + | let sym = super::symbolFor(&a, valueExpr) |
|
| 2872 | + | else throw testing::TestError::Failed; |
|
| 2873 | + | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 2874 | + | else throw testing::TestError::Failed; |
|
| 2875 | + | try testing::expect(valType == super::Type::I32); |
|
| 2876 | + | } |
|
| 2877 | + | // The let-else statement itself should be typed as void. |
|
| 2878 | + | try expectType(&a, letElseNode, super::Type::Void); |
|
| 2302 | 2879 | } |
|
| 2303 | - | // The let-else statement itself should be typed as void. |
|
| 2304 | - | try expectType(&a, letElseNode, super::Type::Void); |
|
| 2305 | 2880 | } |
|
| 2306 | 2881 | ||
| 2307 | 2882 | @test unsafe fn testResolveLetElseDefaultValue() throws (testing::TestError) { |
|
| 2308 | - | let mut a = testResolver(); |
|
| 2309 | - | let program = "let opt: ?i32 = nil; let value = opt else 42; value;"; |
|
| 2310 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2311 | - | try expectNoErrors(&result); |
|
| 2883 | + | let mut testArena185 = testArena(); |
|
| 2884 | + | let testStorage185: 'test185 = &mut testArena185 in { |
|
| 2885 | + | let mut a = testResolver(testStorage185); |
|
| 2886 | + | let program = "let opt: ?i32 = nil; let value = opt else 42; value;"; |
|
| 2887 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2888 | + | try expectNoErrors(&result); |
|
| 2889 | + | } |
|
| 2312 | 2890 | } |
|
| 2313 | 2891 | ||
| 2314 | 2892 | @test unsafe fn testResolveLetElseRequiresDivergentElse() throws (testing::TestError) { |
|
| 2315 | - | let mut a = testResolver(); |
|
| 2316 | - | let program = "let opt: ?i32 = nil; let value = opt else {}; value;"; |
|
| 2317 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2318 | - | let err = try expectError(&result); |
|
| 2319 | - | try expectTypeMismatch(err, super::Type::I32, super::Type::Void); |
|
| 2893 | + | let mut testArena186 = testArena(); |
|
| 2894 | + | let testStorage186: 'test186 = &mut testArena186 in { |
|
| 2895 | + | let mut a = testResolver(testStorage186); |
|
| 2896 | + | let program = "let opt: ?i32 = nil; let value = opt else {}; value;"; |
|
| 2897 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2898 | + | let err = try expectError(&result); |
|
| 2899 | + | try expectTypeMismatch(err, super::Type::I32, super::Type::Void); |
|
| 2900 | + | } |
|
| 2320 | 2901 | } |
|
| 2321 | 2902 | ||
| 2322 | 2903 | @test unsafe fn testResolveLetElseRequiresOptional() throws (testing::TestError) { |
|
| 2323 | - | let mut a = testResolver(); |
|
| 2324 | - | let program = "let x: i32 = 42; let value = x else panic;"; |
|
| 2325 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2326 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedOptional); |
|
| 2904 | + | let mut testArena187 = testArena(); |
|
| 2905 | + | let testStorage187: 'test187 = &mut testArena187 in { |
|
| 2906 | + | let mut a = testResolver(testStorage187); |
|
| 2907 | + | let program = "let x: i32 = 42; let value = x else panic;"; |
|
| 2908 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2909 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedOptional); |
|
| 2910 | + | } |
|
| 2327 | 2911 | } |
|
| 2328 | 2912 | ||
| 2329 | 2913 | /// Test that `if let mut` produces a mutable binding. |
|
| 2330 | 2914 | @test unsafe fn testResolveIfLetMut() throws (testing::TestError) { |
|
| 2331 | - | let mut a = testResolver(); |
|
| 2332 | - | let program = "let opt: ?i32 = 42; if let mut v = opt { set v = v + 1; }"; |
|
| 2333 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2334 | - | try expectNoErrors(&result); |
|
| 2915 | + | let mut testArena188 = testArena(); |
|
| 2916 | + | let testStorage188: 'test188 = &mut testArena188 in { |
|
| 2917 | + | let mut a = testResolver(testStorage188); |
|
| 2918 | + | let program = "let opt: ?i32 = 42; if let mut v = opt { set v = v + 1; }"; |
|
| 2919 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2920 | + | try expectNoErrors(&result); |
|
| 2921 | + | } |
|
| 2335 | 2922 | } |
|
| 2336 | 2923 | ||
| 2337 | 2924 | /// Test that `if let` (without mut) rejects assignment. |
|
| 2338 | 2925 | @test unsafe fn testResolveIfLetImmutable() throws (testing::TestError) { |
|
| 2339 | - | let mut a = testResolver(); |
|
| 2340 | - | let program = "let opt: ?i32 = 42; if let v = opt { set v = 1; }"; |
|
| 2341 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2342 | - | let err = try expectError(&result); |
|
| 2343 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2926 | + | let mut testArena189 = testArena(); |
|
| 2927 | + | let testStorage189: 'test189 = &mut testArena189 in { |
|
| 2928 | + | let mut a = testResolver(testStorage189); |
|
| 2929 | + | let program = "let opt: ?i32 = 42; if let v = opt { set v = 1; }"; |
|
| 2930 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2931 | + | let err = try expectError(&result); |
|
| 2932 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2933 | + | } |
|
| 2344 | 2934 | } |
|
| 2345 | 2935 | ||
| 2346 | 2936 | /// Test that `let mut ... else` produces a mutable binding. |
|
| 2347 | 2937 | @test unsafe fn testResolveLetMutElse() throws (testing::TestError) { |
|
| 2348 | - | let mut a = testResolver(); |
|
| 2349 | - | let program = "let opt: ?i32 = 42; let mut v = opt else panic; set v = v + 1;"; |
|
| 2350 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2351 | - | try expectNoErrors(&result); |
|
| 2938 | + | let mut testArena190 = testArena(); |
|
| 2939 | + | let testStorage190: 'test190 = &mut testArena190 in { |
|
| 2940 | + | let mut a = testResolver(testStorage190); |
|
| 2941 | + | let program = "let opt: ?i32 = 42; let mut v = opt else panic; set v = v + 1;"; |
|
| 2942 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2943 | + | try expectNoErrors(&result); |
|
| 2944 | + | } |
|
| 2352 | 2945 | } |
|
| 2353 | 2946 | ||
| 2354 | 2947 | /// Test that `let ... else` (without mut) rejects assignment. |
|
| 2355 | 2948 | @test unsafe fn testResolveLetElseImmutable() throws (testing::TestError) { |
|
| 2356 | - | let mut a = testResolver(); |
|
| 2357 | - | let program = "let opt: ?i32 = 42; let v = opt else panic; set v = 1;"; |
|
| 2358 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2359 | - | let err = try expectError(&result); |
|
| 2360 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2949 | + | let mut testArena191 = testArena(); |
|
| 2950 | + | let testStorage191: 'test191 = &mut testArena191 in { |
|
| 2951 | + | let mut a = testResolver(testStorage191); |
|
| 2952 | + | let program = "let opt: ?i32 = 42; let v = opt else panic; set v = 1;"; |
|
| 2953 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2954 | + | let err = try expectError(&result); |
|
| 2955 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2956 | + | } |
|
| 2361 | 2957 | } |
|
| 2362 | 2958 | ||
| 2363 | 2959 | @test unsafe fn testResolveLetCaseElse() throws (testing::TestError) { |
|
| 2364 | 2960 | { |
|
| 2365 | - | let mut a = testResolver(); |
|
| 2366 | - | let program = "let case _ = 1 else panic;"; |
|
| 2367 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2368 | - | try expectNoErrors(&result); |
|
| 2961 | + | let mut testArena192 = testArena(); |
|
| 2962 | + | let testStorage192: 'test192 = &mut testArena192 in { |
|
| 2963 | + | let mut a = testResolver(testStorage192); |
|
| 2964 | + | let program = "let case _ = 1 else panic;"; |
|
| 2965 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2966 | + | try expectNoErrors(&result); |
|
| 2967 | + | } |
|
| 2369 | 2968 | } { |
|
| 2370 | - | let mut a = testResolver(); |
|
| 2371 | - | let program = "let case _ = true else false;"; |
|
| 2372 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2373 | - | try expectNoErrors(&result); |
|
| 2969 | + | let mut testArena193 = testArena(); |
|
| 2970 | + | let testStorage193: 'test193 = &mut testArena193 in { |
|
| 2971 | + | let mut a = testResolver(testStorage193); |
|
| 2972 | + | let program = "let case _ = true else false;"; |
|
| 2973 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2974 | + | try expectNoErrors(&result); |
|
| 2975 | + | } |
|
| 2374 | 2976 | } |
|
| 2375 | 2977 | } |
|
| 2376 | 2978 | ||
| 2377 | 2979 | @test unsafe fn testResolveLetCaseElseRequiresDivergentElse() throws (testing::TestError) { |
|
| 2378 | - | let mut a = testResolver(); |
|
| 2379 | - | let program = "let case _ = 1 else {};"; |
|
| 2380 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2381 | - | let err = try expectError(&result); |
|
| 2382 | - | try expectTypeMismatch(err, super::Type::Int, super::Type::Void); |
|
| 2980 | + | let mut testArena194 = testArena(); |
|
| 2981 | + | let testStorage194: 'test194 = &mut testArena194 in { |
|
| 2982 | + | let mut a = testResolver(testStorage194); |
|
| 2983 | + | let program = "let case _ = 1 else {};"; |
|
| 2984 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2985 | + | let err = try expectError(&result); |
|
| 2986 | + | try expectTypeMismatch(err, super::Type::Int, super::Type::Void); |
|
| 2987 | + | } |
|
| 2383 | 2988 | } |
|
| 2384 | 2989 | ||
| 2385 | 2990 | @test unsafe fn testResolveTryValidPropagation() throws (testing::TestError) { |
|
| 2386 | - | let mut a = testResolver(); |
|
| 2387 | - | let program = "fn fallible() throws (i32) {} fn caller() throws (i32) { try fallible() }"; |
|
| 2388 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2389 | - | try expectNoErrors(&result); |
|
| 2991 | + | let mut testArena195 = testArena(); |
|
| 2992 | + | let testStorage195: 'test195 = &mut testArena195 in { |
|
| 2993 | + | let mut a = testResolver(testStorage195); |
|
| 2994 | + | let program = "fn fallible() throws (i32) {} fn caller() throws (i32) { try fallible() }"; |
|
| 2995 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2996 | + | try expectNoErrors(&result); |
|
| 2997 | + | } |
|
| 2390 | 2998 | } |
|
| 2391 | 2999 | ||
| 2392 | 3000 | @test unsafe fn testResolveTryRequiresThrowsClause() throws (testing::TestError) { |
|
| 2393 | - | let mut a = testResolver(); |
|
| 2394 | - | let program = "fn fallible() throws (i32) {} fn caller() { try fallible() }"; |
|
| 2395 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2396 | - | try expectErrorKind(&result, super::ErrorKind::TryRequiresThrows); |
|
| 3001 | + | let mut testArena196 = testArena(); |
|
| 3002 | + | let testStorage196: 'test196 = &mut testArena196 in { |
|
| 3003 | + | let mut a = testResolver(testStorage196); |
|
| 3004 | + | let program = "fn fallible() throws (i32) {} fn caller() { try fallible() }"; |
|
| 3005 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3006 | + | try expectErrorKind(&result, super::ErrorKind::TryRequiresThrows); |
|
| 3007 | + | } |
|
| 2397 | 3008 | } |
|
| 2398 | 3009 | ||
| 2399 | 3010 | @test unsafe fn testResolveTryIncompatibleError() throws (testing::TestError) { |
|
| 2400 | - | let mut a = testResolver(); |
|
| 2401 | - | let program = "fn fallible() throws (i32) {} fn caller() throws (i8) { try fallible() }"; |
|
| 2402 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2403 | - | try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError); |
|
| 3011 | + | let mut testArena197 = testArena(); |
|
| 3012 | + | let testStorage197: 'test197 = &mut testArena197 in { |
|
| 3013 | + | let mut a = testResolver(testStorage197); |
|
| 3014 | + | let program = "fn fallible() throws (i32) {} fn caller() throws (i8) { try fallible() }"; |
|
| 3015 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3016 | + | try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError); |
|
| 3017 | + | } |
|
| 2404 | 3018 | } |
|
| 2405 | 3019 | ||
| 2406 | 3020 | @test unsafe fn testResolveTryNonThrowing() throws (testing::TestError) { |
|
| 2407 | - | let mut a = testResolver(); |
|
| 2408 | - | let program = "fn safe() {} fn caller() throws (i32) { try safe() }"; |
|
| 2409 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2410 | - | try expectErrorKind(&result, super::ErrorKind::TryNonThrowing); |
|
| 3021 | + | let mut testArena198 = testArena(); |
|
| 3022 | + | let testStorage198: 'test198 = &mut testArena198 in { |
|
| 3023 | + | let mut a = testResolver(testStorage198); |
|
| 3024 | + | let program = "fn safe() {} fn caller() throws (i32) { try safe() }"; |
|
| 3025 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3026 | + | try expectErrorKind(&result, super::ErrorKind::TryNonThrowing); |
|
| 3027 | + | } |
|
| 2411 | 3028 | } |
|
| 2412 | 3029 | ||
| 2413 | 3030 | @test unsafe fn testResolveTryCatchBlockMatchesResult() throws (testing::TestError) { |
|
| 2414 | - | let mut a = testResolver(); |
|
| 2415 | - | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 42; }; }"; |
|
| 2416 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2417 | - | try expectNoErrors(&result); |
|
| 3031 | + | let mut testArena199 = testArena(); |
|
| 3032 | + | let testStorage199: 'test199 = &mut testArena199 in { |
|
| 3033 | + | let mut a = testResolver(testStorage199); |
|
| 3034 | + | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 42; }; }"; |
|
| 3035 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3036 | + | try expectNoErrors(&result); |
|
| 3037 | + | } |
|
| 2418 | 3038 | } |
|
| 2419 | 3039 | ||
| 2420 | 3040 | @test unsafe fn testResolveTryCatchBlockDiverges() throws (testing::TestError) { |
|
| 2421 | - | let mut a = testResolver(); |
|
| 2422 | - | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 7; }; }"; |
|
| 2423 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2424 | - | try expectNoErrors(&result); |
|
| 3041 | + | let mut testArena200 = testArena(); |
|
| 3042 | + | let testStorage200: 'test200 = &mut testArena200 in { |
|
| 3043 | + | let mut a = testResolver(testStorage200); |
|
| 3044 | + | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 7; }; }"; |
|
| 3045 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3046 | + | try expectNoErrors(&result); |
|
| 3047 | + | } |
|
| 2425 | 3048 | } |
|
| 2426 | 3049 | ||
| 2427 | 3050 | @test unsafe fn testResolveTryCatchBlockMustDiverge() throws (testing::TestError) { |
|
| 2428 | - | let mut a = testResolver(); |
|
| 2429 | - | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { 7; }; }"; |
|
| 2430 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2431 | - | let err = try expectError(&result); |
|
| 2432 | - | try expectTypeMismatch(err, super::Type::U32, super::Type::Void); |
|
| 3051 | + | let mut testArena201 = testArena(); |
|
| 3052 | + | let testStorage201: 'test201 = &mut testArena201 in { |
|
| 3053 | + | let mut a = testResolver(testStorage201); |
|
| 3054 | + | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { 7; }; }"; |
|
| 3055 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3056 | + | let err = try expectError(&result); |
|
| 3057 | + | try expectTypeMismatch(err, super::Type::U32, super::Type::Void); |
|
| 3058 | + | } |
|
| 2433 | 3059 | } |
|
| 2434 | 3060 | ||
| 2435 | 3061 | @test unsafe fn testResolveCallMissingTry() throws (testing::TestError) { |
|
| 2436 | - | let mut a = testResolver(); |
|
| 2437 | - | let program = "fn fallible() throws (i32) {} fn caller() { fallible() }"; |
|
| 2438 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2439 | - | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
|
| 3062 | + | let mut testArena202 = testArena(); |
|
| 3063 | + | let testStorage202: 'test202 = &mut testArena202 in { |
|
| 3064 | + | let mut a = testResolver(testStorage202); |
|
| 3065 | + | let program = "fn fallible() throws (i32) {} fn caller() { fallible() }"; |
|
| 3066 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3067 | + | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
|
| 3068 | + | } |
|
| 2440 | 3069 | } |
|
| 2441 | 3070 | ||
| 2442 | 3071 | /// Test that `try?` converts errors to optionals without requiring caller to throw. |
|
| 2443 | 3072 | @test unsafe fn testResolveTryOptionalConvertsToOptional() throws (testing::TestError) { |
|
| 2444 | 3073 | // `try?` should wrap the return type in optional and not require caller to throw. |
|
| 2445 | 3074 | { |
|
| 2446 | - | let mut a = testResolver(); |
|
| 2447 | - | let program = "record S {} fn fallible() -> *S throws (i32) { panic; } fn caller() -> ?*S { return try? fallible(); }"; |
|
| 2448 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2449 | - | try expectNoErrors(&result); |
|
| 3075 | + | let mut testArena203 = testArena(); |
|
| 3076 | + | let testStorage203: 'test203 = &mut testArena203 in { |
|
| 3077 | + | let mut a = testResolver(testStorage203); |
|
| 3078 | + | let program = "record S {} fn fallible() -> *S throws (i32) { panic; } fn caller() -> ?*S { return try? fallible(); }"; |
|
| 3079 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3080 | + | try expectNoErrors(&result); |
|
| 3081 | + | } |
|
| 2450 | 3082 | } |
|
| 2451 | 3083 | // `try?` works in non-throwing function. |
|
| 2452 | 3084 | { |
|
| 2453 | - | let mut a = testResolver(); |
|
| 2454 | - | let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> ?i32 { return try? fallible(); }"; |
|
| 2455 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2456 | - | try expectNoErrors(&result); |
|
| 3085 | + | let mut testArena204 = testArena(); |
|
| 3086 | + | let testStorage204: 'test204 = &mut testArena204 in { |
|
| 3087 | + | let mut a = testResolver(testStorage204); |
|
| 3088 | + | let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> ?i32 { return try? fallible(); }"; |
|
| 3089 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3090 | + | try expectNoErrors(&result); |
|
| 3091 | + | } |
|
| 2457 | 3092 | } |
|
| 2458 | 3093 | // `try?` can be used in if-let patterns. |
|
| 2459 | 3094 | { |
|
| 2460 | - | let mut a = testResolver(); |
|
| 2461 | - | let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> i32 { if let x = try? fallible() { return x; } return 0; }"; |
|
| 2462 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2463 | - | try expectNoErrors(&result); |
|
| 3095 | + | let mut testArena205 = testArena(); |
|
| 3096 | + | let testStorage205: 'test205 = &mut testArena205 in { |
|
| 3097 | + | let mut a = testResolver(testStorage205); |
|
| 3098 | + | let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> i32 { if let x = try? fallible() { return x; } return 0; }"; |
|
| 3099 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3100 | + | try expectNoErrors(&result); |
|
| 3101 | + | } |
|
| 2464 | 3102 | } |
|
| 2465 | 3103 | } |
|
| 2466 | 3104 | ||
| 2467 | 3105 | @test unsafe fn testResolveThrowValid() throws (testing::TestError) { |
|
| 2468 | - | let mut a = testResolver(); |
|
| 2469 | - | let program = "fn fail() throws (i32) { throw 1; }"; |
|
| 2470 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2471 | - | try expectNoErrors(&result); |
|
| 3106 | + | let mut testArena206 = testArena(); |
|
| 3107 | + | let testStorage206: 'test206 = &mut testArena206 in { |
|
| 3108 | + | let mut a = testResolver(testStorage206); |
|
| 3109 | + | let program = "fn fail() throws (i32) { throw 1; }"; |
|
| 3110 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3111 | + | try expectNoErrors(&result); |
|
| 3112 | + | } |
|
| 2472 | 3113 | } |
|
| 2473 | 3114 | ||
| 2474 | 3115 | @test unsafe fn testResolveThrowRequiresThrowsClause() throws (testing::TestError) { |
|
| 2475 | - | let mut a = testResolver(); |
|
| 2476 | - | let program = "fn fail() { throw 1; }"; |
|
| 2477 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2478 | - | try expectErrorKind(&result, super::ErrorKind::ThrowRequiresThrows); |
|
| 3116 | + | let mut testArena207 = testArena(); |
|
| 3117 | + | let testStorage207: 'test207 = &mut testArena207 in { |
|
| 3118 | + | let mut a = testResolver(testStorage207); |
|
| 3119 | + | let program = "fn fail() { throw 1; }"; |
|
| 3120 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3121 | + | try expectErrorKind(&result, super::ErrorKind::ThrowRequiresThrows); |
|
| 3122 | + | } |
|
| 2479 | 3123 | } |
|
| 2480 | 3124 | ||
| 2481 | 3125 | @test unsafe fn testResolveThrowIncompatibleError() throws (testing::TestError) { |
|
| 2482 | - | let mut a = testResolver(); |
|
| 2483 | - | let program = "fn fail() throws (i32) { throw true; }"; |
|
| 2484 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2485 | - | try expectErrorKind(&result, super::ErrorKind::ThrowIncompatibleError); |
|
| 3126 | + | let mut testArena208 = testArena(); |
|
| 3127 | + | let testStorage208: 'test208 = &mut testArena208 in { |
|
| 3128 | + | let mut a = testResolver(testStorage208); |
|
| 3129 | + | let program = "fn fail() throws (i32) { throw true; }"; |
|
| 3130 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3131 | + | try expectErrorKind(&result, super::ErrorKind::ThrowIncompatibleError); |
|
| 3132 | + | } |
|
| 2486 | 3133 | } |
|
| 2487 | 3134 | ||
| 2488 | 3135 | // Binary operation tests ////////////////////////////////////////////////////// |
|
| 2489 | 3136 | ||
| 2490 | 3137 | @test unsafe fn testResolveBinaryOpArithmetic() throws (testing::TestError) { |
|
| 2491 | 3138 | { |
|
| 2492 | - | let mut a = testResolver(); |
|
| 2493 | - | let result = try resolveExprStr(&mut a, "4 + 4"); |
|
| 2494 | - | try expectNoErrors(&result); |
|
| 2495 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3139 | + | let mut testArena209 = testArena(); |
|
| 3140 | + | let testStorage209: 'test209 = &mut testArena209 in { |
|
| 3141 | + | let mut a = testResolver(testStorage209); |
|
| 3142 | + | let result = try resolveExprStr(&mut a, "4 + 4"); |
|
| 3143 | + | try expectNoErrors(&result); |
|
| 3144 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3145 | + | } |
|
| 2496 | 3146 | } { |
|
| 2497 | - | let mut a = testResolver(); |
|
| 2498 | - | let result = try resolveExprStr(&mut a, "10 - 3"); |
|
| 2499 | - | try expectNoErrors(&result); |
|
| 2500 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3147 | + | let mut testArena210 = testArena(); |
|
| 3148 | + | let testStorage210: 'test210 = &mut testArena210 in { |
|
| 3149 | + | let mut a = testResolver(testStorage210); |
|
| 3150 | + | let result = try resolveExprStr(&mut a, "10 - 3"); |
|
| 3151 | + | try expectNoErrors(&result); |
|
| 3152 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3153 | + | } |
|
| 2501 | 3154 | } { |
|
| 2502 | - | let mut a = testResolver(); |
|
| 2503 | - | let result = try resolveExprStr(&mut a, "5 * 6"); |
|
| 2504 | - | try expectNoErrors(&result); |
|
| 2505 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3155 | + | let mut testArena211 = testArena(); |
|
| 3156 | + | let testStorage211: 'test211 = &mut testArena211 in { |
|
| 3157 | + | let mut a = testResolver(testStorage211); |
|
| 3158 | + | let result = try resolveExprStr(&mut a, "5 * 6"); |
|
| 3159 | + | try expectNoErrors(&result); |
|
| 3160 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3161 | + | } |
|
| 2506 | 3162 | } { |
|
| 2507 | - | let mut a = testResolver(); |
|
| 2508 | - | let result = try resolveExprStr(&mut a, "20 / 4"); |
|
| 2509 | - | try expectNoErrors(&result); |
|
| 2510 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3163 | + | let mut testArena212 = testArena(); |
|
| 3164 | + | let testStorage212: 'test212 = &mut testArena212 in { |
|
| 3165 | + | let mut a = testResolver(testStorage212); |
|
| 3166 | + | let result = try resolveExprStr(&mut a, "20 / 4"); |
|
| 3167 | + | try expectNoErrors(&result); |
|
| 3168 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3169 | + | } |
|
| 2511 | 3170 | } { |
|
| 2512 | - | let mut a = testResolver(); |
|
| 2513 | - | let result = try resolveExprStr(&mut a, "17 % 5"); |
|
| 2514 | - | try expectNoErrors(&result); |
|
| 2515 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3171 | + | let mut testArena213 = testArena(); |
|
| 3172 | + | let testStorage213: 'test213 = &mut testArena213 in { |
|
| 3173 | + | let mut a = testResolver(testStorage213); |
|
| 3174 | + | let result = try resolveExprStr(&mut a, "17 % 5"); |
|
| 3175 | + | try expectNoErrors(&result); |
|
| 3176 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3177 | + | } |
|
| 2516 | 3178 | } { |
|
| 2517 | - | let mut a = testResolver(); |
|
| 2518 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: i32 = 5; x + y;"); |
|
| 2519 | - | try expectNoErrors(&result); |
|
| 2520 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2521 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3179 | + | let mut testArena214 = testArena(); |
|
| 3180 | + | let testStorage214: 'test214 = &mut testArena214 in { |
|
| 3181 | + | let mut a = testResolver(testStorage214); |
|
| 3182 | + | let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: i32 = 5; x + y;"); |
|
| 3183 | + | try expectNoErrors(&result); |
|
| 3184 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3185 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3186 | + | } |
|
| 2522 | 3187 | } { |
|
| 2523 | - | let mut a = testResolver(); |
|
| 2524 | - | let result = try resolveExprStr(&mut a, "1 + (2 * 3) - 4"); |
|
| 2525 | - | try expectNoErrors(&result); |
|
| 2526 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3188 | + | let mut testArena215 = testArena(); |
|
| 3189 | + | let testStorage215: 'test215 = &mut testArena215 in { |
|
| 3190 | + | let mut a = testResolver(testStorage215); |
|
| 3191 | + | let result = try resolveExprStr(&mut a, "1 + (2 * 3) - 4"); |
|
| 3192 | + | try expectNoErrors(&result); |
|
| 3193 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3194 | + | } |
|
| 2527 | 3195 | } { |
|
| 2528 | - | let mut a = testResolver(); |
|
| 2529 | - | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n * 2;"); |
|
| 2530 | - | try expectNoErrors(&result); |
|
| 2531 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2532 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3196 | + | let mut testArena216 = testArena(); |
|
| 3197 | + | let testStorage216: 'test216 = &mut testArena216 in { |
|
| 3198 | + | let mut a = testResolver(testStorage216); |
|
| 3199 | + | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n * 2;"); |
|
| 3200 | + | try expectNoErrors(&result); |
|
| 3201 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3202 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3203 | + | } |
|
| 2533 | 3204 | } { |
|
| 2534 | - | let mut a = testResolver(); |
|
| 2535 | - | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 2 * n;"); |
|
| 2536 | - | try expectNoErrors(&result); |
|
| 2537 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2538 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3205 | + | let mut testArena217 = testArena(); |
|
| 3206 | + | let testStorage217: 'test217 = &mut testArena217 in { |
|
| 3207 | + | let mut a = testResolver(testStorage217); |
|
| 3208 | + | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 2 * n;"); |
|
| 3209 | + | try expectNoErrors(&result); |
|
| 3210 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3211 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3212 | + | } |
|
| 2539 | 3213 | } { |
|
| 2540 | - | let mut a = testResolver(); |
|
| 2541 | - | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n - 1;"); |
|
| 2542 | - | try expectNoErrors(&result); |
|
| 2543 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2544 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3214 | + | let mut testArena218 = testArena(); |
|
| 3215 | + | let testStorage218: 'test218 = &mut testArena218 in { |
|
| 3216 | + | let mut a = testResolver(testStorage218); |
|
| 3217 | + | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n - 1;"); |
|
| 3218 | + | try expectNoErrors(&result); |
|
| 3219 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3220 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3221 | + | } |
|
| 2545 | 3222 | } |
|
| 2546 | 3223 | } |
|
| 2547 | 3224 | ||
| 2548 | 3225 | @test unsafe fn testResolveBinaryOpComparison() throws (testing::TestError) { |
|
| 2549 | 3226 | { |
|
| 2550 | - | let mut a = testResolver(); |
|
| 2551 | - | let result = try resolveExprStr(&mut a, "5 == 5"); |
|
| 2552 | - | try expectNoErrors(&result); |
|
| 2553 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3227 | + | let mut testArena219 = testArena(); |
|
| 3228 | + | let testStorage219: 'test219 = &mut testArena219 in { |
|
| 3229 | + | let mut a = testResolver(testStorage219); |
|
| 3230 | + | let result = try resolveExprStr(&mut a, "5 == 5"); |
|
| 3231 | + | try expectNoErrors(&result); |
|
| 3232 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3233 | + | } |
|
| 2554 | 3234 | } { |
|
| 2555 | - | let mut a = testResolver(); |
|
| 2556 | - | let result = try resolveExprStr(&mut a, "5 <> 10"); |
|
| 2557 | - | try expectNoErrors(&result); |
|
| 2558 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3235 | + | let mut testArena220 = testArena(); |
|
| 3236 | + | let testStorage220: 'test220 = &mut testArena220 in { |
|
| 3237 | + | let mut a = testResolver(testStorage220); |
|
| 3238 | + | let result = try resolveExprStr(&mut a, "5 <> 10"); |
|
| 3239 | + | try expectNoErrors(&result); |
|
| 3240 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3241 | + | } |
|
| 2559 | 3242 | } { |
|
| 2560 | - | let mut a = testResolver(); |
|
| 2561 | - | let result = try resolveExprStr(&mut a, "5 < 10"); |
|
| 2562 | - | try expectNoErrors(&result); |
|
| 2563 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3243 | + | let mut testArena221 = testArena(); |
|
| 3244 | + | let testStorage221: 'test221 = &mut testArena221 in { |
|
| 3245 | + | let mut a = testResolver(testStorage221); |
|
| 3246 | + | let result = try resolveExprStr(&mut a, "5 < 10"); |
|
| 3247 | + | try expectNoErrors(&result); |
|
| 3248 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3249 | + | } |
|
| 2564 | 3250 | } { |
|
| 2565 | - | let mut a = testResolver(); |
|
| 2566 | - | let result = try resolveExprStr(&mut a, "10 > 5"); |
|
| 2567 | - | try expectNoErrors(&result); |
|
| 2568 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3251 | + | let mut testArena222 = testArena(); |
|
| 3252 | + | let testStorage222: 'test222 = &mut testArena222 in { |
|
| 3253 | + | let mut a = testResolver(testStorage222); |
|
| 3254 | + | let result = try resolveExprStr(&mut a, "10 > 5"); |
|
| 3255 | + | try expectNoErrors(&result); |
|
| 3256 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3257 | + | } |
|
| 2569 | 3258 | } { |
|
| 2570 | - | let mut a = testResolver(); |
|
| 2571 | - | let result = try resolveExprStr(&mut a, "5 <= 5"); |
|
| 2572 | - | try expectNoErrors(&result); |
|
| 2573 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3259 | + | let mut testArena223 = testArena(); |
|
| 3260 | + | let testStorage223: 'test223 = &mut testArena223 in { |
|
| 3261 | + | let mut a = testResolver(testStorage223); |
|
| 3262 | + | let result = try resolveExprStr(&mut a, "5 <= 5"); |
|
| 3263 | + | try expectNoErrors(&result); |
|
| 3264 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3265 | + | } |
|
| 2574 | 3266 | } { |
|
| 2575 | - | let mut a = testResolver(); |
|
| 2576 | - | let result = try resolveExprStr(&mut a, "10 >= 5"); |
|
| 2577 | - | try expectNoErrors(&result); |
|
| 2578 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3267 | + | let mut testArena224 = testArena(); |
|
| 3268 | + | let testStorage224: 'test224 = &mut testArena224 in { |
|
| 3269 | + | let mut a = testResolver(testStorage224); |
|
| 3270 | + | let result = try resolveExprStr(&mut a, "10 >= 5"); |
|
| 3271 | + | try expectNoErrors(&result); |
|
| 3272 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3273 | + | } |
|
| 2579 | 3274 | } { |
|
| 2580 | - | let mut a = testResolver(); |
|
| 2581 | - | let result = try resolveExprStr(&mut a, "true == false"); |
|
| 2582 | - | try expectNoErrors(&result); |
|
| 2583 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3275 | + | let mut testArena225 = testArena(); |
|
| 3276 | + | let testStorage225: 'test225 = &mut testArena225 in { |
|
| 3277 | + | let mut a = testResolver(testStorage225); |
|
| 3278 | + | let result = try resolveExprStr(&mut a, "true == false"); |
|
| 3279 | + | try expectNoErrors(&result); |
|
| 3280 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3281 | + | } |
|
| 2584 | 3282 | } { |
|
| 2585 | - | let mut a = testResolver(); |
|
| 2586 | - | let result = try resolveExprStr(&mut a, "5 + 3 > 10 - 4"); |
|
| 2587 | - | try expectNoErrors(&result); |
|
| 2588 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3283 | + | let mut testArena226 = testArena(); |
|
| 3284 | + | let testStorage226: 'test226 = &mut testArena226 in { |
|
| 3285 | + | let mut a = testResolver(testStorage226); |
|
| 3286 | + | let result = try resolveExprStr(&mut a, "5 + 3 > 10 - 4"); |
|
| 3287 | + | try expectNoErrors(&result); |
|
| 3288 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3289 | + | } |
|
| 2589 | 3290 | } { |
|
| 2590 | - | let mut a = testResolver(); |
|
| 2591 | - | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n == 1;"); |
|
| 2592 | - | try expectNoErrors(&result); |
|
| 2593 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2594 | - | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3291 | + | let mut testArena227 = testArena(); |
|
| 3292 | + | let testStorage227: 'test227 = &mut testArena227 in { |
|
| 3293 | + | let mut a = testResolver(testStorage227); |
|
| 3294 | + | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n == 1;"); |
|
| 3295 | + | try expectNoErrors(&result); |
|
| 3296 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3297 | + | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3298 | + | } |
|
| 2595 | 3299 | } { |
|
| 2596 | - | let mut a = testResolver(); |
|
| 2597 | - | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 1 == n;"); |
|
| 2598 | - | try expectNoErrors(&result); |
|
| 2599 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2600 | - | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3300 | + | let mut testArena228 = testArena(); |
|
| 3301 | + | let testStorage228: 'test228 = &mut testArena228 in { |
|
| 3302 | + | let mut a = testResolver(testStorage228); |
|
| 3303 | + | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 1 == n;"); |
|
| 3304 | + | try expectNoErrors(&result); |
|
| 3305 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3306 | + | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3307 | + | } |
|
| 2601 | 3308 | } |
|
| 2602 | 3309 | } |
|
| 2603 | 3310 | ||
| 2604 | 3311 | @test unsafe fn testResolveBinaryOpLogical() throws (testing::TestError) { |
|
| 2605 | 3312 | { |
|
| 2606 | - | let mut a = testResolver(); |
|
| 2607 | - | let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x and y;"); |
|
| 2608 | - | try expectNoErrors(&result); |
|
| 2609 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2610 | - | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3313 | + | let mut testArena229 = testArena(); |
|
| 3314 | + | let testStorage229: 'test229 = &mut testArena229 in { |
|
| 3315 | + | let mut a = testResolver(testStorage229); |
|
| 3316 | + | let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x and y;"); |
|
| 3317 | + | try expectNoErrors(&result); |
|
| 3318 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3319 | + | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3320 | + | } |
|
| 2611 | 3321 | } { |
|
| 2612 | - | let mut a = testResolver(); |
|
| 2613 | - | let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x or y;"); |
|
| 2614 | - | try expectNoErrors(&result); |
|
| 2615 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2616 | - | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3322 | + | let mut testArena230 = testArena(); |
|
| 3323 | + | let testStorage230: 'test230 = &mut testArena230 in { |
|
| 3324 | + | let mut a = testResolver(testStorage230); |
|
| 3325 | + | let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x or y;"); |
|
| 3326 | + | try expectNoErrors(&result); |
|
| 3327 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3328 | + | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3329 | + | } |
|
| 2617 | 3330 | } { |
|
| 2618 | - | let mut a = testResolver(); |
|
| 2619 | - | let result = try resolveExprStr(&mut a, "true and false"); |
|
| 2620 | - | try expectNoErrors(&result); |
|
| 2621 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3331 | + | let mut testArena231 = testArena(); |
|
| 3332 | + | let testStorage231: 'test231 = &mut testArena231 in { |
|
| 3333 | + | let mut a = testResolver(testStorage231); |
|
| 3334 | + | let result = try resolveExprStr(&mut a, "true and false"); |
|
| 3335 | + | try expectNoErrors(&result); |
|
| 3336 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3337 | + | } |
|
| 2622 | 3338 | } |
|
| 2623 | 3339 | } |
|
| 2624 | 3340 | ||
| 2625 | 3341 | @test unsafe fn testResolveBinaryOpArithmeticTypeMismatch() throws (testing::TestError) { |
|
| 2626 | 3342 | { |
|
| 2627 | - | let mut a = testResolver(); |
|
| 2628 | - | let result = try resolveProgramStr(&mut a, "4 + true"); |
|
| 2629 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3343 | + | let mut testArena232 = testArena(); |
|
| 3344 | + | let testStorage232: 'test232 = &mut testArena232 in { |
|
| 3345 | + | let mut a = testResolver(testStorage232); |
|
| 3346 | + | let result = try resolveProgramStr(&mut a, "4 + true"); |
|
| 3347 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3348 | + | } |
|
| 2630 | 3349 | } { |
|
| 2631 | - | let mut a = testResolver(); |
|
| 2632 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: bool = false; x + y;"); |
|
| 2633 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3350 | + | let mut testArena233 = testArena(); |
|
| 3351 | + | let testStorage233: 'test233 = &mut testArena233 in { |
|
| 3352 | + | let mut a = testResolver(testStorage233); |
|
| 3353 | + | let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: bool = false; x + y;"); |
|
| 3354 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3355 | + | } |
|
| 2634 | 3356 | } { |
|
| 2635 | - | let mut a = testResolver(); |
|
| 2636 | - | let result = try resolveProgramStr(&mut a, "10 - false"); |
|
| 2637 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3357 | + | let mut testArena234 = testArena(); |
|
| 3358 | + | let testStorage234: 'test234 = &mut testArena234 in { |
|
| 3359 | + | let mut a = testResolver(testStorage234); |
|
| 3360 | + | let result = try resolveProgramStr(&mut a, "10 - false"); |
|
| 3361 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3362 | + | } |
|
| 2638 | 3363 | } { |
|
| 2639 | - | let mut a = testResolver(); |
|
| 2640 | - | let result = try resolveProgramStr(&mut a, "5 * true"); |
|
| 2641 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3364 | + | let mut testArena235 = testArena(); |
|
| 3365 | + | let testStorage235: 'test235 = &mut testArena235 in { |
|
| 3366 | + | let mut a = testResolver(testStorage235); |
|
| 3367 | + | let result = try resolveProgramStr(&mut a, "5 * true"); |
|
| 3368 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3369 | + | } |
|
| 2642 | 3370 | } { |
|
| 2643 | - | let mut a = testResolver(); |
|
| 2644 | - | let result = try resolveProgramStr(&mut a, "20 / false"); |
|
| 2645 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3371 | + | let mut testArena236 = testArena(); |
|
| 3372 | + | let testStorage236: 'test236 = &mut testArena236 in { |
|
| 3373 | + | let mut a = testResolver(testStorage236); |
|
| 3374 | + | let result = try resolveProgramStr(&mut a, "20 / false"); |
|
| 3375 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3376 | + | } |
|
| 2646 | 3377 | } { |
|
| 2647 | - | let mut a = testResolver(); |
|
| 2648 | - | let result = try resolveProgramStr(&mut a, "17 % true"); |
|
| 2649 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3378 | + | let mut testArena237 = testArena(); |
|
| 3379 | + | let testStorage237: 'test237 = &mut testArena237 in { |
|
| 3380 | + | let mut a = testResolver(testStorage237); |
|
| 3381 | + | let result = try resolveProgramStr(&mut a, "17 % true"); |
|
| 3382 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3383 | + | } |
|
| 2650 | 3384 | } { |
|
| 2651 | - | let mut a = testResolver(); |
|
| 2652 | - | let result = try resolveProgramStr(&mut a, "1 + (true * 3)"); |
|
| 2653 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3385 | + | let mut testArena238 = testArena(); |
|
| 3386 | + | let testStorage238: 'test238 = &mut testArena238 in { |
|
| 3387 | + | let mut a = testResolver(testStorage238); |
|
| 3388 | + | let result = try resolveProgramStr(&mut a, "1 + (true * 3)"); |
|
| 3389 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3390 | + | } |
|
| 2654 | 3391 | } |
|
| 2655 | 3392 | } |
|
| 2656 | 3393 | ||
| 2657 | 3394 | @test unsafe fn testResolveBinaryOpLogicalTypeMismatch() throws (testing::TestError) { |
|
| 2658 | 3395 | { |
|
| 2659 | - | let mut a = testResolver(); |
|
| 2660 | - | let result = try resolveProgramStr(&mut a, "42 and true"); |
|
| 2661 | - | let err = try expectError(&result); |
|
| 2662 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 3396 | + | let mut testArena239 = testArena(); |
|
| 3397 | + | let testStorage239: 'test239 = &mut testArena239 in { |
|
| 3398 | + | let mut a = testResolver(testStorage239); |
|
| 3399 | + | let result = try resolveProgramStr(&mut a, "42 and true"); |
|
| 3400 | + | let err = try expectError(&result); |
|
| 3401 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 3402 | + | } |
|
| 2663 | 3403 | } { |
|
| 2664 | - | let mut a = testResolver(); |
|
| 2665 | - | let result = try resolveProgramStr(&mut a, "true or 5"); |
|
| 2666 | - | let err = try expectError(&result); |
|
| 2667 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 3404 | + | let mut testArena240 = testArena(); |
|
| 3405 | + | let testStorage240: 'test240 = &mut testArena240 in { |
|
| 3406 | + | let mut a = testResolver(testStorage240); |
|
| 3407 | + | let result = try resolveProgramStr(&mut a, "true or 5"); |
|
| 3408 | + | let err = try expectError(&result); |
|
| 3409 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 3410 | + | } |
|
| 2668 | 3411 | } { |
|
| 2669 | - | let mut a = testResolver(); |
|
| 2670 | - | let result = try resolveProgramStr(&mut a, "1 and 2"); |
|
| 2671 | - | let err = try expectError(&result); |
|
| 2672 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 3412 | + | let mut testArena241 = testArena(); |
|
| 3413 | + | let testStorage241: 'test241 = &mut testArena241 in { |
|
| 3414 | + | let mut a = testResolver(testStorage241); |
|
| 3415 | + | let result = try resolveProgramStr(&mut a, "1 and 2"); |
|
| 3416 | + | let err = try expectError(&result); |
|
| 3417 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 3418 | + | } |
|
| 2673 | 3419 | } |
|
| 2674 | 3420 | } |
|
| 2675 | 3421 | ||
| 2676 | 3422 | @test unsafe fn testResolveBinaryOpComparisonTypeMismatch() throws (testing::TestError) { |
|
| 2677 | - | let mut a = testResolver(); |
|
| 2678 | - | let result = try resolveProgramStr(&mut a, "true < false"); |
|
| 2679 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3423 | + | let mut testArena242 = testArena(); |
|
| 3424 | + | let testStorage242: 'test242 = &mut testArena242 in { |
|
| 3425 | + | let mut a = testResolver(testStorage242); |
|
| 3426 | + | let result = try resolveProgramStr(&mut a, "true < false"); |
|
| 3427 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3428 | + | } |
|
| 2680 | 3429 | } |
|
| 2681 | 3430 | ||
| 2682 | 3431 | // Unary operation tests /////////////////////////////////////////////////////// |
|
| 2683 | 3432 | ||
| 2684 | 3433 | @test unsafe fn testResolveUnaryOpNot() throws (testing::TestError) { |
|
| 2685 | 3434 | { |
|
| 2686 | - | let mut a = testResolver(); |
|
| 2687 | - | let result = try resolveExprStr(&mut a, "not true"); |
|
| 2688 | - | try expectNoErrors(&result); |
|
| 2689 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3435 | + | let mut testArena243 = testArena(); |
|
| 3436 | + | let testStorage243: 'test243 = &mut testArena243 in { |
|
| 3437 | + | let mut a = testResolver(testStorage243); |
|
| 3438 | + | let result = try resolveExprStr(&mut a, "not true"); |
|
| 3439 | + | try expectNoErrors(&result); |
|
| 3440 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3441 | + | } |
|
| 2690 | 3442 | } { |
|
| 2691 | - | let mut a = testResolver(); |
|
| 2692 | - | let result = try resolveBlockStr(&mut a, "let x: bool = true; not x;"); |
|
| 2693 | - | try expectNoErrors(&result); |
|
| 2694 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2695 | - | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3443 | + | let mut testArena244 = testArena(); |
|
| 3444 | + | let testStorage244: 'test244 = &mut testArena244 in { |
|
| 3445 | + | let mut a = testResolver(testStorage244); |
|
| 3446 | + | let result = try resolveBlockStr(&mut a, "let x: bool = true; not x;"); |
|
| 3447 | + | try expectNoErrors(&result); |
|
| 3448 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3449 | + | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 3450 | + | } |
|
| 2696 | 3451 | } { |
|
| 2697 | - | let mut a = testResolver(); |
|
| 2698 | - | let result = try resolveExprStr(&mut a, "not (true and false)"); |
|
| 2699 | - | try expectNoErrors(&result); |
|
| 2700 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3452 | + | let mut testArena245 = testArena(); |
|
| 3453 | + | let testStorage245: 'test245 = &mut testArena245 in { |
|
| 3454 | + | let mut a = testResolver(testStorage245); |
|
| 3455 | + | let result = try resolveExprStr(&mut a, "not (true and false)"); |
|
| 3456 | + | try expectNoErrors(&result); |
|
| 3457 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3458 | + | } |
|
| 2701 | 3459 | } { |
|
| 2702 | - | let mut a = testResolver(); |
|
| 2703 | - | let result = try resolveProgramStr(&mut a, "not 42"); |
|
| 2704 | - | let err = try expectError(&result); |
|
| 2705 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 3460 | + | let mut testArena246 = testArena(); |
|
| 3461 | + | let testStorage246: 'test246 = &mut testArena246 in { |
|
| 3462 | + | let mut a = testResolver(testStorage246); |
|
| 3463 | + | let result = try resolveProgramStr(&mut a, "not 42"); |
|
| 3464 | + | let err = try expectError(&result); |
|
| 3465 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 3466 | + | } |
|
| 2706 | 3467 | } { |
|
| 2707 | - | let mut a = testResolver(); |
|
| 2708 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 5; not x;"); |
|
| 2709 | - | let err = try expectError(&result); |
|
| 2710 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::I32); |
|
| 3468 | + | let mut testArena247 = testArena(); |
|
| 3469 | + | let testStorage247: 'test247 = &mut testArena247 in { |
|
| 3470 | + | let mut a = testResolver(testStorage247); |
|
| 3471 | + | let result = try resolveBlockStr(&mut a, "let x: i32 = 5; not x;"); |
|
| 3472 | + | let err = try expectError(&result); |
|
| 3473 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::I32); |
|
| 3474 | + | } |
|
| 2711 | 3475 | } |
|
| 2712 | 3476 | } |
|
| 2713 | 3477 | ||
| 2714 | 3478 | @test unsafe fn testResolveUnaryOpNeg() throws (testing::TestError) { |
|
| 2715 | 3479 | { |
|
| 2716 | - | let mut a = testResolver(); |
|
| 2717 | - | let result = try resolveExprStr(&mut a, "-42"); |
|
| 2718 | - | try expectNoErrors(&result); |
|
| 2719 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3480 | + | let mut testArena248 = testArena(); |
|
| 3481 | + | let testStorage248: 'test248 = &mut testArena248 in { |
|
| 3482 | + | let mut a = testResolver(testStorage248); |
|
| 3483 | + | let result = try resolveExprStr(&mut a, "-42"); |
|
| 3484 | + | try expectNoErrors(&result); |
|
| 3485 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3486 | + | } |
|
| 2720 | 3487 | } { |
|
| 2721 | - | let mut a = testResolver(); |
|
| 2722 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 10; -x;"); |
|
| 2723 | - | try expectNoErrors(&result); |
|
| 2724 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2725 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3488 | + | let mut testArena249 = testArena(); |
|
| 3489 | + | let testStorage249: 'test249 = &mut testArena249 in { |
|
| 3490 | + | let mut a = testResolver(testStorage249); |
|
| 3491 | + | let result = try resolveBlockStr(&mut a, "let x: i32 = 10; -x;"); |
|
| 3492 | + | try expectNoErrors(&result); |
|
| 3493 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3494 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3495 | + | } |
|
| 2726 | 3496 | } { |
|
| 2727 | - | let mut a = testResolver(); |
|
| 2728 | - | let result = try resolveExprStr(&mut a, "-(5 + 3)"); |
|
| 2729 | - | try expectNoErrors(&result); |
|
| 2730 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3497 | + | let mut testArena250 = testArena(); |
|
| 3498 | + | let testStorage250: 'test250 = &mut testArena250 in { |
|
| 3499 | + | let mut a = testResolver(testStorage250); |
|
| 3500 | + | let result = try resolveExprStr(&mut a, "-(5 + 3)"); |
|
| 3501 | + | try expectNoErrors(&result); |
|
| 3502 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3503 | + | } |
|
| 2731 | 3504 | } { |
|
| 2732 | - | let mut a = testResolver(); |
|
| 2733 | - | let result = try resolveBlockStr(&mut a, "let x: i8 = 5; -x;"); |
|
| 2734 | - | try expectNoErrors(&result); |
|
| 2735 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2736 | - | try expectExprStmtType(&a, stmt, super::Type::I8); |
|
| 3505 | + | let mut testArena251 = testArena(); |
|
| 3506 | + | let testStorage251: 'test251 = &mut testArena251 in { |
|
| 3507 | + | let mut a = testResolver(testStorage251); |
|
| 3508 | + | let result = try resolveBlockStr(&mut a, "let x: i8 = 5; -x;"); |
|
| 3509 | + | try expectNoErrors(&result); |
|
| 3510 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3511 | + | try expectExprStmtType(&a, stmt, super::Type::I8); |
|
| 3512 | + | } |
|
| 2737 | 3513 | } { |
|
| 2738 | - | let mut a = testResolver(); |
|
| 2739 | - | let result = try resolveProgramStr(&mut a, "-true"); |
|
| 2740 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3514 | + | let mut testArena252 = testArena(); |
|
| 3515 | + | let testStorage252: 'test252 = &mut testArena252 in { |
|
| 3516 | + | let mut a = testResolver(testStorage252); |
|
| 3517 | + | let result = try resolveProgramStr(&mut a, "-true"); |
|
| 3518 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3519 | + | } |
|
| 2741 | 3520 | } { |
|
| 2742 | - | let mut a = testResolver(); |
|
| 2743 | - | let result = try resolveBlockStr(&mut a, "let x: bool = false; -x;"); |
|
| 2744 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3521 | + | let mut testArena253 = testArena(); |
|
| 3522 | + | let testStorage253: 'test253 = &mut testArena253 in { |
|
| 3523 | + | let mut a = testResolver(testStorage253); |
|
| 3524 | + | let result = try resolveBlockStr(&mut a, "let x: bool = false; -x;"); |
|
| 3525 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3526 | + | } |
|
| 2745 | 3527 | } |
|
| 2746 | 3528 | } |
|
| 2747 | 3529 | ||
| 2748 | 3530 | @test unsafe fn testResolveUnaryOpBitNot() throws (testing::TestError) { |
|
| 2749 | 3531 | { |
|
| 2750 | - | let mut a = testResolver(); |
|
| 2751 | - | let result = try resolveExprStr(&mut a, "~42"); |
|
| 2752 | - | try expectNoErrors(&result); |
|
| 2753 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3532 | + | let mut testArena254 = testArena(); |
|
| 3533 | + | let testStorage254: 'test254 = &mut testArena254 in { |
|
| 3534 | + | let mut a = testResolver(testStorage254); |
|
| 3535 | + | let result = try resolveExprStr(&mut a, "~42"); |
|
| 3536 | + | try expectNoErrors(&result); |
|
| 3537 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3538 | + | } |
|
| 2754 | 3539 | } { |
|
| 2755 | - | let mut a = testResolver(); |
|
| 2756 | - | let result = try resolveBlockStr(&mut a, "let x: u32 = 255; ~x;"); |
|
| 2757 | - | try expectNoErrors(&result); |
|
| 2758 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2759 | - | try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 3540 | + | let mut testArena255 = testArena(); |
|
| 3541 | + | let testStorage255: 'test255 = &mut testArena255 in { |
|
| 3542 | + | let mut a = testResolver(testStorage255); |
|
| 3543 | + | let result = try resolveBlockStr(&mut a, "let x: u32 = 255; ~x;"); |
|
| 3544 | + | try expectNoErrors(&result); |
|
| 3545 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3546 | + | try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 3547 | + | } |
|
| 2760 | 3548 | } { |
|
| 2761 | - | let mut a = testResolver(); |
|
| 2762 | - | let result = try resolveExprStr(&mut a, "~(0xFF)"); |
|
| 2763 | - | try expectNoErrors(&result); |
|
| 2764 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3549 | + | let mut testArena256 = testArena(); |
|
| 3550 | + | let testStorage256: 'test256 = &mut testArena256 in { |
|
| 3551 | + | let mut a = testResolver(testStorage256); |
|
| 3552 | + | let result = try resolveExprStr(&mut a, "~(0xFF)"); |
|
| 3553 | + | try expectNoErrors(&result); |
|
| 3554 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3555 | + | } |
|
| 2765 | 3556 | } { |
|
| 2766 | - | let mut a = testResolver(); |
|
| 2767 | - | let result = try resolveBlockStr(&mut a, "let x: i8 = 5; ~x;"); |
|
| 2768 | - | try expectNoErrors(&result); |
|
| 2769 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2770 | - | try expectExprStmtType(&a, stmt, super::Type::I8); |
|
| 3557 | + | let mut testArena257 = testArena(); |
|
| 3558 | + | let testStorage257: 'test257 = &mut testArena257 in { |
|
| 3559 | + | let mut a = testResolver(testStorage257); |
|
| 3560 | + | let result = try resolveBlockStr(&mut a, "let x: i8 = 5; ~x;"); |
|
| 3561 | + | try expectNoErrors(&result); |
|
| 3562 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3563 | + | try expectExprStmtType(&a, stmt, super::Type::I8); |
|
| 3564 | + | } |
|
| 2771 | 3565 | } { |
|
| 2772 | - | let mut a = testResolver(); |
|
| 2773 | - | let result = try resolveProgramStr(&mut a, "~true"); |
|
| 2774 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3566 | + | let mut testArena258 = testArena(); |
|
| 3567 | + | let testStorage258: 'test258 = &mut testArena258 in { |
|
| 3568 | + | let mut a = testResolver(testStorage258); |
|
| 3569 | + | let result = try resolveProgramStr(&mut a, "~true"); |
|
| 3570 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3571 | + | } |
|
| 2775 | 3572 | } { |
|
| 2776 | - | let mut a = testResolver(); |
|
| 2777 | - | let result = try resolveBlockStr(&mut a, "let x: bool = false; ~x;"); |
|
| 2778 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3573 | + | let mut testArena259 = testArena(); |
|
| 3574 | + | let testStorage259: 'test259 = &mut testArena259 in { |
|
| 3575 | + | let mut a = testResolver(testStorage259); |
|
| 3576 | + | let result = try resolveBlockStr(&mut a, "let x: bool = false; ~x;"); |
|
| 3577 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 3578 | + | } |
|
| 2779 | 3579 | } |
|
| 2780 | 3580 | } |
|
| 2781 | 3581 | ||
| 2782 | 3582 | @test unsafe fn testResolveUnaryOpNested() throws (testing::TestError) { |
|
| 2783 | 3583 | { |
|
| 2784 | - | let mut a = testResolver(); |
|
| 2785 | - | let result = try resolveExprStr(&mut a, "not not true"); |
|
| 2786 | - | try expectNoErrors(&result); |
|
| 2787 | - | try expectType(&a, result.root, super::Type::Bool); |
|
| 3584 | + | let mut testArena260 = testArena(); |
|
| 3585 | + | let testStorage260: 'test260 = &mut testArena260 in { |
|
| 3586 | + | let mut a = testResolver(testStorage260); |
|
| 3587 | + | let result = try resolveExprStr(&mut a, "not not true"); |
|
| 3588 | + | try expectNoErrors(&result); |
|
| 3589 | + | try expectType(&a, result.root, super::Type::Bool); |
|
| 3590 | + | } |
|
| 2788 | 3591 | } { |
|
| 2789 | - | let mut a = testResolver(); |
|
| 2790 | - | let result = try resolveExprStr(&mut a, "--42"); |
|
| 2791 | - | try expectNoErrors(&result); |
|
| 2792 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3592 | + | let mut testArena261 = testArena(); |
|
| 3593 | + | let testStorage261: 'test261 = &mut testArena261 in { |
|
| 3594 | + | let mut a = testResolver(testStorage261); |
|
| 3595 | + | let result = try resolveExprStr(&mut a, "--42"); |
|
| 3596 | + | try expectNoErrors(&result); |
|
| 3597 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3598 | + | } |
|
| 2793 | 3599 | } { |
|
| 2794 | - | let mut a = testResolver(); |
|
| 2795 | - | let result = try resolveExprStr(&mut a, "~~0xFF"); |
|
| 2796 | - | try expectNoErrors(&result); |
|
| 2797 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3600 | + | let mut testArena262 = testArena(); |
|
| 3601 | + | let testStorage262: 'test262 = &mut testArena262 in { |
|
| 3602 | + | let mut a = testResolver(testStorage262); |
|
| 3603 | + | let result = try resolveExprStr(&mut a, "~~0xFF"); |
|
| 3604 | + | try expectNoErrors(&result); |
|
| 3605 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3606 | + | } |
|
| 2798 | 3607 | } { |
|
| 2799 | - | let mut a = testResolver(); |
|
| 2800 | - | let result = try resolveExprStr(&mut a, "-(~42)"); |
|
| 2801 | - | try expectNoErrors(&result); |
|
| 2802 | - | try expectType(&a, result.root, super::Type::Int); |
|
| 3608 | + | let mut testArena263 = testArena(); |
|
| 3609 | + | let testStorage263: 'test263 = &mut testArena263 in { |
|
| 3610 | + | let mut a = testResolver(testStorage263); |
|
| 3611 | + | let result = try resolveExprStr(&mut a, "-(~42)"); |
|
| 3612 | + | try expectNoErrors(&result); |
|
| 3613 | + | try expectType(&a, result.root, super::Type::Int); |
|
| 3614 | + | } |
|
| 2803 | 3615 | } |
|
| 2804 | 3616 | } |
|
| 2805 | 3617 | ||
| 2806 | 3618 | // test fn testNormalPointerArithmetic() throws (testing::TestError) { |
|
| 2807 | 3619 | // mut a = testResolver(); |
| 2811 | 3623 | ||
| 2812 | 3624 | // Dereference tests ////////////////////////////////////////////////////////// |
|
| 2813 | 3625 | ||
| 2814 | 3626 | @test unsafe fn testResolveDeref() throws (testing::TestError) { |
|
| 2815 | 3627 | { |
|
| 2816 | - | let mut a = testResolver(); |
|
| 2817 | - | let result = try resolveBlockStr(&mut a, "static x: i32 = 42; let ptr: *i32 = &x; *ptr;"); |
|
| 2818 | - | try expectNoErrors(&result); |
|
| 2819 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2820 | - | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3628 | + | let mut testArena264 = testArena(); |
|
| 3629 | + | let testStorage264: 'test264 = &mut testArena264 in { |
|
| 3630 | + | let mut a = testResolver(testStorage264); |
|
| 3631 | + | let result = try resolveBlockStr(&mut a, "static x: i32 = 42; let ptr: *i32 = &x; *ptr;"); |
|
| 3632 | + | try expectNoErrors(&result); |
|
| 3633 | + | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3634 | + | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 3635 | + | } |
|
| 2821 | 3636 | } { |
|
| 2822 | - | let mut a = testResolver(); |
|
| 2823 | - | let result = try resolveExprStr(&mut a, "*42"); |
|
| 2824 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedPointer); |
|
| 3637 | + | let mut testArena265 = testArena(); |
|
| 3638 | + | let testStorage265: 'test265 = &mut testArena265 in { |
|
| 3639 | + | let mut a = testResolver(testStorage265); |
|
| 3640 | + | let result = try resolveExprStr(&mut a, "*42"); |
|
| 3641 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedPointer); |
|
| 3642 | + | } |
|
| 2825 | 3643 | } { |
|
| 2826 | - | let mut a = testResolver(); |
|
| 2827 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 5; *x;"); |
|
| 2828 | - | try expectErrorKind(&result, super::ErrorKind::ExpectedPointer); |
|
| 3644 | + | let mut testArena266 = testArena(); |
|
| 3645 | + | let testStorage266: 'test266 = &mut testArena266 in { |
|
| 3646 | + | let mut a = testResolver(testStorage266); |
|
| 3647 | + | let result = try resolveBlockStr(&mut a, "let x: i32 = 5; *x;"); |
|
| 3648 | + | try expectErrorKind(&result, super::ErrorKind::ExpectedPointer); |
|
| 3649 | + | } |
|
| 2829 | 3650 | } |
|
| 2830 | 3651 | } |
|
| 2831 | 3652 | ||
| 2832 | 3653 | @test unsafe fn testResolveAssignDeref() throws (testing::TestError) { |
|
| 2833 | 3654 | { |
|
| 2834 | - | let mut a = testResolver(); |
|
| 2835 | - | let program = "static x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;"; |
|
| 2836 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2837 | - | try expectNoErrors(&result); |
|
| 3655 | + | let mut testArena267 = testArena(); |
|
| 3656 | + | let testStorage267: 'test267 = &mut testArena267 in { |
|
| 3657 | + | let mut a = testResolver(testStorage267); |
|
| 3658 | + | let program = "static x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;"; |
|
| 3659 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3660 | + | try expectNoErrors(&result); |
|
| 3661 | + | } |
|
| 2838 | 3662 | } { |
|
| 2839 | - | let mut a = testResolver(); |
|
| 2840 | - | let program = "static x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;"; |
|
| 2841 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2842 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3663 | + | let mut testArena268 = testArena(); |
|
| 3664 | + | let testStorage268: 'test268 = &mut testArena268 in { |
|
| 3665 | + | let mut a = testResolver(testStorage268); |
|
| 3666 | + | let program = "static x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;"; |
|
| 3667 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3668 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3669 | + | } |
|
| 2843 | 3670 | } { |
|
| 2844 | - | let mut a = testResolver(); |
|
| 2845 | - | let program = "static x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;"; |
|
| 2846 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2847 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3671 | + | let mut testArena269 = testArena(); |
|
| 3672 | + | let testStorage269: 'test269 = &mut testArena269 in { |
|
| 3673 | + | let mut a = testResolver(testStorage269); |
|
| 3674 | + | let program = "static x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;"; |
|
| 3675 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3676 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3677 | + | } |
|
| 2848 | 3678 | } { |
|
| 2849 | - | let mut a = testResolver(); |
|
| 2850 | - | let program = "static x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;"; |
|
| 2851 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2852 | - | try expectNoErrors(&result); |
|
| 3679 | + | let mut testArena270 = testArena(); |
|
| 3680 | + | let testStorage270: 'test270 = &mut testArena270 in { |
|
| 3681 | + | let mut a = testResolver(testStorage270); |
|
| 3682 | + | let program = "static x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;"; |
|
| 3683 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3684 | + | try expectNoErrors(&result); |
|
| 3685 | + | } |
|
| 2853 | 3686 | } |
|
| 2854 | 3687 | } |
|
| 2855 | 3688 | ||
| 2856 | 3689 | // Type inference tests /////////////////////////////////////////////////////// |
|
| 2857 | 3690 | ||
| 2858 | 3691 | @test unsafe fn testResolveBasicTypeInference() throws (testing::TestError) { |
|
| 2859 | 3692 | { |
|
| 2860 | 3693 | // Boolean literals are unambiguous. |
|
| 2861 | - | let mut a = testResolver(); |
|
| 2862 | - | let result = try resolveProgramStr(&mut a, "let x = true; x;"); |
|
| 2863 | - | try expectNoErrors(&result); |
|
| 2864 | - | ||
| 2865 | - | let xStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2866 | - | try expectExprStmtType(&a, xStmt, super::Type::Bool); |
|
| 3694 | + | let mut testArena271 = testArena(); |
|
| 3695 | + | let testStorage271: 'test271 = &mut testArena271 in { |
|
| 3696 | + | let mut a = testResolver(testStorage271); |
|
| 3697 | + | let result = try resolveProgramStr(&mut a, "let x = true; x;"); |
|
| 3698 | + | try expectNoErrors(&result); |
|
| 3699 | + | ||
| 3700 | + | let xStmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 3701 | + | try expectExprStmtType(&a, xStmt, super::Type::Bool); |
|
| 3702 | + | } |
|
| 2867 | 3703 | } { |
|
| 2868 | 3704 | // Integer literals are ambiguous. |
|
| 2869 | - | let mut a = testResolver(); |
|
| 2870 | - | let result = try resolveProgramStr(&mut a, "let x = 34;"); |
|
| 2871 | - | try expectErrorKind(&result, super::ErrorKind::CannotInferType); |
|
| 3705 | + | let mut testArena272 = testArena(); |
|
| 3706 | + | let testStorage272: 'test272 = &mut testArena272 in { |
|
| 3707 | + | let mut a = testResolver(testStorage272); |
|
| 3708 | + | let result = try resolveProgramStr(&mut a, "let x = 34;"); |
|
| 3709 | + | try expectErrorKind(&result, super::ErrorKind::CannotInferType); |
|
| 3710 | + | } |
|
| 2872 | 3711 | } |
|
| 2873 | 3712 | } |
|
| 2874 | 3713 | ||
| 2875 | 3714 | // Union tests ///////////////////////////////////////////////////////////////// |
|
| 2876 | 3715 | ||
| 2877 | 3716 | @test unsafe fn testResolveUnionVariantWithoutPayload() throws (testing::TestError) { |
|
| 2878 | - | let mut a = testResolver(); |
|
| 2879 | - | let program = "union Status { Ok, Error } Status::Ok;"; |
|
| 2880 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3717 | + | let mut testArena273 = testArena(); |
|
| 3718 | + | let testStorage273: 'test273 = &mut testArena273 in { |
|
| 3719 | + | let mut a = testResolver(testStorage273); |
|
| 3720 | + | let program = "union Status { Ok, Error } Status::Ok;"; |
|
| 3721 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 2881 | 3722 | ||
| 2882 | - | let ty = try getTypeInScopeOf(&a, result.root, "Status"); |
|
| 2883 | - | let case super::NominalType::Union(unionType) = *ty |
|
| 2884 | - | else throw testing::TestError::Failed; |
|
| 2885 | - | try testing::expect(unionType.variants.len == 2); |
|
| 2886 | - | try testing::expect(mem::eq(unionType.variants[0].name, "Ok")); |
|
| 2887 | - | try testing::expect(mem::eq(unionType.variants[1].name, "Error")); |
|
| 2888 | - | if getUnionVariantPayload(ty, "Ok") <> super::Type::Void { |
|
| 2889 | - | throw testing::TestError::Failed; |
|
| 3723 | + | let ty = try getTypeInScopeOf(&a, result.root, "Status"); |
|
| 3724 | + | let case super::NominalType::Union(unionType) = *ty |
|
| 3725 | + | else throw testing::TestError::Failed; |
|
| 3726 | + | try testing::expect(unionType.variants.len == 2); |
|
| 3727 | + | try testing::expect(mem::eq(unionType.variants[0].name, "Ok")); |
|
| 3728 | + | try testing::expect(mem::eq(unionType.variants[1].name, "Error")); |
|
| 3729 | + | if getUnionVariantPayload(ty, "Ok") <> super::Type::Void { |
|
| 3730 | + | throw testing::TestError::Failed; |
|
| 3731 | + | } |
|
| 3732 | + | let stmt = try getBlockStmt(result.root, 1); |
|
| 3733 | + | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 3734 | + | try expectNoErrors(&result); |
|
| 2890 | 3735 | } |
|
| 2891 | - | let stmt = try getBlockStmt(result.root, 1); |
|
| 2892 | - | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 2893 | - | try expectNoErrors(&result); |
|
| 2894 | 3736 | } |
|
| 2895 | 3737 | ||
| 2896 | 3738 | @test unsafe fn testResolveUnionVariantWithPayload() throws (testing::TestError) { |
|
| 2897 | - | let mut a = testResolver(); |
|
| 2898 | - | let program = "union R { Ok(i32), Err(bool) } R::Ok(42);"; |
|
| 2899 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2900 | - | try expectNoErrors(&result); |
|
| 3739 | + | let mut testArena274 = testArena(); |
|
| 3740 | + | let testStorage274: 'test274 = &mut testArena274 in { |
|
| 3741 | + | let mut a = testResolver(testStorage274); |
|
| 3742 | + | let program = "union R { Ok(i32), Err(bool) } R::Ok(42);"; |
|
| 3743 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3744 | + | try expectNoErrors(&result); |
|
| 2901 | 3745 | ||
| 2902 | - | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 3746 | + | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2903 | 3747 | ||
| 2904 | - | let okPayload = getUnionVariantPayload(ty, "Ok"); |
|
| 2905 | - | try testing::expect(okPayload == super::Type::I32); |
|
| 3748 | + | let okPayload = getUnionVariantPayload(ty, "Ok"); |
|
| 3749 | + | try testing::expect(okPayload == super::Type::I32); |
|
| 2906 | 3750 | ||
| 2907 | - | let errPayload = getUnionVariantPayload(ty, "Err"); |
|
| 2908 | - | try testing::expect(errPayload == super::Type::Bool); |
|
| 3751 | + | let errPayload = getUnionVariantPayload(ty, "Err"); |
|
| 3752 | + | try testing::expect(errPayload == super::Type::Bool); |
|
| 2909 | 3753 | ||
| 2910 | - | let stmt = try getBlockStmt(result.root, 1); |
|
| 2911 | - | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 3754 | + | let stmt = try getBlockStmt(result.root, 1); |
|
| 3755 | + | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 2912 | 3756 | ||
| 2913 | - | // TODO: Test payload type. |
|
| 3757 | + | // TODO: Test payload type. |
|
| 3758 | + | } |
|
| 2914 | 3759 | } |
|
| 2915 | 3760 | ||
| 2916 | 3761 | @test unsafe fn testResolveUnionVariantWithoutPayloadExplicitDiscriminant() throws (testing::TestError) { |
|
| 2917 | - | let mut a = testResolver(); |
|
| 2918 | - | let program = "union R { Ok = 7, Err = 11 } R::Ok;"; |
|
| 2919 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2920 | - | try expectNoErrors(&result); |
|
| 3762 | + | let mut testArena275 = testArena(); |
|
| 3763 | + | let testStorage275: 'test275 = &mut testArena275 in { |
|
| 3764 | + | let mut a = testResolver(testStorage275); |
|
| 3765 | + | let program = "union R { Ok = 7, Err = 11 } R::Ok;"; |
|
| 3766 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3767 | + | try expectNoErrors(&result); |
|
| 2921 | 3768 | ||
| 2922 | - | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2923 | - | let stmt = try getBlockStmt(result.root, 1); |
|
| 2924 | - | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 3769 | + | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 3770 | + | let stmt = try getBlockStmt(result.root, 1); |
|
| 3771 | + | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 3772 | + | } |
|
| 2925 | 3773 | } |
|
| 2926 | 3774 | ||
| 2927 | 3775 | @test unsafe fn testResolveUnionVariantPayloadTypeMismatch() throws (testing::TestError) { |
|
| 2928 | - | let mut a = testResolver(); |
|
| 2929 | - | let program = "union R { Ok(i32), Error(bool) } R::Ok(true);"; |
|
| 2930 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2931 | - | let err = try expectError(&result); |
|
| 2932 | - | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 3776 | + | let mut testArena276 = testArena(); |
|
| 3777 | + | let testStorage276: 'test276 = &mut testArena276 in { |
|
| 3778 | + | let mut a = testResolver(testStorage276); |
|
| 3779 | + | let program = "union R { Ok(i32), Error(bool) } R::Ok(true);"; |
|
| 3780 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3781 | + | let err = try expectError(&result); |
|
| 3782 | + | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 2933 | 3783 | ||
| 2934 | - | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2935 | - | let payload = getUnionVariantPayload(ty, "Ok"); |
|
| 2936 | - | try testing::expect(payload == super::Type::I32); |
|
| 3784 | + | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 3785 | + | let payload = getUnionVariantPayload(ty, "Ok"); |
|
| 3786 | + | try testing::expect(payload == super::Type::I32); |
|
| 2937 | 3787 | ||
| 2938 | - | let errNode = err.node |
|
| 2939 | - | else throw testing::TestError::Failed; |
|
| 2940 | - | let case ast::NodeValue::Bool(_) = errNode.value |
|
| 2941 | - | else throw testing::TestError::Failed; |
|
| 3788 | + | let errNode = err.node |
|
| 3789 | + | else throw testing::TestError::Failed; |
|
| 3790 | + | let case ast::NodeValue::Bool(_) = errNode.value |
|
| 3791 | + | else throw testing::TestError::Failed; |
|
| 3792 | + | } |
|
| 2942 | 3793 | } |
|
| 2943 | 3794 | ||
| 2944 | 3795 | @test unsafe fn testResolveUnionVariantUnexpectedPayload() throws (testing::TestError) { |
|
| 2945 | - | let mut a = testResolver(); |
|
| 2946 | - | let program = "union Status { Ok, Error } Status::Ok(42);"; |
|
| 2947 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2948 | - | let err = try expectError(&result); |
|
| 3796 | + | let mut testArena277 = testArena(); |
|
| 3797 | + | let testStorage277: 'test277 = &mut testArena277 in { |
|
| 3798 | + | let mut a = testResolver(testStorage277); |
|
| 3799 | + | let program = "union Status { Ok, Error } Status::Ok(42);"; |
|
| 3800 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3801 | + | let err = try expectError(&result); |
|
| 2949 | 3802 | ||
| 2950 | - | let case super::ErrorKind::UnionVariantPayloadUnexpected(_) = err.kind |
|
| 2951 | - | else throw testing::TestError::Failed; |
|
| 2952 | - | let node = err.node |
|
| 2953 | - | else throw testing::TestError::Failed; |
|
| 2954 | - | let case ast::NodeValue::Call(_) = node.value |
|
| 2955 | - | else throw testing::TestError::Failed; |
|
| 3803 | + | let case super::ErrorKind::UnionVariantPayloadUnexpected(_) = err.kind |
|
| 3804 | + | else throw testing::TestError::Failed; |
|
| 3805 | + | let node = err.node |
|
| 3806 | + | else throw testing::TestError::Failed; |
|
| 3807 | + | let case ast::NodeValue::Call(_) = node.value |
|
| 3808 | + | else throw testing::TestError::Failed; |
|
| 3809 | + | } |
|
| 2956 | 3810 | } |
|
| 2957 | 3811 | ||
| 2958 | 3812 | @test unsafe fn testResolveUnionVariantUnknown() throws (testing::TestError) { |
|
| 2959 | - | let mut a = testResolver(); |
|
| 2960 | - | let program = "union Status { Ok, Error } Status::Unknown;"; |
|
| 2961 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2962 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 3813 | + | let mut testArena278 = testArena(); |
|
| 3814 | + | let testStorage278: 'test278 = &mut testArena278 in { |
|
| 3815 | + | let mut a = testResolver(testStorage278); |
|
| 3816 | + | let program = "union Status { Ok, Error } Status::Unknown;"; |
|
| 3817 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3818 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 3819 | + | } |
|
| 2963 | 3820 | } |
|
| 2964 | 3821 | ||
| 2965 | 3822 | @test unsafe fn testResolveScopeAccessUndefinedType() throws (testing::TestError) { |
|
| 2966 | - | let mut a = testResolver(); |
|
| 2967 | - | let result = try resolveProgramStr(&mut a, "Unknown::X;"); |
|
| 2968 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 3823 | + | let mut testArena279 = testArena(); |
|
| 3824 | + | let testStorage279: 'test279 = &mut testArena279 in { |
|
| 3825 | + | let mut a = testResolver(testStorage279); |
|
| 3826 | + | let result = try resolveProgramStr(&mut a, "Unknown::X;"); |
|
| 3827 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 3828 | + | } |
|
| 2969 | 3829 | } |
|
| 2970 | 3830 | ||
| 2971 | 3831 | @test unsafe fn testResolveUnionVariantVoidPayload() throws (testing::TestError) { |
|
| 2972 | - | let mut a = testResolver(); |
|
| 2973 | - | let program = "union R { Success(i32), Pending } R::Pending;"; |
|
| 2974 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2975 | - | try expectNoErrors(&result); |
|
| 3832 | + | let mut testArena280 = testArena(); |
|
| 3833 | + | let testStorage280: 'test280 = &mut testArena280 in { |
|
| 3834 | + | let mut a = testResolver(testStorage280); |
|
| 3835 | + | let program = "union R { Success(i32), Pending } R::Pending;"; |
|
| 3836 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3837 | + | try expectNoErrors(&result); |
|
| 2976 | 3838 | ||
| 2977 | - | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2978 | - | let payload = getUnionVariantPayload(ty, "Pending"); |
|
| 2979 | - | try testing::expect(payload == super::Type::Void); |
|
| 3839 | + | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 3840 | + | let payload = getUnionVariantPayload(ty, "Pending"); |
|
| 3841 | + | try testing::expect(payload == super::Type::Void); |
|
| 2980 | 3842 | ||
| 2981 | - | let stmt = try getBlockStmt(result.root, 1); |
|
| 2982 | - | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 3843 | + | let stmt = try getBlockStmt(result.root, 1); |
|
| 3844 | + | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 3845 | + | } |
|
| 2983 | 3846 | } |
|
| 2984 | 3847 | ||
| 2985 | 3848 | @test unsafe fn testResolveUnionVariantRecordPayload() throws (testing::TestError) { |
|
| 2986 | - | let mut a = testResolver(); |
|
| 2987 | - | let program = "record P { x: i32, y: i32 } union S { Point(P), Num(u32) } S::Point(P { x: 10, y: 20 });"; |
|
| 2988 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 2989 | - | try expectNoErrors(&result); |
|
| 3849 | + | let mut testArena281 = testArena(); |
|
| 3850 | + | let testStorage281: 'test281 = &mut testArena281 in { |
|
| 3851 | + | let mut a = testResolver(testStorage281); |
|
| 3852 | + | let program = "record P { x: i32, y: i32 } union S { Point(P), Num(u32) } S::Point(P { x: 10, y: 20 });"; |
|
| 3853 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3854 | + | try expectNoErrors(&result); |
|
| 2990 | 3855 | ||
| 2991 | - | let ty = try getTypeInScopeOf(&a, result.root, "S"); |
|
| 2992 | - | let stmt = try getBlockStmt(result.root, 2); |
|
| 2993 | - | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 3856 | + | let ty = try getTypeInScopeOf(&a, result.root, "S"); |
|
| 3857 | + | let stmt = try getBlockStmt(result.root, 2); |
|
| 3858 | + | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 3859 | + | } |
|
| 2994 | 3860 | } |
|
| 2995 | 3861 | ||
| 2996 | 3862 | @test unsafe fn testResolveBuiltinSizeOf() throws (testing::TestError) { |
|
| 2997 | 3863 | try resolveAndExpectConstExpr("@sizeOf(u8)", 1); |
|
| 2998 | 3864 | try resolveAndExpectConstExpr("@sizeOf(u16)", 2); |
| 3043 | 3909 | try resolveAndExpectConstStmt("union T { A, B, C }; @alignOf(T);", 1); |
|
| 3044 | 3910 | try resolveAndExpectConstStmt("union T { A, B(u32), C }; @alignOf(T);", 4); |
|
| 3045 | 3911 | } |
|
| 3046 | 3912 | ||
| 3047 | 3913 | @test unsafe fn testResolveBuiltinSizeOfRecord() throws (testing::TestError) { |
|
| 3048 | - | let mut a = testResolver(); |
|
| 3049 | - | let program = "record T { x: u8, y: u32 } @sizeOf(T);"; |
|
| 3050 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3051 | - | try expectNoErrors(&result); |
|
| 3914 | + | let mut testArena282 = testArena(); |
|
| 3915 | + | let testStorage282: 'test282 = &mut testArena282 in { |
|
| 3916 | + | let mut a = testResolver(testStorage282); |
|
| 3917 | + | let program = "record T { x: u8, y: u32 } @sizeOf(T);"; |
|
| 3918 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3919 | + | try expectNoErrors(&result); |
|
| 3052 | 3920 | ||
| 3053 | - | let stmt = try getBlockStmt(result.root, 1); |
|
| 3054 | - | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 3055 | - | try expectConstInt(&a, expr, 8); |
|
| 3921 | + | let stmt = try getBlockStmt(result.root, 1); |
|
| 3922 | + | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 3923 | + | try expectConstInt(&a, expr, 8); |
|
| 3924 | + | } |
|
| 3056 | 3925 | } |
|
| 3057 | 3926 | ||
| 3058 | 3927 | @test unsafe fn testResolveBuiltinSizeOfUnion() throws (testing::TestError) { |
|
| 3059 | - | let mut a = testResolver(); |
|
| 3060 | - | let program = "union Result { Ok(u32), Err(u8) } @sizeOf(Result);"; |
|
| 3061 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3062 | - | try expectNoErrors(&result); |
|
| 3928 | + | let mut testArena283 = testArena(); |
|
| 3929 | + | let testStorage283: 'test283 = &mut testArena283 in { |
|
| 3930 | + | let mut a = testResolver(testStorage283); |
|
| 3931 | + | let program = "union Result { Ok(u32), Err(u8) } @sizeOf(Result);"; |
|
| 3932 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3933 | + | try expectNoErrors(&result); |
|
| 3063 | 3934 | ||
| 3064 | - | let stmt = try getBlockStmt(result.root, 1); |
|
| 3065 | - | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 3066 | - | try expectConstInt(&a, expr, 8); |
|
| 3935 | + | let stmt = try getBlockStmt(result.root, 1); |
|
| 3936 | + | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 3937 | + | try expectConstInt(&a, expr, 8); |
|
| 3938 | + | } |
|
| 3067 | 3939 | } |
|
| 3068 | 3940 | ||
| 3069 | 3941 | @test unsafe fn testResolveAlignAnnotation() throws (testing::TestError) { |
|
| 3070 | 3942 | { |
|
| 3071 | - | let mut a = testResolver(); |
|
| 3072 | - | let result = try resolveBlockStr(&mut a, "let x: u8 align(8) = 0;"); |
|
| 3073 | - | try expectNoErrors(&result); |
|
| 3074 | - | ||
| 3075 | - | let stmt = try getBlockStmt(result.root, 0); |
|
| 3076 | - | let sym = super::symbolFor(&a, stmt) |
|
| 3077 | - | else throw testing::TestError::Failed; |
|
| 3078 | - | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 3079 | - | else throw testing::TestError::Failed; |
|
| 3080 | - | let layout = super::getLayout(&a, sym.node, valType); |
|
| 3081 | - | try testing::expect(layout.alignment == 8); |
|
| 3943 | + | let mut testArena284 = testArena(); |
|
| 3944 | + | let testStorage284: 'test284 = &mut testArena284 in { |
|
| 3945 | + | let mut a = testResolver(testStorage284); |
|
| 3946 | + | let result = try resolveBlockStr(&mut a, "let x: u8 align(8) = 0;"); |
|
| 3947 | + | try expectNoErrors(&result); |
|
| 3948 | + | ||
| 3949 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 3950 | + | let sym = super::symbolFor(&a, stmt) |
|
| 3951 | + | else throw testing::TestError::Failed; |
|
| 3952 | + | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 3953 | + | else throw testing::TestError::Failed; |
|
| 3954 | + | let layout = super::getLayout(&a, sym.node, valType); |
|
| 3955 | + | try testing::expect(layout.alignment == 8); |
|
| 3956 | + | } |
|
| 3082 | 3957 | } { |
|
| 3083 | - | let mut a = testResolver(); |
|
| 3084 | - | let result = try resolveProgramStr(&mut a, "let x: u32 align(3) = 0;"); |
|
| 3085 | - | let err = try expectError(&result); |
|
| 3086 | - | let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind |
|
| 3087 | - | else throw testing::TestError::Failed; |
|
| 3088 | - | try testing::expect(val == 3); |
|
| 3958 | + | let mut testArena285 = testArena(); |
|
| 3959 | + | let testStorage285: 'test285 = &mut testArena285 in { |
|
| 3960 | + | let mut a = testResolver(testStorage285); |
|
| 3961 | + | let result = try resolveProgramStr(&mut a, "let x: u32 align(3) = 0;"); |
|
| 3962 | + | let err = try expectError(&result); |
|
| 3963 | + | let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind |
|
| 3964 | + | else throw testing::TestError::Failed; |
|
| 3965 | + | try testing::expect(val == 3); |
|
| 3966 | + | } |
|
| 3089 | 3967 | } { |
|
| 3090 | - | let mut a = testResolver(); |
|
| 3091 | - | let result = try resolveProgramStr(&mut a, "let x: u32 align(7) = 0;"); |
|
| 3092 | - | let err = try expectError(&result); |
|
| 3093 | - | let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind |
|
| 3094 | - | else throw testing::TestError::Failed; |
|
| 3095 | - | try testing::expect(val == 7); |
|
| 3968 | + | let mut testArena286 = testArena(); |
|
| 3969 | + | let testStorage286: 'test286 = &mut testArena286 in { |
|
| 3970 | + | let mut a = testResolver(testStorage286); |
|
| 3971 | + | let result = try resolveProgramStr(&mut a, "let x: u32 align(7) = 0;"); |
|
| 3972 | + | let err = try expectError(&result); |
|
| 3973 | + | let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind |
|
| 3974 | + | else throw testing::TestError::Failed; |
|
| 3975 | + | try testing::expect(val == 7); |
|
| 3976 | + | } |
|
| 3096 | 3977 | } |
|
| 3097 | 3978 | } |
|
| 3098 | 3979 | ||
| 3099 | 3980 | @test unsafe fn testResolveVoidAssignmentError() throws (testing::TestError) { |
|
| 3100 | 3981 | { |
|
| 3101 | - | let mut a = testResolver(); |
|
| 3102 | - | let program = "fn voidFn() {} let _ = voidFn();"; |
|
| 3103 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3104 | - | try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid); |
|
| 3982 | + | let mut testArena287 = testArena(); |
|
| 3983 | + | let testStorage287: 'test287 = &mut testArena287 in { |
|
| 3984 | + | let mut a = testResolver(testStorage287); |
|
| 3985 | + | let program = "fn voidFn() {} let _ = voidFn();"; |
|
| 3986 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3987 | + | try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid); |
|
| 3988 | + | } |
|
| 3105 | 3989 | } { |
|
| 3106 | - | let mut a = testResolver(); |
|
| 3107 | - | let program = "fn voidFn() {} let x = voidFn();"; |
|
| 3108 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3109 | - | try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid); |
|
| 3990 | + | let mut testArena288 = testArena(); |
|
| 3991 | + | let testStorage288: 'test288 = &mut testArena288 in { |
|
| 3992 | + | let mut a = testResolver(testStorage288); |
|
| 3993 | + | let program = "fn voidFn() {} let x = voidFn();"; |
|
| 3994 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3995 | + | try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid); |
|
| 3996 | + | } |
|
| 3110 | 3997 | } |
|
| 3111 | 3998 | } |
|
| 3112 | 3999 | ||
| 3113 | 4000 | // |
|
| 3114 | 4001 | // Module Declaration Tests |
|
| 3115 | 4002 | // |
|
| 3116 | 4003 | ||
| 3117 | 4004 | @test unsafe fn testResolveEmptyMod() throws (testing::TestError) { |
|
| 3118 | - | let mut a = testResolver(); |
|
| 3119 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3120 | - | let mut graph = &mut MODULE_GRAPH; |
|
| 4005 | + | let mut testArena289 = testArena(); |
|
| 4006 | + | let testStorage289: 'test289 = &mut testArena289 in { |
|
| 4007 | + | let mut a = testResolver(testStorage289); |
|
| 4008 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4009 | + | let mut graph = &mut MODULE_GRAPH; |
|
| 3121 | 4010 | ||
| 3122 | - | let rootId = try registerModule(graph, nil, "root", "mod child;", &mut arena); |
|
| 3123 | - | let childId = try registerModule(graph, rootId, "child", "{}", &mut arena); |
|
| 3124 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3125 | - | try expectNoErrors(&result); |
|
| 4011 | + | let rootId = try registerModule(graph, nil, "root", "mod child;", &mut arena); |
|
| 4012 | + | let childId = try registerModule(graph, rootId, "child", "{}", &mut arena); |
|
| 4013 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4014 | + | try expectNoErrors(&result); |
|
| 4015 | + | } |
|
| 3126 | 4016 | } |
|
| 3127 | 4017 | ||
| 3128 | 4018 | @test unsafe fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) { |
|
| 3129 | - | let mut a = testResolver(); |
|
| 3130 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4019 | + | let mut testArena290 = testArena(); |
|
| 4020 | + | let testStorage290: 'test290 = &mut testArena290 in { |
|
| 4021 | + | let mut a = testResolver(testStorage290); |
|
| 4022 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3131 | 4023 | ||
| 3132 | - | // Register root and util modules. |
|
| 3133 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena); |
|
| 3134 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn main() { helper(); }", &mut arena); |
|
| 4024 | + | // Register root and util modules. |
|
| 4025 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena); |
|
| 4026 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn main() { helper(); }", &mut arena); |
|
| 3135 | 4027 | ||
| 3136 | - | // Resolve should fail: the parent module is not in scope. |
|
| 3137 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3138 | - | let err = try expectError(&result); |
|
| 3139 | - | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 3140 | - | else throw testing::TestError::Failed; |
|
| 3141 | - | try testing::expect(mem::eq(name, "helper")); |
|
| 4028 | + | // Resolve should fail: the parent module is not in scope. |
|
| 4029 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4030 | + | let err = try expectError(&result); |
|
| 4031 | + | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 4032 | + | else throw testing::TestError::Failed; |
|
| 4033 | + | try testing::expect(mem::eq(name, "helper")); |
|
| 4034 | + | } |
|
| 3142 | 4035 | } |
|
| 3143 | 4036 | ||
| 3144 | 4037 | @test unsafe fn testResolveModuleAccessPrivateSubModule() throws (testing::TestError) { |
|
| 3145 | - | let mut a = testResolver(); |
|
| 3146 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4038 | + | let mut testArena291 = testArena(); |
|
| 4039 | + | let testStorage291: 'test291 = &mut testArena291 in { |
|
| 4040 | + | let mut a = testResolver(testStorage291); |
|
| 4041 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3147 | 4042 | ||
| 3148 | - | // Register root and util modules. |
|
| 3149 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena); |
|
| 3150 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "export fn helper() {}", &mut arena); |
|
| 4043 | + | // Register root and util modules. |
|
| 4044 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena); |
|
| 4045 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "export fn helper() {}", &mut arena); |
|
| 3151 | 4046 | ||
| 3152 | - | // Resolve should succeed: parent can access child. |
|
| 3153 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3154 | - | try expectNoErrors(&result); |
|
| 4047 | + | // Resolve should succeed: parent can access child. |
|
| 4048 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4049 | + | try expectNoErrors(&result); |
|
| 4050 | + | } |
|
| 3155 | 4051 | } |
|
| 3156 | 4052 | ||
| 3157 | 4053 | @test unsafe fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) { |
|
| 3158 | - | let mut a = testResolver(); |
|
| 3159 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4054 | + | let mut testArena292 = testArena(); |
|
| 4055 | + | let testStorage292: 'test292 = &mut testArena292 in { |
|
| 4056 | + | let mut a = testResolver(testStorage292); |
|
| 4057 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3160 | 4058 | ||
| 3161 | - | // Register root with two sibling modules. |
|
| 3162 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
|
| 3163 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "fn main() { patrick::helper(); }", &mut arena); |
|
| 3164 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 4059 | + | // Register root with two sibling modules. |
|
| 4060 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
|
| 4061 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "fn main() { patrick::helper(); }", &mut arena); |
|
| 4062 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3165 | 4063 | ||
| 3166 | - | // Resolve should fail: siblings can't access each other directly. |
|
| 3167 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3168 | - | let err = try expectError(&result); |
|
| 3169 | - | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 3170 | - | else throw testing::TestError::Failed; |
|
| 3171 | - | try testing::expect(mem::eq(name, "patrick")); |
|
| 4064 | + | // Resolve should fail: siblings can't access each other directly. |
|
| 4065 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4066 | + | let err = try expectError(&result); |
|
| 4067 | + | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 4068 | + | else throw testing::TestError::Failed; |
|
| 4069 | + | try testing::expect(mem::eq(name, "patrick")); |
|
| 4070 | + | } |
|
| 3172 | 4071 | } |
|
| 3173 | 4072 | ||
| 3174 | 4073 | @test unsafe fn testResolveSiblingModulesViaRoot() throws (testing::TestError) { |
|
| 3175 | - | let mut a = testResolver(); |
|
| 3176 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4074 | + | let mut testArena293 = testArena(); |
|
| 4075 | + | let testStorage293: 'test293 = &mut testArena293 in { |
|
| 4076 | + | let mut a = testResolver(testStorage293); |
|
| 4077 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3177 | 4078 | ||
| 3178 | - | // Register root with two sibling modules. |
|
| 3179 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
|
| 3180 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "use root::patrick; fn main() -> i32 { return patrick::helper(); }", &mut arena); |
|
| 3181 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 4079 | + | // Register root with two sibling modules. |
|
| 4080 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
|
| 4081 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "use root::patrick; fn main() -> i32 { return patrick::helper(); }", &mut arena); |
|
| 4082 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3182 | 4083 | ||
| 3183 | - | // Resolve should succeed: siblings can access each other via root. |
|
| 3184 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3185 | - | try expectNoErrors(&result); |
|
| 4084 | + | // Resolve should succeed: siblings can access each other via root. |
|
| 4085 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4086 | + | try expectNoErrors(&result); |
|
| 4087 | + | } |
|
| 3186 | 4088 | } |
|
| 3187 | 4089 | ||
| 3188 | 4090 | @test unsafe fn testResolveModuleMutualRecursion() throws (testing::TestError) { |
|
| 3189 | - | let mut a = testResolver(); |
|
| 3190 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4091 | + | let mut testArena294 = testArena(); |
|
| 4092 | + | let testStorage294: 'test294 = &mut testArena294 in { |
|
| 4093 | + | let mut a = testResolver(testStorage294); |
|
| 4094 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3191 | 4095 | ||
| 3192 | - | // Register root with two sibling modules that call each other. |
|
| 3193 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena); |
|
| 3194 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "left", "use root::right; export fn leftHelper() -> i32 { return right::rightHelper(); }", &mut arena); |
|
| 3195 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "right", "use root::left; export fn rightHelper() -> i32 { return left::leftHelper(); }", &mut arena); |
|
| 4096 | + | // Register root with two sibling modules that call each other. |
|
| 4097 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena); |
|
| 4098 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "left", "use root::right; export fn leftHelper() -> i32 { return right::rightHelper(); }", &mut arena); |
|
| 4099 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "right", "use root::left; export fn rightHelper() -> i32 { return left::leftHelper(); }", &mut arena); |
|
| 3196 | 4100 | ||
| 3197 | - | // Resolve should succeed: cyclic use is allowed. |
|
| 3198 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3199 | - | try expectNoErrors(&result); |
|
| 4101 | + | // Resolve should succeed: cyclic use is allowed. |
|
| 4102 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4103 | + | try expectNoErrors(&result); |
|
| 4104 | + | } |
|
| 3200 | 4105 | } |
|
| 3201 | 4106 | ||
| 3202 | 4107 | @test unsafe fn testResolveAccessModuleType() throws (testing::TestError) { |
|
| 3203 | - | let mut a = testResolver(); |
|
| 3204 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4108 | + | let mut testArena295 = testArena(); |
|
| 4109 | + | let testStorage295: 'test295 = &mut testArena295 in { |
|
| 4110 | + | let mut a = testResolver(testStorage295); |
|
| 4111 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3205 | 4112 | ||
| 3206 | - | // Register root with types module containing a record. |
|
| 3207 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 3208 | - | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Point { x: i32, y: i32 }", &mut arena); |
|
| 3209 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> i32 { let p = types::Point { x: 1, y: 2 }; return p.x; }", &mut arena); |
|
| 4113 | + | // Register root with types module containing a record. |
|
| 4114 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 4115 | + | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Point { x: i32, y: i32 }", &mut arena); |
|
| 4116 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> i32 { let p = types::Point { x: 1, y: 2 }; return p.x; }", &mut arena); |
|
| 3210 | 4117 | ||
| 3211 | - | // Resolve should succeed: types can be accessed. |
|
| 3212 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3213 | - | try expectNoErrors(&result); |
|
| 4118 | + | // Resolve should succeed: types can be accessed. |
|
| 4119 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4120 | + | try expectNoErrors(&result); |
|
| 4121 | + | } |
|
| 3214 | 4122 | } |
|
| 3215 | 4123 | ||
| 3216 | 4124 | @test unsafe fn testResolveAccessModuleConstant() throws (testing::TestError) { |
|
| 3217 | - | let mut a = testResolver(); |
|
| 3218 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4125 | + | let mut testArena296 = testArena(); |
|
| 4126 | + | let testStorage296: 'test296 = &mut testArena296 in { |
|
| 4127 | + | let mut a = testResolver(testStorage296); |
|
| 4128 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3219 | 4129 | ||
| 3220 | - | // Register root with constants module. |
|
| 3221 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3222 | - | let constantsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant MAX_SIZE: i32 = 100;", &mut arena); |
|
| 3223 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() -> i32 { return consts::MAX_SIZE; }", &mut arena); |
|
| 4130 | + | // Register root with constants module. |
|
| 4131 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 4132 | + | let constantsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant MAX_SIZE: i32 = 100;", &mut arena); |
|
| 4133 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() -> i32 { return consts::MAX_SIZE; }", &mut arena); |
|
| 3224 | 4134 | ||
| 3225 | - | // Resolve should succeed: constants can be accessed. |
|
| 3226 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3227 | - | try expectNoErrors(&result); |
|
| 4135 | + | // Resolve should succeed: constants can be accessed. |
|
| 4136 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4137 | + | try expectNoErrors(&result); |
|
| 4138 | + | } |
|
| 3228 | 4139 | } |
|
| 3229 | 4140 | ||
| 3230 | 4141 | @test unsafe fn testResolveRootSymbolMustBeImported() throws (testing::TestError) { |
|
| 3231 | - | let mut a = testResolver(); |
|
| 3232 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4142 | + | let mut testArena297 = testArena(); |
|
| 4143 | + | let testStorage297: 'test297 = &mut testArena297 in { |
|
| 4144 | + | let mut a = testResolver(testStorage297); |
|
| 4145 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3233 | 4146 | ||
| 3234 | - | // Register deeply nested modules: `root::app::services::auth`. |
|
| 3235 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3236 | - | let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "fn run() -> i32 { return root::helper(); }", &mut arena); |
|
| 4147 | + | // Register deeply nested modules: `root::app::services::auth`. |
|
| 4148 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 4149 | + | let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "fn run() -> i32 { return root::helper(); }", &mut arena); |
|
| 3237 | 4150 | ||
| 3238 | - | // Resolve should fail: the `root` module must be imported. |
|
| 3239 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3240 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root")); |
|
| 4151 | + | // Resolve should fail: the `root` module must be imported. |
|
| 4152 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4153 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root")); |
|
| 4154 | + | } |
|
| 3241 | 4155 | } |
|
| 3242 | 4156 | ||
| 3243 | 4157 | @test unsafe fn testResolveUseImportsNestedSymbol() throws (testing::TestError) { |
|
| 3244 | - | let mut a = testResolver(); |
|
| 3245 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4158 | + | let mut testArena298 = testArena(); |
|
| 4159 | + | let testStorage298: 'test298 = &mut testArena298 in { |
|
| 4160 | + | let mut a = testResolver(testStorage298); |
|
| 4161 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3246 | 4162 | ||
| 3247 | - | // Register deeply nested modules: `root::app::services::auth`. |
|
| 3248 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena); |
|
| 3249 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "export mod services;", &mut arena); |
|
| 3250 | - | let servicesId = try registerModule(&mut MODULE_GRAPH, appId, "services", "export mod auth;", &mut arena); |
|
| 3251 | - | let authId = try registerModule(&mut MODULE_GRAPH, servicesId, "auth", "export fn login() -> i32 { return 1; }", &mut arena); |
|
| 3252 | - | let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::app::services::auth; fn run() -> i32 { return auth::login(); }", &mut arena); |
|
| 3253 | - | let otherId = try registerModule(&mut MODULE_GRAPH, rootId, "other", "use root; fn run() -> i32 { return root::app::services::auth::login(); }", &mut arena); |
|
| 4163 | + | // Register deeply nested modules: `root::app::services::auth`. |
|
| 4164 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena); |
|
| 4165 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "export mod services;", &mut arena); |
|
| 4166 | + | let servicesId = try registerModule(&mut MODULE_GRAPH, appId, "services", "export mod auth;", &mut arena); |
|
| 4167 | + | let authId = try registerModule(&mut MODULE_GRAPH, servicesId, "auth", "export fn login() -> i32 { return 1; }", &mut arena); |
|
| 4168 | + | let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::app::services::auth; fn run() -> i32 { return auth::login(); }", &mut arena); |
|
| 4169 | + | let otherId = try registerModule(&mut MODULE_GRAPH, rootId, "other", "use root; fn run() -> i32 { return root::app::services::auth::login(); }", &mut arena); |
|
| 3254 | 4170 | ||
| 3255 | - | // Resolve should succeed: use imports the module symbol. |
|
| 3256 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3257 | - | try expectNoErrors(&result); |
|
| 4171 | + | // Resolve should succeed: use imports the module symbol. |
|
| 4172 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4173 | + | try expectNoErrors(&result); |
|
| 4174 | + | } |
|
| 3258 | 4175 | } |
|
| 3259 | 4176 | ||
| 3260 | 4177 | @test unsafe fn testResolveUseNonExistentModule() throws (testing::TestError) { |
|
| 3261 | - | let mut a = testResolver(); |
|
| 3262 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4178 | + | let mut testArena299 = testArena(); |
|
| 4179 | + | let testStorage299: 'test299 = &mut testArena299 in { |
|
| 4180 | + | let mut a = testResolver(testStorage299); |
|
| 4181 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3263 | 4182 | ||
| 3264 | - | // Register root with app trying to use a non-existent module. |
|
| 3265 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod app;", &mut arena); |
|
| 3266 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::unknown;", &mut arena); |
|
| 4183 | + | // Register root with app trying to use a non-existent module. |
|
| 4184 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod app;", &mut arena); |
|
| 4185 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::unknown;", &mut arena); |
|
| 3267 | 4186 | ||
| 3268 | - | // Resolve should fail: module doesn't exist. |
|
| 3269 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3270 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("unknown")); |
|
| 4187 | + | // Resolve should fail: module doesn't exist. |
|
| 4188 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4189 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("unknown")); |
|
| 4190 | + | } |
|
| 3271 | 4191 | } |
|
| 3272 | 4192 | ||
| 3273 | 4193 | @test unsafe fn testResolveUsePrivateFn() throws (testing::TestError) { |
|
| 3274 | - | let mut a = testResolver(); |
|
| 3275 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4194 | + | let mut testArena300 = testArena(); |
|
| 4195 | + | let testStorage300: 'test300 = &mut testArena300 in { |
|
| 4196 | + | let mut a = testResolver(testStorage300); |
|
| 4197 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3276 | 4198 | ||
| 3277 | - | // Register root with util module containing a private function. |
|
| 3278 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena); |
|
| 3279 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn private() -> i32 { return 42; }", &mut arena); |
|
| 3280 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::util; fn main() -> i32 { return util::private(); }", &mut arena); |
|
| 4199 | + | // Register root with util module containing a private function. |
|
| 4200 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena); |
|
| 4201 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn private() -> i32 { return 42; }", &mut arena); |
|
| 4202 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::util; fn main() -> i32 { return util::private(); }", &mut arena); |
|
| 3281 | 4203 | ||
| 3282 | - | // Resolve should fail: function is not public. |
|
| 3283 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3284 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 4204 | + | // Resolve should fail: function is not public. |
|
| 4205 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4206 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 4207 | + | } |
|
| 3285 | 4208 | } |
|
| 3286 | 4209 | ||
| 3287 | 4210 | @test unsafe fn testResolveUsePrivateMod() throws (testing::TestError) { |
|
| 3288 | - | let mut a = testResolver(); |
|
| 3289 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4211 | + | let mut testArena301 = testArena(); |
|
| 4212 | + | let testStorage301: 'test301 = &mut testArena301 in { |
|
| 4213 | + | let mut a = testResolver(testStorage301); |
|
| 4214 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3290 | 4215 | ||
| 3291 | - | // Register root with public and private child modules. |
|
| 3292 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; mod private;", &mut arena); |
|
| 3293 | - | let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "private", "{}", &mut arena); |
|
| 3294 | - | let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::private;", &mut arena); |
|
| 4216 | + | // Register root with public and private child modules. |
|
| 4217 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; mod private;", &mut arena); |
|
| 4218 | + | let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "private", "{}", &mut arena); |
|
| 4219 | + | let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::private;", &mut arena); |
|
| 3295 | 4220 | ||
| 3296 | - | // Resolve should fail: module is not public. |
|
| 3297 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3298 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 4221 | + | // Resolve should fail: module is not public. |
|
| 4222 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4223 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 4224 | + | } |
|
| 3299 | 4225 | } |
|
| 3300 | 4226 | ||
| 3301 | 4227 | @test unsafe fn testResolveUsePublicMod() throws (testing::TestError) { |
|
| 3302 | - | let mut a = testResolver(); |
|
| 3303 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4228 | + | let mut testArena302 = testArena(); |
|
| 4229 | + | let testStorage302: 'test302 = &mut testArena302 in { |
|
| 4230 | + | let mut a = testResolver(testStorage302); |
|
| 4231 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3304 | 4232 | ||
| 3305 | - | // Register root with public and private child modules. |
|
| 3306 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena); |
|
| 3307 | - | let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "public", "{}", &mut arena); |
|
| 3308 | - | let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::public;", &mut arena); |
|
| 4233 | + | // Register root with public and private child modules. |
|
| 4234 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena); |
|
| 4235 | + | let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "public", "{}", &mut arena); |
|
| 4236 | + | let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::public;", &mut arena); |
|
| 3309 | 4237 | ||
| 3310 | - | // Resolve should succeed: module is public. |
|
| 3311 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3312 | - | try expectNoErrors(&result); |
|
| 4238 | + | // Resolve should succeed: module is public. |
|
| 4239 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4240 | + | try expectNoErrors(&result); |
|
| 4241 | + | } |
|
| 3313 | 4242 | } |
|
| 3314 | 4243 | ||
| 3315 | 4244 | @test unsafe fn testResolveUseNonPublicType() throws (testing::TestError) { |
|
| 3316 | - | let mut a = testResolver(); |
|
| 3317 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4245 | + | let mut testArena303 = testArena(); |
|
| 4246 | + | let testStorage303: 'test303 = &mut testArena303 in { |
|
| 4247 | + | let mut a = testResolver(testStorage303); |
|
| 4248 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3318 | 4249 | ||
| 3319 | - | // Register root with types module containing a private record. |
|
| 3320 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 3321 | - | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "record Priv { x: i32 }", &mut arena); |
|
| 3322 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Priv { return types::Priv { x: 1 }; }", &mut arena); |
|
| 4250 | + | // Register root with types module containing a private record. |
|
| 4251 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 4252 | + | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "record Priv { x: i32 }", &mut arena); |
|
| 4253 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Priv { return types::Priv { x: 1 }; }", &mut arena); |
|
| 3323 | 4254 | ||
| 3324 | - | // Resolve should fail: record is not public. |
|
| 3325 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3326 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Priv")); |
|
| 4255 | + | // Resolve should fail: record is not public. |
|
| 4256 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4257 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Priv")); |
|
| 4258 | + | } |
|
| 3327 | 4259 | } |
|
| 3328 | 4260 | ||
| 3329 | 4261 | @test unsafe fn testResolveImportPublicType() throws (testing::TestError) { |
|
| 3330 | - | let mut a = testResolver(); |
|
| 3331 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4262 | + | let mut testArena304 = testArena(); |
|
| 4263 | + | let testStorage304: 'test304 = &mut testArena304 in { |
|
| 4264 | + | let mut a = testResolver(testStorage304); |
|
| 4265 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3332 | 4266 | ||
| 3333 | - | // Register root with types module containing a public record. |
|
| 3334 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 3335 | - | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pub { x: i32 }", &mut arena); |
|
| 3336 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Pub { return types::Pub { x: 1 }; }", &mut arena); |
|
| 4267 | + | // Register root with types module containing a public record. |
|
| 4268 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 4269 | + | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pub { x: i32 }", &mut arena); |
|
| 4270 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Pub { return types::Pub { x: 1 }; }", &mut arena); |
|
| 3337 | 4271 | ||
| 3338 | - | // Resolve should succeed: record is public. |
|
| 3339 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3340 | - | try expectNoErrors(&result); |
|
| 4272 | + | // Resolve should succeed: record is public. |
|
| 4273 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4274 | + | try expectNoErrors(&result); |
|
| 4275 | + | } |
|
| 3341 | 4276 | } |
|
| 3342 | 4277 | ||
| 3343 | 4278 | @test unsafe fn testResolveUseNonPublicStatic() throws (testing::TestError) { |
|
| 3344 | - | let mut a = testResolver(); |
|
| 3345 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4279 | + | let mut testArena305 = testArena(); |
|
| 4280 | + | let testStorage305: 'test305 = &mut testArena305 in { |
|
| 4281 | + | let mut a = testResolver(testStorage305); |
|
| 4282 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3346 | 4283 | ||
| 3347 | - | // Register root with statics module containing a private static. |
|
| 3348 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 3349 | - | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "static PRIVATE: i32 = 42;", &mut arena); |
|
| 3350 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PRIVATE; }", &mut arena); |
|
| 4284 | + | // Register root with statics module containing a private static. |
|
| 4285 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 4286 | + | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "static PRIVATE: i32 = 42;", &mut arena); |
|
| 4287 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PRIVATE; }", &mut arena); |
|
| 3351 | 4288 | ||
| 3352 | - | // Resolve should fail: static is not public. |
|
| 3353 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3354 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("PRIVATE")); |
|
| 4289 | + | // Resolve should fail: static is not public. |
|
| 4290 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4291 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("PRIVATE")); |
|
| 4292 | + | } |
|
| 3355 | 4293 | } |
|
| 3356 | 4294 | ||
| 3357 | 4295 | @test unsafe fn testResolveImportPublicStatic() throws (testing::TestError) { |
|
| 3358 | - | let mut a = testResolver(); |
|
| 3359 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4296 | + | let mut testArena306 = testArena(); |
|
| 4297 | + | let testStorage306: 'test306 = &mut testArena306 in { |
|
| 4298 | + | let mut a = testResolver(testStorage306); |
|
| 4299 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3360 | 4300 | ||
| 3361 | - | // Register root with statics module containing a public static. |
|
| 3362 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 3363 | - | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static PUBLIC: i32 = 42;", &mut arena); |
|
| 3364 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PUBLIC; }", &mut arena); |
|
| 4301 | + | // Register root with statics module containing a public static. |
|
| 4302 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 4303 | + | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static PUBLIC: i32 = 42;", &mut arena); |
|
| 4304 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PUBLIC; }", &mut arena); |
|
| 3365 | 4305 | ||
| 3366 | - | // Resolve should succeed: static is public. |
|
| 3367 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3368 | - | try expectNoErrors(&result); |
|
| 4306 | + | // Resolve should succeed: static is public. |
|
| 4307 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4308 | + | try expectNoErrors(&result); |
|
| 4309 | + | } |
|
| 3369 | 4310 | } |
|
| 3370 | 4311 | ||
| 3371 | 4312 | @test unsafe fn testResolveAccessSuper() throws (testing::TestError) { |
|
| 3372 | 4313 | { |
|
| 3373 | - | let mut a = testResolver(); |
|
| 3374 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3375 | - | ||
| 3376 | - | // Test function access. |
|
| 3377 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export fn parentFn() -> i32 { return 42; }", &mut arena); |
|
| 3378 | - | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn main() -> i32 { return super::parentFn(); }", &mut arena); |
|
| 3379 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3380 | - | try expectNoErrors(&result); |
|
| 4314 | + | let mut testArena307 = testArena(); |
|
| 4315 | + | let testStorage307: 'test307 = &mut testArena307 in { |
|
| 4316 | + | let mut a = testResolver(testStorage307); |
|
| 4317 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4318 | + | ||
| 4319 | + | // Test function access. |
|
| 4320 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export fn parentFn() -> i32 { return 42; }", &mut arena); |
|
| 4321 | + | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn main() -> i32 { return super::parentFn(); }", &mut arena); |
|
| 4322 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4323 | + | try expectNoErrors(&result); |
|
| 4324 | + | } |
|
| 3381 | 4325 | } { |
|
| 3382 | - | let mut a = testResolver(); |
|
| 3383 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4326 | + | let mut testArena308 = testArena(); |
|
| 4327 | + | let testStorage308: 'test308 = &mut testArena308 in { |
|
| 4328 | + | let mut a = testResolver(testStorage308); |
|
| 4329 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3384 | 4330 | ||
| 3385 | - | // Test type access. |
|
| 3386 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export record Point { x: i32, y: i32 }", &mut arena); |
|
| 3387 | - | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn make() -> super::Point { return super::Point { x: 1, y: 2 }; }", &mut arena); |
|
| 3388 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3389 | - | try expectNoErrors(&result); |
|
| 4331 | + | // Test type access. |
|
| 4332 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export record Point { x: i32, y: i32 }", &mut arena); |
|
| 4333 | + | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn make() -> super::Point { return super::Point { x: 1, y: 2 }; }", &mut arena); |
|
| 4334 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4335 | + | try expectNoErrors(&result); |
|
| 4336 | + | } |
|
| 3390 | 4337 | } |
|
| 3391 | 4338 | } |
|
| 3392 | 4339 | ||
| 3393 | 4340 | /// Test nested super access to union variants (e.g. `super::E::A`). |
|
| 3394 | 4341 | @test unsafe fn testResolveSuperUnionVariant() throws (testing::TestError) { |
|
| 3395 | - | let mut a = testResolver(); |
|
| 3396 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4342 | + | let mut testArena309 = testArena(); |
|
| 4343 | + | let testStorage309: 'test309 = &mut testArena309 in { |
|
| 4344 | + | let mut a = testResolver(testStorage309); |
|
| 4345 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3397 | 4346 | ||
| 3398 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena); |
|
| 3399 | - | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c", |
|
| 3400 | - | "fn f(x: super::E) { match x { case super::E::A => {}, case super::E::B => {} } }", |
|
| 3401 | - | &mut arena); |
|
| 3402 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3403 | - | try expectNoErrors(&result); |
|
| 4347 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena); |
|
| 4348 | + | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c", |
|
| 4349 | + | "fn f(x: super::E) { match x { case super::E::A => {}, case super::E::B => {} } }", |
|
| 4350 | + | &mut arena); |
|
| 4351 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4352 | + | try expectNoErrors(&result); |
|
| 4353 | + | } |
|
| 3404 | 4354 | } |
|
| 3405 | 4355 | ||
| 3406 | 4356 | @test unsafe fn testResolveUseSuper() throws (testing::TestError) { |
|
| 3407 | - | let mut a = testResolver(); |
|
| 3408 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4357 | + | let mut testArena310 = testArena(); |
|
| 4358 | + | let testStorage310: 'test310 = &mut testArena310 in { |
|
| 4359 | + | let mut a = testResolver(testStorage310); |
|
| 4360 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3409 | 4361 | ||
| 3410 | - | // Register root with a function, and a child module that uses super to access it. |
|
| 3411 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena); |
|
| 3412 | - | let kateId = try registerModule(&mut MODULE_GRAPH, rootId, "kate", "export fn run() {}", &mut arena); |
|
| 3413 | - | let joeId = try registerModule(&mut MODULE_GRAPH, rootId, "joe", "use super::kate; fn main() { kate::run(); }", &mut arena); |
|
| 4362 | + | // Register root with a function, and a child module that uses super to access it. |
|
| 4363 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena); |
|
| 4364 | + | let kateId = try registerModule(&mut MODULE_GRAPH, rootId, "kate", "export fn run() {}", &mut arena); |
|
| 4365 | + | let joeId = try registerModule(&mut MODULE_GRAPH, rootId, "joe", "use super::kate; fn main() { kate::run(); }", &mut arena); |
|
| 3414 | 4366 | ||
| 3415 | - | // Resolve should succeed - super allows accessing parent module. |
|
| 3416 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3417 | - | try expectNoErrors(&result); |
|
| 4367 | + | // Resolve should succeed - super allows accessing parent module. |
|
| 4368 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4369 | + | try expectNoErrors(&result); |
|
| 4370 | + | } |
|
| 3418 | 4371 | } |
|
| 3419 | 4372 | ||
| 3420 | 4373 | @test unsafe fn testResolveModNotFound() throws (testing::TestError) { |
|
| 3421 | - | let mut a = testResolver(); |
|
| 3422 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4374 | + | let mut testArena311 = testArena(); |
|
| 4375 | + | let testStorage311: 'test311 = &mut testArena311 in { |
|
| 4376 | + | let mut a = testResolver(testStorage311); |
|
| 4377 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3423 | 4378 | ||
| 3424 | - | // Register root that declares a module that doesn't exist. |
|
| 3425 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod unknown;", &mut arena); |
|
| 4379 | + | // Register root that declares a module that doesn't exist. |
|
| 4380 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod unknown;", &mut arena); |
|
| 3426 | 4381 | ||
| 3427 | - | // Resolve should fail: module doesn't exist. |
|
| 3428 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3429 | - | let err = try expectError(&result); |
|
| 3430 | - | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 3431 | - | else throw testing::TestError::Failed; |
|
| 3432 | - | try testing::expect(mem::eq(name, "unknown")); |
|
| 4382 | + | // Resolve should fail: module doesn't exist. |
|
| 4383 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4384 | + | let err = try expectError(&result); |
|
| 4385 | + | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 4386 | + | else throw testing::TestError::Failed; |
|
| 4387 | + | try testing::expect(mem::eq(name, "unknown")); |
|
| 4388 | + | } |
|
| 3433 | 4389 | } |
|
| 3434 | 4390 | ||
| 3435 | 4391 | @test unsafe fn testResolveDuplicateSubModule() throws (testing::TestError) { |
|
| 3436 | - | let mut a = testResolver(); |
|
| 3437 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4392 | + | let mut testArena312 = testArena(); |
|
| 4393 | + | let testStorage312: 'test312 = &mut testArena312 in { |
|
| 4394 | + | let mut a = testResolver(testStorage312); |
|
| 4395 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3438 | 4396 | ||
| 3439 | - | // Register root that declares a module twice. |
|
| 3440 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; mod child;", &mut arena); |
|
| 3441 | - | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena); |
|
| 4397 | + | // Register root that declares a module twice. |
|
| 4398 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; mod child;", &mut arena); |
|
| 4399 | + | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena); |
|
| 3442 | 4400 | ||
| 3443 | - | // Resolve should fail: can't declare the same module twice. |
|
| 3444 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3445 | - | let err = try expectError(&result); |
|
| 3446 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child")); |
|
| 4401 | + | // Resolve should fail: can't declare the same module twice. |
|
| 4402 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4403 | + | let err = try expectError(&result); |
|
| 4404 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child")); |
|
| 4405 | + | } |
|
| 3447 | 4406 | } |
|
| 3448 | 4407 | ||
| 3449 | 4408 | @test unsafe fn testResolveUseSubModule() throws (testing::TestError) { |
|
| 3450 | - | let mut a = testResolver(); |
|
| 3451 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4409 | + | let mut testArena313 = testArena(); |
|
| 4410 | + | let testStorage313: 'test313 = &mut testArena313 in { |
|
| 4411 | + | let mut a = testResolver(testStorage313); |
|
| 4412 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3452 | 4413 | ||
| 3453 | - | // Register root that declares and imports the same module. |
|
| 3454 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; use child;", &mut arena); |
|
| 3455 | - | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena); |
|
| 4414 | + | // Register root that declares and imports the same module. |
|
| 4415 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; use child;", &mut arena); |
|
| 4416 | + | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena); |
|
| 3456 | 4417 | ||
| 3457 | - | // Resolve should fail: Both `mod` and `use` are trying to create the same binding. |
|
| 3458 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3459 | - | let err = try expectError(&result); |
|
| 3460 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child")); |
|
| 4418 | + | // Resolve should fail: Both `mod` and `use` are trying to create the same binding. |
|
| 4419 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4420 | + | let err = try expectError(&result); |
|
| 4421 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child")); |
|
| 4422 | + | } |
|
| 3461 | 4423 | } |
|
| 3462 | 4424 | ||
| 3463 | 4425 | @test unsafe fn testResolveDuplicateUse() throws (testing::TestError) { |
|
| 3464 | - | let mut a = testResolver(); |
|
| 3465 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4426 | + | let mut testArena314 = testArena(); |
|
| 4427 | + | let testStorage314: 'test314 = &mut testArena314 in { |
|
| 4428 | + | let mut a = testResolver(testStorage314); |
|
| 4429 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3466 | 4430 | ||
| 3467 | - | // Register a module that imports the same module twice. |
|
| 3468 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child", &mut arena); |
|
| 3469 | - | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "use root; use root;", &mut arena); |
|
| 4431 | + | // Register a module that imports the same module twice. |
|
| 4432 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child", &mut arena); |
|
| 4433 | + | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "use root; use root;", &mut arena); |
|
| 3470 | 4434 | ||
| 3471 | - | // Resolve should fail. |
|
| 3472 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3473 | - | let err = try expectError(&result); |
|
| 3474 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("root")); |
|
| 4435 | + | // Resolve should fail. |
|
| 4436 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4437 | + | let err = try expectError(&result); |
|
| 4438 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("root")); |
|
| 4439 | + | } |
|
| 3475 | 4440 | } |
|
| 3476 | 4441 | ||
| 3477 | 4442 | /// Test that opaque pointers are allowed in record fields. |
|
| 3478 | 4443 | @test unsafe fn testOpaquePointerInRecordField() throws (testing::TestError) { |
|
| 3479 | - | let mut a = testResolver(); |
|
| 3480 | - | let result = try resolveProgramStr(&mut a, "record T { x: *opaque }"); |
|
| 3481 | - | try expectNoErrors(&result); |
|
| 4444 | + | let mut testArena315 = testArena(); |
|
| 4445 | + | let testStorage315: 'test315 = &mut testArena315 in { |
|
| 4446 | + | let mut a = testResolver(testStorage315); |
|
| 4447 | + | let result = try resolveProgramStr(&mut a, "record T { x: *opaque }"); |
|
| 4448 | + | try expectNoErrors(&result); |
|
| 4449 | + | } |
|
| 3482 | 4450 | } |
|
| 3483 | 4451 | ||
| 3484 | 4452 | /// You cannot use `@sizeOf` or `@alignOf` on opaque type. |
|
| 3485 | 4453 | @test unsafe fn testOpaqueTypeNoSizeOfAlignOf() throws (testing::TestError) { |
|
| 3486 | - | let mut a = testResolver(); |
|
| 4454 | + | let mut testArena316 = testArena(); |
|
| 4455 | + | let testStorage316: 'test316 = &mut testArena316 in { |
|
| 4456 | + | let mut a = testResolver(testStorage316); |
|
| 3487 | 4457 | ||
| 3488 | - | let result1 = try resolveExprStr(&mut a, "@sizeOf(opaque)"); |
|
| 3489 | - | let err1 = try expectError(&result1); |
|
| 3490 | - | try expectErrorKind(&result1, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4458 | + | let result1 = try resolveExprStr(&mut a, "@sizeOf(opaque)"); |
|
| 4459 | + | let err1 = try expectError(&result1); |
|
| 4460 | + | try expectErrorKind(&result1, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 3491 | 4461 | ||
| 3492 | - | let result2 = try resolveExprStr(&mut a, "@alignOf(opaque)"); |
|
| 3493 | - | let err2 = try expectError(&result2); |
|
| 3494 | - | try expectErrorKind(&result2, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4462 | + | let result2 = try resolveExprStr(&mut a, "@alignOf(opaque)"); |
|
| 4463 | + | let err2 = try expectError(&result2); |
|
| 4464 | + | try expectErrorKind(&result2, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4465 | + | } |
|
| 3495 | 4466 | } |
|
| 3496 | 4467 | ||
| 3497 | 4468 | /// Test that immutable slice/pointer parameters cannot be borrowed mutably. |
|
| 3498 | 4469 | @test unsafe fn testMutableBorrowFromImmutablePointer() throws (testing::TestError) { |
|
| 3499 | - | let mut a = testResolver(); |
|
| 3500 | - | let program = "fn f(p: *i32) { let x = &mut *p; }"; |
|
| 3501 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3502 | - | let err = try expectError(&result); |
|
| 3503 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4470 | + | let mut testArena317 = testArena(); |
|
| 4471 | + | let testStorage317: 'test317 = &mut testArena317 in { |
|
| 4472 | + | let mut a = testResolver(testStorage317); |
|
| 4473 | + | let program = "fn f(p: *i32) { let x = &mut *p; }"; |
|
| 4474 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4475 | + | let err = try expectError(&result); |
|
| 4476 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4477 | + | } |
|
| 3504 | 4478 | } |
|
| 3505 | 4479 | ||
| 3506 | 4480 | /// Test that immutable slice parameters cannot be borrowed mutably. |
|
| 3507 | 4481 | @test unsafe fn testMutableBorrowFromImmutableSlice() throws (testing::TestError) { |
|
| 3508 | - | let mut a = testResolver(); |
|
| 3509 | - | let program = "fn f(s: *[i32]) { let x: *mut i32 = &mut s[0]; }"; |
|
| 3510 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3511 | - | let err = try expectError(&result); |
|
| 3512 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4482 | + | let mut testArena318 = testArena(); |
|
| 4483 | + | let testStorage318: 'test318 = &mut testArena318 in { |
|
| 4484 | + | let mut a = testResolver(testStorage318); |
|
| 4485 | + | let program = "fn f(s: *[i32]) { let x: *mut i32 = &mut s[0]; }"; |
|
| 4486 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4487 | + | let err = try expectError(&result); |
|
| 4488 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4489 | + | } |
|
| 3513 | 4490 | } |
|
| 3514 | 4491 | ||
| 3515 | 4492 | /// Test that mutable pointer parameters can be borrowed mutably. |
|
| 3516 | 4493 | @test unsafe fn testMutableBorrowFromMutablePointer() throws (testing::TestError) { |
|
| 3517 | - | let mut a = testResolver(); |
|
| 3518 | - | let program = "fn f(p: *mut i32) { let x: *mut i32 = &mut *p; }"; |
|
| 3519 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3520 | - | try expectNoErrors(&result); |
|
| 4494 | + | let mut testArena319 = testArena(); |
|
| 4495 | + | let testStorage319: 'test319 = &mut testArena319 in { |
|
| 4496 | + | let mut a = testResolver(testStorage319); |
|
| 4497 | + | let program = "fn f(p: *mut i32) { let x: *mut i32 = &mut *p; }"; |
|
| 4498 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4499 | + | try expectNoErrors(&result); |
|
| 4500 | + | } |
|
| 3521 | 4501 | } |
|
| 3522 | 4502 | ||
| 3523 | 4503 | /// Test that mutable slice parameters can be borrowed mutably. |
|
| 3524 | 4504 | @test unsafe fn testMutableBorrowFromMutableSlice() throws (testing::TestError) { |
|
| 3525 | - | let mut a = testResolver(); |
|
| 3526 | - | let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }"; |
|
| 3527 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3528 | - | try expectNoErrors(&result); |
|
| 4505 | + | let mut testArena320 = testArena(); |
|
| 4506 | + | let testStorage320: 'test320 = &mut testArena320 in { |
|
| 4507 | + | let mut a = testResolver(testStorage320); |
|
| 4508 | + | let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }"; |
|
| 4509 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4510 | + | try expectNoErrors(&result); |
|
| 4511 | + | } |
|
| 3529 | 4512 | } |
|
| 3530 | 4513 | ||
| 3531 | 4514 | /// Test borrowing mutably from a field access on a call returning `*mut`. |
|
| 3532 | 4515 | @test unsafe fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) { |
|
| 3533 | - | let mut a = testResolver(); |
|
| 3534 | - | let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { static b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }"; |
|
| 3535 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3536 | - | try expectNoErrors(&result); |
|
| 4516 | + | let mut testArena321 = testArena(); |
|
| 4517 | + | let testStorage321: 'test321 = &mut testArena321 in { |
|
| 4518 | + | let mut a = testResolver(testStorage321); |
|
| 4519 | + | let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { static b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }"; |
|
| 4520 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4521 | + | try expectNoErrors(&result); |
|
| 4522 | + | } |
|
| 3537 | 4523 | } |
|
| 3538 | 4524 | ||
| 3539 | 4525 | /// Test that calls returning immutable pointers cannot be mutably borrowed. |
|
| 3540 | 4526 | @test unsafe fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) { |
|
| 3541 | - | let mut a = testResolver(); |
|
| 3542 | - | let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { constant b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }"; |
|
| 3543 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3544 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4527 | + | let mut testArena322 = testArena(); |
|
| 4528 | + | let testStorage322: 'test322 = &mut testArena322 in { |
|
| 4529 | + | let mut a = testResolver(testStorage322); |
|
| 4530 | + | let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { constant b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }"; |
|
| 4531 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4532 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4533 | + | } |
|
| 3545 | 4534 | } |
|
| 3546 | 4535 | ||
| 3547 | 4536 | /// Test borrowing mutably from a public static through scope access. |
|
| 3548 | 4537 | @test unsafe fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) { |
|
| 3549 | - | let mut a = testResolver(); |
|
| 3550 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4538 | + | let mut testArena323 = testArena(); |
|
| 4539 | + | let testStorage323: 'test323 = &mut testArena323 in { |
|
| 4540 | + | let mut a = testResolver(testStorage323); |
|
| 4541 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3551 | 4542 | ||
| 3552 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 3553 | - | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena); |
|
| 3554 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() { let p: *mut i32 = &mut statics::COUNTER; set *p = 7; }", &mut arena); |
|
| 4543 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 4544 | + | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena); |
|
| 4545 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() { let p: *mut i32 = &mut statics::COUNTER; set *p = 7; }", &mut arena); |
|
| 3555 | 4546 | ||
| 3556 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3557 | - | try expectNoErrors(&result); |
|
| 4547 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4548 | + | try expectNoErrors(&result); |
|
| 4549 | + | } |
|
| 3558 | 4550 | } |
|
| 3559 | 4551 | ||
| 3560 | 4552 | /// Test that constants through scope access cannot be mutably borrowed. |
|
| 3561 | 4553 | @test unsafe fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) { |
|
| 3562 | - | let mut a = testResolver(); |
|
| 3563 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4554 | + | let mut testArena324 = testArena(); |
|
| 4555 | + | let testStorage324: 'test324 = &mut testArena324 in { |
|
| 4556 | + | let mut a = testResolver(testStorage324); |
|
| 4557 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3564 | 4558 | ||
| 3565 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3566 | - | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena); |
|
| 3567 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() { let p: *mut i32 = &mut consts::LIMIT; set *p = 9; }", &mut arena); |
|
| 4559 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 4560 | + | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena); |
|
| 4561 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() { let p: *mut i32 = &mut consts::LIMIT; set *p = 9; }", &mut arena); |
|
| 3568 | 4562 | ||
| 3569 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3570 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4563 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4564 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4565 | + | } |
|
| 3571 | 4566 | } |
|
| 3572 | 4567 | ||
| 3573 | 4568 | /// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer. |
|
| 3574 | 4569 | @test unsafe fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) { |
|
| 3575 | - | let mut a = testResolver(); |
|
| 3576 | - | let program = "fn f() { static x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }"; |
|
| 3577 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3578 | - | let err = try expectError(&result); |
|
| 3579 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4570 | + | let mut testArena325 = testArena(); |
|
| 4571 | + | let testStorage325: 'test325 = &mut testArena325 in { |
|
| 4572 | + | let mut a = testResolver(testStorage325); |
|
| 4573 | + | let program = "fn f() { static x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }"; |
|
| 4574 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4575 | + | let err = try expectError(&result); |
|
| 4576 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4577 | + | } |
|
| 3580 | 4578 | } |
|
| 3581 | 4579 | ||
| 3582 | 4580 | /// Test that mutable pointer to immutable slice cannot be assigned through index. |
|
| 3583 | 4581 | /// This tests the case where we have `*mut *[T]`; the outer pointer is mutable but |
|
| 3584 | 4582 | /// the inner slice is immutable, so we shouldn't be able to mutate the elements. |
|
| 3585 | 4583 | @test unsafe fn testAssignThroughMutablePointerToImmutableSlice() throws (testing::TestError) { |
|
| 3586 | - | let mut a = testResolver(); |
|
| 3587 | - | let program = "fn f(slice: *[i32]) { let p: *mut *[i32] = &mut slice; set p[0] = 1; }"; |
|
| 3588 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4584 | + | let mut testArena326 = testArena(); |
|
| 4585 | + | let testStorage326: 'test326 = &mut testArena326 in { |
|
| 4586 | + | let mut a = testResolver(testStorage326); |
|
| 4587 | + | let program = "fn f(slice: *[i32]) { let p: *mut *[i32] = &mut slice; set p[0] = 1; }"; |
|
| 4588 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 3589 | 4589 | ||
| 3590 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4590 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4591 | + | } |
|
| 3591 | 4592 | } |
|
| 3592 | 4593 | ||
| 3593 | 4594 | /// Test that mutable slice parameters can be assigned through index. |
|
| 3594 | 4595 | @test unsafe fn testAssignThroughMutableSliceParam() throws (testing::TestError) { |
|
| 3595 | 4596 | { |
|
| 3596 | 4597 | // Mutable slice param: direct assignment should work |
|
| 3597 | - | let mut a = testResolver(); |
|
| 3598 | - | let program = "fn f(slice: *mut [i32]) { set slice[0] = 1; }"; |
|
| 3599 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3600 | - | try expectNoErrors(&result); |
|
| 4598 | + | let mut testArena327 = testArena(); |
|
| 4599 | + | let testStorage327: 'test327 = &mut testArena327 in { |
|
| 4600 | + | let mut a = testResolver(testStorage327); |
|
| 4601 | + | let program = "fn f(slice: *mut [i32]) { set slice[0] = 1; }"; |
|
| 4602 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4603 | + | try expectNoErrors(&result); |
|
| 4604 | + | } |
|
| 3601 | 4605 | } { |
|
| 3602 | 4606 | // Immutable slice param: direct assignment should fail |
|
| 3603 | - | let mut a = testResolver(); |
|
| 3604 | - | let program = "fn f(slice: *[i32]) { set slice[0] = 1; }"; |
|
| 3605 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3606 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4607 | + | let mut testArena328 = testArena(); |
|
| 4608 | + | let testStorage328: 'test328 = &mut testArena328 in { |
|
| 4609 | + | let mut a = testResolver(testStorage328); |
|
| 4610 | + | let program = "fn f(slice: *[i32]) { set slice[0] = 1; }"; |
|
| 4611 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4612 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4613 | + | } |
|
| 3607 | 4614 | } |
|
| 3608 | 4615 | } |
|
| 3609 | 4616 | ||
| 3610 | 4617 | /// Test range end type coercion with assignable types. |
|
| 3611 | 4618 | @test unsafe fn testRangeEndTypeCoercion() throws (testing::TestError) { |
|
| 3612 | 4619 | { |
|
| 3613 | - | let mut a = testResolver(); |
|
| 3614 | - | let program = "fn f(end: u32) { for i in 0..end {} }"; |
|
| 3615 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3616 | - | try expectNoErrors(&result); |
|
| 4620 | + | let mut testArena329 = testArena(); |
|
| 4621 | + | let testStorage329: 'test329 = &mut testArena329 in { |
|
| 4622 | + | let mut a = testResolver(testStorage329); |
|
| 4623 | + | let program = "fn f(end: u32) { for i in 0..end {} }"; |
|
| 4624 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4625 | + | try expectNoErrors(&result); |
|
| 4626 | + | } |
|
| 3617 | 4627 | } { |
|
| 3618 | - | let mut a = testResolver(); |
|
| 3619 | - | let program = "fn f(start: u32) { for i in start..9 {} }"; |
|
| 3620 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3621 | - | try expectNoErrors(&result); |
|
| 4628 | + | let mut testArena330 = testArena(); |
|
| 4629 | + | let testStorage330: 'test330 = &mut testArena330 in { |
|
| 4630 | + | let mut a = testResolver(testStorage330); |
|
| 4631 | + | let program = "fn f(start: u32) { for i in start..9 {} }"; |
|
| 4632 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4633 | + | try expectNoErrors(&result); |
|
| 4634 | + | } |
|
| 3622 | 4635 | } |
|
| 3623 | 4636 | } |
|
| 3624 | 4637 | ||
| 3625 | 4638 | /// Mixed-width range bounds require an explicit cast. |
|
| 3626 | 4639 | @test unsafe fn testRangeEndTypeSubType() throws (testing::TestError) { |
|
| 3627 | 4640 | { |
|
| 3628 | - | let mut a = testResolver(); |
|
| 3629 | - | let program = "fn f(start: i8, end: u32) { for i in start..end {} }"; |
|
| 3630 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3631 | - | let err = try expectError(&result); |
|
| 3632 | - | try expectTypeMismatch(err, super::Type::I8, super::Type::U32); |
|
| 4641 | + | let mut testArena331 = testArena(); |
|
| 4642 | + | let testStorage331: 'test331 = &mut testArena331 in { |
|
| 4643 | + | let mut a = testResolver(testStorage331); |
|
| 4644 | + | let program = "fn f(start: i8, end: u32) { for i in start..end {} }"; |
|
| 4645 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4646 | + | let err = try expectError(&result); |
|
| 4647 | + | try expectTypeMismatch(err, super::Type::I8, super::Type::U32); |
|
| 4648 | + | } |
|
| 3633 | 4649 | } { |
|
| 3634 | - | let mut a = testResolver(); |
|
| 3635 | - | let program = "fn f(start: i8, end: u32) { for i in (start as u32)..end {} }"; |
|
| 3636 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3637 | - | try expectNoErrors(&result); |
|
| 4650 | + | let mut testArena332 = testArena(); |
|
| 4651 | + | let testStorage332: 'test332 = &mut testArena332 in { |
|
| 4652 | + | let mut a = testResolver(testStorage332); |
|
| 4653 | + | let program = "fn f(start: i8, end: u32) { for i in (start as u32)..end {} }"; |
|
| 4654 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4655 | + | try expectNoErrors(&result); |
|
| 4656 | + | } |
|
| 3638 | 4657 | } |
|
| 3639 | 4658 | } |
|
| 3640 | 4659 | ||
| 3641 | 4660 | /// Test that try-catch expressions in statement context accept mismatched types. |
|
| 3642 | 4661 | @test unsafe fn testTryCatchInStatementContextTypeMismatchOk() throws (testing::TestError) { |
|
| 3643 | - | let mut a = testResolver(); |
|
| 3644 | - | let program = "fn f() { try g() catch {}; } fn g() -> bool throws (i32) { panic; }"; |
|
| 3645 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3646 | - | try expectNoErrors(&result); |
|
| 4662 | + | let mut testArena333 = testArena(); |
|
| 4663 | + | let testStorage333: 'test333 = &mut testArena333 in { |
|
| 4664 | + | let mut a = testResolver(testStorage333); |
|
| 4665 | + | let program = "fn f() { try g() catch {}; } fn g() -> bool throws (i32) { panic; }"; |
|
| 4666 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4667 | + | try expectNoErrors(&result); |
|
| 4668 | + | } |
|
| 3647 | 4669 | } |
|
| 3648 | 4670 | ||
| 3649 | 4671 | /// Test that try-catch blocks in value context require divergence or void. |
|
| 3650 | 4672 | @test unsafe fn testTryCatchInValueContextTypeMismatch() throws (testing::TestError) { |
|
| 3651 | - | let mut a = testResolver(); |
|
| 3652 | - | let program = "fn f() -> bool { return try g() catch {}; } fn g() -> bool throws (i32) { panic; }"; |
|
| 3653 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3654 | - | let err = try expectError(&result); |
|
| 3655 | - | try expectTypeMismatch(err, super::Type::Bool, super::Type::Void); |
|
| 4673 | + | let mut testArena334 = testArena(); |
|
| 4674 | + | let testStorage334: 'test334 = &mut testArena334 in { |
|
| 4675 | + | let mut a = testResolver(testStorage334); |
|
| 4676 | + | let program = "fn f() -> bool { return try g() catch {}; } fn g() -> bool throws (i32) { panic; }"; |
|
| 4677 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4678 | + | let err = try expectError(&result); |
|
| 4679 | + | try expectTypeMismatch(err, super::Type::Bool, super::Type::Void); |
|
| 4680 | + | } |
|
| 3656 | 4681 | } |
|
| 3657 | 4682 | ||
| 3658 | 4683 | /// Test that try-catch blocks in value context work when they diverge. |
|
| 3659 | 4684 | @test unsafe fn testTryCatchInValueContextDiverges() throws (testing::TestError) { |
|
| 3660 | - | let mut a = testResolver(); |
|
| 3661 | - | let program = "fn f() -> bool { return try g() catch { return false; }; } fn g() -> bool throws (i32) { panic; }"; |
|
| 3662 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3663 | - | try expectNoErrors(&result); |
|
| 4685 | + | let mut testArena335 = testArena(); |
|
| 4686 | + | let testStorage335: 'test335 = &mut testArena335 in { |
|
| 4687 | + | let mut a = testResolver(testStorage335); |
|
| 4688 | + | let program = "fn f() -> bool { return try g() catch { return false; }; } fn g() -> bool throws (i32) { panic; }"; |
|
| 4689 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4690 | + | try expectNoErrors(&result); |
|
| 4691 | + | } |
|
| 3664 | 4692 | } |
|
| 3665 | 4693 | ||
| 3666 | 4694 | /// Test that `try?` lifts result type to optional. |
|
| 3667 | 4695 | @test unsafe fn testTryOptionalLiftsToOptional() throws (testing::TestError) { |
|
| 3668 | - | let mut a = testResolver(); |
|
| 3669 | - | let program = "record S {} fn f() -> ?*S { return try? g(); } fn g() -> *S throws (i32) { panic; }"; |
|
| 3670 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3671 | - | try expectNoErrors(&result); |
|
| 4696 | + | let mut testArena336 = testArena(); |
|
| 4697 | + | let testStorage336: 'test336 = &mut testArena336 in { |
|
| 4698 | + | let mut a = testResolver(testStorage336); |
|
| 4699 | + | let program = "record S {} fn f() -> ?*S { return try? g(); } fn g() -> *S throws (i32) { panic; }"; |
|
| 4700 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4701 | + | try expectNoErrors(&result); |
|
| 4702 | + | } |
|
| 3672 | 4703 | } |
|
| 3673 | 4704 | ||
| 3674 | 4705 | /// Test that record fields can be assigned if the record binding is mutable. |
|
| 3675 | 4706 | @test unsafe fn testMutableAssignToMutableRecordBinding() throws (testing::TestError) { |
|
| 3676 | - | let mut a = testResolver(); |
|
| 3677 | - | let program = "record S { x: i32 } fn f() { let mut s = S { x: 1 }; set s.x = 2; }"; |
|
| 3678 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3679 | - | try expectNoErrors(&result); |
|
| 4707 | + | let mut testArena337 = testArena(); |
|
| 4708 | + | let testStorage337: 'test337 = &mut testArena337 in { |
|
| 4709 | + | let mut a = testResolver(testStorage337); |
|
| 4710 | + | let program = "record S { x: i32 } fn f() { let mut s = S { x: 1 }; set s.x = 2; }"; |
|
| 4711 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4712 | + | try expectNoErrors(&result); |
|
| 4713 | + | } |
|
| 3680 | 4714 | } |
|
| 3681 | 4715 | ||
| 3682 | 4716 | /// Test that record fields cannot be assigned if the record binding is immutable. |
|
| 3683 | 4717 | @test unsafe fn testMutableAssignToImmutableRecordBinding() throws (testing::TestError) { |
|
| 3684 | - | let mut a = testResolver(); |
|
| 3685 | - | let program = "record S { x: i32 } fn f() { let s = S { x: 1 }; set s.x = 2; }"; |
|
| 3686 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3687 | - | let err = try expectError(&result); |
|
| 3688 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4718 | + | let mut testArena338 = testArena(); |
|
| 4719 | + | let testStorage338: 'test338 = &mut testArena338 in { |
|
| 4720 | + | let mut a = testResolver(testStorage338); |
|
| 4721 | + | let program = "record S { x: i32 } fn f() { let s = S { x: 1 }; set s.x = 2; }"; |
|
| 4722 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4723 | + | let err = try expectError(&result); |
|
| 4724 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4725 | + | } |
|
| 3689 | 4726 | } |
|
| 3690 | 4727 | ||
| 3691 | 4728 | /// Test that record fields can be assigned through a mutable pointer. |
|
| 3692 | 4729 | @test unsafe fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) { |
|
| 3693 | - | let mut a = testResolver(); |
|
| 3694 | - | let program = "record S { x: i32 } fn f(p: *mut S) { set p.x = 2; }"; |
|
| 3695 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3696 | - | try expectNoErrors(&result); |
|
| 4730 | + | let mut testArena339 = testArena(); |
|
| 4731 | + | let testStorage339: 'test339 = &mut testArena339 in { |
|
| 4732 | + | let mut a = testResolver(testStorage339); |
|
| 4733 | + | let program = "record S { x: i32 } fn f(p: *mut S) { set p.x = 2; }"; |
|
| 4734 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4735 | + | try expectNoErrors(&result); |
|
| 4736 | + | } |
|
| 3697 | 4737 | } |
|
| 3698 | 4738 | ||
| 3699 | 4739 | /// Test that record fields cannot be assigned through an immutable pointer. |
|
| 3700 | 4740 | @test unsafe fn testMutableAssignToImmutablePointerToRecord() throws (testing::TestError) { |
|
| 3701 | - | let mut a = testResolver(); |
|
| 3702 | - | let program = "record S { x: i32 } fn f(p: *S) { set p.x = 2; }"; |
|
| 3703 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3704 | - | let err = try expectError(&result); |
|
| 3705 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4741 | + | let mut testArena340 = testArena(); |
|
| 4742 | + | let testStorage340: 'test340 = &mut testArena340 in { |
|
| 4743 | + | let mut a = testResolver(testStorage340); |
|
| 4744 | + | let program = "record S { x: i32 } fn f(p: *S) { set p.x = 2; }"; |
|
| 4745 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4746 | + | let err = try expectError(&result); |
|
| 4747 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4748 | + | } |
|
| 3706 | 4749 | } |
|
| 3707 | 4750 | ||
| 3708 | 4751 | // Opaque pointer tests. |
|
| 3709 | 4752 | ||
| 3710 | 4753 | /// You can assign any pointer (*T) to an opaque pointer (*opaque) without a cast. |
|
| 3711 | 4754 | @test unsafe fn testOpaquePointerAutoCoercion() throws (testing::TestError) { |
|
| 3712 | - | let mut a = testResolver(); |
|
| 3713 | - | let result = try resolveProgramStr(&mut a, "unsafe fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }"); |
|
| 3714 | - | try expectNoErrors(&result); |
|
| 4755 | + | let mut testArena341 = testArena(); |
|
| 4756 | + | let testStorage341: 'test341 = &mut testArena341 in { |
|
| 4757 | + | let mut a = testResolver(testStorage341); |
|
| 4758 | + | let result = try resolveProgramStr(&mut a, "unsafe fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }"); |
|
| 4759 | + | try expectNoErrors(&result); |
|
| 4760 | + | } |
|
| 3715 | 4761 | } |
|
| 3716 | 4762 | ||
| 3717 | 4763 | /// You cannot assign an opaque pointer to a non-opaque pointer without a cast. |
|
| 3718 | 4764 | @test unsafe fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) { |
|
| 3719 | - | let mut a = testResolver(); |
|
| 3720 | - | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let ptr: *i32 = o; }"); |
|
| 3721 | - | let err = try expectError(&result); |
|
| 3722 | - | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
|
| 3723 | - | else throw testing::TestError::Failed; |
|
| 3724 | - | let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected |
|
| 3725 | - | else throw testing::TestError::Failed; |
|
| 3726 | - | let case super::Type::Pointer { target: actualTarget, .. } = mismatch.actual |
|
| 3727 | - | else throw testing::TestError::Failed; |
|
| 4765 | + | let mut testArena342 = testArena(); |
|
| 4766 | + | let testStorage342: 'test342 = &mut testArena342 in { |
|
| 4767 | + | let mut a = testResolver(testStorage342); |
|
| 4768 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let ptr: *i32 = o; }"); |
|
| 4769 | + | let err = try expectError(&result); |
|
| 4770 | + | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
|
| 4771 | + | else throw testing::TestError::Failed; |
|
| 4772 | + | let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected |
|
| 4773 | + | else throw testing::TestError::Failed; |
|
| 4774 | + | let case super::Type::Pointer { target: actualTarget, .. } = mismatch.actual |
|
| 4775 | + | else throw testing::TestError::Failed; |
|
| 3728 | 4776 | ||
| 3729 | - | try testing::expect(*expectedTarget == super::Type::I32); |
|
| 3730 | - | try testing::expect(*actualTarget == super::Type::Opaque); |
|
| 4777 | + | try testing::expect(*expectedTarget == super::Type::I32); |
|
| 4778 | + | try testing::expect(*actualTarget == super::Type::Opaque); |
|
| 4779 | + | } |
|
| 3731 | 4780 | } |
|
| 3732 | 4781 | ||
| 3733 | 4782 | /// You cannot have a value of type `opaque` (function parameter). |
|
| 3734 | 4783 | @test unsafe fn testOpaqueValue() throws (testing::TestError) { |
|
| 3735 | 4784 | { |
|
| 3736 | - | let mut a = testResolver(); |
|
| 3737 | - | let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}"); |
|
| 3738 | - | let err = try expectError(&result); |
|
| 3739 | - | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4785 | + | let mut testArena343 = testArena(); |
|
| 4786 | + | let testStorage343: 'test343 = &mut testArena343 in { |
|
| 4787 | + | let mut a = testResolver(testStorage343); |
|
| 4788 | + | let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}"); |
|
| 4789 | + | let err = try expectError(&result); |
|
| 4790 | + | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4791 | + | } |
|
| 3740 | 4792 | } { |
|
| 3741 | - | let mut a = testResolver(); |
|
| 3742 | - | let result = try resolveProgramStr(&mut a, "unsafe fn f() { let x: opaque = undefined; }"); |
|
| 3743 | - | let err = try expectError(&result); |
|
| 3744 | - | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4793 | + | let mut testArena344 = testArena(); |
|
| 4794 | + | let testStorage344: 'test344 = &mut testArena344 in { |
|
| 4795 | + | let mut a = testResolver(testStorage344); |
|
| 4796 | + | let result = try resolveProgramStr(&mut a, "unsafe fn f() { let x: opaque = undefined; }"); |
|
| 4797 | + | let err = try expectError(&result); |
|
| 4798 | + | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4799 | + | } |
|
| 3745 | 4800 | } { |
|
| 3746 | - | let mut a = testResolver(); |
|
| 3747 | - | let result = try resolveProgramStr(&mut a, "record R { x: opaque }"); |
|
| 3748 | - | let err = try expectError(&result); |
|
| 3749 | - | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4801 | + | let mut testArena345 = testArena(); |
|
| 4802 | + | let testStorage345: 'test345 = &mut testArena345 in { |
|
| 4803 | + | let mut a = testResolver(testStorage345); |
|
| 4804 | + | let result = try resolveProgramStr(&mut a, "record R { x: opaque }"); |
|
| 4805 | + | let err = try expectError(&result); |
|
| 4806 | + | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4807 | + | } |
|
| 3750 | 4808 | } |
|
| 3751 | 4809 | } |
|
| 3752 | 4810 | ||
| 3753 | 4811 | /// You cannot dereference an opaque pointer, you have to cast it first. |
|
| 3754 | 4812 | @test unsafe fn testOpaquePointerNoDereference() throws (testing::TestError) { |
|
| 3755 | - | let mut a = testResolver(); |
|
| 3756 | - | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = *o; }"); |
|
| 3757 | - | let err = try expectError(&result); |
|
| 3758 | - | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref); |
|
| 4813 | + | let mut testArena346 = testArena(); |
|
| 4814 | + | let testStorage346: 'test346 = &mut testArena346 in { |
|
| 4815 | + | let mut a = testResolver(testStorage346); |
|
| 4816 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = *o; }"); |
|
| 4817 | + | let err = try expectError(&result); |
|
| 4818 | + | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref); |
|
| 4819 | + | } |
|
| 3759 | 4820 | } |
|
| 3760 | 4821 | ||
| 3761 | 4822 | /// Test that you can dereference after casting. |
|
| 3762 | 4823 | @test unsafe fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) { |
|
| 3763 | - | let mut a = testResolver(); |
|
| 3764 | - | let result = try resolveProgramStr(&mut a, "unsafe fn f() { let o: *opaque = undefined; let x = *(o as *i32); }"); |
|
| 3765 | - | try expectNoErrors(&result); |
|
| 4824 | + | let mut testArena347 = testArena(); |
|
| 4825 | + | let testStorage347: 'test347 = &mut testArena347 in { |
|
| 4826 | + | let mut a = testResolver(testStorage347); |
|
| 4827 | + | let result = try resolveProgramStr(&mut a, "unsafe fn f() { let o: *opaque = undefined; let x = *(o as *i32); }"); |
|
| 4828 | + | try expectNoErrors(&result); |
|
| 4829 | + | } |
|
| 3766 | 4830 | } |
|
| 3767 | 4831 | ||
| 3768 | 4832 | /// You cannot do pointer arithmetic with an opaque pointer. |
|
| 3769 | 4833 | @test unsafe fn testOpaquePointerNoArithmetic() throws (testing::TestError) { |
|
| 3770 | 4834 | { |
|
| 3771 | - | let mut a = testResolver(); |
|
| 3772 | - | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o + 1; }"); |
|
| 3773 | - | let err = try expectError(&result); |
|
| 3774 | - | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 4835 | + | let mut testArena348 = testArena(); |
|
| 4836 | + | let testStorage348: 'test348 = &mut testArena348 in { |
|
| 4837 | + | let mut a = testResolver(testStorage348); |
|
| 4838 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o + 1; }"); |
|
| 4839 | + | let err = try expectError(&result); |
|
| 4840 | + | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 4841 | + | } |
|
| 3775 | 4842 | } { |
|
| 3776 | - | let mut a = testResolver(); |
|
| 3777 | - | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 + o; }"); |
|
| 3778 | - | let err = try expectError(&result); |
|
| 3779 | - | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 4843 | + | let mut testArena349 = testArena(); |
|
| 4844 | + | let testStorage349: 'test349 = &mut testArena349 in { |
|
| 4845 | + | let mut a = testResolver(testStorage349); |
|
| 4846 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 + o; }"); |
|
| 4847 | + | let err = try expectError(&result); |
|
| 4848 | + | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 4849 | + | } |
|
| 3780 | 4850 | } { |
|
| 3781 | - | let mut a = testResolver(); |
|
| 3782 | - | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o - 1; }"); |
|
| 3783 | - | let err = try expectError(&result); |
|
| 3784 | - | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 4851 | + | let mut testArena350 = testArena(); |
|
| 4852 | + | let testStorage350: 'test350 = &mut testArena350 in { |
|
| 4853 | + | let mut a = testResolver(testStorage350); |
|
| 4854 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o - 1; }"); |
|
| 4855 | + | let err = try expectError(&result); |
|
| 4856 | + | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 4857 | + | } |
|
| 3785 | 4858 | } { |
|
| 3786 | - | let mut a = testResolver(); |
|
| 3787 | - | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 - o; }"); |
|
| 3788 | - | let err = try expectError(&result); |
|
| 3789 | - | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 4859 | + | let mut testArena351 = testArena(); |
|
| 4860 | + | let testStorage351: 'test351 = &mut testArena351 in { |
|
| 4861 | + | let mut a = testResolver(testStorage351); |
|
| 4862 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 - o; }"); |
|
| 4863 | + | let err = try expectError(&result); |
|
| 4864 | + | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 4865 | + | } |
|
| 3790 | 4866 | } |
|
| 3791 | 4867 | } |
|
| 3792 | 4868 | ||
| 3793 | 4869 | // Wildcard import/reexport tests. |
|
| 3794 | 4870 | ||
| 3795 | 4871 | /// Test transitive re-export. |
|
| 3796 | 4872 | @test unsafe fn testWildcardReexportTransitive() throws (testing::TestError) { |
|
| 3797 | - | let mut a = testResolver(); |
|
| 3798 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4873 | + | let mut testArena352 = testArena(); |
|
| 4874 | + | let testStorage352: 'test352 = &mut testArena352 in { |
|
| 4875 | + | let mut a = testResolver(testStorage352); |
|
| 4876 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3799 | 4877 | ||
| 3800 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena); |
|
| 3801 | - | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena); |
|
| 3802 | - | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "mod c; export use c::*;", &mut arena); |
|
| 3803 | - | let cId = try registerModule(&mut MODULE_GRAPH, bId, "c", "mod d; export use d::*; export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3804 | - | let dId = try registerModule(&mut MODULE_GRAPH, cId, "d", "export constant MAX: i32 = 100;", &mut arena); |
|
| 4878 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena); |
|
| 4879 | + | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena); |
|
| 4880 | + | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "mod c; export use c::*;", &mut arena); |
|
| 4881 | + | let cId = try registerModule(&mut MODULE_GRAPH, bId, "c", "mod d; export use d::*; export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 4882 | + | let dId = try registerModule(&mut MODULE_GRAPH, cId, "d", "export constant MAX: i32 = 100;", &mut arena); |
|
| 3805 | 4883 | ||
| 3806 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3807 | - | try expectNoErrors(&result); |
|
| 4884 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4885 | + | try expectNoErrors(&result); |
|
| 4886 | + | } |
|
| 3808 | 4887 | } |
|
| 3809 | 4888 | ||
| 3810 | 4889 | /// Test that wildcard import can access public symbols. |
|
| 3811 | 4890 | @test unsafe fn testWildcardImportPublicOnly() throws (testing::TestError) { |
|
| 3812 | - | let mut a = testResolver(); |
|
| 3813 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4891 | + | let mut testArena353 = testArena(); |
|
| 4892 | + | let testStorage353: 'test353 = &mut testArena353 in { |
|
| 4893 | + | let mut a = testResolver(testStorage353); |
|
| 4894 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3814 | 4895 | ||
| 3815 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
|
| 3816 | - | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export record Value { number: i32 } export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
|
| 3817 | - | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn id(value: Value) -> Value { return value; } fn main() -> i32 { return public() + id(Value { number: 2 }).number; }", &mut arena); |
|
| 4896 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
|
| 4897 | + | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export record Value { number: i32 } export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
|
| 4898 | + | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn id(value: Value) -> Value { return value; } fn main() -> i32 { return public() + id(Value { number: 2 }).number; }", &mut arena); |
|
| 3818 | 4899 | ||
| 3819 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3820 | - | try expectNoErrors(&result); |
|
| 4900 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4901 | + | try expectNoErrors(&result); |
|
| 4902 | + | } |
|
| 3821 | 4903 | } |
|
| 3822 | 4904 | ||
| 3823 | 4905 | /// Test that wildcard import cannot access private symbols. |
|
| 3824 | 4906 | @test unsafe fn testWildcardImportSkipsPrivate() throws (testing::TestError) { |
|
| 3825 | - | let mut a = testResolver(); |
|
| 3826 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4907 | + | let mut testArena354 = testArena(); |
|
| 4908 | + | let testStorage354: 'test354 = &mut testArena354 in { |
|
| 4909 | + | let mut a = testResolver(testStorage354); |
|
| 4910 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3827 | 4911 | ||
| 3828 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
|
| 3829 | - | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
|
| 3830 | - | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn main() -> i32 { return private(); }", &mut arena); |
|
| 4912 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
|
| 4913 | + | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
|
| 4914 | + | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn main() -> i32 { return private(); }", &mut arena); |
|
| 3831 | 4915 | ||
| 3832 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3833 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 4916 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4917 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 4918 | + | } |
|
| 3834 | 4919 | } |
|
| 3835 | 4920 | ||
| 3836 | 4921 | /// Test that a constant array can use another constant as its length. |
|
| 3837 | 4922 | @test unsafe fn testConstArrayWithConstLength() throws (testing::TestError) { |
|
| 3838 | - | let mut a = testResolver(); |
|
| 3839 | - | let program = "constant LEN: u32 = 3; constant ARR: [i32; LEN] = [1, 2, 3];"; |
|
| 3840 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3841 | - | try expectNoErrors(&result); |
|
| 3842 | - | ||
| 3843 | - | // Verify the array constant has the correct type with length 3. |
|
| 3844 | - | let arrStmt = try getBlockStmt(result.root, 1); |
|
| 3845 | - | let sym = super::symbolFor(&a, arrStmt) |
|
| 3846 | - | else throw testing::TestError::Failed; |
|
| 3847 | - | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 3848 | - | else throw testing::TestError::Failed; |
|
| 3849 | - | try testing::expect(arrType.length == 3); |
|
| 4923 | + | let mut testArena355 = testArena(); |
|
| 4924 | + | let testStorage355: 'test355 = &mut testArena355 in { |
|
| 4925 | + | let mut a = testResolver(testStorage355); |
|
| 4926 | + | let program = "constant LEN: u32 = 3; constant ARR: [i32; LEN] = [1, 2, 3];"; |
|
| 4927 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4928 | + | try expectNoErrors(&result); |
|
| 4929 | + | ||
| 4930 | + | // Verify the array constant has the correct type with length 3. |
|
| 4931 | + | let arrStmt = try getBlockStmt(result.root, 1); |
|
| 4932 | + | let sym = super::symbolFor(&a, arrStmt) |
|
| 4933 | + | else throw testing::TestError::Failed; |
|
| 4934 | + | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 4935 | + | else throw testing::TestError::Failed; |
|
| 4936 | + | try testing::expect(arrType.length == 3); |
|
| 4937 | + | } |
|
| 3850 | 4938 | } |
|
| 3851 | 4939 | ||
| 3852 | 4940 | /// Test that a record field can use a constant as its array length. |
|
| 3853 | 4941 | @test unsafe fn testRecordFieldWithConstArrayLength() throws (testing::TestError) { |
|
| 3854 | - | let mut a = testResolver(); |
|
| 3855 | - | let program = "constant SIZE: u32 = 4; record Buffer { data: [i32; SIZE], }"; |
|
| 3856 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3857 | - | try expectNoErrors(&result); |
|
| 4942 | + | let mut testArena356 = testArena(); |
|
| 4943 | + | let testStorage356: 'test356 = &mut testArena356 in { |
|
| 4944 | + | let mut a = testResolver(testStorage356); |
|
| 4945 | + | let program = "constant SIZE: u32 = 4; record Buffer { data: [i32; SIZE], }"; |
|
| 4946 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4947 | + | try expectNoErrors(&result); |
|
| 4948 | + | } |
|
| 3858 | 4949 | } |
|
| 3859 | 4950 | ||
| 3860 | 4951 | /// Test that a constant can have a record literal value (lazy record body resolution). |
|
| 3861 | 4952 | @test unsafe fn testConstWithRecordLiteral() throws (testing::TestError) { |
|
| 3862 | - | let mut a = testResolver(); |
|
| 3863 | - | let program = "record Point { x: i32, y: i32 } constant ORIGIN: Point = Point { x: 0, y: 0 };"; |
|
| 3864 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3865 | - | try expectNoErrors(&result); |
|
| 4953 | + | let mut testArena357 = testArena(); |
|
| 4954 | + | let testStorage357: 'test357 = &mut testArena357 in { |
|
| 4955 | + | let mut a = testResolver(testStorage357); |
|
| 4956 | + | let program = "record Point { x: i32, y: i32 } constant ORIGIN: Point = Point { x: 0, y: 0 };"; |
|
| 4957 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4958 | + | try expectNoErrors(&result); |
|
| 4959 | + | } |
|
| 3866 | 4960 | } |
|
| 3867 | 4961 | ||
| 3868 | 4962 | /// Test that a constant can have a union variant value (lazy union body resolution). |
|
| 3869 | 4963 | @test unsafe fn testConstWithUnionVariant() throws (testing::TestError) { |
|
| 3870 | - | let mut a = testResolver(); |
|
| 3871 | - | let program = "union Color { Red, Green, Blue } constant DEFAULT: Color = Color::Red;"; |
|
| 3872 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3873 | - | try expectNoErrors(&result); |
|
| 4964 | + | let mut testArena358 = testArena(); |
|
| 4965 | + | let testStorage358: 'test358 = &mut testArena358 in { |
|
| 4966 | + | let mut a = testResolver(testStorage358); |
|
| 4967 | + | let program = "union Color { Red, Green, Blue } constant DEFAULT: Color = Color::Red;"; |
|
| 4968 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4969 | + | try expectNoErrors(&result); |
|
| 4970 | + | } |
|
| 3874 | 4971 | } |
|
| 3875 | 4972 | ||
| 3876 | 4973 | /// Test that record field types can reference imported types. |
|
| 3877 | 4974 | /// |
|
| 3878 | 4975 | /// This tests that `use` statements are processed before record body resolution, |
|
| 3879 | 4976 | /// allowing record fields to use types from imported modules. |
|
| 3880 | 4977 | @test unsafe fn testRecordFieldUsesImportedType() throws (testing::TestError) { |
|
| 3881 | - | let mut a = testResolver(); |
|
| 3882 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4978 | + | let mut testArena359 = testArena(); |
|
| 4979 | + | let testStorage359: 'test359 = &mut testArena359 in { |
|
| 4980 | + | let mut a = testResolver(testStorage359); |
|
| 4981 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3883 | 4982 | ||
| 3884 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena); |
|
| 3885 | - | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena); |
|
| 3886 | - | let scannerId = try registerModule(&mut MODULE_GRAPH, rootId, "scanner", "use root::types; record Scanner { pool: *types::Pool }", &mut arena); |
|
| 4983 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena); |
|
| 4984 | + | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena); |
|
| 4985 | + | let scannerId = try registerModule(&mut MODULE_GRAPH, rootId, "scanner", "use root::types; record Scanner { pool: *types::Pool }", &mut arena); |
|
| 3887 | 4986 | ||
| 3888 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3889 | - | try expectNoErrors(&result); |
|
| 4987 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4988 | + | try expectNoErrors(&result); |
|
| 4989 | + | } |
|
| 3890 | 4990 | } |
|
| 3891 | 4991 | ||
| 3892 | 4992 | /// Test that imported constants can be used in array size expressions. |
|
| 3893 | 4993 | /// |
|
| 3894 | 4994 | /// This tests that constant values are propagated through scope access expressions, |
|
| 3895 | 4995 | /// enabling compile-time evaluation of array sizes using imported constants. |
|
| 3896 | 4996 | @test unsafe fn testImportedConstantInArraySize() throws (testing::TestError) { |
|
| 3897 | - | let mut a = testResolver(); |
|
| 3898 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4997 | + | let mut testArena360 = testArena(); |
|
| 4998 | + | let testStorage360: 'test360 = &mut testArena360 in { |
|
| 4999 | + | let mut a = testResolver(testStorage360); |
|
| 5000 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3899 | 5001 | ||
| 3900 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3901 | - | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena); |
|
| 3902 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = [0; consts::SIZE];", &mut arena); |
|
| 5002 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 5003 | + | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena); |
|
| 5004 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = [0; consts::SIZE];", &mut arena); |
|
| 3903 | 5005 | ||
| 3904 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3905 | - | try expectNoErrors(&result); |
|
| 5006 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5007 | + | try expectNoErrors(&result); |
|
| 5008 | + | } |
|
| 3906 | 5009 | } |
|
| 3907 | 5010 | ||
| 3908 | 5011 | /// Test that `if let case` binds payload variables in the then branch. |
|
| 3909 | 5012 | /// |
|
| 3910 | 5013 | /// When using `if let case Union::Variant(x) = expr { ... }`, the variable `x` should |
|
| 3911 | 5014 | /// be bound to the payload value within the then branch scope. |
|
| 3912 | 5015 | @test unsafe fn testResolveIfCaseBindsPayload() throws (testing::TestError) { |
|
| 3913 | - | let mut a = testResolver(); |
|
| 3914 | - | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value { return x; } return 0; }"; |
|
| 3915 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3916 | - | try expectNoErrors(&result); |
|
| 5016 | + | let mut testArena361 = testArena(); |
|
| 5017 | + | let testStorage361: 'test361 = &mut testArena361 in { |
|
| 5018 | + | let mut a = testResolver(testStorage361); |
|
| 5019 | + | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value { return x; } return 0; }"; |
|
| 5020 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5021 | + | try expectNoErrors(&result); |
|
| 5022 | + | } |
|
| 3917 | 5023 | } |
|
| 3918 | 5024 | ||
| 3919 | 5025 | /// Test that `if let case` payload binding is scoped to the then branch. |
|
| 3920 | 5026 | /// |
|
| 3921 | 5027 | /// The payload variable should not be accessible outside the then branch. |
|
| 3922 | 5028 | @test unsafe fn testResolveIfCasePayloadScopeError() throws (testing::TestError) { |
|
| 3923 | - | let mut a = testResolver(); |
|
| 3924 | - | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value {} return x; }"; |
|
| 3925 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3926 | - | let err = try expectError(&result); |
|
| 3927 | - | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 3928 | - | else throw testing::TestError::Failed; |
|
| 3929 | - | try testing::expect(mem::eq(name, "x")); |
|
| 5029 | + | let mut testArena362 = testArena(); |
|
| 5030 | + | let testStorage362: 'test362 = &mut testArena362 in { |
|
| 5031 | + | let mut a = testResolver(testStorage362); |
|
| 5032 | + | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value {} return x; }"; |
|
| 5033 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5034 | + | let err = try expectError(&result); |
|
| 5035 | + | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 5036 | + | else throw testing::TestError::Failed; |
|
| 5037 | + | try testing::expect(mem::eq(name, "x")); |
|
| 5038 | + | } |
|
| 3930 | 5039 | } |
|
| 3931 | 5040 | ||
| 3932 | 5041 | /// Test that `let case` binds payload variables in the current scope. |
|
| 3933 | 5042 | /// |
|
| 3934 | 5043 | /// When using `let case Union::Variant(x) = expr else { ... }`, the variable `x` |
|
| 3935 | 5044 | /// should be bound in the scope after the statement. |
|
| 3936 | 5045 | @test unsafe fn testResolveLetCaseElseBindsPayload() throws (testing::TestError) { |
|
| 3937 | - | let mut a = testResolver(); |
|
| 3938 | - | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { let case Opt::Some(x) = value else panic; return x; }"; |
|
| 3939 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3940 | - | try expectNoErrors(&result); |
|
| 5046 | + | let mut testArena363 = testArena(); |
|
| 5047 | + | let testStorage363: 'test363 = &mut testArena363 in { |
|
| 5048 | + | let mut a = testResolver(testStorage363); |
|
| 5049 | + | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { let case Opt::Some(x) = value else panic; return x; }"; |
|
| 5050 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5051 | + | try expectNoErrors(&result); |
|
| 5052 | + | } |
|
| 3941 | 5053 | } |
|
| 3942 | 5054 | ||
| 3943 | 5055 | /// Test that function pointers with identical signatures are assignable. |
|
| 3944 | 5056 | /// |
|
| 3945 | 5057 | /// Two function types with the same parameters, return type, and throw list |
|
| 3946 | 5058 | /// should be considered structurally equal, even if they are separate allocations. |
|
| 3947 | 5059 | @test unsafe fn testFnPointerAssignability() throws (testing::TestError) { |
|
| 3948 | - | let mut a = testResolver(); |
|
| 3949 | - | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn double(n: i32) -> i32 { return n * 2; } apply(double, 5);"; |
|
| 3950 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3951 | - | try expectNoErrors(&result); |
|
| 5060 | + | let mut testArena364 = testArena(); |
|
| 5061 | + | let testStorage364: 'test364 = &mut testArena364 in { |
|
| 5062 | + | let mut a = testResolver(testStorage364); |
|
| 5063 | + | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn double(n: i32) -> i32 { return n * 2; } apply(double, 5);"; |
|
| 5064 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5065 | + | try expectNoErrors(&result); |
|
| 5066 | + | } |
|
| 3952 | 5067 | } |
|
| 3953 | 5068 | ||
| 3954 | 5069 | /// Test that function pointers with different parameter types are not assignable. |
|
| 3955 | 5070 | @test unsafe fn testFnPointerParamMismatch() throws (testing::TestError) { |
|
| 3956 | - | let mut a = testResolver(); |
|
| 3957 | - | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i8) -> i32 { return n as i32; } apply(other, 5);"; |
|
| 3958 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3959 | - | let err = try expectError(&result); |
|
| 3960 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 3961 | - | else throw testing::TestError::Failed; |
|
| 5071 | + | let mut testArena365 = testArena(); |
|
| 5072 | + | let testStorage365: 'test365 = &mut testArena365 in { |
|
| 5073 | + | let mut a = testResolver(testStorage365); |
|
| 5074 | + | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i8) -> i32 { return n as i32; } apply(other, 5);"; |
|
| 5075 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5076 | + | let err = try expectError(&result); |
|
| 5077 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5078 | + | else throw testing::TestError::Failed; |
|
| 5079 | + | } |
|
| 3962 | 5080 | } |
|
| 3963 | 5081 | ||
| 3964 | 5082 | /// Test that function pointers with different return types are not assignable. |
|
| 3965 | 5083 | @test unsafe fn testFnPointerReturnMismatch() throws (testing::TestError) { |
|
| 3966 | - | let mut a = testResolver(); |
|
| 3967 | - | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i32) -> i8 { return n as i8; } apply(other, 5);"; |
|
| 3968 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3969 | - | let err = try expectError(&result); |
|
| 3970 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 3971 | - | else throw testing::TestError::Failed; |
|
| 5084 | + | let mut testArena366 = testArena(); |
|
| 5085 | + | let testStorage366: 'test366 = &mut testArena366 in { |
|
| 5086 | + | let mut a = testResolver(testStorage366); |
|
| 5087 | + | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i32) -> i8 { return n as i8; } apply(other, 5);"; |
|
| 5088 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5089 | + | let err = try expectError(&result); |
|
| 5090 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5091 | + | else throw testing::TestError::Failed; |
|
| 5092 | + | } |
|
| 3972 | 5093 | } |
|
| 3973 | 5094 | ||
| 3974 | 5095 | /// Test that named records use nominal typing, not structural. |
|
| 3975 | 5096 | /// |
|
| 3976 | 5097 | /// Two different named record types with identical fields should NOT be |
|
| 3977 | 5098 | /// assignable to each other, because they are distinct nominal types. |
|
| 3978 | 5099 | @test unsafe fn testNamedRecordNominalTyping() throws (testing::TestError) { |
|
| 3979 | - | let mut a = testResolver(); |
|
| 3980 | - | let program = "record Point { x: i32, y: i32 } record Vec2 { x: i32, y: i32 } fn take(p: Point) -> i32 { return p.x; } let v = Vec2 { x: 1, y: 2 }; take(v);"; |
|
| 3981 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3982 | - | let err = try expectError(&result); |
|
| 3983 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 3984 | - | else throw testing::TestError::Failed; |
|
| 5100 | + | let mut testArena367 = testArena(); |
|
| 5101 | + | let testStorage367: 'test367 = &mut testArena367 in { |
|
| 5102 | + | let mut a = testResolver(testStorage367); |
|
| 5103 | + | let program = "record Point { x: i32, y: i32 } record Vec2 { x: i32, y: i32 } fn take(p: Point) -> i32 { return p.x; } let v = Vec2 { x: 1, y: 2 }; take(v);"; |
|
| 5104 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5105 | + | let err = try expectError(&result); |
|
| 5106 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5107 | + | else throw testing::TestError::Failed; |
|
| 5108 | + | } |
|
| 3985 | 5109 | } |
|
| 3986 | 5110 | ||
| 3987 | 5111 | /// Test that union variants with labeled record payloads can be constructed. |
|
| 3988 | 5112 | @test unsafe fn testUnionVariantAnonRecordPayload() throws (testing::TestError) { |
|
| 3989 | - | let mut a = testResolver(); |
|
| 3990 | - | let program = "union Event { Click { x: i32, y: i32 }, Key { code: u32 } } let e = Event::Click { x: 10, y: 20 };"; |
|
| 3991 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 3992 | - | try expectNoErrors(&result); |
|
| 5113 | + | let mut testArena368 = testArena(); |
|
| 5114 | + | let testStorage368: 'test368 = &mut testArena368 in { |
|
| 5115 | + | let mut a = testResolver(testStorage368); |
|
| 5116 | + | let program = "union Event { Click { x: i32, y: i32 }, Key { code: u32 } } let e = Event::Click { x: 10, y: 20 };"; |
|
| 5117 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5118 | + | try expectNoErrors(&result); |
|
| 5119 | + | } |
|
| 3993 | 5120 | } |
|
| 3994 | 5121 | ||
| 3995 | 5122 | /// Test that unlabeled record literals with positional fields work correctly. |
|
| 3996 | 5123 | /// |
|
| 3997 | 5124 | /// When a record is declared with positional fields (e.g., `record R(i32, bool)`), |
|
| 3998 | 5125 | /// the literal must use constructor call syntax with positional arguments. |
|
| 3999 | 5126 | @test unsafe fn testResolveUnlabeledRecordLitValid() throws (testing::TestError) { |
|
| 4000 | - | let mut a = testResolver(); |
|
| 4001 | - | let program = "record R(i32, bool); let r: R = R(1, true);"; |
|
| 4002 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4003 | - | try expectNoErrors(&result); |
|
| 5127 | + | let mut testArena369 = testArena(); |
|
| 5128 | + | let testStorage369: 'test369 = &mut testArena369 in { |
|
| 5129 | + | let mut a = testResolver(testStorage369); |
|
| 5130 | + | let program = "record R(i32, bool); let r: R = R(1, true);"; |
|
| 5131 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5132 | + | try expectNoErrors(&result); |
|
| 5133 | + | } |
|
| 4004 | 5134 | } |
|
| 4005 | 5135 | ||
| 4006 | 5136 | /// Test that using brace syntax for an unlabeled record causes an error. |
|
| 4007 | 5137 | @test unsafe fn testResolveUnlabeledRecordLitStyleMismatch() throws (testing::TestError) { |
|
| 4008 | - | let mut a = testResolver(); |
|
| 4009 | - | let program = "record R(i32); let r = R { x: 1 };"; |
|
| 4010 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4011 | - | try expectErrorKind(&result, super::ErrorKind::RecordFieldStyleMismatch); |
|
| 5138 | + | let mut testArena370 = testArena(); |
|
| 5139 | + | let testStorage370: 'test370 = &mut testArena370 in { |
|
| 5140 | + | let mut a = testResolver(testStorage370); |
|
| 5141 | + | let program = "record R(i32); let r = R { x: 1 };"; |
|
| 5142 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5143 | + | try expectErrorKind(&result, super::ErrorKind::RecordFieldStyleMismatch); |
|
| 5144 | + | } |
|
| 4012 | 5145 | } |
|
| 4013 | 5146 | ||
| 4014 | 5147 | /// Test that providing too many fields for an unlabeled record causes count mismatch. |
|
| 4015 | 5148 | @test unsafe fn testResolveUnlabeledRecordLitTooManyFields() throws (testing::TestError) { |
|
| 4016 | - | let mut a = testResolver(); |
|
| 4017 | - | let program = "record R(i32, bool); let r = R(1, true, 3);"; |
|
| 4018 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4019 | - | let err = try expectError(&result); |
|
| 4020 | - | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 4021 | - | else throw testing::TestError::Failed; |
|
| 5149 | + | let mut testArena371 = testArena(); |
|
| 5150 | + | let testStorage371: 'test371 = &mut testArena371 in { |
|
| 5151 | + | let mut a = testResolver(testStorage371); |
|
| 5152 | + | let program = "record R(i32, bool); let r = R(1, true, 3);"; |
|
| 5153 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5154 | + | let err = try expectError(&result); |
|
| 5155 | + | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 5156 | + | else throw testing::TestError::Failed; |
|
| 5157 | + | } |
|
| 4022 | 5158 | } |
|
| 4023 | 5159 | ||
| 4024 | 5160 | /// Test that match pattern with wrong number of bindings causes count mismatch. |
|
| 4025 | 5161 | @test unsafe fn testResolveMatchPatternWrongBindingCount() throws (testing::TestError) { |
|
| 4026 | - | let mut a = testResolver(); |
|
| 4027 | - | let program = "union Event { Click { x: i32, y: i32 } } fn f(e: Event) { match e { case Event::Click(a) => {} } }"; |
|
| 4028 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4029 | - | let err = try expectError(&result); |
|
| 4030 | - | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 4031 | - | else throw testing::TestError::Failed; |
|
| 5162 | + | let mut testArena372 = testArena(); |
|
| 5163 | + | let testStorage372: 'test372 = &mut testArena372 in { |
|
| 5164 | + | let mut a = testResolver(testStorage372); |
|
| 5165 | + | let program = "union Event { Click { x: i32, y: i32 } } fn f(e: Event) { match e { case Event::Click(a) => {} } }"; |
|
| 5166 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5167 | + | let err = try expectError(&result); |
|
| 5168 | + | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 5169 | + | else throw testing::TestError::Failed; |
|
| 5170 | + | } |
|
| 4032 | 5171 | } |
|
| 4033 | 5172 | ||
| 4034 | 5173 | /// Test that shorthand field syntax works in record literals. |
|
| 4035 | 5174 | /// `Point { x, y }` should be equivalent to `Point { x: x, y: y }`. |
|
| 4036 | 5175 | @test unsafe fn testResolveRecordLiteralShorthand() throws (testing::TestError) { |
|
| 4037 | - | let mut a = testResolver(); |
|
| 4038 | - | let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 1; let y: i32 = 2; let p = Point { x, y }; }"; |
|
| 4039 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4040 | - | try expectNoErrors(&result); |
|
| 5176 | + | let mut testArena373 = testArena(); |
|
| 5177 | + | let testStorage373: 'test373 = &mut testArena373 in { |
|
| 5178 | + | let mut a = testResolver(testStorage373); |
|
| 5179 | + | let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 1; let y: i32 = 2; let p = Point { x, y }; }"; |
|
| 5180 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5181 | + | try expectNoErrors(&result); |
|
| 5182 | + | } |
|
| 4041 | 5183 | } |
|
| 4042 | 5184 | ||
| 4043 | 5185 | /// Test shorthand field syntax with mixed explicit and shorthand fields. |
|
| 4044 | 5186 | @test unsafe fn testResolveRecordLiteralMixedShorthand() throws (testing::TestError) { |
|
| 4045 | - | let mut a = testResolver(); |
|
| 4046 | - | let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 5; let p = Point { x, y: 10 }; }"; |
|
| 4047 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4048 | - | try expectNoErrors(&result); |
|
| 5187 | + | let mut testArena374 = testArena(); |
|
| 5188 | + | let testStorage374: 'test374 = &mut testArena374 in { |
|
| 5189 | + | let mut a = testResolver(testStorage374); |
|
| 5190 | + | let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 5; let p = Point { x, y: 10 }; }"; |
|
| 5191 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5192 | + | try expectNoErrors(&result); |
|
| 5193 | + | } |
|
| 4049 | 5194 | } |
|
| 4050 | 5195 | ||
| 4051 | 5196 | /// Test record-style union variant patterns with shorthand syntax. |
|
| 4052 | 5197 | @test unsafe fn testResolveMatchRecordPatternShorthand() throws (testing::TestError) { |
|
| 4053 | - | let mut a = testResolver(); |
|
| 4054 | - | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height } => return width + height } }"; |
|
| 4055 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4056 | - | try expectNoErrors(&result); |
|
| 5198 | + | let mut testArena375 = testArena(); |
|
| 5199 | + | let testStorage375: 'test375 = &mut testArena375 in { |
|
| 5200 | + | let mut a = testResolver(testStorage375); |
|
| 5201 | + | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height } => return width + height } }"; |
|
| 5202 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5203 | + | try expectNoErrors(&result); |
|
| 5204 | + | } |
|
| 4057 | 5205 | } |
|
| 4058 | 5206 | ||
| 4059 | 5207 | /// Test record pattern with mixed shorthand and explicit labels. |
|
| 4060 | 5208 | @test unsafe fn testResolveMatchRecordPatternMixed() throws (testing::TestError) { |
|
| 4061 | - | let mut a = testResolver(); |
|
| 4062 | - | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height: h } => return width + h } }"; |
|
| 4063 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4064 | - | try expectNoErrors(&result); |
|
| 5209 | + | let mut testArena376 = testArena(); |
|
| 5210 | + | let testStorage376: 'test376 = &mut testArena376 in { |
|
| 5211 | + | let mut a = testResolver(testStorage376); |
|
| 5212 | + | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height: h } => return width + h } }"; |
|
| 5213 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5214 | + | try expectNoErrors(&result); |
|
| 5215 | + | } |
|
| 4065 | 5216 | } |
|
| 4066 | 5217 | ||
| 4067 | 5218 | /// Test record pattern with fields in reverse order. |
|
| 4068 | 5219 | @test unsafe fn testResolveMatchRecordPatternReversed() throws (testing::TestError) { |
|
| 4069 | - | let mut a = testResolver(); |
|
| 4070 | - | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height: h, width: w } => return w + h } }"; |
|
| 4071 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4072 | - | try expectNoErrors(&result); |
|
| 5220 | + | let mut testArena377 = testArena(); |
|
| 5221 | + | let testStorage377: 'test377 = &mut testArena377 in { |
|
| 5222 | + | let mut a = testResolver(testStorage377); |
|
| 5223 | + | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height: h, width: w } => return w + h } }"; |
|
| 5224 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5225 | + | try expectNoErrors(&result); |
|
| 5226 | + | } |
|
| 4073 | 5227 | } |
|
| 4074 | 5228 | ||
| 4075 | 5229 | /// Test record pattern with shorthand syntax in reverse order. |
|
| 4076 | 5230 | /// Pattern `{ height, width }` binds all fields using shorthand, but not in definition order. |
|
| 4077 | 5231 | @test unsafe fn testResolveMatchRecordPatternShorthandReversed() throws (testing::TestError) { |
|
| 4078 | - | let mut a = testResolver(); |
|
| 4079 | - | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height, width } => return width + height } }"; |
|
| 4080 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4081 | - | try expectNoErrors(&result); |
|
| 5232 | + | let mut testArena378 = testArena(); |
|
| 5233 | + | let testStorage378: 'test378 = &mut testArena378 in { |
|
| 5234 | + | let mut a = testResolver(testStorage378); |
|
| 5235 | + | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height, width } => return width + height } }"; |
|
| 5236 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5237 | + | try expectNoErrors(&result); |
|
| 5238 | + | } |
|
| 4082 | 5239 | } |
|
| 4083 | 5240 | ||
| 4084 | 5241 | /// Test record pattern with `..` ignoring fields. |
|
| 4085 | 5242 | @test unsafe fn testResolveMatchRecordPatternIgnoreRest() throws (testing::TestError) { |
|
| 4086 | 5243 | { |
|
| 4087 | - | let mut a = testResolver(); |
|
| 4088 | - | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x, .. } => return x } }"; |
|
| 4089 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4090 | - | try expectNoErrors(&result); |
|
| 5244 | + | let mut testArena379 = testArena(); |
|
| 5245 | + | let testStorage379: 'test379 = &mut testArena379 in { |
|
| 5246 | + | let mut a = testResolver(testStorage379); |
|
| 5247 | + | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x, .. } => return x } }"; |
|
| 5248 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5249 | + | try expectNoErrors(&result); |
|
| 5250 | + | } |
|
| 4091 | 5251 | } { |
|
| 4092 | - | let mut a = testResolver(); |
|
| 4093 | - | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x: val, .. } => return val } }"; |
|
| 4094 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4095 | - | try expectNoErrors(&result); |
|
| 5252 | + | let mut testArena380 = testArena(); |
|
| 5253 | + | let testStorage380: 'test380 = &mut testArena380 in { |
|
| 5254 | + | let mut a = testResolver(testStorage380); |
|
| 5255 | + | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x: val, .. } => return val } }"; |
|
| 5256 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5257 | + | try expectNoErrors(&result); |
|
| 5258 | + | } |
|
| 4096 | 5259 | } { |
|
| 4097 | - | let mut a = testResolver(); |
|
| 4098 | - | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, .. } => return z } }"; |
|
| 4099 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4100 | - | try expectNoErrors(&result); |
|
| 5260 | + | let mut testArena381 = testArena(); |
|
| 5261 | + | let testStorage381: 'test381 = &mut testArena381 in { |
|
| 5262 | + | let mut a = testResolver(testStorage381); |
|
| 5263 | + | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, .. } => return z } }"; |
|
| 5264 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5265 | + | try expectNoErrors(&result); |
|
| 5266 | + | } |
|
| 4101 | 5267 | } { |
|
| 4102 | - | let mut a = testResolver(); |
|
| 4103 | - | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, x, .. } => return x + z } }"; |
|
| 4104 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4105 | - | try expectNoErrors(&result); |
|
| 5268 | + | let mut testArena382 = testArena(); |
|
| 5269 | + | let testStorage382: 'test382 = &mut testArena382 in { |
|
| 5270 | + | let mut a = testResolver(testStorage382); |
|
| 5271 | + | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, x, .. } => return x + z } }"; |
|
| 5272 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5273 | + | try expectNoErrors(&result); |
|
| 5274 | + | } |
|
| 4106 | 5275 | } { |
|
| 4107 | - | let mut a = testResolver(); |
|
| 4108 | - | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> bool { match g { case G::Point { .. } => return true } }"; |
|
| 4109 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4110 | - | try expectNoErrors(&result); |
|
| 5276 | + | let mut testArena383 = testArena(); |
|
| 5277 | + | let testStorage383: 'test383 = &mut testArena383 in { |
|
| 5278 | + | let mut a = testResolver(testStorage383); |
|
| 5279 | + | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> bool { match g { case G::Point { .. } => return true } }"; |
|
| 5280 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5281 | + | try expectNoErrors(&result); |
|
| 5282 | + | } |
|
| 4111 | 5283 | } |
|
| 4112 | 5284 | } |
|
| 4113 | 5285 | ||
| 4114 | 5286 | /// Test standalone record pattern matching with unlabeled patterns. |
|
| 4115 | 5287 | @test unsafe fn testResolveMatchStandaloneRecordUnlabeledPattern() throws (testing::TestError) { |
|
| 4116 | - | let mut a = testResolver(); |
|
| 4117 | - | let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x) => return x, else => return 0 } }"; |
|
| 4118 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4119 | - | try expectNoErrors(&result); |
|
| 5288 | + | let mut testArena384 = testArena(); |
|
| 5289 | + | let testStorage384: 'test384 = &mut testArena384 in { |
|
| 5290 | + | let mut a = testResolver(testStorage384); |
|
| 5291 | + | let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x) => return x, else => return 0 } }"; |
|
| 5292 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5293 | + | try expectNoErrors(&result); |
|
| 5294 | + | } |
|
| 4120 | 5295 | } |
|
| 4121 | 5296 | ||
| 4122 | 5297 | /// Test standalone record pattern matching with labeled patterns. |
|
| 4123 | 5298 | /// Pattern syntax: `T { x }` matches a named record and binds x to the field. |
|
| 4124 | 5299 | @test unsafe fn testResolveMatchStandaloneRecordLabeledPattern() throws (testing::TestError) { |
|
| 4125 | - | let mut a = testResolver(); |
|
| 4126 | - | let program = "record T { x: i32 } fn f(t: T) -> i32 { match t { case T { x } => return x, else => return 0 } }"; |
|
| 4127 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4128 | - | try expectNoErrors(&result); |
|
| 5300 | + | let mut testArena385 = testArena(); |
|
| 5301 | + | let testStorage385: 'test385 = &mut testArena385 in { |
|
| 5302 | + | let mut a = testResolver(testStorage385); |
|
| 5303 | + | let program = "record T { x: i32 } fn f(t: T) -> i32 { match t { case T { x } => return x, else => return 0 } }"; |
|
| 5304 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5305 | + | try expectNoErrors(&result); |
|
| 5306 | + | } |
|
| 4129 | 5307 | } |
|
| 4130 | 5308 | ||
| 4131 | 5309 | /// Test standalone record pattern with multiple fields. |
|
| 4132 | 5310 | /// Pattern syntax: `R(a, b)` matches an unlabeled record with multiple fields. |
|
| 4133 | 5311 | @test unsafe fn testResolveMatchStandaloneRecordMultipleFields() throws (testing::TestError) { |
|
| 4134 | - | let mut a = testResolver(); |
|
| 4135 | - | let program = "record R(bool, u8); fn f(r: R) -> u8 { match r { case R(_, x) => return x, else => return 0 } }"; |
|
| 4136 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4137 | - | try expectNoErrors(&result); |
|
| 5312 | + | let mut testArena386 = testArena(); |
|
| 5313 | + | let testStorage386: 'test386 = &mut testArena386 in { |
|
| 5314 | + | let mut a = testResolver(testStorage386); |
|
| 5315 | + | let program = "record R(bool, u8); fn f(r: R) -> u8 { match r { case R(_, x) => return x, else => return 0 } }"; |
|
| 5316 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5317 | + | try expectNoErrors(&result); |
|
| 5318 | + | } |
|
| 4138 | 5319 | } |
|
| 4139 | 5320 | ||
| 4140 | 5321 | /// Test standalone record pattern with wrong field count. |
|
| 4141 | 5322 | /// Pattern `S(x, y)` should fail for a single-field record. |
|
| 4142 | 5323 | @test unsafe fn testResolveMatchStandaloneRecordWrongFieldCount() throws (testing::TestError) { |
|
| 4143 | - | let mut a = testResolver(); |
|
| 4144 | - | let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x, y) => return x + y, else => return 0 } }"; |
|
| 4145 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4146 | - | let err = try expectError(&result); |
|
| 4147 | - | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 4148 | - | else throw testing::TestError::Failed; |
|
| 5324 | + | let mut testArena387 = testArena(); |
|
| 5325 | + | let testStorage387: 'test387 = &mut testArena387 in { |
|
| 5326 | + | let mut a = testResolver(testStorage387); |
|
| 5327 | + | let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x, y) => return x + y, else => return 0 } }"; |
|
| 5328 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5329 | + | let err = try expectError(&result); |
|
| 5330 | + | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 5331 | + | else throw testing::TestError::Failed; |
|
| 5332 | + | } |
|
| 4149 | 5333 | } |
|
| 4150 | 5334 | ||
| 4151 | 5335 | /// Test array pattern matching with element bindings. |
|
| 4152 | 5336 | /// Pattern syntax: `[x, y]` matches an array and binds elements. |
|
| 4153 | 5337 | @test unsafe fn testResolveMatchArrayPattern() throws (testing::TestError) { |
|
| 4154 | - | let mut a = testResolver(); |
|
| 4155 | - | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [x, y] => return x + y } }"; |
|
| 4156 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4157 | - | try expectNoErrors(&result); |
|
| 5338 | + | let mut testArena388 = testArena(); |
|
| 5339 | + | let testStorage388: 'test388 = &mut testArena388 in { |
|
| 5340 | + | let mut a = testResolver(testStorage388); |
|
| 5341 | + | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [x, y] => return x + y } }"; |
|
| 5342 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5343 | + | try expectNoErrors(&result); |
|
| 5344 | + | } |
|
| 4158 | 5345 | } |
|
| 4159 | 5346 | ||
| 4160 | 5347 | /// Test array pattern with placeholder elements. |
|
| 4161 | 5348 | /// Pattern syntax: `[_, y]` ignores first element. |
|
| 4162 | 5349 | @test unsafe fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) { |
|
| 4163 | - | let mut a = testResolver(); |
|
| 4164 | - | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }"; |
|
| 4165 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4166 | - | try expectNoErrors(&result); |
|
| 5350 | + | let mut testArena389 = testArena(); |
|
| 5351 | + | let testStorage389: 'test389 = &mut testArena389 in { |
|
| 5352 | + | let mut a = testResolver(testStorage389); |
|
| 5353 | + | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }"; |
|
| 5354 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5355 | + | try expectNoErrors(&result); |
|
| 5356 | + | } |
|
| 4167 | 5357 | } |
|
| 4168 | 5358 | ||
| 4169 | 5359 | /// Test identifier pattern that binds the whole value. |
|
| 4170 | 5360 | /// Pattern syntax: `x` matches any value and binds it. |
|
| 4171 | 5361 | @test unsafe fn testResolveMatchIdentPattern() throws (testing::TestError) { |
|
| 4172 | - | let mut a = testResolver(); |
|
| 4173 | - | let program = "fn f(val: i32) -> i32 { match val { x => return x } }"; |
|
| 4174 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4175 | - | try expectNoErrors(&result); |
|
| 5362 | + | let mut testArena390 = testArena(); |
|
| 5363 | + | let testStorage390: 'test390 = &mut testArena390 in { |
|
| 5364 | + | let mut a = testResolver(testStorage390); |
|
| 5365 | + | let program = "fn f(val: i32) -> i32 { match val { x => return x } }"; |
|
| 5366 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5367 | + | try expectNoErrors(&result); |
|
| 5368 | + | } |
|
| 4176 | 5369 | } |
|
| 4177 | 5370 | ||
| 4178 | 5371 | /// Test numeric literal pattern matching. |
|
| 4179 | 5372 | @test unsafe fn testResolveMatchNumericLiteralPattern() throws (testing::TestError) { |
|
| 4180 | - | let mut a = testResolver(); |
|
| 4181 | - | let program = "fn f(val: i32) -> i32 { match val { case 42 => return 1, else => return 0 } }"; |
|
| 4182 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4183 | - | try expectNoErrors(&result); |
|
| 5373 | + | let mut testArena391 = testArena(); |
|
| 5374 | + | let testStorage391: 'test391 = &mut testArena391 in { |
|
| 5375 | + | let mut a = testResolver(testStorage391); |
|
| 5376 | + | let program = "fn f(val: i32) -> i32 { match val { case 42 => return 1, else => return 0 } }"; |
|
| 5377 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5378 | + | try expectNoErrors(&result); |
|
| 5379 | + | } |
|
| 4184 | 5380 | } |
|
| 4185 | 5381 | ||
| 4186 | 5382 | /// Test string literal pattern matching. |
|
| 4187 | 5383 | @test unsafe fn testResolveMatchStringLiteralPattern() throws (testing::TestError) { |
|
| 4188 | - | let mut a = testResolver(); |
|
| 4189 | - | let program = "fn f(val: *[u8]) -> i32 { match val { case \"hello\" => return 1, else => return 0 } }"; |
|
| 4190 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4191 | - | try expectNoErrors(&result); |
|
| 5384 | + | let mut testArena392 = testArena(); |
|
| 5385 | + | let testStorage392: 'test392 = &mut testArena392 in { |
|
| 5386 | + | let mut a = testResolver(testStorage392); |
|
| 5387 | + | let program = "fn f(val: *[u8]) -> i32 { match val { case \"hello\" => return 1, else => return 0 } }"; |
|
| 5388 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5389 | + | try expectNoErrors(&result); |
|
| 5390 | + | } |
|
| 4192 | 5391 | } |
|
| 4193 | 5392 | ||
| 4194 | 5393 | /// Test boolean literal pattern matching. |
|
| 4195 | 5394 | @test unsafe fn testResolveMatchBoolLiteralPattern() throws (testing::TestError) { |
|
| 4196 | - | let mut a = testResolver(); |
|
| 4197 | - | let program = "fn f(val: bool) -> i32 { match val { case true => return 1, case false => return 0 } }"; |
|
| 4198 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4199 | - | try expectNoErrors(&result); |
|
| 5395 | + | let mut testArena393 = testArena(); |
|
| 5396 | + | let testStorage393: 'test393 = &mut testArena393 in { |
|
| 5397 | + | let mut a = testResolver(testStorage393); |
|
| 5398 | + | let program = "fn f(val: bool) -> i32 { match val { case true => return 1, case false => return 0 } }"; |
|
| 5399 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5400 | + | try expectNoErrors(&result); |
|
| 5401 | + | } |
|
| 4200 | 5402 | } |
|
| 4201 | 5403 | ||
| 4202 | 5404 | /// Test @sliceOf with correct arguments succeeds. |
|
| 4203 | 5405 | @test unsafe fn testResolveSliceOfCorrect() throws (testing::TestError) { |
|
| 4204 | 5406 | // Immutable pointer. |
|
| 4205 | 5407 | { |
|
| 4206 | - | let mut a = testResolver(); |
|
| 4207 | - | let program = "unsafe fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4208 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4209 | - | try expectNoErrors(&result); |
|
| 5408 | + | let mut testArena394 = testArena(); |
|
| 5409 | + | let testStorage394: 'test394 = &mut testArena394 in { |
|
| 5410 | + | let mut a = testResolver(testStorage394); |
|
| 5411 | + | let program = "unsafe fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 5412 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5413 | + | try expectNoErrors(&result); |
|
| 5414 | + | } |
|
| 4210 | 5415 | } |
|
| 4211 | 5416 | // Mutable pointer produces mutable slice. |
|
| 4212 | 5417 | { |
|
| 4213 | - | let mut a = testResolver(); |
|
| 4214 | - | let program = "unsafe fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }"; |
|
| 4215 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4216 | - | try expectNoErrors(&result); |
|
| 5418 | + | let mut testArena395 = testArena(); |
|
| 5419 | + | let testStorage395: 'test395 = &mut testArena395 in { |
|
| 5420 | + | let mut a = testResolver(testStorage395); |
|
| 5421 | + | let program = "unsafe fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }"; |
|
| 5422 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5423 | + | try expectNoErrors(&result); |
|
| 5424 | + | } |
|
| 4217 | 5425 | } |
|
| 4218 | 5426 | } |
|
| 4219 | 5427 | ||
| 4220 | 5428 | /// Test @sliceOf with wrong argument count produces an error. |
|
| 4221 | 5429 | @test unsafe fn testResolveSliceOfWrongArgCount() throws (testing::TestError) { |
|
| 4222 | 5430 | // No arguments. |
|
| 4223 | 5431 | { |
|
| 4224 | - | let mut a = testResolver(); |
|
| 4225 | - | let program = "fn f() -> *[u8] { return @sliceOf(); }"; |
|
| 4226 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4227 | - | let err = try expectError(&result); |
|
| 4228 | - | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
|
| 4229 | - | else throw testing::TestError::Failed; |
|
| 4230 | - | try testing::expect(mismatch.expected == 2); |
|
| 4231 | - | try testing::expect(mismatch.actual == 0); |
|
| 5432 | + | let mut testArena396 = testArena(); |
|
| 5433 | + | let testStorage396: 'test396 = &mut testArena396 in { |
|
| 5434 | + | let mut a = testResolver(testStorage396); |
|
| 5435 | + | let program = "fn f() -> *[u8] { return @sliceOf(); }"; |
|
| 5436 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5437 | + | let err = try expectError(&result); |
|
| 5438 | + | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
|
| 5439 | + | else throw testing::TestError::Failed; |
|
| 5440 | + | try testing::expect(mismatch.expected == 2); |
|
| 5441 | + | try testing::expect(mismatch.actual == 0); |
|
| 5442 | + | } |
|
| 4232 | 5443 | } |
|
| 4233 | 5444 | // Too few arguments. |
|
| 4234 | 5445 | { |
|
| 4235 | - | let mut a = testResolver(); |
|
| 4236 | - | let program = "fn f(ptr: *u8) -> *[u8] { return @sliceOf(ptr); }"; |
|
| 4237 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4238 | - | let err = try expectError(&result); |
|
| 4239 | - | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
|
| 4240 | - | else throw testing::TestError::Failed; |
|
| 4241 | - | try testing::expect(mismatch.expected == 2); |
|
| 4242 | - | try testing::expect(mismatch.actual == 1); |
|
| 5446 | + | let mut testArena397 = testArena(); |
|
| 5447 | + | let testStorage397: 'test397 = &mut testArena397 in { |
|
| 5448 | + | let mut a = testResolver(testStorage397); |
|
| 5449 | + | let program = "fn f(ptr: *u8) -> *[u8] { return @sliceOf(ptr); }"; |
|
| 5450 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5451 | + | let err = try expectError(&result); |
|
| 5452 | + | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
|
| 5453 | + | else throw testing::TestError::Failed; |
|
| 5454 | + | try testing::expect(mismatch.expected == 2); |
|
| 5455 | + | try testing::expect(mismatch.actual == 1); |
|
| 5456 | + | } |
|
| 4243 | 5457 | } |
|
| 4244 | 5458 | // Too many arguments. |
|
| 4245 | 5459 | { |
|
| 4246 | - | let mut a = testResolver(); |
|
| 4247 | - | let program = "fn f(ptr: *u8, len: u32, cap: u32, extra: u32) -> *[u8] { return @sliceOf(ptr, len, cap, extra); }"; |
|
| 4248 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4249 | - | let err = try expectError(&result); |
|
| 4250 | - | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
|
| 4251 | - | else throw testing::TestError::Failed; |
|
| 4252 | - | try testing::expect(mismatch.expected == 2); |
|
| 4253 | - | try testing::expect(mismatch.actual == 4); |
|
| 5460 | + | let mut testArena398 = testArena(); |
|
| 5461 | + | let testStorage398: 'test398 = &mut testArena398 in { |
|
| 5462 | + | let mut a = testResolver(testStorage398); |
|
| 5463 | + | let program = "fn f(ptr: *u8, len: u32, cap: u32, extra: u32) -> *[u8] { return @sliceOf(ptr, len, cap, extra); }"; |
|
| 5464 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5465 | + | let err = try expectError(&result); |
|
| 5466 | + | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
|
| 5467 | + | else throw testing::TestError::Failed; |
|
| 5468 | + | try testing::expect(mismatch.expected == 2); |
|
| 5469 | + | try testing::expect(mismatch.actual == 4); |
|
| 5470 | + | } |
|
| 4254 | 5471 | } |
|
| 4255 | 5472 | } |
|
| 4256 | 5473 | ||
| 4257 | 5474 | /// Test @sliceOf with wrong argument types produces errors. |
|
| 4258 | 5475 | @test unsafe fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) { |
|
| 4259 | 5476 | // Non-pointer first argument. |
|
| 4260 | 5477 | { |
|
| 4261 | - | let mut a = testResolver(); |
|
| 4262 | - | let program = "fn f(val: u32, len: u32) -> *[u8] { return @sliceOf(val, len); }"; |
|
| 4263 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4264 | - | let err = try expectError(&result); |
|
| 4265 | - | let case super::ErrorKind::ExpectedPointer = err.kind |
|
| 4266 | - | else throw testing::TestError::Failed; |
|
| 5478 | + | let mut testArena399 = testArena(); |
|
| 5479 | + | let testStorage399: 'test399 = &mut testArena399 in { |
|
| 5480 | + | let mut a = testResolver(testStorage399); |
|
| 5481 | + | let program = "fn f(val: u32, len: u32) -> *[u8] { return @sliceOf(val, len); }"; |
|
| 5482 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5483 | + | let err = try expectError(&result); |
|
| 5484 | + | let case super::ErrorKind::ExpectedPointer = err.kind |
|
| 5485 | + | else throw testing::TestError::Failed; |
|
| 5486 | + | } |
|
| 4267 | 5487 | } |
|
| 4268 | 5488 | // Array instead of pointer. |
|
| 4269 | 5489 | { |
|
| 4270 | - | let mut a = testResolver(); |
|
| 4271 | - | let program = "fn f(arr: [u8; 4], len: u32) -> *[u8] { return @sliceOf(arr, len); }"; |
|
| 4272 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4273 | - | let err = try expectError(&result); |
|
| 4274 | - | let case super::ErrorKind::ExpectedPointer = err.kind |
|
| 4275 | - | else throw testing::TestError::Failed; |
|
| 5490 | + | let mut testArena400 = testArena(); |
|
| 5491 | + | let testStorage400: 'test400 = &mut testArena400 in { |
|
| 5492 | + | let mut a = testResolver(testStorage400); |
|
| 5493 | + | let program = "fn f(arr: [u8; 4], len: u32) -> *[u8] { return @sliceOf(arr, len); }"; |
|
| 5494 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5495 | + | let err = try expectError(&result); |
|
| 5496 | + | let case super::ErrorKind::ExpectedPointer = err.kind |
|
| 5497 | + | else throw testing::TestError::Failed; |
|
| 5498 | + | } |
|
| 4276 | 5499 | } |
|
| 4277 | 5500 | // Non-numeric second argument. |
|
| 4278 | 5501 | { |
|
| 4279 | - | let mut a = testResolver(); |
|
| 4280 | - | let program = "fn f(ptr: *u8, len: bool) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4281 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4282 | - | let err = try expectError(&result); |
|
| 4283 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4284 | - | else throw testing::TestError::Failed; |
|
| 5502 | + | let mut testArena401 = testArena(); |
|
| 5503 | + | let testStorage401: 'test401 = &mut testArena401 in { |
|
| 5504 | + | let mut a = testResolver(testStorage401); |
|
| 5505 | + | let program = "fn f(ptr: *u8, len: bool) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 5506 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5507 | + | let err = try expectError(&result); |
|
| 5508 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5509 | + | else throw testing::TestError::Failed; |
|
| 5510 | + | } |
|
| 4285 | 5511 | } |
|
| 4286 | 5512 | // Pointer second argument. |
|
| 4287 | 5513 | { |
|
| 4288 | - | let mut a = testResolver(); |
|
| 4289 | - | let program = "fn f(ptr: *u8, len: *u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4290 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4291 | - | let err = try expectError(&result); |
|
| 4292 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4293 | - | else throw testing::TestError::Failed; |
|
| 5514 | + | let mut testArena402 = testArena(); |
|
| 5515 | + | let testStorage402: 'test402 = &mut testArena402 in { |
|
| 5516 | + | let mut a = testResolver(testStorage402); |
|
| 5517 | + | let program = "fn f(ptr: *u8, len: *u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 5518 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5519 | + | let err = try expectError(&result); |
|
| 5520 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5521 | + | else throw testing::TestError::Failed; |
|
| 5522 | + | } |
|
| 4294 | 5523 | } |
|
| 4295 | 5524 | } |
|
| 4296 | 5525 | ||
| 4297 | 5526 | /// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds. |
|
| 4298 | 5527 | @test unsafe fn testResolveSliceOfWithCap() throws (testing::TestError) { |
|
| 4299 | 5528 | { |
|
| 4300 | - | let mut a = testResolver(); |
|
| 4301 | - | let program = "unsafe fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4302 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4303 | - | try expectNoErrors(&result); |
|
| 5529 | + | let mut testArena403 = testArena(); |
|
| 5530 | + | let testStorage403: 'test403 = &mut testArena403 in { |
|
| 5531 | + | let mut a = testResolver(testStorage403); |
|
| 5532 | + | let program = "unsafe fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 5533 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5534 | + | try expectNoErrors(&result); |
|
| 5535 | + | } |
|
| 4304 | 5536 | } |
|
| 4305 | 5537 | // Mutable pointer produces mutable slice. |
|
| 4306 | 5538 | { |
|
| 4307 | - | let mut a = testResolver(); |
|
| 4308 | - | let program = "unsafe fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4309 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4310 | - | try expectNoErrors(&result); |
|
| 5539 | + | let mut testArena404 = testArena(); |
|
| 5540 | + | let testStorage404: 'test404 = &mut testArena404 in { |
|
| 5541 | + | let mut a = testResolver(testStorage404); |
|
| 5542 | + | let program = "unsafe fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 5543 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5544 | + | try expectNoErrors(&result); |
|
| 5545 | + | } |
|
| 4311 | 5546 | } |
|
| 4312 | 5547 | } |
|
| 4313 | 5548 | ||
| 4314 | 5549 | /// Test @sliceOf with 3 arguments but wrong cap type. |
|
| 4315 | 5550 | @test unsafe fn testResolveSliceOfCapWrongType() throws (testing::TestError) { |
|
| 4316 | - | let mut a = testResolver(); |
|
| 4317 | - | let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4318 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4319 | - | let err = try expectError(&result); |
|
| 4320 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4321 | - | else throw testing::TestError::Failed; |
|
| 5551 | + | let mut testArena405 = testArena(); |
|
| 5552 | + | let testStorage405: 'test405 = &mut testArena405 in { |
|
| 5553 | + | let mut a = testResolver(testStorage405); |
|
| 5554 | + | let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 5555 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5556 | + | let err = try expectError(&result); |
|
| 5557 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5558 | + | else throw testing::TestError::Failed; |
|
| 5559 | + | } |
|
| 4322 | 5560 | } |
|
| 4323 | 5561 | ||
| 4324 | 5562 | /// Test .cap field access on slices resolves to u32. |
|
| 4325 | 5563 | @test unsafe fn testResolveSliceCapField() throws (testing::TestError) { |
|
| 4326 | - | let mut a = testResolver(); |
|
| 4327 | - | let program = "fn f(s: *[u8]) -> u32 { return s.cap; }"; |
|
| 4328 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4329 | - | try expectNoErrors(&result); |
|
| 5564 | + | let mut testArena406 = testArena(); |
|
| 5565 | + | let testStorage406: 'test406 = &mut testArena406 in { |
|
| 5566 | + | let mut a = testResolver(testStorage406); |
|
| 5567 | + | let program = "fn f(s: *[u8]) -> u32 { return s.cap; }"; |
|
| 5568 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5569 | + | try expectNoErrors(&result); |
|
| 5570 | + | } |
|
| 4330 | 5571 | } |
|
| 4331 | 5572 | ||
| 4332 | 5573 | /// Test `.append()` on immutable slice produces an error. |
|
| 4333 | 5574 | @test unsafe fn testResolveSliceAppendImmutable() throws (testing::TestError) { |
|
| 4334 | - | let mut a = testResolver(); |
|
| 4335 | - | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *[i32], a: A) { s.append(1, a); }"; |
|
| 4336 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4337 | - | let err = try expectError(&result); |
|
| 4338 | - | let case super::ErrorKind::ImmutableBinding = err.kind |
|
| 4339 | - | else throw testing::TestError::Failed; |
|
| 5575 | + | let mut testArena407 = testArena(); |
|
| 5576 | + | let testStorage407: 'test407 = &mut testArena407 in { |
|
| 5577 | + | let mut a = testResolver(testStorage407); |
|
| 5578 | + | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *[i32], a: A) { s.append(1, a); }"; |
|
| 5579 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5580 | + | let err = try expectError(&result); |
|
| 5581 | + | let case super::ErrorKind::ImmutableBinding = err.kind |
|
| 5582 | + | else throw testing::TestError::Failed; |
|
| 5583 | + | } |
|
| 4340 | 5584 | } |
|
| 4341 | 5585 | ||
| 4342 | 5586 | /// Test `.append()` with wrong argument count produces an error. |
|
| 4343 | 5587 | @test unsafe fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) { |
|
| 4344 | 5588 | // Too few arguments. |
|
| 4345 | 5589 | { |
|
| 4346 | - | let mut a = testResolver(); |
|
| 4347 | - | let program = "fn f(s: *mut [i32]) { s.append(1); }"; |
|
| 4348 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4349 | - | let err = try expectError(&result); |
|
| 4350 | - | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
|
| 4351 | - | else throw testing::TestError::Failed; |
|
| 4352 | - | try testing::expect(m.expected == 2); |
|
| 4353 | - | try testing::expect(m.actual == 1); |
|
| 5590 | + | let mut testArena408 = testArena(); |
|
| 5591 | + | let testStorage408: 'test408 = &mut testArena408 in { |
|
| 5592 | + | let mut a = testResolver(testStorage408); |
|
| 5593 | + | let program = "fn f(s: *mut [i32]) { s.append(1); }"; |
|
| 5594 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5595 | + | let err = try expectError(&result); |
|
| 5596 | + | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
|
| 5597 | + | else throw testing::TestError::Failed; |
|
| 5598 | + | try testing::expect(m.expected == 2); |
|
| 5599 | + | try testing::expect(m.actual == 1); |
|
| 5600 | + | } |
|
| 4354 | 5601 | } |
|
| 4355 | 5602 | // Too many arguments. |
|
| 4356 | 5603 | { |
|
| 4357 | - | let mut a = testResolver(); |
|
| 4358 | - | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a, 0); }"; |
|
| 4359 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4360 | - | let err = try expectError(&result); |
|
| 4361 | - | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
|
| 4362 | - | else throw testing::TestError::Failed; |
|
| 4363 | - | try testing::expect(m.expected == 2); |
|
| 4364 | - | try testing::expect(m.actual == 3); |
|
| 5604 | + | let mut testArena409 = testArena(); |
|
| 5605 | + | let testStorage409: 'test409 = &mut testArena409 in { |
|
| 5606 | + | let mut a = testResolver(testStorage409); |
|
| 5607 | + | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a, 0); }"; |
|
| 5608 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5609 | + | let err = try expectError(&result); |
|
| 5610 | + | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
|
| 5611 | + | else throw testing::TestError::Failed; |
|
| 5612 | + | try testing::expect(m.expected == 2); |
|
| 5613 | + | try testing::expect(m.actual == 3); |
|
| 5614 | + | } |
|
| 4365 | 5615 | } |
|
| 4366 | 5616 | } |
|
| 4367 | 5617 | ||
| 4368 | 5618 | /// Test `.append()` with correct arguments succeeds. |
|
| 4369 | 5619 | @test unsafe fn testResolveSliceAppendCorrect() throws (testing::TestError) { |
|
| 4370 | - | let mut a = testResolver(); |
|
| 4371 | - | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a); }"; |
|
| 4372 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4373 | - | try expectNoErrors(&result); |
|
| 5620 | + | let mut testArena410 = testArena(); |
|
| 5621 | + | let testStorage410: 'test410 = &mut testArena410 in { |
|
| 5622 | + | let mut a = testResolver(testStorage410); |
|
| 5623 | + | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a); }"; |
|
| 5624 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5625 | + | try expectNoErrors(&result); |
|
| 5626 | + | } |
|
| 4374 | 5627 | } |
|
| 4375 | 5628 | ||
| 4376 | 5629 | /// Test `.append()` with wrong element type produces an error. |
|
| 4377 | 5630 | @test unsafe fn testResolveSliceAppendWrongElemType() throws (testing::TestError) { |
|
| 4378 | - | let mut a = testResolver(); |
|
| 4379 | - | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(true, a); }"; |
|
| 4380 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4381 | - | let err = try expectError(&result); |
|
| 4382 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4383 | - | else throw testing::TestError::Failed; |
|
| 5631 | + | let mut testArena411 = testArena(); |
|
| 5632 | + | let testStorage411: 'test411 = &mut testArena411 in { |
|
| 5633 | + | let mut a = testResolver(testStorage411); |
|
| 5634 | + | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(true, a); }"; |
|
| 5635 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5636 | + | let err = try expectError(&result); |
|
| 5637 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5638 | + | else throw testing::TestError::Failed; |
|
| 5639 | + | } |
|
| 4384 | 5640 | } |
|
| 4385 | 5641 | ||
| 4386 | 5642 | /// Test `.delete()` on immutable slice produces an error. |
|
| 4387 | 5643 | @test unsafe fn testResolveSliceDeleteImmutable() throws (testing::TestError) { |
|
| 4388 | - | let mut a = testResolver(); |
|
| 4389 | - | let program = "fn f(s: *[i32]) { s.delete(0); }"; |
|
| 4390 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4391 | - | let err = try expectError(&result); |
|
| 4392 | - | let case super::ErrorKind::ImmutableBinding = err.kind |
|
| 4393 | - | else throw testing::TestError::Failed; |
|
| 5644 | + | let mut testArena412 = testArena(); |
|
| 5645 | + | let testStorage412: 'test412 = &mut testArena412 in { |
|
| 5646 | + | let mut a = testResolver(testStorage412); |
|
| 5647 | + | let program = "fn f(s: *[i32]) { s.delete(0); }"; |
|
| 5648 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5649 | + | let err = try expectError(&result); |
|
| 5650 | + | let case super::ErrorKind::ImmutableBinding = err.kind |
|
| 5651 | + | else throw testing::TestError::Failed; |
|
| 5652 | + | } |
|
| 4394 | 5653 | } |
|
| 4395 | 5654 | ||
| 4396 | 5655 | /// Test `.delete()` with wrong argument count produces an error. |
|
| 4397 | 5656 | @test unsafe fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) { |
|
| 4398 | 5657 | // No arguments. |
|
| 4399 | 5658 | { |
|
| 4400 | - | let mut a = testResolver(); |
|
| 4401 | - | let program = "fn f(s: *mut [i32]) { s.delete(); }"; |
|
| 4402 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4403 | - | let err = try expectError(&result); |
|
| 4404 | - | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
|
| 4405 | - | else throw testing::TestError::Failed; |
|
| 4406 | - | try testing::expect(m.expected == 1); |
|
| 4407 | - | try testing::expect(m.actual == 0); |
|
| 5659 | + | let mut testArena413 = testArena(); |
|
| 5660 | + | let testStorage413: 'test413 = &mut testArena413 in { |
|
| 5661 | + | let mut a = testResolver(testStorage413); |
|
| 5662 | + | let program = "fn f(s: *mut [i32]) { s.delete(); }"; |
|
| 5663 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5664 | + | let err = try expectError(&result); |
|
| 5665 | + | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
|
| 5666 | + | else throw testing::TestError::Failed; |
|
| 5667 | + | try testing::expect(m.expected == 1); |
|
| 5668 | + | try testing::expect(m.actual == 0); |
|
| 5669 | + | } |
|
| 4408 | 5670 | } |
|
| 4409 | 5671 | // Too many arguments. |
|
| 4410 | 5672 | { |
|
| 4411 | - | let mut a = testResolver(); |
|
| 4412 | - | let program = "fn f(s: *mut [i32]) { s.delete(0, 1); }"; |
|
| 4413 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4414 | - | let err = try expectError(&result); |
|
| 4415 | - | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
|
| 4416 | - | else throw testing::TestError::Failed; |
|
| 4417 | - | try testing::expect(m.expected == 1); |
|
| 4418 | - | try testing::expect(m.actual == 2); |
|
| 5673 | + | let mut testArena414 = testArena(); |
|
| 5674 | + | let testStorage414: 'test414 = &mut testArena414 in { |
|
| 5675 | + | let mut a = testResolver(testStorage414); |
|
| 5676 | + | let program = "fn f(s: *mut [i32]) { s.delete(0, 1); }"; |
|
| 5677 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5678 | + | let err = try expectError(&result); |
|
| 5679 | + | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
|
| 5680 | + | else throw testing::TestError::Failed; |
|
| 5681 | + | try testing::expect(m.expected == 1); |
|
| 5682 | + | try testing::expect(m.actual == 2); |
|
| 5683 | + | } |
|
| 4419 | 5684 | } |
|
| 4420 | 5685 | } |
|
| 4421 | 5686 | ||
| 4422 | 5687 | /// Test `.delete()` with correct arguments succeeds. |
|
| 4423 | 5688 | @test unsafe fn testResolveSliceDeleteCorrect() throws (testing::TestError) { |
|
| 4424 | - | let mut a = testResolver(); |
|
| 4425 | - | let program = "fn f(s: *mut [i32]) { s.delete(0); }"; |
|
| 4426 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4427 | - | try expectNoErrors(&result); |
|
| 5689 | + | let mut testArena415 = testArena(); |
|
| 5690 | + | let testStorage415: 'test415 = &mut testArena415 in { |
|
| 5691 | + | let mut a = testResolver(testStorage415); |
|
| 5692 | + | let program = "fn f(s: *mut [i32]) { s.delete(0); }"; |
|
| 5693 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5694 | + | try expectNoErrors(&result); |
|
| 5695 | + | } |
|
| 4428 | 5696 | } |
|
| 4429 | 5697 | ||
| 4430 | 5698 | /// Test `.delete()` with wrong argument type produces an error. |
|
| 4431 | 5699 | @test unsafe fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) { |
|
| 4432 | - | let mut a = testResolver(); |
|
| 4433 | - | let program = "fn f(s: *mut [i32]) { s.delete(true); }"; |
|
| 4434 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4435 | - | let err = try expectError(&result); |
|
| 4436 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4437 | - | else throw testing::TestError::Failed; |
|
| 5700 | + | let mut testArena416 = testArena(); |
|
| 5701 | + | let testStorage416: 'test416 = &mut testArena416 in { |
|
| 5702 | + | let mut a = testResolver(testStorage416); |
|
| 5703 | + | let program = "fn f(s: *mut [i32]) { s.delete(true); }"; |
|
| 5704 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5705 | + | let err = try expectError(&result); |
|
| 5706 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5707 | + | else throw testing::TestError::Failed; |
|
| 5708 | + | } |
|
| 4438 | 5709 | } |
|
| 4439 | 5710 | ||
| 4440 | 5711 | /// Test `match &opt` produces immutable pointer bindings. |
|
| 4441 | 5712 | @test unsafe fn testResolveMatchRefUnionBinding() throws (testing::TestError) { |
|
| 4442 | - | let mut a = testResolver(); |
|
| 4443 | - | let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4444 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4445 | - | try expectNoErrors(&result); |
|
| 4446 | - | ||
| 4447 | - | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 4448 | - | let matchNode = fnBlock.statements[1]; |
|
| 4449 | - | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 4450 | - | else throw testing::TestError::Failed; |
|
| 4451 | - | let caseNode = sw.prongs[0]; |
|
| 5713 | + | let mut testArena417 = testArena(); |
|
| 5714 | + | let testStorage417: 'test417 = &mut testArena417 in { |
|
| 5715 | + | let mut a = testResolver(testStorage417); |
|
| 5716 | + | let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 5717 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5718 | + | try expectNoErrors(&result); |
|
| 4452 | 5719 | ||
| 4453 | - | let scope = super::scopeFor(&a, caseNode) |
|
| 4454 | - | else throw testing::TestError::Failed; |
|
| 4455 | - | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 4456 | - | else throw testing::TestError::Failed; |
|
| 4457 | - | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 4458 | - | else throw testing::TestError::Failed; |
|
| 4459 | - | let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType |
|
| 4460 | - | else throw testing::TestError::Failed; |
|
| 4461 | - | try testing::expect(not mutable); |
|
| 4462 | - | try testing::expect(*target == super::Type::I32); |
|
| 5720 | + | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 5721 | + | let matchNode = fnBlock.statements[1]; |
|
| 5722 | + | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 5723 | + | else throw testing::TestError::Failed; |
|
| 5724 | + | let caseNode = sw.prongs[0]; |
|
| 5725 | + | ||
| 5726 | + | let scope = super::scopeFor(&a, caseNode) |
|
| 5727 | + | else throw testing::TestError::Failed; |
|
| 5728 | + | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 5729 | + | else throw testing::TestError::Failed; |
|
| 5730 | + | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 5731 | + | else throw testing::TestError::Failed; |
|
| 5732 | + | let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType |
|
| 5733 | + | else throw testing::TestError::Failed; |
|
| 5734 | + | try testing::expect(not mutable); |
|
| 5735 | + | try testing::expect(*target == super::Type::I32); |
|
| 5736 | + | } |
|
| 4463 | 5737 | } |
|
| 4464 | 5738 | ||
| 4465 | 5739 | /// Test `match &mut opt` produces mutable pointer bindings. |
|
| 4466 | 5740 | @test unsafe fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) { |
|
| 4467 | - | let mut a = testResolver(); |
|
| 4468 | - | let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4469 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4470 | - | try expectNoErrors(&result); |
|
| 4471 | - | ||
| 4472 | - | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 4473 | - | let matchNode = fnBlock.statements[1]; |
|
| 4474 | - | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 4475 | - | else throw testing::TestError::Failed; |
|
| 4476 | - | let caseNode = sw.prongs[0]; |
|
| 5741 | + | let mut testArena418 = testArena(); |
|
| 5742 | + | let testStorage418: 'test418 = &mut testArena418 in { |
|
| 5743 | + | let mut a = testResolver(testStorage418); |
|
| 5744 | + | let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 5745 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5746 | + | try expectNoErrors(&result); |
|
| 4477 | 5747 | ||
| 4478 | - | let scope = super::scopeFor(&a, caseNode) |
|
| 4479 | - | else throw testing::TestError::Failed; |
|
| 4480 | - | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 4481 | - | else throw testing::TestError::Failed; |
|
| 4482 | - | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 4483 | - | else throw testing::TestError::Failed; |
|
| 4484 | - | let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType |
|
| 4485 | - | else throw testing::TestError::Failed; |
|
| 4486 | - | try testing::expect(mutable); |
|
| 4487 | - | try testing::expect(*target == super::Type::I32); |
|
| 5748 | + | let fnBlock = try getFnBody(&a, result.root, "f"); |
|
| 5749 | + | let matchNode = fnBlock.statements[1]; |
|
| 5750 | + | let case ast::NodeValue::Match(sw) = matchNode.value |
|
| 5751 | + | else throw testing::TestError::Failed; |
|
| 5752 | + | let caseNode = sw.prongs[0]; |
|
| 5753 | + | ||
| 5754 | + | let scope = super::scopeFor(&a, caseNode) |
|
| 5755 | + | else throw testing::TestError::Failed; |
|
| 5756 | + | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 5757 | + | else throw testing::TestError::Failed; |
|
| 5758 | + | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 5759 | + | else throw testing::TestError::Failed; |
|
| 5760 | + | let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType |
|
| 5761 | + | else throw testing::TestError::Failed; |
|
| 5762 | + | try testing::expect(mutable); |
|
| 5763 | + | try testing::expect(*target == super::Type::I32); |
|
| 5764 | + | } |
|
| 4488 | 5765 | } |
|
| 4489 | 5766 | ||
| 4490 | 5767 | /// Non-constant integer widening must use an explicit cast. |
|
| 4491 | 5768 | @test unsafe fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) { |
|
| 4492 | 5769 | { |
|
| 4493 | - | let mut a = testResolver(); |
|
| 4494 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x;"); |
|
| 4495 | - | let err = try expectError(&result); |
|
| 4496 | - | try expectTypeMismatch(err, super::Type::U32, super::Type::U8); |
|
| 5770 | + | let mut testArena419 = testArena(); |
|
| 5771 | + | let testStorage419: 'test419 = &mut testArena419 in { |
|
| 5772 | + | let mut a = testResolver(testStorage419); |
|
| 5773 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x;"); |
|
| 5774 | + | let err = try expectError(&result); |
|
| 5775 | + | try expectTypeMismatch(err, super::Type::U32, super::Type::U8); |
|
| 5776 | + | } |
|
| 4497 | 5777 | } { |
|
| 4498 | - | let mut a = testResolver(); |
|
| 4499 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u16 = x;"); |
|
| 4500 | - | let err = try expectError(&result); |
|
| 4501 | - | try expectTypeMismatch(err, super::Type::U16, super::Type::U8); |
|
| 5778 | + | let mut testArena420 = testArena(); |
|
| 5779 | + | let testStorage420: 'test420 = &mut testArena420 in { |
|
| 5780 | + | let mut a = testResolver(testStorage420); |
|
| 5781 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u16 = x;"); |
|
| 5782 | + | let err = try expectError(&result); |
|
| 5783 | + | try expectTypeMismatch(err, super::Type::U16, super::Type::U8); |
|
| 5784 | + | } |
|
| 4502 | 5785 | } { |
|
| 4503 | - | let mut a = testResolver(); |
|
| 4504 | - | let result = try resolveBlockStr(&mut a, "let x: u16 = 1; let y: u32 = x;"); |
|
| 4505 | - | let err = try expectError(&result); |
|
| 4506 | - | try expectTypeMismatch(err, super::Type::U32, super::Type::U16); |
|
| 5786 | + | let mut testArena421 = testArena(); |
|
| 5787 | + | let testStorage421: 'test421 = &mut testArena421 in { |
|
| 5788 | + | let mut a = testResolver(testStorage421); |
|
| 5789 | + | let result = try resolveBlockStr(&mut a, "let x: u16 = 1; let y: u32 = x;"); |
|
| 5790 | + | let err = try expectError(&result); |
|
| 5791 | + | try expectTypeMismatch(err, super::Type::U32, super::Type::U16); |
|
| 5792 | + | } |
|
| 4507 | 5793 | } { |
|
| 4508 | - | let mut a = testResolver(); |
|
| 4509 | - | let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x;"); |
|
| 4510 | - | let err = try expectError(&result); |
|
| 4511 | - | try expectTypeMismatch(err, super::Type::I32, super::Type::I8); |
|
| 5794 | + | let mut testArena422 = testArena(); |
|
| 5795 | + | let testStorage422: 'test422 = &mut testArena422 in { |
|
| 5796 | + | let mut a = testResolver(testStorage422); |
|
| 5797 | + | let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x;"); |
|
| 5798 | + | let err = try expectError(&result); |
|
| 5799 | + | try expectTypeMismatch(err, super::Type::I32, super::Type::I8); |
|
| 5800 | + | } |
|
| 4512 | 5801 | } { |
|
| 4513 | - | let mut a = testResolver(); |
|
| 4514 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x as u32;"); |
|
| 4515 | - | try expectNoErrors(&result); |
|
| 5802 | + | let mut testArena423 = testArena(); |
|
| 5803 | + | let testStorage423: 'test423 = &mut testArena423 in { |
|
| 5804 | + | let mut a = testResolver(testStorage423); |
|
| 5805 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x as u32;"); |
|
| 5806 | + | try expectNoErrors(&result); |
|
| 5807 | + | } |
|
| 4516 | 5808 | } { |
|
| 4517 | - | let mut a = testResolver(); |
|
| 4518 | - | let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x as i32;"); |
|
| 4519 | - | try expectNoErrors(&result); |
|
| 5809 | + | let mut testArena424 = testArena(); |
|
| 5810 | + | let testStorage424: 'test424 = &mut testArena424 in { |
|
| 5811 | + | let mut a = testResolver(testStorage424); |
|
| 5812 | + | let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x as i32;"); |
|
| 5813 | + | try expectNoErrors(&result); |
|
| 5814 | + | } |
|
| 4520 | 5815 | } |
|
| 4521 | 5816 | } |
|
| 4522 | 5817 | ||
| 4523 | 5818 | /// Mixed-width integer binary ops require an explicit cast. |
|
| 4524 | 5819 | @test unsafe fn testResolveIntegerWideningBinOpRequiresCast() throws (testing::TestError) { |
|
| 4525 | 5820 | { |
|
| 4526 | - | let mut a = testResolver(); |
|
| 4527 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x | y;"); |
|
| 4528 | - | let err = try expectError(&result); |
|
| 4529 | - | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
|
| 5821 | + | let mut testArena425 = testArena(); |
|
| 5822 | + | let testStorage425: 'test425 = &mut testArena425 in { |
|
| 5823 | + | let mut a = testResolver(testStorage425); |
|
| 5824 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x | y;"); |
|
| 5825 | + | let err = try expectError(&result); |
|
| 5826 | + | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
|
| 5827 | + | } |
|
| 4530 | 5828 | } { |
|
| 4531 | - | let mut a = testResolver(); |
|
| 4532 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 0xFF; let z: u32 = x & y;"); |
|
| 4533 | - | let err = try expectError(&result); |
|
| 4534 | - | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
|
| 5829 | + | let mut testArena426 = testArena(); |
|
| 5830 | + | let testStorage426: 'test426 = &mut testArena426 in { |
|
| 5831 | + | let mut a = testResolver(testStorage426); |
|
| 5832 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 0xFF; let z: u32 = x & y;"); |
|
| 5833 | + | let err = try expectError(&result); |
|
| 5834 | + | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
|
| 5835 | + | } |
|
| 4535 | 5836 | } { |
|
| 4536 | - | let mut a = testResolver(); |
|
| 4537 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x + y;"); |
|
| 4538 | - | let err = try expectError(&result); |
|
| 4539 | - | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
|
| 5837 | + | let mut testArena427 = testArena(); |
|
| 5838 | + | let testStorage427: 'test427 = &mut testArena427 in { |
|
| 5839 | + | let mut a = testResolver(testStorage427); |
|
| 5840 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x + y;"); |
|
| 5841 | + | let err = try expectError(&result); |
|
| 5842 | + | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
|
| 5843 | + | } |
|
| 4540 | 5844 | } { |
|
| 4541 | - | let mut a = testResolver(); |
|
| 4542 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u8 = x << 2;"); |
|
| 4543 | - | try expectNoErrors(&result); |
|
| 5845 | + | let mut testArena428 = testArena(); |
|
| 5846 | + | let testStorage428: 'test428 = &mut testArena428 in { |
|
| 5847 | + | let mut a = testResolver(testStorage428); |
|
| 5848 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u8 = x << 2;"); |
|
| 5849 | + | try expectNoErrors(&result); |
|
| 5850 | + | } |
|
| 4544 | 5851 | } { |
|
| 4545 | - | let mut a = testResolver(); |
|
| 4546 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) | y;"); |
|
| 4547 | - | try expectNoErrors(&result); |
|
| 5852 | + | let mut testArena429 = testArena(); |
|
| 5853 | + | let testStorage429: 'test429 = &mut testArena429 in { |
|
| 5854 | + | let mut a = testResolver(testStorage429); |
|
| 5855 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) | y;"); |
|
| 5856 | + | try expectNoErrors(&result); |
|
| 5857 | + | } |
|
| 4548 | 5858 | } { |
|
| 4549 | - | let mut a = testResolver(); |
|
| 4550 | - | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) + y;"); |
|
| 4551 | - | try expectNoErrors(&result); |
|
| 5859 | + | let mut testArena430 = testArena(); |
|
| 5860 | + | let testStorage430: 'test430 = &mut testArena430 in { |
|
| 5861 | + | let mut a = testResolver(testStorage430); |
|
| 5862 | + | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) + y;"); |
|
| 5863 | + | try expectNoErrors(&result); |
|
| 5864 | + | } |
|
| 4552 | 5865 | } |
|
| 4553 | 5866 | } |
|
| 4554 | 5867 | ||
| 4555 | 5868 | /// A mutable slice pointer should be assignable to an immutable slice pointer. |
|
| 4556 | 5869 | @test unsafe fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) { |
|
| 4557 | - | let mut a = testResolver(); |
|
| 4558 | - | let result = try resolveBlockStr(&mut a, "static arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;"); |
|
| 4559 | - | try expectNoErrors(&result); |
|
| 5870 | + | let mut testArena431 = testArena(); |
|
| 5871 | + | let testStorage431: 'test431 = &mut testArena431 in { |
|
| 5872 | + | let mut a = testResolver(testStorage431); |
|
| 5873 | + | let result = try resolveBlockStr(&mut a, "static arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;"); |
|
| 5874 | + | try expectNoErrors(&result); |
|
| 5875 | + | } |
|
| 4560 | 5876 | } |
|
| 4561 | 5877 | ||
| 4562 | 5878 | /// Comprehensive tests for `as` cast expressions. |
|
| 4563 | 5879 | @test unsafe fn testResolveAsCasts() throws (testing::TestError) { |
|
| 4564 | 5880 | { // Pointer to numeric. |
|
| 4565 | - | let mut a = testResolver(); |
|
| 4566 | - | let result = try resolveBlockStr(&mut a, "static x: i32 = 0; let p = &x; p as u32;"); |
|
| 4567 | - | try expectNoErrors(&result); |
|
| 5881 | + | let mut testArena432 = testArena(); |
|
| 5882 | + | let testStorage432: 'test432 = &mut testArena432 in { |
|
| 5883 | + | let mut a = testResolver(testStorage432); |
|
| 5884 | + | let result = try resolveBlockStr(&mut a, "static x: i32 = 0; let p = &x; p as u32;"); |
|
| 5885 | + | try expectNoErrors(&result); |
|
| 5886 | + | } |
|
| 4568 | 5887 | } { // Function pointer to numeric. |
|
| 4569 | - | let mut a = testResolver(); |
|
| 4570 | - | let result = try resolveProgramStr(&mut a, "fn run(f: fn()) { f as u32; }"); |
|
| 4571 | - | try expectNoErrors(&result); |
|
| 5888 | + | let mut testArena433 = testArena(); |
|
| 5889 | + | let testStorage433: 'test433 = &mut testArena433 in { |
|
| 5890 | + | let mut a = testResolver(testStorage433); |
|
| 5891 | + | let result = try resolveProgramStr(&mut a, "fn run(f: fn()) { f as u32; }"); |
|
| 5892 | + | try expectNoErrors(&result); |
|
| 5893 | + | } |
|
| 4572 | 5894 | } { // *u8 to *i32 (u8 to i32 is valid). |
|
| 4573 | - | let mut a = testResolver(); |
|
| 4574 | - | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: *u8 = undefined; p as *i32; }"); |
|
| 4575 | - | try expectNoErrors(&result); |
|
| 5895 | + | let mut testArena434 = testArena(); |
|
| 5896 | + | let testStorage434: 'test434 = &mut testArena434 in { |
|
| 5897 | + | let mut a = testResolver(testStorage434); |
|
| 5898 | + | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: *u8 = undefined; p as *i32; }"); |
|
| 5899 | + | try expectNoErrors(&result); |
|
| 5900 | + | } |
|
| 4576 | 5901 | } { // **u8 to **i32 (*u8 to *i32 is valid). |
|
| 4577 | - | let mut a = testResolver(); |
|
| 4578 | - | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: **u8 = undefined; p as **i32; }"); |
|
| 4579 | - | try expectNoErrors(&result); |
|
| 5902 | + | let mut testArena435 = testArena(); |
|
| 5903 | + | let testStorage435: 'test435 = &mut testArena435 in { |
|
| 5904 | + | let mut a = testResolver(testStorage435); |
|
| 5905 | + | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: **u8 = undefined; p as **i32; }"); |
|
| 5906 | + | try expectNoErrors(&result); |
|
| 5907 | + | } |
|
| 4580 | 5908 | } |
|
| 4581 | 5909 | ||
| 4582 | 5910 | { // *[i32] to *[opaque]. |
|
| 4583 | - | let mut a = testResolver(); |
|
| 4584 | - | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *[opaque]; }"); |
|
| 4585 | - | try expectNoErrors(&result); |
|
| 5911 | + | let mut testArena436 = testArena(); |
|
| 5912 | + | let testStorage436: 'test436 = &mut testArena436 in { |
|
| 5913 | + | let mut a = testResolver(testStorage436); |
|
| 5914 | + | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *[opaque]; }"); |
|
| 5915 | + | try expectNoErrors(&result); |
|
| 5916 | + | } |
|
| 4586 | 5917 | } { // *[opaque] to *[i32]. |
|
| 4587 | - | let mut a = testResolver(); |
|
| 4588 | - | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[opaque] = undefined; s as *[i32]; }"); |
|
| 4589 | - | try expectNoErrors(&result); |
|
| 5918 | + | let mut testArena437 = testArena(); |
|
| 5919 | + | let testStorage437: 'test437 = &mut testArena437 in { |
|
| 5920 | + | let mut a = testResolver(testStorage437); |
|
| 5921 | + | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[opaque] = undefined; s as *[i32]; }"); |
|
| 5922 | + | try expectNoErrors(&result); |
|
| 5923 | + | } |
|
| 4590 | 5924 | } |
|
| 4591 | 5925 | ||
| 4592 | 5926 | { // *[i32] to *[u8]. |
|
| 4593 | - | let mut a = testResolver(); |
|
| 4594 | - | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[i32] = undefined; s as *[u8]; }"); |
|
| 4595 | - | try expectNoErrors(&result); |
|
| 5927 | + | let mut testArena438 = testArena(); |
|
| 5928 | + | let testStorage438: 'test438 = &mut testArena438 in { |
|
| 5929 | + | let mut a = testResolver(testStorage438); |
|
| 5930 | + | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[i32] = undefined; s as *[u8]; }"); |
|
| 5931 | + | try expectNoErrors(&result); |
|
| 5932 | + | } |
|
| 4596 | 5933 | } { // *[record] to *[u8]. |
|
| 4597 | - | let mut a = testResolver(); |
|
| 4598 | - | let result = try resolveProgramStr(&mut a, "record R { x: i32 } unsafe fn f(s: *[R]) { s as *[u8]; }"); |
|
| 4599 | - | try expectNoErrors(&result); |
|
| 5934 | + | let mut testArena439 = testArena(); |
|
| 5935 | + | let testStorage439: 'test439 = &mut testArena439 in { |
|
| 5936 | + | let mut a = testResolver(testStorage439); |
|
| 5937 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } unsafe fn f(s: *[R]) { s as *[u8]; }"); |
|
| 5938 | + | try expectNoErrors(&result); |
|
| 5939 | + | } |
|
| 4600 | 5940 | } |
|
| 4601 | 5941 | ||
| 4602 | 5942 | { // *[u8] to *[i32]. |
|
| 4603 | - | let mut a = testResolver(); |
|
| 4604 | - | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[u8] = undefined; s as *[i32]; }"); |
|
| 4605 | - | try expectNoErrors(&result); |
|
| 5943 | + | let mut testArena440 = testArena(); |
|
| 5944 | + | let testStorage440: 'test440 = &mut testArena440 in { |
|
| 5945 | + | let mut a = testResolver(testStorage440); |
|
| 5946 | + | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[u8] = undefined; s as *[i32]; }"); |
|
| 5947 | + | try expectNoErrors(&result); |
|
| 5948 | + | } |
|
| 4606 | 5949 | } { // *[*u8] to *[*i32] |
|
| 4607 | - | let mut a = testResolver(); |
|
| 4608 | - | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[*u8] = undefined; s as *[*i32]; }"); |
|
| 4609 | - | try expectNoErrors(&result); |
|
| 5950 | + | let mut testArena441 = testArena(); |
|
| 5951 | + | let testStorage441: 'test441 = &mut testArena441 in { |
|
| 5952 | + | let mut a = testResolver(testStorage441); |
|
| 5953 | + | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[*u8] = undefined; s as *[*i32]; }"); |
|
| 5954 | + | try expectNoErrors(&result); |
|
| 5955 | + | } |
|
| 4610 | 5956 | } |
|
| 4611 | 5957 | ||
| 4612 | 5958 | { // Identity cast: *mut [i32] to *mut [i32]. |
|
| 4613 | - | let mut a = testResolver(); |
|
| 4614 | - | let result = try resolveProgramStr(&mut a, "fn run(s: *mut [i32]) { s as *mut [i32]; }"); |
|
| 4615 | - | try expectNoErrors(&result); |
|
| 5959 | + | let mut testArena442 = testArena(); |
|
| 5960 | + | let testStorage442: 'test442 = &mut testArena442 in { |
|
| 5961 | + | let mut a = testResolver(testStorage442); |
|
| 5962 | + | let result = try resolveProgramStr(&mut a, "fn run(s: *mut [i32]) { s as *mut [i32]; }"); |
|
| 5963 | + | try expectNoErrors(&result); |
|
| 5964 | + | } |
|
| 4616 | 5965 | } { // Identity cast: *i32 to *i32. |
|
| 4617 | - | let mut a = testResolver(); |
|
| 4618 | - | let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *i32; }"); |
|
| 4619 | - | try expectNoErrors(&result); |
|
| 5966 | + | let mut testArena443 = testArena(); |
|
| 5967 | + | let testStorage443: 'test443 = &mut testArena443 in { |
|
| 5968 | + | let mut a = testResolver(testStorage443); |
|
| 5969 | + | let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *i32; }"); |
|
| 5970 | + | try expectNoErrors(&result); |
|
| 5971 | + | } |
|
| 4620 | 5972 | } { // Identity cast: i32 to i32. |
|
| 4621 | - | let mut a = testResolver(); |
|
| 4622 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;"); |
|
| 4623 | - | try expectNoErrors(&result); |
|
| 5973 | + | let mut testArena444 = testArena(); |
|
| 5974 | + | let testStorage444: 'test444 = &mut testArena444 in { |
|
| 5975 | + | let mut a = testResolver(testStorage444); |
|
| 5976 | + | let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;"); |
|
| 5977 | + | try expectNoErrors(&result); |
|
| 5978 | + | } |
|
| 4624 | 5979 | } |
|
| 4625 | 5980 | } |
|
| 4626 | 5981 | ||
| 4627 | 5982 | /// Tests for invalid `as` casts that should be rejected. |
|
| 4628 | 5983 | @test unsafe fn testResolveAsCastsInvalid() throws (testing::TestError) { |
|
| 4629 | 5984 | { // Pointer to slice is invalid. |
|
| 4630 | - | let mut a = testResolver(); |
|
| 4631 | - | let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *[i32]; }"); |
|
| 4632 | - | let err = try expectError(&result); |
|
| 4633 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4634 | - | else throw testing::TestError::Failed; |
|
| 5985 | + | let mut testArena445 = testArena(); |
|
| 5986 | + | let testStorage445: 'test445 = &mut testArena445 in { |
|
| 5987 | + | let mut a = testResolver(testStorage445); |
|
| 5988 | + | let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *[i32]; }"); |
|
| 5989 | + | let err = try expectError(&result); |
|
| 5990 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5991 | + | else throw testing::TestError::Failed; |
|
| 5992 | + | } |
|
| 4635 | 5993 | } { // Slice to pointer is invalid. |
|
| 4636 | - | let mut a = testResolver(); |
|
| 4637 | - | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *i32; }"); |
|
| 4638 | - | let err = try expectError(&result); |
|
| 4639 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4640 | - | else throw testing::TestError::Failed; |
|
| 5994 | + | let mut testArena446 = testArena(); |
|
| 5995 | + | let testStorage446: 'test446 = &mut testArena446 in { |
|
| 5996 | + | let mut a = testResolver(testStorage446); |
|
| 5997 | + | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *i32; }"); |
|
| 5998 | + | let err = try expectError(&result); |
|
| 5999 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 6000 | + | else throw testing::TestError::Failed; |
|
| 6001 | + | } |
|
| 4641 | 6002 | } { // *T to *i32 is invalid. |
|
| 4642 | - | let mut a = testResolver(); |
|
| 4643 | - | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *i32; }"); |
|
| 4644 | - | let err = try expectError(&result); |
|
| 4645 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4646 | - | else throw testing::TestError::Failed; |
|
| 6003 | + | let mut testArena447 = testArena(); |
|
| 6004 | + | let testStorage447: 'test447 = &mut testArena447 in { |
|
| 6005 | + | let mut a = testResolver(testStorage447); |
|
| 6006 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *i32; }"); |
|
| 6007 | + | let err = try expectError(&result); |
|
| 6008 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 6009 | + | else throw testing::TestError::Failed; |
|
| 6010 | + | } |
|
| 4647 | 6011 | } { // *[T] to *[i32] is invalid. |
|
| 4648 | - | let mut a = testResolver(); |
|
| 4649 | - | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) { s as *[i32]; }"); |
|
| 4650 | - | let err = try expectError(&result); |
|
| 4651 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4652 | - | else throw testing::TestError::Failed; |
|
| 6012 | + | let mut testArena448 = testArena(); |
|
| 6013 | + | let testStorage448: 'test448 = &mut testArena448 in { |
|
| 6014 | + | let mut a = testResolver(testStorage448); |
|
| 6015 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) { s as *[i32]; }"); |
|
| 6016 | + | let err = try expectError(&result); |
|
| 6017 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 6018 | + | else throw testing::TestError::Failed; |
|
| 6019 | + | } |
|
| 4653 | 6020 | } { // Slice to numeric is invalid. |
|
| 4654 | - | let mut a = testResolver(); |
|
| 4655 | - | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as u32; }"); |
|
| 4656 | - | let err = try expectError(&result); |
|
| 4657 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4658 | - | else throw testing::TestError::Failed; |
|
| 6021 | + | let mut testArena449 = testArena(); |
|
| 6022 | + | let testStorage449: 'test449 = &mut testArena449 in { |
|
| 6023 | + | let mut a = testResolver(testStorage449); |
|
| 6024 | + | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as u32; }"); |
|
| 6025 | + | let err = try expectError(&result); |
|
| 6026 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 6027 | + | else throw testing::TestError::Failed; |
|
| 6028 | + | } |
|
| 4659 | 6029 | } { // *record to *u8 is invalid. |
|
| 4660 | - | let mut a = testResolver(); |
|
| 4661 | - | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *u8; }"); |
|
| 4662 | - | let err = try expectError(&result); |
|
| 4663 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4664 | - | else throw testing::TestError::Failed; |
|
| 6030 | + | let mut testArena450 = testArena(); |
|
| 6031 | + | let testStorage450: 'test450 = &mut testArena450 in { |
|
| 6032 | + | let mut a = testResolver(testStorage450); |
|
| 6033 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *u8; }"); |
|
| 6034 | + | let err = try expectError(&result); |
|
| 6035 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 6036 | + | else throw testing::TestError::Failed; |
|
| 6037 | + | } |
|
| 4665 | 6038 | } |
|
| 4666 | 6039 | } |
|
| 4667 | 6040 | ||
| 4668 | 6041 | /// Test that catch binding is available in catch block scope. |
|
| 4669 | 6042 | @test unsafe fn testResolveTryCatchBinding() throws (testing::TestError) { |
|
| 4670 | 6043 | { |
|
| 4671 | - | let mut a = testResolver(); |
|
| 4672 | - | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { return 0; }; }"; |
|
| 4673 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4674 | - | try expectNoErrors(&result); |
|
| 6044 | + | let mut testArena451 = testArena(); |
|
| 6045 | + | let testStorage451: 'test451 = &mut testArena451 in { |
|
| 6046 | + | let mut a = testResolver(testStorage451); |
|
| 6047 | + | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { return 0; }; }"; |
|
| 6048 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6049 | + | try expectNoErrors(&result); |
|
| 6050 | + | } |
|
| 4675 | 6051 | } { |
|
| 4676 | - | let mut a = testResolver(); |
|
| 4677 | - | let program = "union Error { A, B } fn fallible() -> u32 throws (Error) { throw Error::A; } fn caller() -> u32 { return try fallible() catch e { if e == Error::A { return 1; } else { return 2; } }; }"; |
|
| 4678 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4679 | - | try expectNoErrors(&result); |
|
| 6052 | + | let mut testArena452 = testArena(); |
|
| 6053 | + | let testStorage452: 'test452 = &mut testArena452 in { |
|
| 6054 | + | let mut a = testResolver(testStorage452); |
|
| 6055 | + | let program = "union Error { A, B } fn fallible() -> u32 throws (Error) { throw Error::A; } fn caller() -> u32 { return try fallible() catch e { if e == Error::A { return 1; } else { return 2; } }; }"; |
|
| 6056 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6057 | + | try expectNoErrors(&result); |
|
| 6058 | + | } |
|
| 4680 | 6059 | } { |
|
| 4681 | - | let mut a = testResolver(); |
|
| 4682 | - | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { if err == Error::Fail { return 1; } return 0; }; }"; |
|
| 4683 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4684 | - | try expectNoErrors(&result); |
|
| 6060 | + | let mut testArena453 = testArena(); |
|
| 6061 | + | let testStorage453: 'test453 = &mut testArena453 in { |
|
| 6062 | + | let mut a = testResolver(testStorage453); |
|
| 6063 | + | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { if err == Error::Fail { return 1; } return 0; }; }"; |
|
| 6064 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6065 | + | try expectNoErrors(&result); |
|
| 6066 | + | } |
|
| 4685 | 6067 | } { |
|
| 4686 | - | let mut a = testResolver(); |
|
| 4687 | - | let program = "union Error { Fail(u32) } fn fallible() -> u32 throws (Error) { throw Error::Fail(42); } fn caller() -> u32 { return try fallible() catch err { match err { case Error::Fail(x) => return x, } }; }"; |
|
| 4688 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4689 | - | try expectNoErrors(&result); |
|
| 6068 | + | let mut testArena454 = testArena(); |
|
| 6069 | + | let testStorage454: 'test454 = &mut testArena454 in { |
|
| 6070 | + | let mut a = testResolver(testStorage454); |
|
| 6071 | + | let program = "union Error { Fail(u32) } fn fallible() -> u32 throws (Error) { throw Error::Fail(42); } fn caller() -> u32 { return try fallible() catch err { match err { case Error::Fail(x) => return x, } }; }"; |
|
| 6072 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6073 | + | try expectNoErrors(&result); |
|
| 6074 | + | } |
|
| 4690 | 6075 | } |
|
| 4691 | 6076 | } |
|
| 4692 | 6077 | ||
| 4693 | 6078 | /// Test that duplicate union variant patterns are detected. |
|
| 4694 | 6079 | @test unsafe fn testResolveMatchDuplicateUnionPattern() throws (testing::TestError) { |
|
| 4695 | 6080 | { |
|
| 4696 | - | let mut a = testResolver(); |
|
| 4697 | - | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::A => {}, else => {} } }"; |
|
| 4698 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4699 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6081 | + | let mut testArena455 = testArena(); |
|
| 6082 | + | let testStorage455: 'test455 = &mut testArena455 in { |
|
| 6083 | + | let mut a = testResolver(testStorage455); |
|
| 6084 | + | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::A => {}, else => {} } }"; |
|
| 6085 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6086 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6087 | + | } |
|
| 4700 | 6088 | } { |
|
| 4701 | 6089 | // No duplicate: distinct variants are fine. |
|
| 4702 | - | let mut a = testResolver(); |
|
| 4703 | - | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {} } }"; |
|
| 4704 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4705 | - | try expectNoErrors(&result); |
|
| 6090 | + | let mut testArena456 = testArena(); |
|
| 6091 | + | let testStorage456: 'test456 = &mut testArena456 in { |
|
| 6092 | + | let mut a = testResolver(testStorage456); |
|
| 6093 | + | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {} } }"; |
|
| 6094 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6095 | + | try expectNoErrors(&result); |
|
| 6096 | + | } |
|
| 4706 | 6097 | } |
|
| 4707 | 6098 | } |
|
| 4708 | 6099 | ||
| 4709 | 6100 | /// Test that duplicate bool patterns are detected. |
|
| 4710 | 6101 | @test unsafe fn testResolveMatchDuplicateBoolPattern() throws (testing::TestError) { |
|
| 4711 | 6102 | { |
|
| 4712 | - | let mut a = testResolver(); |
|
| 4713 | - | let program = "fn f(x: bool) { match x { case true => {}, case true => {}, else => {} } }"; |
|
| 4714 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4715 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6103 | + | let mut testArena457 = testArena(); |
|
| 6104 | + | let testStorage457: 'test457 = &mut testArena457 in { |
|
| 6105 | + | let mut a = testResolver(testStorage457); |
|
| 6106 | + | let program = "fn f(x: bool) { match x { case true => {}, case true => {}, else => {} } }"; |
|
| 6107 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6108 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6109 | + | } |
|
| 4716 | 6110 | } { |
|
| 4717 | - | let mut a = testResolver(); |
|
| 4718 | - | let program = "fn f(x: bool) { match x { case false => {}, case false => {}, else => {} } }"; |
|
| 4719 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4720 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6111 | + | let mut testArena458 = testArena(); |
|
| 6112 | + | let testStorage458: 'test458 = &mut testArena458 in { |
|
| 6113 | + | let mut a = testResolver(testStorage458); |
|
| 6114 | + | let program = "fn f(x: bool) { match x { case false => {}, case false => {}, else => {} } }"; |
|
| 6115 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6116 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6117 | + | } |
|
| 4721 | 6118 | } |
|
| 4722 | 6119 | } |
|
| 4723 | 6120 | ||
| 4724 | 6121 | /// Test that duplicate nil patterns in optional match are detected. |
|
| 4725 | 6122 | @test unsafe fn testResolveMatchDuplicateOptionalPattern() throws (testing::TestError) { |
|
| 4726 | 6123 | { |
|
| 4727 | - | let mut a = testResolver(); |
|
| 4728 | - | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, case nil => {} } }"; |
|
| 4729 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4730 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6124 | + | let mut testArena459 = testArena(); |
|
| 6125 | + | let testStorage459: 'test459 = &mut testArena459 in { |
|
| 6126 | + | let mut a = testResolver(testStorage459); |
|
| 6127 | + | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, case nil => {} } }"; |
|
| 6128 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6129 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6130 | + | } |
|
| 4731 | 6131 | } { |
|
| 4732 | 6132 | // Duplicate value binding. |
|
| 4733 | - | let mut a = testResolver(); |
|
| 4734 | - | let program = "fn f(opt: ?i32) { match opt { v => {}, w => {}, case nil => {} } }"; |
|
| 4735 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4736 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6133 | + | let mut testArena460 = testArena(); |
|
| 6134 | + | let testStorage460: 'test460 = &mut testArena460 in { |
|
| 6135 | + | let mut a = testResolver(testStorage460); |
|
| 6136 | + | let program = "fn f(opt: ?i32) { match opt { v => {}, w => {}, case nil => {} } }"; |
|
| 6137 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6138 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 6139 | + | } |
|
| 4737 | 6140 | } |
|
| 4738 | 6141 | } |
|
| 4739 | 6142 | ||
| 4740 | 6143 | /// Test that guarded match arms are not considered duplicates. |
|
| 4741 | 6144 | @test unsafe fn testResolveMatchGuardedNotDuplicate() throws (testing::TestError) { |
|
| 4742 | 6145 | { |
|
| 4743 | 6146 | // Guarded union variant followed by same variant is fine. |
|
| 4744 | - | let mut a = testResolver(); |
|
| 4745 | - | let program = "union U { A, B } fn f(u: U) { match u { case U::A if true => {}, case U::A => {}, case U::B => {} } }"; |
|
| 4746 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4747 | - | try expectNoErrors(&result); |
|
| 6147 | + | let mut testArena461 = testArena(); |
|
| 6148 | + | let testStorage461: 'test461 = &mut testArena461 in { |
|
| 6149 | + | let mut a = testResolver(testStorage461); |
|
| 6150 | + | let program = "union U { A, B } fn f(u: U) { match u { case U::A if true => {}, case U::A => {}, case U::B => {} } }"; |
|
| 6151 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6152 | + | try expectNoErrors(&result); |
|
| 6153 | + | } |
|
| 4748 | 6154 | } { |
|
| 4749 | 6155 | // Guarded bool pattern followed by same bool is fine. |
|
| 4750 | - | let mut a = testResolver(); |
|
| 4751 | - | let program = "fn f(x: bool) { match x { case true if true => {}, case true => {}, case false => {} } }"; |
|
| 4752 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4753 | - | try expectNoErrors(&result); |
|
| 6156 | + | let mut testArena462 = testArena(); |
|
| 6157 | + | let testStorage462: 'test462 = &mut testArena462 in { |
|
| 6158 | + | let mut a = testResolver(testStorage462); |
|
| 6159 | + | let program = "fn f(x: bool) { match x { case true if true => {}, case true => {}, case false => {} } }"; |
|
| 6160 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6161 | + | try expectNoErrors(&result); |
|
| 6162 | + | } |
|
| 4754 | 6163 | } { |
|
| 4755 | 6164 | // Guarded nil pattern followed by nil is fine. |
|
| 4756 | - | let mut a = testResolver(); |
|
| 4757 | - | let program = "fn f(opt: ?i32) { match opt { case nil if true => {}, case nil => {}, v => {} } }"; |
|
| 4758 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4759 | - | try expectNoErrors(&result); |
|
| 6165 | + | let mut testArena463 = testArena(); |
|
| 6166 | + | let testStorage463: 'test463 = &mut testArena463 in { |
|
| 6167 | + | let mut a = testResolver(testStorage463); |
|
| 6168 | + | let program = "fn f(opt: ?i32) { match opt { case nil if true => {}, case nil => {}, v => {} } }"; |
|
| 6169 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6170 | + | try expectNoErrors(&result); |
|
| 6171 | + | } |
|
| 4760 | 6172 | } { |
|
| 4761 | 6173 | // Guarded value binding followed by another binding is fine. |
|
| 4762 | - | let mut a = testResolver(); |
|
| 4763 | - | let program = "fn f(opt: ?i32) { match opt { v if true => {}, w => {}, case nil => {} } }"; |
|
| 4764 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4765 | - | try expectNoErrors(&result); |
|
| 6174 | + | let mut testArena464 = testArena(); |
|
| 6175 | + | let testStorage464: 'test464 = &mut testArena464 in { |
|
| 6176 | + | let mut a = testResolver(testStorage464); |
|
| 6177 | + | let program = "fn f(opt: ?i32) { match opt { v if true => {}, w => {}, case nil => {} } }"; |
|
| 6178 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6179 | + | try expectNoErrors(&result); |
|
| 6180 | + | } |
|
| 4766 | 6181 | } |
|
| 4767 | 6182 | } |
|
| 4768 | 6183 | ||
| 4769 | 6184 | /// Test that unreachable else is detected when all union variants are covered. |
|
| 4770 | 6185 | @test unsafe fn testResolveMatchUnreachableElseUnion() throws (testing::TestError) { |
|
| 4771 | 6186 | { |
|
| 4772 | - | let mut a = testResolver(); |
|
| 4773 | - | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {}, else => {} } }"; |
|
| 4774 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4775 | - | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
|
| 6187 | + | let mut testArena465 = testArena(); |
|
| 6188 | + | let testStorage465: 'test465 = &mut testArena465 in { |
|
| 6189 | + | let mut a = testResolver(testStorage465); |
|
| 6190 | + | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {}, else => {} } }"; |
|
| 6191 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6192 | + | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
|
| 6193 | + | } |
|
| 4776 | 6194 | } { |
|
| 4777 | 6195 | // Partial coverage with else is fine. |
|
| 4778 | - | let mut a = testResolver(); |
|
| 4779 | - | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, else => {} } }"; |
|
| 4780 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4781 | - | try expectNoErrors(&result); |
|
| 6196 | + | let mut testArena466 = testArena(); |
|
| 6197 | + | let testStorage466: 'test466 = &mut testArena466 in { |
|
| 6198 | + | let mut a = testResolver(testStorage466); |
|
| 6199 | + | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, else => {} } }"; |
|
| 6200 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6201 | + | try expectNoErrors(&result); |
|
| 6202 | + | } |
|
| 4782 | 6203 | } |
|
| 4783 | 6204 | } |
|
| 4784 | 6205 | ||
| 4785 | 6206 | /// Test that unreachable else is detected when both bool cases are covered. |
|
| 4786 | 6207 | @test unsafe fn testResolveMatchUnreachableElseBool() throws (testing::TestError) { |
|
| 4787 | 6208 | { |
|
| 4788 | - | let mut a = testResolver(); |
|
| 4789 | - | let program = "fn f(x: bool) { match x { case true => {}, case false => {}, else => {} } }"; |
|
| 4790 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4791 | - | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
|
| 6209 | + | let mut testArena467 = testArena(); |
|
| 6210 | + | let testStorage467: 'test467 = &mut testArena467 in { |
|
| 6211 | + | let mut a = testResolver(testStorage467); |
|
| 6212 | + | let program = "fn f(x: bool) { match x { case true => {}, case false => {}, else => {} } }"; |
|
| 6213 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6214 | + | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
|
| 6215 | + | } |
|
| 4792 | 6216 | } { |
|
| 4793 | 6217 | // Only one case with else is fine. |
|
| 4794 | - | let mut a = testResolver(); |
|
| 4795 | - | let program = "fn f(x: bool) { match x { case true => {}, else => {} } }"; |
|
| 4796 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4797 | - | try expectNoErrors(&result); |
|
| 6218 | + | let mut testArena468 = testArena(); |
|
| 6219 | + | let testStorage468: 'test468 = &mut testArena468 in { |
|
| 6220 | + | let mut a = testResolver(testStorage468); |
|
| 6221 | + | let program = "fn f(x: bool) { match x { case true => {}, else => {} } }"; |
|
| 6222 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6223 | + | try expectNoErrors(&result); |
|
| 6224 | + | } |
|
| 4798 | 6225 | } |
|
| 4799 | 6226 | } |
|
| 4800 | 6227 | ||
| 4801 | 6228 | /// Test that unreachable else is detected when both optional cases are covered. |
|
| 4802 | 6229 | @test unsafe fn testResolveMatchUnreachableElseOptional() throws (testing::TestError) { |
|
| 4803 | 6230 | { |
|
| 4804 | - | let mut a = testResolver(); |
|
| 4805 | - | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, else => {} } }"; |
|
| 4806 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4807 | - | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
|
| 6231 | + | let mut testArena469 = testArena(); |
|
| 6232 | + | let testStorage469: 'test469 = &mut testArena469 in { |
|
| 6233 | + | let mut a = testResolver(testStorage469); |
|
| 6234 | + | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, else => {} } }"; |
|
| 6235 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6236 | + | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
|
| 6237 | + | } |
|
| 4808 | 6238 | } { |
|
| 4809 | 6239 | // Only value binding with else is fine. |
|
| 4810 | - | let mut a = testResolver(); |
|
| 4811 | - | let program = "fn f(opt: ?i32) { match opt { v => {}, else => {} } }"; |
|
| 4812 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4813 | - | try expectNoErrors(&result); |
|
| 6240 | + | let mut testArena470 = testArena(); |
|
| 6241 | + | let testStorage470: 'test470 = &mut testArena470 in { |
|
| 6242 | + | let mut a = testResolver(testStorage470); |
|
| 6243 | + | let program = "fn f(opt: ?i32) { match opt { v => {}, else => {} } }"; |
|
| 6244 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6245 | + | try expectNoErrors(&result); |
|
| 6246 | + | } |
|
| 4814 | 6247 | } |
|
| 4815 | 6248 | } |
|
| 4816 | 6249 | ||
| 4817 | 6250 | // --- Multi-error typed catch tests --- |
|
| 4818 | 6251 | ||
| 4819 | 6252 | @test unsafe fn testTypedCatchExhaustive() throws (testing::TestError) { |
|
| 4820 | - | let mut a = testResolver(); |
|
| 4821 | - | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrB { return 1; }; }"; |
|
| 4822 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4823 | - | try expectNoErrors(&result); |
|
| 6253 | + | let mut testArena471 = testArena(); |
|
| 6254 | + | let testStorage471: 'test471 = &mut testArena471 in { |
|
| 6255 | + | let mut a = testResolver(testStorage471); |
|
| 6256 | + | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrB { return 1; }; }"; |
|
| 6257 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6258 | + | try expectNoErrors(&result); |
|
| 6259 | + | } |
|
| 4824 | 6260 | } |
|
| 4825 | 6261 | ||
| 4826 | 6262 | @test unsafe fn testTypedCatchNonExhaustive() throws (testing::TestError) { |
|
| 4827 | - | let mut a = testResolver(); |
|
| 4828 | - | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; }; }"; |
|
| 4829 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4830 | - | try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive); |
|
| 6263 | + | let mut testArena472 = testArena(); |
|
| 6264 | + | let testStorage472: 'test472 = &mut testArena472 in { |
|
| 6265 | + | let mut a = testResolver(testStorage472); |
|
| 6266 | + | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; }; }"; |
|
| 6267 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6268 | + | try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive); |
|
| 6269 | + | } |
|
| 4831 | 6270 | } |
|
| 4832 | 6271 | ||
| 4833 | 6272 | @test unsafe fn testTypedCatchDuplicate() throws (testing::TestError) { |
|
| 4834 | - | let mut a = testResolver(); |
|
| 4835 | - | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrA { return 1; }; }"; |
|
| 4836 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4837 | - | try expectErrorKind(&result, super::ErrorKind::TryCatchDuplicateType); |
|
| 6273 | + | let mut testArena473 = testArena(); |
|
| 6274 | + | let testStorage473: 'test473 = &mut testArena473 in { |
|
| 6275 | + | let mut a = testResolver(testStorage473); |
|
| 6276 | + | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrA { return 1; }; }"; |
|
| 6277 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6278 | + | try expectErrorKind(&result, super::ErrorKind::TryCatchDuplicateType); |
|
| 6279 | + | } |
|
| 4838 | 6280 | } |
|
| 4839 | 6281 | ||
| 4840 | 6282 | @test unsafe fn testTypedCatchWithCatchAll() throws (testing::TestError) { |
|
| 4841 | - | let mut a = testResolver(); |
|
| 4842 | - | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch { return 1; }; }"; |
|
| 4843 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4844 | - | try expectNoErrors(&result); |
|
| 6283 | + | let mut testArena474 = testArena(); |
|
| 6284 | + | let testStorage474: 'test474 = &mut testArena474 in { |
|
| 6285 | + | let mut a = testResolver(testStorage474); |
|
| 6286 | + | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch { return 1; }; }"; |
|
| 6287 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6288 | + | try expectNoErrors(&result); |
|
| 6289 | + | } |
|
| 4845 | 6290 | } |
|
| 4846 | 6291 | ||
| 4847 | 6292 | @test unsafe fn testTypedCatchWrongType() throws (testing::TestError) { |
|
| 4848 | - | let mut a = testResolver(); |
|
| 4849 | - | let program = "union ErrA { A } union ErrB { B } union ErrC { C } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrC { return 0; } catch e as ErrA { return 1; }; }"; |
|
| 4850 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4851 | - | try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError); |
|
| 6293 | + | let mut testArena475 = testArena(); |
|
| 6294 | + | let testStorage475: 'test475 = &mut testArena475 in { |
|
| 6295 | + | let mut a = testResolver(testStorage475); |
|
| 6296 | + | let program = "union ErrA { A } union ErrB { B } union ErrC { C } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrC { return 0; } catch e as ErrA { return 1; }; }"; |
|
| 6297 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6298 | + | try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError); |
|
| 6299 | + | } |
|
| 4852 | 6300 | } |
|
| 4853 | 6301 | ||
| 4854 | 6302 | @test unsafe fn testInferredCatchMultiError() throws (testing::TestError) { |
|
| 4855 | - | let mut a = testResolver(); |
|
| 4856 | - | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e { return 0; }; }"; |
|
| 4857 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4858 | - | try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError); |
|
| 6303 | + | let mut testArena476 = testArena(); |
|
| 6304 | + | let testStorage476: 'test476 = &mut testArena476 in { |
|
| 6305 | + | let mut a = testResolver(testStorage476); |
|
| 6306 | + | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e { return 0; }; }"; |
|
| 6307 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6308 | + | try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError); |
|
| 6309 | + | } |
|
| 4859 | 6310 | } |
|
| 4860 | 6311 | ||
| 4861 | 6312 | @test unsafe fn testResolveInstanceMissingMethod() throws (testing::TestError) { |
|
| 4862 | - | let mut a = testResolver(); |
|
| 4863 | - | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}"; |
|
| 4864 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4865 | - | try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f")); |
|
| 6313 | + | let mut testArena477 = testArena(); |
|
| 6314 | + | let testStorage477: 'test477 = &mut testArena477 in { |
|
| 6315 | + | let mut a = testResolver(testStorage477); |
|
| 6316 | + | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}"; |
|
| 6317 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6318 | + | try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f")); |
|
| 6319 | + | } |
|
| 4866 | 6320 | } |
|
| 4867 | 6321 | ||
| 4868 | 6322 | @test unsafe fn testResolveInstanceUnknownMethod() throws (testing::TestError) { |
|
| 4869 | - | let mut a = testResolver(); |
|
| 4870 | - | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }"; |
|
| 4871 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4872 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 6323 | + | let mut testArena478 = testArena(); |
|
| 6324 | + | let testStorage478: 'test478 = &mut testArena478 in { |
|
| 6325 | + | let mut a = testResolver(testStorage478); |
|
| 6326 | + | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }"; |
|
| 6327 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6328 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 6329 | + | } |
|
| 4873 | 6330 | } |
|
| 4874 | 6331 | ||
| 4875 | 6332 | @test unsafe fn testResolveTraitDuplicateMethodRejected() throws (testing::TestError) { |
|
| 4876 | - | let mut a = testResolver(); |
|
| 4877 | - | let program = "trait Adder { fn (*mut Adder) add(n: i32) -> i32; fn (*mut Adder) add(n: i32) -> i32; }"; |
|
| 4878 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4879 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("add")); |
|
| 6333 | + | let mut testArena479 = testArena(); |
|
| 6334 | + | let testStorage479: 'test479 = &mut testArena479 in { |
|
| 6335 | + | let mut a = testResolver(testStorage479); |
|
| 6336 | + | let program = "trait Adder { fn (*mut Adder) add(n: i32) -> i32; fn (*mut Adder) add(n: i32) -> i32; }"; |
|
| 6337 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6338 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("add")); |
|
| 6339 | + | } |
|
| 4880 | 6340 | } |
|
| 4881 | 6341 | ||
| 4882 | 6342 | @test unsafe fn testResolveInstanceReceiverTypeMustMatchTarget() throws (testing::TestError) { |
|
| 4883 | - | let mut a = testResolver(); |
|
| 4884 | - | let program = "record Counter { value: i32 } record Wrong { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Wrong) add(n: i32) -> i32 { return n; } }"; |
|
| 4885 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4886 | - | let err = try expectError(&result); |
|
| 4887 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4888 | - | else throw testing::TestError::Failed; |
|
| 6343 | + | let mut testArena480 = testArena(); |
|
| 6344 | + | let testStorage480: 'test480 = &mut testArena480 in { |
|
| 6345 | + | let mut a = testResolver(testStorage480); |
|
| 6346 | + | let program = "record Counter { value: i32 } record Wrong { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Wrong) add(n: i32) -> i32 { return n; } }"; |
|
| 6347 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6348 | + | let err = try expectError(&result); |
|
| 6349 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 6350 | + | else throw testing::TestError::Failed; |
|
| 6351 | + | } |
|
| 4889 | 6352 | } |
|
| 4890 | 6353 | ||
| 4891 | 6354 | @test unsafe fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) { |
|
| 4892 | - | let mut a = testResolver(); |
|
| 4893 | - | let program = "union Error { Fail } record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32 throws (Error); } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 throws (Error) { throw Error::Fail; return n; } } fn caller(a: *mut opaque Adder) -> i32 { return a.add(1); }"; |
|
| 4894 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4895 | - | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
|
| 6355 | + | let mut testArena481 = testArena(); |
|
| 6356 | + | let testStorage481: 'test481 = &mut testArena481 in { |
|
| 6357 | + | let mut a = testResolver(testStorage481); |
|
| 6358 | + | let program = "union Error { Fail } record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32 throws (Error); } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 throws (Error) { throw Error::Fail; return n; } } fn caller(a: *mut opaque Adder) -> i32 { return a.add(1); }"; |
|
| 6359 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6360 | + | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
|
| 6361 | + | } |
|
| 4896 | 6362 | } |
|
| 4897 | 6363 | ||
| 4898 | 6364 | /// Trait declares immutable receiver (*Trait) but instance uses mutable (*mut Type). |
|
| 4899 | 6365 | /// The instance method could mutate through what was originally an immutable pointer. |
|
| 4900 | 6366 | @test unsafe fn testResolveInstanceMutReceiverOnImmutableTrait() throws (testing::TestError) { |
|
| 4901 | - | let mut a = testResolver(); |
|
| 4902 | - | let program = "record Counter { value: i32 } trait Reader { fn (*Reader) read() -> i32; } instance Reader for Counter { fn (c: *mut Counter) read() -> i32 { set c.value = c.value + 1; return c.value; } }"; |
|
| 4903 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4904 | - | // Should reject: instance declares *mut receiver but trait only requires immutable. |
|
| 4905 | - | try expectErrorKind(&result, super::ErrorKind::ReceiverMutabilityMismatch); |
|
| 6367 | + | let mut testArena482 = testArena(); |
|
| 6368 | + | let testStorage482: 'test482 = &mut testArena482 in { |
|
| 6369 | + | let mut a = testResolver(testStorage482); |
|
| 6370 | + | let program = "record Counter { value: i32 } trait Reader { fn (*Reader) read() -> i32; } instance Reader for Counter { fn (c: *mut Counter) read() -> i32 { set c.value = c.value + 1; return c.value; } }"; |
|
| 6371 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6372 | + | // Should reject: instance declares *mut receiver but trait only requires immutable. |
|
| 6373 | + | try expectErrorKind(&result, super::ErrorKind::ReceiverMutabilityMismatch); |
|
| 6374 | + | } |
|
| 4906 | 6375 | } |
|
| 4907 | 6376 | ||
| 4908 | 6377 | /// Instance method declares different parameter types than the trait. |
|
| 4909 | 6378 | /// The resolver should reject the mismatch rather than silently using the trait's types. |
|
| 4910 | 6379 | @test unsafe fn testResolveInstanceParamTypeMismatch() throws (testing::TestError) { |
|
| 4911 | - | let mut a = testResolver(); |
|
| 4912 | - | let program = "record Acc { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Acc { fn (a: *mut Acc) add(n: u8) -> i32 { set a.value = a.value + n as i32; return a.value; } }"; |
|
| 4913 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4914 | - | // Should reject: instance param type u8 doesn't match trait param type i32. |
|
| 4915 | - | let err = try expectError(&result); |
|
| 4916 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4917 | - | else throw testing::TestError::Failed; |
|
| 6380 | + | let mut testArena483 = testArena(); |
|
| 6381 | + | let testStorage483: 'test483 = &mut testArena483 in { |
|
| 6382 | + | let mut a = testResolver(testStorage483); |
|
| 6383 | + | let program = "record Acc { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Acc { fn (a: *mut Acc) add(n: u8) -> i32 { set a.value = a.value + n as i32; return a.value; } }"; |
|
| 6384 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6385 | + | // Should reject: instance param type u8 doesn't match trait param type i32. |
|
| 6386 | + | let err = try expectError(&result); |
|
| 6387 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 6388 | + | else throw testing::TestError::Failed; |
|
| 6389 | + | } |
|
| 4918 | 6390 | } |
|
| 4919 | 6391 | ||
| 4920 | 6392 | /// Duplicate instance declarations for the same (trait, type) pair should be rejected. |
|
| 4921 | 6393 | @test unsafe fn testResolveInstanceDuplicateRejected() throws (testing::TestError) { |
|
| 4922 | - | let mut a = testResolver(); |
|
| 4923 | - | let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n + 100; return c.value; } }"; |
|
| 4924 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4925 | - | // Should reject: duplicate instance for (Adder, Counter). |
|
| 4926 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateInstance); |
|
| 6394 | + | let mut testArena484 = testArena(); |
|
| 6395 | + | let testStorage484: 'test484 = &mut testArena484 in { |
|
| 6396 | + | let mut a = testResolver(testStorage484); |
|
| 6397 | + | let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n + 100; return c.value; } }"; |
|
| 6398 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6399 | + | // Should reject: duplicate instance for (Adder, Counter). |
|
| 6400 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateInstance); |
|
| 6401 | + | } |
|
| 4927 | 6402 | } |
|
| 4928 | 6403 | ||
| 4929 | 6404 | /// Trait method receiver must point to the declaring trait type. |
|
| 4930 | 6405 | @test unsafe fn testResolveTraitReceiverMismatch() throws (testing::TestError) { |
|
| 4931 | - | let mut a = testResolver(); |
|
| 4932 | - | let program = "record Other { x: i32 } trait Foo { fn (*mut Other) bar() -> i32; }"; |
|
| 4933 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4934 | - | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
|
| 6406 | + | let mut testArena485 = testArena(); |
|
| 6407 | + | let testStorage485: 'test485 = &mut testArena485 in { |
|
| 6408 | + | let mut a = testResolver(testStorage485); |
|
| 6409 | + | let program = "record Other { x: i32 } trait Foo { fn (*mut Other) bar() -> i32; }"; |
|
| 6410 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6411 | + | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
|
| 6412 | + | } |
|
| 4935 | 6413 | } |
|
| 4936 | 6414 | ||
| 4937 | 6415 | /// Using a trait name as a value expression should be rejected. |
|
| 4938 | 6416 | @test unsafe fn testResolveTraitNameAsValueRejected() throws (testing::TestError) { |
|
| 4939 | - | let mut a = testResolver(); |
|
| 4940 | - | let program = "trait Foo { fn (*Foo) bar() -> i32; } fn test() -> i32 { let x = Foo; return 0; }"; |
|
| 4941 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4942 | - | try expectErrorKind(&result, super::ErrorKind::UnexpectedTraitName); |
|
| 6417 | + | let mut testArena486 = testArena(); |
|
| 6418 | + | let testStorage486: 'test486 = &mut testArena486 in { |
|
| 6419 | + | let mut a = testResolver(testStorage486); |
|
| 6420 | + | let program = "trait Foo { fn (*Foo) bar() -> i32; } fn test() -> i32 { let x = Foo; return 0; }"; |
|
| 6421 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6422 | + | try expectErrorKind(&result, super::ErrorKind::UnexpectedTraitName); |
|
| 6423 | + | } |
|
| 4943 | 6424 | } |
|
| 4944 | 6425 | ||
| 4945 | 6426 | /// Cross-module trait: coerce to trait object and dispatch from a different module. |
|
| 4946 | 6427 | @test unsafe fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) { |
|
| 4947 | - | let mut a = testResolver(); |
|
| 4948 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 6428 | + | let mut testArena487 = testArena(); |
|
| 6429 | + | let testStorage487: 'test487 = &mut testArena487 in { |
|
| 6430 | + | let mut a = testResolver(testStorage487); |
|
| 6431 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4949 | 6432 | ||
| 4950 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena); |
|
| 4951 | - | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 4952 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 6433 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena); |
|
| 6434 | + | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 6435 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 4953 | 6436 | ||
| 4954 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4955 | - | try expectNoErrors(&result); |
|
| 6437 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 6438 | + | try expectNoErrors(&result); |
|
| 6439 | + | } |
|
| 4956 | 6440 | } |
|
| 4957 | 6441 | ||
| 4958 | 6442 | /// Instance in a different module from trait and type. |
|
| 4959 | 6443 | @test unsafe fn testResolveInstanceCrossModule() throws (testing::TestError) { |
|
| 4960 | - | let mut a = testResolver(); |
|
| 4961 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 6444 | + | let mut testArena488 = testArena(); |
|
| 6445 | + | let testStorage488: 'test488 = &mut testArena488 in { |
|
| 6446 | + | let mut a = testResolver(testStorage488); |
|
| 6447 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4962 | 6448 | ||
| 4963 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena); |
|
| 4964 | - | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena); |
|
| 4965 | - | let implsId = try registerModule(&mut MODULE_GRAPH, rootId, "impls", "use root::defs; instance defs::Adder for defs::Counter { fn (c: *mut defs::Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 4966 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 6449 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena); |
|
| 6450 | + | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena); |
|
| 6451 | + | let implsId = try registerModule(&mut MODULE_GRAPH, rootId, "impls", "use root::defs; instance defs::Adder for defs::Counter { fn (c: *mut defs::Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 6452 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 4967 | 6453 | ||
| 4968 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4969 | - | try expectNoErrors(&result); |
|
| 6454 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 6455 | + | try expectNoErrors(&result); |
|
| 6456 | + | } |
|
| 4970 | 6457 | } |
|
| 4971 | 6458 | ||
| 4972 | 6459 | /// Calling a mutable-receiver trait method on an immutable trait object |
|
| 4973 | 6460 | /// must be rejected. |
|
| 4974 | 6461 | @test unsafe fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) { |
|
| 4975 | - | let mut a = testResolver(); |
|
| 4976 | - | let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } fn caller(a: *opaque Adder) -> i32 { return a.add(1); }"; |
|
| 4977 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4978 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 6462 | + | let mut testArena489 = testArena(); |
|
| 6463 | + | let testStorage489: 'test489 = &mut testArena489 in { |
|
| 6464 | + | let mut a = testResolver(testStorage489); |
|
| 6465 | + | let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } fn caller(a: *opaque Adder) -> i32 { return a.add(1); }"; |
|
| 6466 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6467 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 6468 | + | } |
|
| 4979 | 6469 | } |
|
| 4980 | 6470 | ||
| 4981 | 6471 | /// Immutable methods on an immutable trait object should be accepted. |
|
| 4982 | 6472 | @test unsafe fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) { |
|
| 4983 | - | let mut a = testResolver(); |
|
| 4984 | - | let program = "record Counter { value: i32 } trait Reader { fn (*Reader) get() -> i32; } instance Reader for Counter { fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(r: *opaque Reader) -> i32 { return r.get(); }"; |
|
| 4985 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4986 | - | try expectNoErrors(&result); |
|
| 6473 | + | let mut testArena490 = testArena(); |
|
| 6474 | + | let testStorage490: 'test490 = &mut testArena490 in { |
|
| 6475 | + | let mut a = testResolver(testStorage490); |
|
| 6476 | + | let program = "record Counter { value: i32 } trait Reader { fn (*Reader) get() -> i32; } instance Reader for Counter { fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(r: *opaque Reader) -> i32 { return r.get(); }"; |
|
| 6477 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6478 | + | try expectNoErrors(&result); |
|
| 6479 | + | } |
|
| 4987 | 6480 | } |
|
| 4988 | 6481 | ||
| 4989 | 6482 | /// Both mutable and immutable methods on a mutable trait object should work. |
|
| 4990 | 6483 | @test unsafe fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) { |
|
| 4991 | - | let mut a = testResolver(); |
|
| 4992 | - | let program = "record Counter { value: i32 } trait Ops { fn (&mut Ops) inc(); fn (&Ops) get() -> i32; } instance Ops for Counter { fn (c: &mut Counter) inc() { set c.value = c.value + 1; } fn (c: &Counter) get() -> i32 { return c.value; } } fn caller(o: *mut opaque Ops) -> i32 { o.inc(); return o.get(); }"; |
|
| 4993 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 4994 | - | try expectNoErrors(&result); |
|
| 6484 | + | let mut testArena491 = testArena(); |
|
| 6485 | + | let testStorage491: 'test491 = &mut testArena491 in { |
|
| 6486 | + | let mut a = testResolver(testStorage491); |
|
| 6487 | + | let program = "record Counter { value: i32 } trait Ops { fn (&mut Ops) inc(); fn (&Ops) get() -> i32; } instance Ops for Counter { fn (c: &mut Counter) inc() { set c.value = c.value + 1; } fn (c: &Counter) get() -> i32 { return c.value; } } fn caller(o: *mut opaque Ops) -> i32 { o.inc(); return o.get(); }"; |
|
| 6488 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6489 | + | try expectNoErrors(&result); |
|
| 6490 | + | } |
|
| 4995 | 6491 | } |
|
| 4996 | 6492 | ||
| 4997 | 6493 | /// Instance method body type must match the trait return type. |
|
| 4998 | 6494 | /// The trait declares `-> i32` but the body returns `bool`. |
|
| 4999 | 6495 | @test unsafe fn testResolveInstanceReturnTypeMismatch() throws (testing::TestError) { |
|
| 5000 | - | let mut a = testResolver(); |
|
| 5001 | - | let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> bool { return true; } }"; |
|
| 5002 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5003 | - | let err = try expectError(&result); |
|
| 5004 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5005 | - | else throw testing::TestError::Failed; |
|
| 6496 | + | let mut testArena492 = testArena(); |
|
| 6497 | + | let testStorage492: 'test492 = &mut testArena492 in { |
|
| 6498 | + | let mut a = testResolver(testStorage492); |
|
| 6499 | + | let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> bool { return true; } }"; |
|
| 6500 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6501 | + | let err = try expectError(&result); |
|
| 6502 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 6503 | + | else throw testing::TestError::Failed; |
|
| 6504 | + | } |
|
| 5006 | 6505 | } |
|
| 5007 | 6506 | ||
| 5008 | 6507 | /// Diamond supertrait inheritance: traits B and C both extend A. |
|
| 5009 | 6508 | /// Declaring them independently should work fine. |
|
| 5010 | 6509 | @test unsafe fn testResolveTraitDiamondSupertrait() throws (testing::TestError) { |
|
| 5011 | - | let mut a = testResolver(); |
|
| 5012 | - | let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; }"; |
|
| 5013 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5014 | - | try expectNoErrors(&result); |
|
| 6510 | + | let mut testArena493 = testArena(); |
|
| 6511 | + | let testStorage493: 'test493 = &mut testArena493 in { |
|
| 6512 | + | let mut a = testResolver(testStorage493); |
|
| 6513 | + | let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; }"; |
|
| 6514 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6515 | + | try expectNoErrors(&result); |
|
| 6516 | + | } |
|
| 5015 | 6517 | } |
|
| 5016 | 6518 | ||
| 5017 | 6519 | /// Diamond supertrait with a combined trait that would cause duplicate |
|
| 5018 | 6520 | /// method names should be detected. |
|
| 5019 | 6521 | @test unsafe fn testResolveTraitDiamondDuplicateMethod() throws (testing::TestError) { |
|
| 5020 | - | let mut a = testResolver(); |
|
| 5021 | - | let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; } trait D: B + C { fn (*D) i() -> i32; }"; |
|
| 5022 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5023 | - | // B inherits `f` from A, C inherits `f` from A. D: B + C sees duplicate `f`. |
|
| 5024 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("f")); |
|
| 6522 | + | let mut testArena494 = testArena(); |
|
| 6523 | + | let testStorage494: 'test494 = &mut testArena494 in { |
|
| 6524 | + | let mut a = testResolver(testStorage494); |
|
| 6525 | + | let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; } trait D: B + C { fn (*D) i() -> i32; }"; |
|
| 6526 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6527 | + | // B inherits `f` from A, C inherits `f` from A. D: B + C sees duplicate `f`. |
|
| 6528 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("f")); |
|
| 6529 | + | } |
|
| 5025 | 6530 | } |
|
| 5026 | 6531 | ||
| 5027 | 6532 | /// Supertrait instance must exist when declaring a combined trait instance. |
|
| 5028 | 6533 | @test unsafe fn testResolveInstanceMissingSupertraitInstance() throws (testing::TestError) { |
|
| 5029 | - | let mut a = testResolver(); |
|
| 5030 | - | let program = "trait Base { fn (*Base) f() -> i32; } trait Child: Base { fn (*Child) g() -> i32; } record R { x: i32 } instance Child for R { fn (r: *R) g() -> i32 { return r.x; } }"; |
|
| 5031 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5032 | - | try expectErrorKind(&result, super::ErrorKind::MissingSupertraitInstance("Base")); |
|
| 6534 | + | let mut testArena495 = testArena(); |
|
| 6535 | + | let testStorage495: 'test495 = &mut testArena495 in { |
|
| 6536 | + | let mut a = testResolver(testStorage495); |
|
| 6537 | + | let program = "trait Base { fn (*Base) f() -> i32; } trait Child: Base { fn (*Child) g() -> i32; } record R { x: i32 } instance Child for R { fn (r: *R) g() -> i32 { return r.x; } }"; |
|
| 6538 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6539 | + | try expectErrorKind(&result, super::ErrorKind::MissingSupertraitInstance("Base")); |
|
| 6540 | + | } |
|
| 5033 | 6541 | } |
|
| 5034 | 6542 | ||
| 5035 | 6543 | /// Instance method omits return type when the trait declares `-> i32`. |
|
| 5036 | 6544 | /// This is rejected -- the return type must be stated explicitly. |
|
| 5037 | 6545 | @test unsafe fn testResolveInstanceReturnTypeOmitted() throws (testing::TestError) { |
|
| 5038 | - | let mut a = testResolver(); |
|
| 5039 | - | let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() { } }"; |
|
| 5040 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5041 | - | let err = try expectError(&result); |
|
| 5042 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5043 | - | else throw testing::TestError::Failed; |
|
| 6546 | + | let mut testArena496 = testArena(); |
|
| 6547 | + | let testStorage496: 'test496 = &mut testArena496 in { |
|
| 6548 | + | let mut a = testResolver(testStorage496); |
|
| 6549 | + | let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() { } }"; |
|
| 6550 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6551 | + | let err = try expectError(&result); |
|
| 6552 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 6553 | + | else throw testing::TestError::Failed; |
|
| 6554 | + | } |
|
| 5044 | 6555 | } |
|
| 5045 | 6556 | ||
| 5046 | 6557 | /// Instance method declares throws but the trait method does not throw. |
|
| 5047 | 6558 | @test unsafe fn testResolveInstanceThrowsMismatchExtra() throws (testing::TestError) { |
|
| 5048 | - | let mut a = testResolver(); |
|
| 5049 | - | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> i32 throws (E) { return r.x; } }"; |
|
| 5050 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5051 | - | let err = try expectError(&result); |
|
| 5052 | - | let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind |
|
| 5053 | - | else throw testing::TestError::Failed; |
|
| 6559 | + | let mut testArena497 = testArena(); |
|
| 6560 | + | let testStorage497: 'test497 = &mut testArena497 in { |
|
| 6561 | + | let mut a = testResolver(testStorage497); |
|
| 6562 | + | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> i32 throws (E) { return r.x; } }"; |
|
| 6563 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6564 | + | let err = try expectError(&result); |
|
| 6565 | + | let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind |
|
| 6566 | + | else throw testing::TestError::Failed; |
|
| 6567 | + | } |
|
| 5054 | 6568 | } |
|
| 5055 | 6569 | ||
| 5056 | 6570 | /// Instance method declares a different throws type than the trait. |
|
| 5057 | 6571 | @test unsafe fn testResolveInstanceThrowsMismatchWrongType() throws (testing::TestError) { |
|
| 5058 | - | let mut a = testResolver(); |
|
| 5059 | - | let program = "union E1 { Fail } union E2 { Oops } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E1); } instance T for R { fn (r: *R) get() -> i32 throws (E2) { return r.x; } }"; |
|
| 5060 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5061 | - | let err = try expectError(&result); |
|
| 5062 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5063 | - | else throw testing::TestError::Failed; |
|
| 6572 | + | let mut testArena498 = testArena(); |
|
| 6573 | + | let testStorage498: 'test498 = &mut testArena498 in { |
|
| 6574 | + | let mut a = testResolver(testStorage498); |
|
| 6575 | + | let program = "union E1 { Fail } union E2 { Oops } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E1); } instance T for R { fn (r: *R) get() -> i32 throws (E2) { return r.x; } }"; |
|
| 6576 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6577 | + | let err = try expectError(&result); |
|
| 6578 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 6579 | + | else throw testing::TestError::Failed; |
|
| 6580 | + | } |
|
| 5064 | 6581 | } |
|
| 5065 | 6582 | ||
| 5066 | 6583 | /// Instance method omits throws clause when trait declares throws. |
|
| 5067 | 6584 | /// This is rejected -- the throws clause must match exactly. |
|
| 5068 | 6585 | @test unsafe fn testResolveInstanceThrowsOmitted() throws (testing::TestError) { |
|
| 5069 | - | let mut a = testResolver(); |
|
| 5070 | - | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 { throw E::Fail; return r.x; } }"; |
|
| 5071 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5072 | - | let err = try expectError(&result); |
|
| 5073 | - | let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind |
|
| 5074 | - | else throw testing::TestError::Failed; |
|
| 6586 | + | let mut testArena499 = testArena(); |
|
| 6587 | + | let testStorage499: 'test499 = &mut testArena499 in { |
|
| 6588 | + | let mut a = testResolver(testStorage499); |
|
| 6589 | + | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 { throw E::Fail; return r.x; } }"; |
|
| 6590 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6591 | + | let err = try expectError(&result); |
|
| 6592 | + | let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind |
|
| 6593 | + | else throw testing::TestError::Failed; |
|
| 6594 | + | } |
|
| 5075 | 6595 | } |
|
| 5076 | 6596 | ||
| 5077 | 6597 | /// Instance method correctly matches the trait's throws clause. |
|
| 5078 | 6598 | @test unsafe fn testResolveInstanceThrowsMatch() throws (testing::TestError) { |
|
| 5079 | - | let mut a = testResolver(); |
|
| 5080 | - | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 throws (E) { throw E::Fail; return r.x; } }"; |
|
| 5081 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5082 | - | try expectNoErrors(&result); |
|
| 6599 | + | let mut testArena500 = testArena(); |
|
| 6600 | + | let testStorage500: 'test500 = &mut testArena500 in { |
|
| 6601 | + | let mut a = testResolver(testStorage500); |
|
| 6602 | + | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 throws (E) { throw E::Fail; return r.x; } }"; |
|
| 6603 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6604 | + | try expectNoErrors(&result); |
|
| 6605 | + | } |
|
| 5083 | 6606 | } |
|
| 5084 | 6607 | ||
| 5085 | 6608 | // Constant expression folding tests ////////////////////////////////////////// |
|
| 5086 | 6609 | ||
| 5087 | 6610 | /// Resolve a program and verify that the constant at the given statement index |
|
| 5088 | 6611 | /// has the expected integer magnitude. |
|
| 5089 | 6612 | unsafe fn expectConstFold(program: *[u8], stmtIdx: u32, expected: u64) |
|
| 5090 | 6613 | throws (testing::TestError) |
|
| 5091 | 6614 | { |
|
| 5092 | - | let mut a = testResolver(); |
|
| 5093 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5094 | - | try expectNoErrors(&result); |
|
| 6615 | + | let mut testArena501 = testArena(); |
|
| 6616 | + | let testStorage501: 'test501 = &mut testArena501 in { |
|
| 6617 | + | let mut a = testResolver(testStorage501); |
|
| 6618 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6619 | + | try expectNoErrors(&result); |
|
| 5095 | 6620 | ||
| 5096 | - | let stmt = try getBlockStmt(result.root, stmtIdx); |
|
| 5097 | - | let sym = super::symbolFor(&a, stmt) |
|
| 5098 | - | else throw testing::TestError::Failed; |
|
| 5099 | - | let case super::SymbolData::Constant { value, .. } = sym.data |
|
| 5100 | - | else throw testing::TestError::Failed; |
|
| 5101 | - | let val = value else throw testing::TestError::Failed; |
|
| 5102 | - | let case super::ConstValue::Int(intVal) = val |
|
| 5103 | - | else throw testing::TestError::Failed; |
|
| 5104 | - | try testing::expect(intVal.magnitude == expected); |
|
| 6621 | + | let stmt = try getBlockStmt(result.root, stmtIdx); |
|
| 6622 | + | let sym = super::symbolFor(&a, stmt) |
|
| 6623 | + | else throw testing::TestError::Failed; |
|
| 6624 | + | let case super::SymbolData::Constant { value, .. } = sym.data |
|
| 6625 | + | else throw testing::TestError::Failed; |
|
| 6626 | + | let val = value else throw testing::TestError::Failed; |
|
| 6627 | + | let case super::ConstValue::Int(intVal) = val |
|
| 6628 | + | else throw testing::TestError::Failed; |
|
| 6629 | + | try testing::expect(intVal.magnitude == expected); |
|
| 6630 | + | } |
|
| 5105 | 6631 | } |
|
| 5106 | 6632 | ||
| 5107 | 6633 | /// Test arithmetic constant folding: add, sub, mul, div. |
|
| 5108 | 6634 | @test unsafe fn testConstExprArithmetic() throws (testing::TestError) { |
|
| 5109 | 6635 | try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B;", 2, 30); |
| 5130 | 6656 | try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B; constant D: i32 = C * 2;", 3, 60); |
|
| 5131 | 6657 | } |
|
| 5132 | 6658 | ||
| 5133 | 6659 | /// Test constant expression used as array size. |
|
| 5134 | 6660 | @test unsafe fn testConstExprAsArraySize() throws (testing::TestError) { |
|
| 5135 | - | let mut a = testResolver(); |
|
| 5136 | - | let program = "constant A: u32 = 2; constant B: u32 = 3; constant SIZE: u32 = A + B; constant ARR: [i32; SIZE] = [1, 2, 3, 4, 5];"; |
|
| 5137 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5138 | - | try expectNoErrors(&result); |
|
| 6661 | + | let mut testArena502 = testArena(); |
|
| 6662 | + | let testStorage502: 'test502 = &mut testArena502 in { |
|
| 6663 | + | let mut a = testResolver(testStorage502); |
|
| 6664 | + | let program = "constant A: u32 = 2; constant B: u32 = 3; constant SIZE: u32 = A + B; constant ARR: [i32; SIZE] = [1, 2, 3, 4, 5];"; |
|
| 6665 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6666 | + | try expectNoErrors(&result); |
|
| 5139 | 6667 | ||
| 5140 | - | let arrStmt = try getBlockStmt(result.root, 3); |
|
| 5141 | - | let sym = super::symbolFor(&a, arrStmt) |
|
| 5142 | - | else throw testing::TestError::Failed; |
|
| 5143 | - | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 5144 | - | else throw testing::TestError::Failed; |
|
| 5145 | - | try testing::expect(arrType.length == 5); |
|
| 6668 | + | let arrStmt = try getBlockStmt(result.root, 3); |
|
| 6669 | + | let sym = super::symbolFor(&a, arrStmt) |
|
| 6670 | + | else throw testing::TestError::Failed; |
|
| 6671 | + | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 6672 | + | else throw testing::TestError::Failed; |
|
| 6673 | + | try testing::expect(arrType.length == 5); |
|
| 6674 | + | } |
|
| 5146 | 6675 | } |
|
| 5147 | 6676 | ||
| 5148 | 6677 | /// Test cross-module constant expression: a constant in one module references |
|
| 5149 | 6678 | /// a constant from another module via scope access. |
|
| 5150 | 6679 | @test unsafe fn testCrossModuleConstExpr() throws (testing::TestError) { |
|
| 5151 | - | let mut a = testResolver(); |
|
| 5152 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 6680 | + | let mut testArena503 = testArena(); |
|
| 6681 | + | let testStorage503: 'test503 = &mut testArena503 in { |
|
| 6682 | + | let mut a = testResolver(testStorage503); |
|
| 6683 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5153 | 6684 | ||
| 5154 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 5155 | - | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant BASE: i32 = 100;", &mut arena); |
|
| 5156 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant DERIVED: i32 = consts::BASE + 50;", &mut arena); |
|
| 6685 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 6686 | + | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant BASE: i32 = 100;", &mut arena); |
|
| 6687 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant DERIVED: i32 = consts::BASE + 50;", &mut arena); |
|
| 5157 | 6688 | ||
| 5158 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5159 | - | try expectNoErrors(&result); |
|
| 6689 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 6690 | + | try expectNoErrors(&result); |
|
| 6691 | + | } |
|
| 5160 | 6692 | } |
|
| 5161 | 6693 | ||
| 5162 | 6694 | /// Test cross-module constant expression used as array size. |
|
| 5163 | 6695 | @test unsafe fn testCrossModuleConstExprArraySize() throws (testing::TestError) { |
|
| 5164 | - | let mut a = testResolver(); |
|
| 5165 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 6696 | + | let mut testArena504 = testArena(); |
|
| 6697 | + | let testStorage504: 'test504 = &mut testArena504 in { |
|
| 6698 | + | let mut a = testResolver(testStorage504); |
|
| 6699 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5166 | 6700 | ||
| 5167 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 5168 | - | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena); |
|
| 5169 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = [0; TOTAL];", &mut arena); |
|
| 6701 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 6702 | + | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena); |
|
| 6703 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = [0; TOTAL];", &mut arena); |
|
| 5170 | 6704 | ||
| 5171 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5172 | - | try expectNoErrors(&result); |
|
| 6705 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 6706 | + | try expectNoErrors(&result); |
|
| 6707 | + | } |
|
| 5173 | 6708 | } |
|
| 5174 | 6709 | ||
| 5175 | 6710 | /// Test that non-constant expressions in constant declarations are still rejected. |
|
| 5176 | 6711 | @test unsafe fn testConstExprNonConstRejected() throws (testing::TestError) { |
|
| 5177 | - | let mut a = testResolver(); |
|
| 5178 | - | let program = "fn value() -> i32 { return 1; } constant BAD: i32 = value() + 1;"; |
|
| 5179 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5180 | - | let err = try expectError(&result); |
|
| 5181 | - | let case super::ErrorKind::ConstExprRequired = err.kind |
|
| 5182 | - | else throw testing::TestError::Failed; |
|
| 6712 | + | let mut testArena505 = testArena(); |
|
| 6713 | + | let testStorage505: 'test505 = &mut testArena505 in { |
|
| 6714 | + | let mut a = testResolver(testStorage505); |
|
| 6715 | + | let program = "fn value() -> i32 { return 1; } constant BAD: i32 = value() + 1;"; |
|
| 6716 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6717 | + | let err = try expectError(&result); |
|
| 6718 | + | let case super::ErrorKind::ConstExprRequired = err.kind |
|
| 6719 | + | else throw testing::TestError::Failed; |
|
| 6720 | + | } |
|
| 5183 | 6721 | } |
|
| 5184 | 6722 | ||
| 5185 | 6723 | /// Test unary negation in constant expressions. |
|
| 5186 | 6724 | @test unsafe fn testConstExprUnaryNeg() throws (testing::TestError) { |
|
| 5187 | - | let mut a = testResolver(); |
|
| 5188 | - | let program = "constant A: i32 = 10; constant B: i32 = -A;"; |
|
| 5189 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5190 | - | try expectNoErrors(&result); |
|
| 6725 | + | let mut testArena506 = testArena(); |
|
| 6726 | + | let testStorage506: 'test506 = &mut testArena506 in { |
|
| 6727 | + | let mut a = testResolver(testStorage506); |
|
| 6728 | + | let program = "constant A: i32 = 10; constant B: i32 = -A;"; |
|
| 6729 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6730 | + | try expectNoErrors(&result); |
|
| 6731 | + | } |
|
| 5191 | 6732 | } |
|
| 5192 | 6733 | ||
| 5193 | 6734 | /// Test unary not in constant expressions. |
|
| 5194 | 6735 | @test unsafe fn testConstExprUnaryNot() throws (testing::TestError) { |
|
| 5195 | - | let mut a = testResolver(); |
|
| 5196 | - | let program = "constant A: bool = true; constant B: bool = not A;"; |
|
| 5197 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5198 | - | try expectNoErrors(&result); |
|
| 6736 | + | let mut testArena507 = testArena(); |
|
| 6737 | + | let testStorage507: 'test507 = &mut testArena507 in { |
|
| 6738 | + | let mut a = testResolver(testStorage507); |
|
| 6739 | + | let program = "constant A: bool = true; constant B: bool = not A;"; |
|
| 6740 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6741 | + | try expectNoErrors(&result); |
|
| 6742 | + | } |
|
| 5199 | 6743 | } |
|
| 5200 | 6744 | ||
| 5201 | 6745 | /// Test `as` casts in constant expressions: widening, narrowing, sign changes, chaining. |
|
| 5202 | 6746 | @test unsafe fn testConstExprCast() throws (testing::TestError) { |
|
| 5203 | 6747 | try expectConstFold("constant A: i32 = 42; constant B: u64 = A as u64;", 1, 42); |
| 5213 | 6757 | try expectConstFold("constant A: i32 = (2 as i32) * (3 + 4);", 0, 14); |
|
| 5214 | 6758 | } |
|
| 5215 | 6759 | ||
| 5216 | 6760 | /// Test `as` cast in constant expressions used as array size. |
|
| 5217 | 6761 | @test unsafe fn testConstExprCastAsArraySize() throws (testing::TestError) { |
|
| 5218 | - | let mut a = testResolver(); |
|
| 5219 | - | let program = "constant LEN: u64 = 4; constant SIZE: u32 = LEN as u32; constant ARR: [i32; SIZE] = [1, 2, 3, 4];"; |
|
| 5220 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5221 | - | try expectNoErrors(&result); |
|
| 6762 | + | let mut testArena508 = testArena(); |
|
| 6763 | + | let testStorage508: 'test508 = &mut testArena508 in { |
|
| 6764 | + | let mut a = testResolver(testStorage508); |
|
| 6765 | + | let program = "constant LEN: u64 = 4; constant SIZE: u32 = LEN as u32; constant ARR: [i32; SIZE] = [1, 2, 3, 4];"; |
|
| 6766 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6767 | + | try expectNoErrors(&result); |
|
| 5222 | 6768 | ||
| 5223 | - | let arrStmt = try getBlockStmt(result.root, 2); |
|
| 5224 | - | let sym = super::symbolFor(&a, arrStmt) |
|
| 5225 | - | else throw testing::TestError::Failed; |
|
| 5226 | - | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 5227 | - | else throw testing::TestError::Failed; |
|
| 5228 | - | try testing::expect(arrType.length == 4); |
|
| 6769 | + | let arrStmt = try getBlockStmt(result.root, 2); |
|
| 6770 | + | let sym = super::symbolFor(&a, arrStmt) |
|
| 6771 | + | else throw testing::TestError::Failed; |
|
| 6772 | + | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 6773 | + | else throw testing::TestError::Failed; |
|
| 6774 | + | try testing::expect(arrType.length == 4); |
|
| 6775 | + | } |
|
| 5229 | 6776 | } |
|
| 5230 | 6777 | ||
| 5231 | 6778 | /// Test unsuffixed integer literals in constant expressions. |
|
| 5232 | 6779 | @test unsafe fn testConstExprUnsuffixedLiterals() throws (testing::TestError) { |
|
| 5233 | 6780 | try expectConstFold("constant A: u32 = 4 * 4;", 0, 16); |
| 5237 | 6784 | try expectConstFold("constant F: i32 = -(3 + 4);", 0, 7); |
|
| 5238 | 6785 | } |
|
| 5239 | 6786 | ||
| 5240 | 6787 | /// References cannot escape through return types. |
|
| 5241 | 6788 | @test unsafe fn testRefReturnRejected() throws (testing::TestError) { |
|
| 5242 | - | let mut a = testResolver(); |
|
| 5243 | - | let program = "record Marker: Once {} fn bad(value: &u32) -> &u32 { return value; }"; |
|
| 5244 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5245 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 6789 | + | let mut testArena509 = testArena(); |
|
| 6790 | + | let testStorage509: 'test509 = &mut testArena509 in { |
|
| 6791 | + | let mut a = testResolver(testStorage509); |
|
| 6792 | + | let program = "record Marker: Once {} fn bad(value: &u32) -> &u32 { return value; }"; |
|
| 6793 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6794 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 6795 | + | } |
|
| 5246 | 6796 | } |
|
| 5247 | 6797 | ||
| 5248 | 6798 | /// Case-pattern fallbacks must terminate instead of synthesizing bindings. |
|
| 5249 | 6799 | @test unsafe fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) { |
|
| 5250 | - | let mut a = testResolver(); |
|
| 5251 | - | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }"; |
|
| 5252 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5253 | - | try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate); |
|
| 6800 | + | let mut testArena510 = testArena(); |
|
| 6801 | + | let testStorage510: 'test510 = &mut testArena510 in { |
|
| 6802 | + | let mut a = testResolver(testStorage510); |
|
| 6803 | + | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }"; |
|
| 6804 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6805 | + | try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate); |
|
| 6806 | + | } |
|
| 5254 | 6807 | } |
|
| 5255 | 6808 | ||
| 5256 | 6809 | /// Case bindings are unavailable on the pattern-failure path. |
|
| 5257 | 6810 | @test unsafe fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) { |
|
| 5258 | - | let mut a = testResolver(); |
|
| 5259 | - | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }"; |
|
| 5260 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5261 | - | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item")); |
|
| 6811 | + | let mut testArena511 = testArena(); |
|
| 6812 | + | let testStorage511: 'test511 = &mut testArena511 in { |
|
| 6813 | + | let mut a = testResolver(testStorage511); |
|
| 6814 | + | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }"; |
|
| 6815 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6816 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item")); |
|
| 6817 | + | } |
|
| 5262 | 6818 | } |
|
| 5263 | 6819 | ||
| 5264 | 6820 | /// Unsafe pointer dereference requires an unsafe declaration. |
|
| 5265 | 6821 | @test unsafe fn testUnsafePointerOperationRejected() throws (testing::TestError) { |
|
| 5266 | - | let mut a = testResolver(); |
|
| 5267 | - | let program = "record Marker: Once {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }"; |
|
| 5268 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5269 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 6822 | + | let mut testArena512 = testArena(); |
|
| 6823 | + | let testStorage512: 'test512 = &mut testArena512 in { |
|
| 6824 | + | let mut a = testResolver(testStorage512); |
|
| 6825 | + | let program = "record Marker: Once {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }"; |
|
| 6826 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6827 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 6828 | + | } |
|
| 5270 | 6829 | } |
|
| 5271 | 6830 | ||
| 5272 | 6831 | /// Unsafe pointers remain freely copyable inside an unsafe declaration. |
|
| 5273 | 6832 | @test unsafe fn testUnsafePointerOperationAllowed() throws (testing::TestError) { |
|
| 5274 | 6833 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; }"; |
|
| 5275 | 6834 | try expectAnalyzeOk(program); |
|
| 5276 | 6835 | } |
|
| 5277 | 6836 | ||
| 5278 | 6837 | /// Safe code cannot call a function that accepts unsafe operations. |
|
| 5279 | 6838 | @test unsafe fn testUnsafeFunctionCallRejected() throws (testing::TestError) { |
|
| 5280 | - | let mut a = testResolver(); |
|
| 5281 | - | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }"; |
|
| 5282 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5283 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 6839 | + | let mut testArena513 = testArena(); |
|
| 6840 | + | let testStorage513: 'test513 = &mut testArena513 in { |
|
| 6841 | + | let mut a = testResolver(testStorage513); |
|
| 6842 | + | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }"; |
|
| 6843 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6844 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 6845 | + | } |
|
| 5284 | 6846 | } |
|
| 5285 | 6847 | ||
| 5286 | 6848 | /// Unsafe function values retain their call-site safety requirement. |
|
| 5287 | 6849 | @test unsafe fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) { |
|
| 5288 | - | let mut a = testResolver(); |
|
| 5289 | - | let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }"; |
|
| 5290 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5291 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 6850 | + | let mut testArena514 = testArena(); |
|
| 6851 | + | let testStorage514: 'test514 = &mut testArena514 in { |
|
| 6852 | + | let mut a = testResolver(testStorage514); |
|
| 6853 | + | let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }"; |
|
| 6854 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6855 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 6856 | + | } |
|
| 5292 | 6857 | } |
|
| 5293 | 6858 | ||
| 5294 | 6859 | /// References cannot be embedded in aggregate fields. |
|
| 5295 | 6860 | @test unsafe fn testRefFieldRejected() throws (testing::TestError) { |
|
| 5296 | - | let mut a = testResolver(); |
|
| 5297 | - | let program = "record Marker: Once {} record Bad { value: &u32 }"; |
|
| 5298 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5299 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 6861 | + | let mut testArena515 = testArena(); |
|
| 6862 | + | let testStorage515: 'test515 = &mut testArena515 in { |
|
| 6863 | + | let mut a = testResolver(testStorage515); |
|
| 6864 | + | let program = "record Marker: Once {} record Bad { value: &u32 }"; |
|
| 6865 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6866 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 6867 | + | } |
|
| 5300 | 6868 | } |
|
| 5301 | 6869 | ||
| 5302 | 6870 | /// Trait methods may use reference receivers. |
|
| 5303 | 6871 | @test unsafe fn testTraitRefReceiver() throws (testing::TestError) { |
|
| 5304 | 6872 | let program = "record Marker: Once {} record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: &Value) get() -> i32 { return value.number; } } fn inspect(object: &opaque Read) -> i32 { return object.get(); } fn call(value: &Value) -> i32 { return inspect(value); }"; |
|
| 5305 | 6873 | try expectAnalyzeOk(program); |
|
| 5306 | 6874 | } |
|
| 5307 | 6875 | ||
| 5308 | 6876 | /// Trait implementations must preserve the receiver pointer class. |
|
| 5309 | 6877 | @test unsafe fn testTraitReceiverClassMismatch() throws (testing::TestError) { |
|
| 5310 | - | let mut a = testResolver(); |
|
| 5311 | - | let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }"; |
|
| 5312 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5313 | - | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
|
| 6878 | + | let mut testArena516 = testArena(); |
|
| 6879 | + | let testStorage516: 'test516 = &mut testArena516 in { |
|
| 6880 | + | let mut a = testResolver(testStorage516); |
|
| 6881 | + | let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }"; |
|
| 6882 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6883 | + | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
|
| 6884 | + | } |
|
| 5314 | 6885 | } |
|
| 5315 | 6886 | ||
| 5316 | 6887 | /// Unmarked composite values may be discarded. |
|
| 5317 | 6888 | @test unsafe fn testAffineCompositeMayBeDiscarded() throws (testing::TestError) { |
|
| 5318 | 6889 | let program = "record Value { number: u32 } fn run() { let value = Value { number: 1 }; }"; |
|
| 5319 | 6890 | try expectAnalyzeOk(program); |
|
| 5320 | 6891 | } |
|
| 5321 | 6892 | ||
| 5322 | 6893 | /// A by-value use moves an unmarked composite value. |
|
| 5323 | 6894 | @test unsafe fn testAffineCompositeUseAfterMoveRejected() throws (testing::TestError) { |
|
| 5324 | - | let mut a = testResolver(); |
|
| 5325 | - | let program = "record Value { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }"; |
|
| 5326 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5327 | - | try expectErrorKind(&result, super::ErrorKind::AffineUseAfterMove("value")); |
|
| 6895 | + | let mut testArena517 = testArena(); |
|
| 6896 | + | let testStorage517: 'test517 = &mut testArena517 in { |
|
| 6897 | + | let mut a = testResolver(testStorage517); |
|
| 6898 | + | let program = "record Value { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }"; |
|
| 6899 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6900 | + | try expectErrorKind(&result, super::ErrorKind::AffineUseAfterMove("value")); |
|
| 6901 | + | } |
|
| 5328 | 6902 | } |
|
| 5329 | 6903 | ||
| 5330 | 6904 | /// Affine values may move on only one branch when not used later. |
|
| 5331 | 6905 | @test unsafe fn testAffineConditionalMoveMayBeDiscarded() throws (testing::TestError) { |
|
| 5332 | 6906 | let program = "record Value { number: u32 } fn take(value: Value) {} fn run(condition: bool) { let value = Value { number: 1 }; if condition { take(value); } }"; |
| 5339 | 6913 | try expectAnalyzeOk(program); |
|
| 5340 | 6914 | } |
|
| 5341 | 6915 | ||
| 5342 | 6916 | /// A `Copy` composite may contain only copy values. |
|
| 5343 | 6917 | @test unsafe fn testCopyCompositeRejectsAffineField() throws (testing::TestError) { |
|
| 5344 | - | let mut a = testResolver(); |
|
| 5345 | - | let program = "record Inner { number: u32 } record Outer: Copy { inner: Inner }"; |
|
| 5346 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5347 | - | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
|
| 6918 | + | let mut testArena518 = testArena(); |
|
| 6919 | + | let testStorage518: 'test518 = &mut testArena518 in { |
|
| 6920 | + | let mut a = testResolver(testStorage518); |
|
| 6921 | + | let program = "record Inner { number: u32 } record Outer: Copy { inner: Inner }"; |
|
| 6922 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6923 | + | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
|
| 6924 | + | } |
|
| 5348 | 6925 | } |
|
| 5349 | 6926 | ||
| 5350 | 6927 | /// A composite cannot carry conflicting ownership markers. |
|
| 5351 | 6928 | @test unsafe fn testConflictingOwnershipMarkersRejected() throws (testing::TestError) { |
|
| 5352 | - | let mut a = testResolver(); |
|
| 5353 | - | let program = "record Value: Copy + Once { number: u32 }"; |
|
| 5354 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5355 | - | try expectErrorKind(&result, super::ErrorKind::ConflictingOwnershipMarkers); |
|
| 6929 | + | let mut testArena519 = testArena(); |
|
| 6930 | + | let testStorage519: 'test519 = &mut testArena519 in { |
|
| 6931 | + | let mut a = testResolver(testStorage519); |
|
| 6932 | + | let program = "record Value: Copy + Once { number: u32 }"; |
|
| 6933 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6934 | + | try expectErrorKind(&result, super::ErrorKind::ConflictingOwnershipMarkers); |
|
| 6935 | + | } |
|
| 5356 | 6936 | } |
|
| 5357 | 6937 | ||
| 5358 | 6938 | /// Linear composites still require one consuming use. |
|
| 5359 | 6939 | @test unsafe fn testLinearCompositeMustBeConsumed() throws (testing::TestError) { |
|
| 5360 | - | let mut a = testResolver(); |
|
| 5361 | - | let program = "record Token: Once { number: u32 } fn run() { let token = Token { number: 1 }; }"; |
|
| 5362 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5363 | - | try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token")); |
|
| 6940 | + | let mut testArena520 = testArena(); |
|
| 6941 | + | let testStorage520: 'test520 = &mut testArena520 in { |
|
| 6942 | + | let mut a = testResolver(testStorage520); |
|
| 6943 | + | let program = "record Token: Once { number: u32 } fn run() { let token = Token { number: 1 }; }"; |
|
| 6944 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6945 | + | try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token")); |
|
| 6946 | + | } |
|
| 5364 | 6947 | } |
|
| 5365 | 6948 | ||
| 5366 | 6949 | /// The compiler-known marker cannot be derived more than once. |
|
| 5367 | 6950 | @test unsafe fn testDuplicateOnceMarkerRejected() throws (testing::TestError) { |
|
| 5368 | - | let mut a = testResolver(); |
|
| 5369 | - | let program = "record Token: Once + Once { value: u32 }"; |
|
| 5370 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5371 | - | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Once")); |
|
| 6951 | + | let mut testArena521 = testArena(); |
|
| 6952 | + | let testStorage521: 'test521 = &mut testArena521 in { |
|
| 6953 | + | let mut a = testResolver(testStorage521); |
|
| 6954 | + | let program = "record Token: Once + Once { value: u32 }"; |
|
| 6955 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6956 | + | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Once")); |
|
| 6957 | + | } |
|
| 5372 | 6958 | } |
|
| 5373 | 6959 | ||
| 5374 | 6960 | /// Stack storage cannot produce a safe stored pointer or slice. |
|
| 5375 | 6961 | @test unsafe fn testStackPointerRejected() throws (testing::TestError) { |
|
| 5376 | 6962 | let programs = &[ |
| 5395 | 6981 | "record Cell { value: u32 } fn run(value: &Cell) -> *u32 { return &value.value; }", |
|
| 5396 | 6982 | "record Cell { value: *u32 } fn run(value: &u32) -> Cell { return Cell { value }; }", |
|
| 5397 | 6983 | "fn run(value: &u32) -> *[u32] { return @sliceOf(value, 1); }", |
|
| 5398 | 6984 | ]; |
|
| 5399 | 6985 | for program in programs { |
|
| 5400 | - | let mut resolver = testResolver(); |
|
| 5401 | - | let result = try resolveProgramStr(&mut resolver, program); |
|
| 5402 | - | let _ = try expectError(&result); |
|
| 6986 | + | let mut testArena522 = testArena(); |
|
| 6987 | + | let testStorage522: 'test522 = &mut testArena522 in { |
|
| 6988 | + | let mut resolver = testResolver(testStorage522); |
|
| 6989 | + | let result = try resolveProgramStr(&mut resolver, program); |
|
| 6990 | + | let _ = try expectError(&result); |
|
| 6991 | + | } |
|
| 5403 | 6992 | } |
|
| 5404 | 6993 | } |
|
| 5405 | 6994 | ||
| 5406 | 6995 | /// Stack values can be borrowed for a call. |
|
| 5407 | 6996 | @test unsafe fn testStackBorrowAllowed() throws (testing::TestError) { |
| 5433 | 7022 | "fn write(p: &mut [u32]) {} fn run(p: *unsafe mut [u32]) { write(p); }", |
|
| 5434 | 7023 | "trait Read { fn (&Read) get(); } fn read(p: &opaque Read) {} fn run(p: *unsafe opaque Read) { read(p); }", |
|
| 5435 | 7024 | "fn read(p: &u32) {} fn run(p: *unsafe u32) { unsafe { read(p); } read(p); }", |
|
| 5436 | 7025 | ]; |
|
| 5437 | 7026 | for program in programs { |
|
| 5438 | - | let mut a = testResolver(); |
|
| 5439 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5440 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7027 | + | let mut testArena523 = testArena(); |
|
| 7028 | + | let testStorage523: 'test523 = &mut testArena523 in { |
|
| 7029 | + | let mut a = testResolver(testStorage523); |
|
| 7030 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7031 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7032 | + | } |
|
| 5441 | 7033 | } |
|
| 5442 | 7034 | } |
|
| 5443 | 7035 | ||
| 5444 | 7036 | /// Raw borrows preserve mutability, storage type, and ownership constraints. |
|
| 5445 | 7037 | @test unsafe fn testImplicitRawBorrowPreservesTypes() throws (testing::TestError) { |
| 5451 | 7043 | "fn take(p: *u32) {} unsafe fn run(p: *unsafe u32) { take(p); }", |
|
| 5452 | 7044 | "fn take(p: *[u32]) {} unsafe fn run(p: *unsafe [u32]) { take(p); }", |
|
| 5453 | 7045 | "fn read(p: &*u32) {} unsafe fn run(p: *unsafe *unsafe u32) { read(p); }", |
|
| 5454 | 7046 | ]; |
|
| 5455 | 7047 | for program in programs { |
|
| 5456 | - | let mut a = testResolver(); |
|
| 5457 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5458 | - | let err = try expectError(&result); |
|
| 5459 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5460 | - | else throw testing::TestError::Failed; |
|
| 7048 | + | let mut testArena524 = testArena(); |
|
| 7049 | + | let testStorage524: 'test524 = &mut testArena524 in { |
|
| 7050 | + | let mut a = testResolver(testStorage524); |
|
| 7051 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7052 | + | let err = try expectError(&result); |
|
| 7053 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 7054 | + | else throw testing::TestError::Failed; |
|
| 7055 | + | } |
|
| 5461 | 7056 | } |
|
| 5462 | 7057 | } |
|
| 5463 | 7058 | ||
| 5464 | 7059 | /// Implicit raw borrows retain the call's exclusive-borrow checks. |
|
| 5465 | 7060 | @test unsafe fn testImplicitRawBorrowConflict() throws (testing::TestError) { |
|
| 5466 | - | let mut a = testResolver(); |
|
| 5467 | - | let result = try resolveProgramStr(&mut a, |
|
| 5468 | - | "fn useBoth(a: &mut u32, b: &u32) {} unsafe fn run(p: *unsafe mut u32) { useBoth(p, p); }"); |
|
| 5469 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p")); |
|
| 7061 | + | let mut testArena525 = testArena(); |
|
| 7062 | + | let testStorage525: 'test525 = &mut testArena525 in { |
|
| 7063 | + | let mut a = testResolver(testStorage525); |
|
| 7064 | + | let result = try resolveProgramStr(&mut a, |
|
| 7065 | + | "fn useBoth(a: &mut u32, b: &u32) {} unsafe fn run(p: *unsafe mut u32) { useBoth(p, p); }"); |
|
| 7066 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p")); |
|
| 7067 | + | } |
|
| 5470 | 7068 | } |
|
| 5471 | 7069 | ||
| 5472 | 7070 | /// Unsafe declarations can store raw pointers to stack values. |
|
| 5473 | 7071 | @test unsafe fn testUnsafeStackPointerAllowed() throws (testing::TestError) { |
|
| 5474 | 7072 | try expectAnalyzeOk("unsafe fn run() { let mut value: u32 = 0; let pointer: *unsafe mut u32 = &mut value as *unsafe mut u32; set *pointer = 7; }"); |
| 5491 | 7089 | } |
|
| 5492 | 7090 | ||
| 5493 | 7091 | /// References are rejected from every nested or storable type position. |
|
| 5494 | 7092 | @test unsafe fn testNestedRefPositionsRejected() throws (testing::TestError) { |
|
| 5495 | 7093 | { |
|
| 5496 | - | let mut a = testResolver(); |
|
| 5497 | - | let program = "record Marker: Once {} union Bad { Value(&u32) }"; |
|
| 5498 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5499 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7094 | + | let mut testArena526 = testArena(); |
|
| 7095 | + | let testStorage526: 'test526 = &mut testArena526 in { |
|
| 7096 | + | let mut a = testResolver(testStorage526); |
|
| 7097 | + | let program = "record Marker: Once {} union Bad { Value(&u32) }"; |
|
| 7098 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7099 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7100 | + | } |
|
| 5500 | 7101 | } { |
|
| 5501 | - | let mut a = testResolver(); |
|
| 5502 | - | let program = "record Marker: Once {} fn bad(value: ?&u32) {}"; |
|
| 5503 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5504 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7102 | + | let mut testArena527 = testArena(); |
|
| 7103 | + | let testStorage527: 'test527 = &mut testArena527 in { |
|
| 7104 | + | let mut a = testResolver(testStorage527); |
|
| 7105 | + | let program = "record Marker: Once {} fn bad(value: ?&u32) {}"; |
|
| 7106 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7107 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7108 | + | } |
|
| 5505 | 7109 | } { |
|
| 5506 | - | let mut a = testResolver(); |
|
| 5507 | - | let program = "record Marker: Once {} fn bad(value: [&u32; 1]) {}"; |
|
| 5508 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5509 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7110 | + | let mut testArena528 = testArena(); |
|
| 7111 | + | let testStorage528: 'test528 = &mut testArena528 in { |
|
| 7112 | + | let mut a = testResolver(testStorage528); |
|
| 7113 | + | let program = "record Marker: Once {} fn bad(value: [&u32; 1]) {}"; |
|
| 7114 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7115 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7116 | + | } |
|
| 5510 | 7117 | } { |
|
| 5511 | - | let mut a = testResolver(); |
|
| 5512 | - | let program = "record Marker: Once {} fn bad(value: *&u32) {}"; |
|
| 5513 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5514 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7118 | + | let mut testArena529 = testArena(); |
|
| 7119 | + | let testStorage529: 'test529 = &mut testArena529 in { |
|
| 7120 | + | let mut a = testResolver(testStorage529); |
|
| 7121 | + | let program = "record Marker: Once {} fn bad(value: *&u32) {}"; |
|
| 7122 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7123 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7124 | + | } |
|
| 5515 | 7125 | } { |
|
| 5516 | - | let mut a = testResolver(); |
|
| 5517 | - | let program = "record Marker: Once {} static BAD: &u32 = undefined;"; |
|
| 5518 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5519 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7126 | + | let mut testArena530 = testArena(); |
|
| 7127 | + | let testStorage530: 'test530 = &mut testArena530 in { |
|
| 7128 | + | let mut a = testResolver(testStorage530); |
|
| 7129 | + | let program = "record Marker: Once {} static BAD: &u32 = undefined;"; |
|
| 7130 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7131 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7132 | + | } |
|
| 5520 | 7133 | } { |
|
| 5521 | - | let mut a = testResolver(); |
|
| 5522 | - | let program = "record Marker: Once {} fn bad(callback: fn() -> &u32) {}"; |
|
| 5523 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5524 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7134 | + | let mut testArena531 = testArena(); |
|
| 7135 | + | let testStorage531: 'test531 = &mut testArena531 in { |
|
| 7136 | + | let mut a = testResolver(testStorage531); |
|
| 7137 | + | let program = "record Marker: Once {} fn bad(callback: fn() -> &u32) {}"; |
|
| 7138 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7139 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 7140 | + | } |
|
| 5525 | 7141 | } |
|
| 5526 | 7142 | } |
|
| 5527 | 7143 | ||
| 5528 | 7144 | /// Function pointer parameter references remain call-scoped and valid. |
|
| 5529 | 7145 | @test unsafe fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) { |
| 5532 | 7148 | } |
|
| 5533 | 7149 | ||
| 5534 | 7150 | /// Pointer and slice casts cannot change reference ownership. |
|
| 5535 | 7151 | @test unsafe fn testRefCastClassPreserved() throws (testing::TestError) { |
|
| 5536 | 7152 | { |
|
| 5537 | - | let mut a = testResolver(); |
|
| 5538 | - | let program = "record Marker: Once {} fn cast(value: &u32) { value as *u32; }"; |
|
| 5539 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5540 | - | let err = try expectError(&result); |
|
| 5541 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5542 | - | else throw testing::TestError::Failed; |
|
| 7153 | + | let mut testArena532 = testArena(); |
|
| 7154 | + | let testStorage532: 'test532 = &mut testArena532 in { |
|
| 7155 | + | let mut a = testResolver(testStorage532); |
|
| 7156 | + | let program = "record Marker: Once {} fn cast(value: &u32) { value as *u32; }"; |
|
| 7157 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7158 | + | let err = try expectError(&result); |
|
| 7159 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 7160 | + | else throw testing::TestError::Failed; |
|
| 7161 | + | } |
|
| 5543 | 7162 | } { |
|
| 5544 | - | let mut a = testResolver(); |
|
| 5545 | - | let program = "record Marker: Once {} fn cast(values: &[u32]) { values as *[u32]; }"; |
|
| 5546 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5547 | - | let err = try expectError(&result); |
|
| 5548 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5549 | - | else throw testing::TestError::Failed; |
|
| 7163 | + | let mut testArena533 = testArena(); |
|
| 7164 | + | let testStorage533: 'test533 = &mut testArena533 in { |
|
| 7165 | + | let mut a = testResolver(testStorage533); |
|
| 7166 | + | let program = "record Marker: Once {} fn cast(values: &[u32]) { values as *[u32]; }"; |
|
| 7167 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7168 | + | let err = try expectError(&result); |
|
| 7169 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 7170 | + | else throw testing::TestError::Failed; |
|
| 7171 | + | } |
|
| 5550 | 7172 | } |
|
| 5551 | 7173 | } |
|
| 5552 | 7174 | ||
| 5553 | 7175 | /// Every operation that interprets an unsafe address requires an unsafe declaration. |
|
| 5554 | 7176 | @test unsafe fn testUnsafePointerOperationsRejected() throws (testing::TestError) { |
|
| 5555 | 7177 | { |
|
| 5556 | - | let mut a = testResolver(); |
|
| 5557 | - | let program = "record Marker: Once {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }"; |
|
| 5558 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5559 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7178 | + | let mut testArena534 = testArena(); |
|
| 7179 | + | let testStorage534: 'test534 = &mut testArena534 in { |
|
| 7180 | + | let mut a = testResolver(testStorage534); |
|
| 7181 | + | let program = "record Marker: Once {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }"; |
|
| 7182 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7183 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7184 | + | } |
|
| 5560 | 7185 | } { |
|
| 5561 | - | let mut a = testResolver(); |
|
| 5562 | - | let program = "record Marker: Once {} fn compare(pointer: *unsafe u32) -> bool { return pointer == pointer; }"; |
|
| 5563 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5564 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7186 | + | let mut testArena535 = testArena(); |
|
| 7187 | + | let testStorage535: 'test535 = &mut testArena535 in { |
|
| 7188 | + | let mut a = testResolver(testStorage535); |
|
| 7189 | + | let program = "record Marker: Once {} fn compare(pointer: *unsafe u32) -> bool { return pointer == pointer; }"; |
|
| 7190 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7191 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7192 | + | } |
|
| 5565 | 7193 | } { |
|
| 5566 | - | let mut a = testResolver(); |
|
| 5567 | - | let program = "record Marker: Once {} fn offset(pointer: *unsafe u32) -> *unsafe u32 { return pointer + 1; }"; |
|
| 5568 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5569 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7194 | + | let mut testArena536 = testArena(); |
|
| 7195 | + | let testStorage536: 'test536 = &mut testArena536 in { |
|
| 7196 | + | let mut a = testResolver(testStorage536); |
|
| 7197 | + | let program = "record Marker: Once {} fn offset(pointer: *unsafe u32) -> *unsafe u32 { return pointer + 1; }"; |
|
| 7198 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7199 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7200 | + | } |
|
| 5570 | 7201 | } { |
|
| 5571 | - | let mut a = testResolver(); |
|
| 5572 | - | let program = "record Marker: Once {} fn index(values: *unsafe [u32]) -> u32 { return values[0]; }"; |
|
| 5573 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5574 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7202 | + | let mut testArena537 = testArena(); |
|
| 7203 | + | let testStorage537: 'test537 = &mut testArena537 in { |
|
| 7204 | + | let mut a = testResolver(testStorage537); |
|
| 7205 | + | let program = "record Marker: Once {} fn index(values: *unsafe [u32]) -> u32 { return values[0]; }"; |
|
| 7206 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7207 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7208 | + | } |
|
| 5575 | 7209 | } { |
|
| 5576 | - | let mut a = testResolver(); |
|
| 5577 | - | let program = "record Marker: Once {} record Cell { value: u32 } fn field(cell: *unsafe Cell) -> u32 { return cell.value; }"; |
|
| 5578 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5579 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7210 | + | let mut testArena538 = testArena(); |
|
| 7211 | + | let testStorage538: 'test538 = &mut testArena538 in { |
|
| 7212 | + | let mut a = testResolver(testStorage538); |
|
| 7213 | + | let program = "record Marker: Once {} record Cell { value: u32 } fn field(cell: *unsafe Cell) -> u32 { return cell.value; }"; |
|
| 7214 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7215 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7216 | + | } |
|
| 5580 | 7217 | } { |
|
| 5581 | - | let mut a = testResolver(); |
|
| 5582 | - | let program = "record Marker: Once {} fn store(pointer: *unsafe mut u32) { set *pointer = 1; }"; |
|
| 5583 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5584 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7218 | + | let mut testArena539 = testArena(); |
|
| 7219 | + | let testStorage539: 'test539 = &mut testArena539 in { |
|
| 7220 | + | let mut a = testResolver(testStorage539); |
|
| 7221 | + | let program = "record Marker: Once {} fn store(pointer: *unsafe mut u32) { set *pointer = 1; }"; |
|
| 7222 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7223 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7224 | + | } |
|
| 5585 | 7225 | } { |
|
| 5586 | - | let mut a = testResolver(); |
|
| 5587 | - | let program = "record Marker: Once {} fn cast() { let value: u32 = 0; let pointer = &value as *unsafe u32; }"; |
|
| 5588 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5589 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7226 | + | let mut testArena540 = testArena(); |
|
| 7227 | + | let testStorage540: 'test540 = &mut testArena540 in { |
|
| 7228 | + | let mut a = testResolver(testStorage540); |
|
| 7229 | + | let program = "record Marker: Once {} fn cast() { let value: u32 = 0; let pointer = &value as *unsafe u32; }"; |
|
| 7230 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7231 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7232 | + | } |
|
| 5590 | 7233 | } |
|
| 5591 | 7234 | } |
|
| 5592 | 7235 | ||
| 5593 | 7236 | /// Pointer offsets require an unsafe function for either operand order. |
|
| 5594 | 7237 | @test unsafe fn testPointerArithmeticRequiresUnsafe() throws (testing::TestError) { |
| 5598 | 7241 | "fn run(p: *u8) -> *u8 { return p - 1; }", |
|
| 5599 | 7242 | "fn run(input: *mut u8) { let mut p = input; set p += 1; }", |
|
| 5600 | 7243 | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let p = &DATA[0]; return *(p + 1); }", |
|
| 5601 | 7244 | ]; |
|
| 5602 | 7245 | for program in programs { |
|
| 5603 | - | let mut a = testResolver(); |
|
| 5604 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5605 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7246 | + | let mut testArena541 = testArena(); |
|
| 7247 | + | let testStorage541: 'test541 = &mut testArena541 in { |
|
| 7248 | + | let mut a = testResolver(testStorage541); |
|
| 7249 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7250 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7251 | + | } |
|
| 5606 | 7252 | } |
|
| 5607 | 7253 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return p + 1; }"); |
|
| 5608 | 7254 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return 1 + p; }"); |
|
| 5609 | 7255 | try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut u8 { return p - 1; }"); |
|
| 5610 | 7256 | try expectAnalyzeOk("fn run(value: u32) -> u32 { return value + 1; }"); |
| 5622 | 7268 | "fn run(s: *[opaque]) -> *[u64] { return s as *[u64]; }", |
|
| 5623 | 7269 | "fn run(s: &mut [u64]) { let _ = s as &mut [u8]; }", |
|
| 5624 | 7270 | "static DATA: [u8; 1] = [42]; fn run() -> u64 { return *(&DATA[0] as *u64); }", |
|
| 5625 | 7271 | ]; |
|
| 5626 | 7272 | for program in programs { |
|
| 5627 | - | let mut a = testResolver(); |
|
| 5628 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5629 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7273 | + | let mut testArena542 = testArena(); |
|
| 7274 | + | let testStorage542: 'test542 = &mut testArena542 in { |
|
| 7275 | + | let mut a = testResolver(testStorage542); |
|
| 7276 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7277 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7278 | + | } |
|
| 5630 | 7279 | } |
|
| 5631 | 7280 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u64 { return p as *u64; }"); |
|
| 5632 | 7281 | try expectAnalyzeOk("unsafe fn run(p: &u8) -> u64 { return *(p as &u64); }"); |
|
| 5633 | 7282 | try expectAnalyzeOk("unsafe fn run(s: *[u8]) -> *[u64] { return s as *[u64]; }"); |
|
| 5634 | 7283 | try expectAnalyzeOk("fn run(p: *u8) -> *u8 { return p as *u8; }"); |
| 5645 | 7294 | "fn run(p: &u8) -> u8 { return @sliceOf(p, 100)[99]; }", |
|
| 5646 | 7295 | "fn run(p: &mut u8) { set @sliceOf(p, 100)[99] = 1; }", |
|
| 5647 | 7296 | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let s = @sliceOf(&DATA[0], 100); return s[99]; }", |
|
| 5648 | 7297 | ]; |
|
| 5649 | 7298 | for program in programs { |
|
| 5650 | - | let mut a = testResolver(); |
|
| 5651 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5652 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7299 | + | let mut testArena543 = testArena(); |
|
| 7300 | + | let testStorage543: 'test543 = &mut testArena543 in { |
|
| 7301 | + | let mut a = testResolver(testStorage543); |
|
| 7302 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7303 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7304 | + | } |
|
| 5653 | 7305 | } |
|
| 5654 | 7306 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *[u8] { return @sliceOf(p, 100); }"); |
|
| 5655 | 7307 | try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut [u8] { return @sliceOf(p, 0, 100); }"); |
|
| 5656 | 7308 | try expectAnalyzeOk("unsafe fn run(p: &u8) -> u8 { return @sliceOf(p, 1)[0]; }"); |
|
| 5657 | 7309 | try expectAnalyzeOk("fn run(s: *[u8]) -> *[u8] { return &s[..]; }"); |
| 5669 | 7321 | "fn change(p: &mut *u8, q: *u8) { set *p = q; } fn run(q: *u8) { let mut s: *[u8] = &[]; change(&mut s.ptr, q); }", |
|
| 5670 | 7322 | "fn run(s: &mut *[u8]) { set s.len = 100; }", |
|
| 5671 | 7323 | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let mut s = &DATA[..]; set s.len = 100; return s[99]; }", |
|
| 5672 | 7324 | ]; |
|
| 5673 | 7325 | for program in programs { |
|
| 5674 | - | let mut a = testResolver(); |
|
| 5675 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5676 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7326 | + | let mut testArena544 = testArena(); |
|
| 7327 | + | let testStorage544: 'test544 = &mut testArena544 in { |
|
| 7328 | + | let mut a = testResolver(testStorage544); |
|
| 7329 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7330 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7331 | + | } |
|
| 5677 | 7332 | } |
|
| 5678 | 7333 | try expectAnalyzeOk("unsafe fn run(s: *mut [u8]) { set s.len = 0; set s.cap = 0; }"); |
|
| 5679 | 7334 | try expectAnalyzeOk("unsafe fn run(s: *mut [u8], p: *mut u8) { set s.ptr = p; }"); |
|
| 5680 | 7335 | try expectAnalyzeOk("fn change(n: &mut u32) { set *n = 0; } unsafe fn run(s: *mut [u8]) { change(&mut s.len); }"); |
|
| 5681 | 7336 | try expectAnalyzeOk("fn run(s: *mut [u8]) { set s[0] = 1; }"); |
| 5697 | 7352 | "fn run(p: *u8) -> *u8 { unsafe { let q = p + 1; } return p + 1; }", |
|
| 5698 | 7353 | "fn run(p: *u8) -> *u8 { unsafe { unsafe {} } return p + 1; }", |
|
| 5699 | 7354 | "unsafe fn first(p: *u8) -> *u8 { return p + 1; } fn second(p: *u8) -> *u8 { return p + 1; }", |
|
| 5700 | 7355 | ]; |
|
| 5701 | 7356 | for program in programs { |
|
| 5702 | - | let mut a = testResolver(); |
|
| 5703 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5704 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7357 | + | let mut testArena545 = testArena(); |
|
| 7358 | + | let testStorage545: 'test545 = &mut testArena545 in { |
|
| 7359 | + | let mut a = testResolver(testStorage545); |
|
| 7360 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7361 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7362 | + | } |
|
| 7363 | + | } |
|
| 7364 | + | let mut testArena546 = testArena(); |
|
| 7365 | + | let testStorage546: 'test546 = &mut testArena546 in { |
|
| 7366 | + | let mut a = testResolver(testStorage546); |
|
| 7367 | + | let result = try resolveProgramStr(&mut a, "unsafe fn act() {} fn run() { unsafe { act(); } act(); }"); |
|
| 7368 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 5705 | 7369 | } |
|
| 5706 | - | let mut a = testResolver(); |
|
| 5707 | - | let result = try resolveProgramStr(&mut a, "unsafe fn act() {} fn run() { unsafe { act(); } act(); }"); |
|
| 5708 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 5709 | 7370 | } |
|
| 5710 | 7371 | ||
| 5711 | 7372 | /// Resolution errors do not extend an unsafe block's permission. |
|
| 5712 | 7373 | @test unsafe fn testUnsafeBlockErrorRestoresContext() throws (testing::TestError) { |
|
| 5713 | - | let mut a = testResolver(); |
|
| 5714 | - | let result = try resolveProgramStr(&mut a, "fn run(p: *u8) { unsafe { missing; } let q = p + 1; }"); |
|
| 5715 | - | let mut found = false; |
|
| 5716 | - | for err in result.diagnostics.errors { |
|
| 5717 | - | if let case super::ErrorKind::UnsafeOperation = err.kind { |
|
| 5718 | - | set found = true; |
|
| 7374 | + | let mut testArena547 = testArena(); |
|
| 7375 | + | let testStorage547: 'test547 = &mut testArena547 in { |
|
| 7376 | + | let mut a = testResolver(testStorage547); |
|
| 7377 | + | let result = try resolveProgramStr(&mut a, "fn run(p: *u8) { unsafe { missing; } let q = p + 1; }"); |
|
| 7378 | + | let mut found = false; |
|
| 7379 | + | for err in result.diagnostics.errors { |
|
| 7380 | + | if let case super::ErrorKind::UnsafeOperation = err.kind { |
|
| 7381 | + | set found = true; |
|
| 7382 | + | } |
|
| 5719 | 7383 | } |
|
| 7384 | + | try testing::expect(found); |
|
| 5720 | 7385 | } |
|
| 5721 | - | try testing::expect(found); |
|
| 5722 | 7386 | } |
|
| 5723 | 7387 | ||
| 5724 | 7388 | /// Unsafe blocks preserve reference lifetime checks. |
|
| 5725 | 7389 | @test unsafe fn testUnsafeBlockPreservesReferences() throws (testing::TestError) { |
|
| 5726 | - | let mut a = testResolver(); |
|
| 5727 | - | let result = try resolveProgramStr(&mut a, "fn run() { let mut n: u32 = 1; let p = &n; unsafe { set n = 2; } }"); |
|
| 5728 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n")); |
|
| 7390 | + | let mut testArena548 = testArena(); |
|
| 7391 | + | let testStorage548: 'test548 = &mut testArena548 in { |
|
| 7392 | + | let mut a = testResolver(testStorage548); |
|
| 7393 | + | let result = try resolveProgramStr(&mut a, "fn run() { let mut n: u32 = 1; let p = &n; unsafe { set n = 2; } }"); |
|
| 7394 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n")); |
|
| 7395 | + | } |
|
| 5729 | 7396 | } |
|
| 5730 | 7397 | ||
| 5731 | 7398 | /// Slice pointer access requires an unsafe context for every slice class. |
|
| 5732 | 7399 | @test unsafe fn testSlicePointerRequiresUnsafe() throws (testing::TestError) { |
|
| 5733 | 7400 | let programs = &[ |
| 5738 | 7405 | "fn run(s: &*[u8]) -> *u8 { return s.ptr; }", |
|
| 5739 | 7406 | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let s = &DATA[1..]; return *s.ptr; }", |
|
| 5740 | 7407 | "fn run(s: *[u8]) -> u64 { return s.ptr as u64; }", |
|
| 5741 | 7408 | ]; |
|
| 5742 | 7409 | for program in programs { |
|
| 5743 | - | let mut a = testResolver(); |
|
| 5744 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5745 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7410 | + | let mut testArena549 = testArena(); |
|
| 7411 | + | let testStorage549: 'test549 = &mut testArena549 in { |
|
| 7412 | + | let mut a = testResolver(testStorage549); |
|
| 7413 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7414 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7415 | + | } |
|
| 5746 | 7416 | } |
|
| 5747 | 7417 | try expectAnalyzeOk("unsafe fn run(s: *[u8]) -> *u8 { return s.ptr; }"); |
|
| 5748 | 7418 | try expectAnalyzeOk("fn run(s: *[u8]) -> u64 { unsafe { return s.ptr as u64; } }"); |
|
| 5749 | 7419 | try expectAnalyzeOk("fn run(s: *[u8]) -> *u8 { return &s[0]; }"); |
|
| 5750 | 7420 | try expectAnalyzeOk("fn run(s: *[u8]) -> u32 { return s.len + s.cap; }"); |
| 5761 | 7431 | "unsafe static DATA: [u8; 1] = [42]; fn run() -> u8 { return DATA[0]; }", |
|
| 5762 | 7432 | "unsafe static DATA: [u8; 1] = [42]; fn run() -> u32 { return DATA.len; }", |
|
| 5763 | 7433 | "record R { value: u32 } unsafe static DATA: R = R { value: 7 }; fn run() -> u32 { return DATA.value; }", |
|
| 5764 | 7434 | ]; |
|
| 5765 | 7435 | for program in programs { |
|
| 5766 | - | let mut a = testResolver(); |
|
| 5767 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5768 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7436 | + | let mut testArena550 = testArena(); |
|
| 7437 | + | let testStorage550: 'test550 = &mut testArena550 in { |
|
| 7438 | + | let mut a = testResolver(testStorage550); |
|
| 7439 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7440 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7441 | + | } |
|
| 5769 | 7442 | } |
|
| 5770 | 7443 | } |
|
| 5771 | 7444 | ||
| 5772 | 7445 | /// Unsafe blocks and functions can access unsafe statics. |
|
| 5773 | 7446 | @test unsafe fn testUnsafeStaticAccessAllowed() throws (testing::TestError) { |
| 5783 | 7456 | let programs = &[ |
|
| 5784 | 7457 | "use root::storage; fn run() -> u32 { return storage::VALUE; }", |
|
| 5785 | 7458 | "use root::storage::*; fn run() -> u32 { return VALUE; }", |
|
| 5786 | 7459 | ]; |
|
| 5787 | 7460 | for program in programs { |
|
| 5788 | - | let mut a = testResolver(); |
|
| 5789 | - | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5790 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod storage; mod app;", &mut arena); |
|
| 5791 | - | let _ = try registerModule(&mut MODULE_GRAPH, rootId, "storage", "export unsafe static VALUE: u32 = 7;", &mut arena); |
|
| 5792 | - | let _ = try registerModule(&mut MODULE_GRAPH, rootId, "app", program, &mut arena); |
|
| 5793 | - | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5794 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7461 | + | let mut testArena551 = testArena(); |
|
| 7462 | + | let testStorage551: 'test551 = &mut testArena551 in { |
|
| 7463 | + | let mut a = testResolver(testStorage551); |
|
| 7464 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 7465 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod storage; mod app;", &mut arena); |
|
| 7466 | + | let _ = try registerModule(&mut MODULE_GRAPH, rootId, "storage", "export unsafe static VALUE: u32 = 7;", &mut arena); |
|
| 7467 | + | let _ = try registerModule(&mut MODULE_GRAPH, rootId, "app", program, &mut arena); |
|
| 7468 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 7469 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7470 | + | } |
|
| 5795 | 7471 | } |
|
| 5796 | 7472 | } |
|
| 5797 | 7473 | ||
| 5798 | 7474 | /// An invalid unsafe static initializer does not grant permission to other initializers. |
|
| 5799 | 7475 | @test unsafe fn testUnsafeStaticInitializerRestoresContext() throws (testing::TestError) { |
|
| 5800 | - | let mut a = testResolver(); |
|
| 5801 | - | let result = try resolveProgramStr(&mut a, "unsafe static BAD: u32 = true; static DATA: *[u64] = &[1 as u8] as *[u64];"); |
|
| 5802 | - | let _ = try expectError(&result); |
|
| 5803 | - | try testing::expect(not a.inUnsafeContext); |
|
| 7476 | + | let mut testArena552 = testArena(); |
|
| 7477 | + | let testStorage552: 'test552 = &mut testArena552 in { |
|
| 7478 | + | let mut a = testResolver(testStorage552); |
|
| 7479 | + | let result = try resolveProgramStr(&mut a, "unsafe static BAD: u32 = true; static DATA: *[u64] = &[1 as u8] as *[u64];"); |
|
| 7480 | + | let _ = try expectError(&result); |
|
| 7481 | + | try testing::expect(not a.inUnsafeContext); |
|
| 5804 | 7482 | ||
| 5805 | - | let mut b = testResolver(); |
|
| 5806 | - | let next = try resolveProgramStr(&mut b, "constant BYTES: *[u8] = \"x\"; unsafe static VALUE: u32 = 7; static DATA: *[u64] = BYTES as *[u64];"); |
|
| 5807 | - | try expectErrorKind(&next, super::ErrorKind::UnsafeOperation); |
|
| 7483 | + | let mut testArena553 = testArena(); |
|
| 7484 | + | let testStorage553: 'test553 = &mut testArena553 in { |
|
| 7485 | + | let mut b = testResolver(testStorage553); |
|
| 7486 | + | let next = try resolveProgramStr(&mut b, "constant BYTES: *[u8] = \"x\"; unsafe static VALUE: u32 = 7; static DATA: *[u64] = BYTES as *[u64];"); |
|
| 7487 | + | try expectErrorKind(&next, super::ErrorKind::UnsafeOperation); |
|
| 7488 | + | } |
|
| 7489 | + | } |
|
| 5808 | 7490 | } |
|
| 5809 | 7491 | ||
| 5810 | 7492 | /// Uninitialized values require an explicit unsafe context in every value position. |
|
| 5811 | 7493 | @test unsafe fn testUndefinedRequiresUnsafe() throws (testing::TestError) { |
|
| 5812 | 7494 | let programs = &[ |
| 5823 | 7505 | "constant P: *u8 = undefined;", |
|
| 5824 | 7506 | "static DATA: [u8; 4] = undefined;", |
|
| 5825 | 7507 | "record R: Copy { p: *u8 } static VALUE: R = R { p: undefined };", |
|
| 5826 | 7508 | ]; |
|
| 5827 | 7509 | for program in programs { |
|
| 5828 | - | let mut a = testResolver(); |
|
| 5829 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5830 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7510 | + | let mut testArena554 = testArena(); |
|
| 7511 | + | let testStorage554: 'test554 = &mut testArena554 in { |
|
| 7512 | + | let mut a = testResolver(testStorage554); |
|
| 7513 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7514 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7515 | + | } |
|
| 5831 | 7516 | } |
|
| 5832 | 7517 | try expectAnalyzeOk("unsafe fn run() { let p: *u8 = undefined; }"); |
|
| 5833 | 7518 | try expectAnalyzeOk("fn run() -> u32 { unsafe { let mut n: u32 = undefined; set n = 7; return n; } }"); |
|
| 5834 | 7519 | try expectAnalyzeOk("unsafe static P: *u8 = undefined;"); |
|
| 5835 | 7520 | try expectAnalyzeOk("record R: Copy { p: *u8 } unsafe static VALUE: R = R { p: undefined };"); |
| 5837 | 7522 | try expectAnalyzeOk("static DATA: [u8; 4] = [0; 4];"); |
|
| 5838 | 7523 | } |
|
| 5839 | 7524 | ||
| 5840 | 7525 | /// Unsafe initialization does not grant permission to subsequent safe expressions. |
|
| 5841 | 7526 | @test unsafe fn testUndefinedContextRestored() throws (testing::TestError) { |
|
| 5842 | - | let mut a = testResolver(); |
|
| 5843 | - | let result = try resolveProgramStr(&mut a, "fn run() { unsafe { let p: *u8 = undefined; } let q: *u8 = undefined; }"); |
|
| 5844 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5845 | - | let mut b = testResolver(); |
|
| 5846 | - | let next = try resolveProgramStr(&mut b, "unsafe static P: *u8 = undefined; static Q: *u8 = undefined;"); |
|
| 5847 | - | try expectErrorKind(&next, super::ErrorKind::UnsafeOperation); |
|
| 7527 | + | let mut testArena555 = testArena(); |
|
| 7528 | + | let testStorage555: 'test555 = &mut testArena555 in { |
|
| 7529 | + | let mut a = testResolver(testStorage555); |
|
| 7530 | + | let result = try resolveProgramStr(&mut a, "fn run() { unsafe { let p: *u8 = undefined; } let q: *u8 = undefined; }"); |
|
| 7531 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7532 | + | let mut testArena556 = testArena(); |
|
| 7533 | + | let testStorage556: 'test556 = &mut testArena556 in { |
|
| 7534 | + | let mut b = testResolver(testStorage556); |
|
| 7535 | + | let next = try resolveProgramStr(&mut b, "unsafe static P: *u8 = undefined; static Q: *u8 = undefined;"); |
|
| 7536 | + | try expectErrorKind(&next, super::ErrorKind::UnsafeOperation); |
|
| 7537 | + | } |
|
| 7538 | + | } |
|
| 5848 | 7539 | } |
|
| 5849 | 7540 | ||
| 5850 | 7541 | /// Unsafe declarations may compose unsafe operations and calls. |
|
| 5851 | 7542 | @test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) { |
|
| 5852 | 7543 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }"; |
| 5859 | 7550 | try expectAnalyzeOk(program); |
|
| 5860 | 7551 | } |
|
| 5861 | 7552 | ||
| 5862 | 7553 | /// Dropping a reference to an unsafe pointer cannot add mutability. |
|
| 5863 | 7554 | @test unsafe fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) { |
|
| 5864 | - | let mut a = testResolver(); |
|
| 5865 | - | let program = "record Marker: Once {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }"; |
|
| 5866 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5867 | - | let err = try expectError(&result); |
|
| 5868 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5869 | - | else throw testing::TestError::Failed; |
|
| 7555 | + | let mut testArena557 = testArena(); |
|
| 7556 | + | let testStorage557: 'test557 = &mut testArena557 in { |
|
| 7557 | + | let mut a = testResolver(testStorage557); |
|
| 7558 | + | let program = "record Marker: Once {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }"; |
|
| 7559 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7560 | + | let err = try expectError(&result); |
|
| 7561 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 7562 | + | else throw testing::TestError::Failed; |
|
| 7563 | + | } |
|
| 5870 | 7564 | } |
|
| 5871 | 7565 | ||
| 5872 | 7566 | /// Recursive cast validation cannot hide a checked-to-unsafe transition. |
|
| 5873 | 7567 | @test unsafe fn testNestedUnsafePointerCastRejected() throws (testing::TestError) { |
|
| 5874 | - | let mut a = testResolver(); |
|
| 5875 | - | let program = "record Marker: Once {} fn run(value: **u32) { value as **unsafe u32; }"; |
|
| 5876 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5877 | - | let err = try expectError(&result); |
|
| 5878 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5879 | - | else throw testing::TestError::Failed; |
|
| 7568 | + | let mut testArena558 = testArena(); |
|
| 7569 | + | let testStorage558: 'test558 = &mut testArena558 in { |
|
| 7570 | + | let mut a = testResolver(testStorage558); |
|
| 7571 | + | let program = "record Marker: Once {} fn run(value: **u32) { value as **unsafe u32; }"; |
|
| 7572 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7573 | + | let err = try expectError(&result); |
|
| 7574 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 7575 | + | else throw testing::TestError::Failed; |
|
| 7576 | + | } |
|
| 5880 | 7577 | } |
|
| 5881 | 7578 | ||
| 5882 | 7579 | /// Unsafe code may drop a checked slice reference to an unsafe slice. |
|
| 5883 | 7580 | @test unsafe fn testUnsafeSliceFromReference() throws (testing::TestError) { |
|
| 5884 | 7581 | let program = "record Marker: Once {} unsafe fn run(values: &[u32]) { let raw: *unsafe [u32] = values as *unsafe [u32]; }"; |
|
| 5885 | 7582 | try expectAnalyzeOk(program); |
|
| 5886 | 7583 | } |
|
| 5887 | 7584 | ||
| 5888 | 7585 | /// Slice casts cannot add mutability. |
|
| 5889 | 7586 | @test unsafe fn testSliceCastCannotAddMutability() throws (testing::TestError) { |
|
| 5890 | - | let mut a = testResolver(); |
|
| 5891 | - | let program = "record Marker: Once {} fn run(values: &[u32]) { values as &mut [u32]; }"; |
|
| 5892 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5893 | - | let err = try expectError(&result); |
|
| 5894 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5895 | - | else throw testing::TestError::Failed; |
|
| 7587 | + | let mut testArena559 = testArena(); |
|
| 7588 | + | let testStorage559: 'test559 = &mut testArena559 in { |
|
| 7589 | + | let mut a = testResolver(testStorage559); |
|
| 7590 | + | let program = "record Marker: Once {} fn run(values: &[u32]) { values as &mut [u32]; }"; |
|
| 7591 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7592 | + | let err = try expectError(&result); |
|
| 7593 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 7594 | + | else throw testing::TestError::Failed; |
|
| 7595 | + | } |
|
| 5896 | 7596 | } |
|
| 5897 | 7597 | ||
| 5898 | 7598 | /// Mutable unsafe receivers do not create checked exclusive loans. |
|
| 5899 | 7599 | @test unsafe fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) { |
|
| 5900 | 7600 | let program = "record Marker: Once {} record Value { number: u32 } unsafe fn (value: *unsafe mut Value) update(other: *unsafe mut Value) {} unsafe fn run(value: *unsafe mut Value) { value.update(value); }"; |
| 5907 | 7607 | try expectAnalyzeOk(program); |
|
| 5908 | 7608 | } |
|
| 5909 | 7609 | ||
| 5910 | 7610 | /// Unsafe instance methods cannot implement safe trait contracts. |
|
| 5911 | 7611 | @test unsafe fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) { |
|
| 5912 | - | let mut a = testResolver(); |
|
| 5913 | - | let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }"; |
|
| 5914 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5915 | - | try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch); |
|
| 7612 | + | let mut testArena560 = testArena(); |
|
| 7613 | + | let testStorage560: 'test560 = &mut testArena560 in { |
|
| 7614 | + | let mut a = testResolver(testStorage560); |
|
| 7615 | + | let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }"; |
|
| 7616 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7617 | + | try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch); |
|
| 7618 | + | } |
|
| 5916 | 7619 | } |
|
| 5917 | 7620 | ||
| 5918 | 7621 | /// Unsafe trait methods retain their call-site requirement through dispatch. |
|
| 5919 | 7622 | @test unsafe fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) { |
|
| 5920 | - | let mut a = testResolver(); |
|
| 5921 | - | let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (&Read) get() -> u32; } instance Read for Value { unsafe fn (value: &Value) get() -> u32 { return value.number; } } fn inspect(object: &opaque Read) -> u32 { return object.get(); }"; |
|
| 5922 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5923 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 7623 | + | let mut testArena561 = testArena(); |
|
| 7624 | + | let testStorage561: 'test561 = &mut testArena561 in { |
|
| 7625 | + | let mut a = testResolver(testStorage561); |
|
| 7626 | + | let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (&Read) get() -> u32; } instance Read for Value { unsafe fn (value: &Value) get() -> u32 { return value.number; } } fn inspect(object: &opaque Read) -> u32 { return object.get(); }"; |
|
| 7627 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7628 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 7629 | + | } |
|
| 5924 | 7630 | } |
|
| 5925 | 7631 | ||
| 5926 | 7632 | /// An unsafe callback retains its requirement at an indirect call. |
|
| 5927 | 7633 | @test unsafe fn testUnsafeCallbackCallRejected() throws (testing::TestError) { |
|
| 5928 | - | let mut a = testResolver(); |
|
| 5929 | - | let result = try resolveProgramStr(&mut a, |
|
| 5930 | - | "fn run(callback: unsafe fn() -> u32) -> u32 { return callback(); }"); |
|
| 5931 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 7634 | + | let mut testArena562 = testArena(); |
|
| 7635 | + | let testStorage562: 'test562 = &mut testArena562 in { |
|
| 7636 | + | let mut a = testResolver(testStorage562); |
|
| 7637 | + | let result = try resolveProgramStr(&mut a, |
|
| 7638 | + | "fn run(callback: unsafe fn() -> u32) -> u32 { return callback(); }"); |
|
| 7639 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 7640 | + | } |
|
| 5932 | 7641 | } |
|
| 5933 | 7642 | ||
| 5934 | 7643 | /// An unsafe function cannot enter a safe callback slot. |
|
| 5935 | 7644 | @test unsafe fn testUnsafeCallbackAssignmentRejected() throws (testing::TestError) { |
|
| 5936 | - | let mut a = testResolver(); |
|
| 5937 | - | let result = try resolveProgramStr(&mut a, |
|
| 5938 | - | "unsafe fn load() -> u32 { return 1; } fn run() { let callback: fn() -> u32 = load; }"); |
|
| 5939 | - | let err = try expectError(&result); |
|
| 5940 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5941 | - | else throw testing::TestError::Failed; |
|
| 7645 | + | let mut testArena563 = testArena(); |
|
| 7646 | + | let testStorage563: 'test563 = &mut testArena563 in { |
|
| 7647 | + | let mut a = testResolver(testStorage563); |
|
| 7648 | + | let result = try resolveProgramStr(&mut a, |
|
| 7649 | + | "unsafe fn load() -> u32 { return 1; } fn run() { let callback: fn() -> u32 = load; }"); |
|
| 7650 | + | let err = try expectError(&result); |
|
| 7651 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 7652 | + | else throw testing::TestError::Failed; |
|
| 7653 | + | } |
|
| 5942 | 7654 | } |
|
| 5943 | 7655 | ||
| 5944 | 7656 | /// A safe function can enter an unsafe callback slot. |
|
| 5945 | 7657 | @test unsafe fn testSafeCallbackIntoUnsafeSlot() throws (testing::TestError) { |
|
| 5946 | 7658 | try expectAnalyzeOk( |
|
| 5947 | 7659 | "fn load() -> u32 { return 1; } unsafe fn run() -> u32 { let callback: unsafe fn() -> u32 = load; return callback(); }"); |
|
| 5948 | 7660 | } |
|
| 5949 | 7661 | ||
| 5950 | 7662 | /// Borrowed callback storage must preserve its function safety type. |
|
| 5951 | 7663 | @test unsafe fn testBorrowedCallbackSafetyInvariant() throws (testing::TestError) { |
|
| 5952 | - | let mut a = testResolver(); |
|
| 5953 | - | let result = try resolveProgramStr(&mut a, |
|
| 5954 | - | "fn replace(slot: &mut unsafe fn()) {} fn load() {} fn run() { let mut callback: fn() = load; replace(&mut callback); }"); |
|
| 5955 | - | let err = try expectError(&result); |
|
| 5956 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5957 | - | else throw testing::TestError::Failed; |
|
| 7664 | + | let mut testArena564 = testArena(); |
|
| 7665 | + | let testStorage564: 'test564 = &mut testArena564 in { |
|
| 7666 | + | let mut a = testResolver(testStorage564); |
|
| 7667 | + | let result = try resolveProgramStr(&mut a, |
|
| 7668 | + | "fn replace(slot: &mut unsafe fn()) {} fn load() {} fn run() { let mut callback: fn() = load; replace(&mut callback); }"); |
|
| 7669 | + | let err = try expectError(&result); |
|
| 7670 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 7671 | + | else throw testing::TestError::Failed; |
|
| 7672 | + | } |
|
| 5958 | 7673 | } |
|
| 5959 | 7674 | ||
| 5960 | 7675 | /// Referenced storage must preserve its exact element type in every context. |
|
| 5961 | 7676 | @test unsafe fn testPointerStorageCoercionsRejected() throws (testing::TestError) { |
|
| 5962 | 7677 | let programs = &[ |
| 5968 | 7683 | "fn take(p: *[?*u8]) {} fn run(p: *[*u8]) { take(p); }", |
|
| 5969 | 7684 | "unsafe fn take(p: *mut [?*u8]) {} unsafe fn run(p: *mut [*u8]) { take(p); }", |
|
| 5970 | 7685 | "fn take(p: &mut **u8) {} fn run(p: &mut *mut *u8) { take(p); }", |
|
| 5971 | 7686 | ]; |
|
| 5972 | 7687 | for program in programs { |
|
| 5973 | - | let mut a = testResolver(); |
|
| 5974 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5975 | - | let err = try expectError(&result); |
|
| 5976 | - | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5977 | - | else throw testing::TestError::Failed; |
|
| 7688 | + | let mut testArena565 = testArena(); |
|
| 7689 | + | let testStorage565: 'test565 = &mut testArena565 in { |
|
| 7690 | + | let mut a = testResolver(testStorage565); |
|
| 7691 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7692 | + | let err = try expectError(&result); |
|
| 7693 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 7694 | + | else throw testing::TestError::Failed; |
|
| 7695 | + | } |
|
| 5978 | 7696 | } |
|
| 5979 | 7697 | try expectAnalyzeOk("fn take(p: &mut ?*u8) {} fn run() { let mut p: ?*u8 = nil; take(&mut p); }"); |
|
| 5980 | 7698 | try expectAnalyzeOk("fn run(p: *mut u8) -> *u8 { return p; }"); |
|
| 5981 | 7699 | try expectAnalyzeOk("fn run(p: *u8) -> *opaque { return p; }"); |
|
| 5982 | 7700 | } |
| 5990 | 7708 | "unsafe fn run(p: *mut ?*u8) { p as *mut *u8; }", |
|
| 5991 | 7709 | "unsafe fn run(p: *unsafe mut *u8) { p as *unsafe mut ?*u8; }", |
|
| 5992 | 7710 | "unsafe fn run(p: *mut [*u8]) { p as *mut [?*u8]; }", |
|
| 5993 | 7711 | ]; |
|
| 5994 | 7712 | for program in programs { |
|
| 5995 | - | let mut a = testResolver(); |
|
| 5996 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5997 | - | let err = try expectError(&result); |
|
| 5998 | - | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5999 | - | else throw testing::TestError::Failed; |
|
| 7713 | + | let mut testArena566 = testArena(); |
|
| 7714 | + | let testStorage566: 'test566 = &mut testArena566 in { |
|
| 7715 | + | let mut a = testResolver(testStorage566); |
|
| 7716 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7717 | + | let err = try expectError(&result); |
|
| 7718 | + | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 7719 | + | else throw testing::TestError::Failed; |
|
| 7720 | + | } |
|
| 6000 | 7721 | } |
|
| 6001 | 7722 | } |
|
| 6002 | 7723 | ||
| 6003 | 7724 | /// Pattern references prevent replacement of their source in every context. |
|
| 6004 | 7725 | @test unsafe fn testPatternLoanMutationRejected() throws (testing::TestError) { |
| 6008 | 7729 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); match &mut u { case U::A(p) => { unsafe { set u = U::B(1); } *p; } else => {} } }", |
|
| 6009 | 7730 | "union U: Copy { A(u64), B(u64) } fn change(u: &mut U) { set *u = U::B(1); } fn run() { let mut u = U::A(7); match &u { case U::A(p) => { change(&mut u); *p; } else => {} } }", |
|
| 6010 | 7731 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); if let case U::A(p) = &u { set u = U::B(1); *p; } }", |
|
| 6011 | 7732 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); while let case U::A(p) = &u { set u = U::B(1); *p; } }", |
|
| 6012 | 7733 | "union U: Copy { A(u64), B(u64) } fn run(u: *mut U) { match u { case U::A(p) => { let moved = u; *p; } else => {} } }", |
|
| 7734 | + | "record R: 'r + Copy { p: &'r u32 } fn run 'r (p: &'r u32) { let mut u = R 'r { p }; match &u { case R { p: q } => { set u = R 'r { p }; **q; } else => {} } }", |
|
| 7735 | + | "record R: 'r + Copy { p: &'r u32 } unsafe fn run 'r (p: &'r u32) { let mut u = R 'r { p }; match &u { case R { p: q } => { set u = R 'r { p }; **q; } else => {} } }", |
|
| 6013 | 7736 | ]; |
|
| 6014 | 7737 | for program in programs { |
|
| 6015 | - | let mut a = testResolver(); |
|
| 6016 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6017 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
|
| 7738 | + | let mut testArena567 = testArena(); |
|
| 7739 | + | let testStorage567: 'test567 = &mut testArena567 in { |
|
| 7740 | + | let mut a = testResolver(testStorage567); |
|
| 7741 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7742 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
|
| 7743 | + | } |
|
| 6018 | 7744 | } |
|
| 6019 | 7745 | } |
|
| 6020 | 7746 | ||
| 6021 | 7747 | /// Pattern loans end at their scope and permit writes through mutable payload references. |
|
| 6022 | 7748 | @test unsafe fn testPatternLoanScopeAllowed() throws (testing::TestError) { |
| 6030 | 7756 | let programs = &[ |
|
| 6031 | 7757 | "static DATA: u8 = 7; union U: Copy { A(*u8), B(u64) } fn run() -> u8 { let mut u = U::A(&DATA); match &u { case U::A(p) => { set u = U::B(1); return **p; } else => return 0, } }", |
|
| 6032 | 7758 | "static DATA: u8 = 7; union U: Copy { A(*u8), B(u64) } unsafe fn run() -> u8 { let mut u = U::A(&DATA); match &u { case U::A(p) => { set u = U::B(1); return **p; } else => return 0, } }", |
|
| 6033 | 7759 | ]; |
|
| 6034 | 7760 | for program in programs { |
|
| 6035 | - | let mut a = testResolver(); |
|
| 6036 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6037 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
|
| 7761 | + | let mut testArena568 = testArena(); |
|
| 7762 | + | let testStorage568: 'test568 = &mut testArena568 in { |
|
| 7763 | + | let mut a = testResolver(testStorage568); |
|
| 7764 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7765 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
|
| 7766 | + | } |
|
| 6038 | 7767 | } |
|
| 6039 | 7768 | } |
|
| 6040 | 7769 | ||
| 6041 | 7770 | /// Guards, nested patterns, and unsafe calls must preserve pattern source storage. |
|
| 6042 | 7771 | @test unsafe fn testPatternLoanIndirectMutationRejected() throws (testing::TestError) { |
| 6047 | 7776 | "union U: Copy { A(u64), B(u64) } record R: Copy { value: U } fn run() { let mut u = R { value: U::A(7) }; match &u.value { case U::A(p) => { set u.value = U::B(1); *p; } else => {} } }", |
|
| 6048 | 7777 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = [U::A(7)]; match &u[0] { case U::A(p) => { set u[0] = U::B(1); *p; } else => {} } }", |
|
| 6049 | 7778 | "union U: Copy { A(u64), B(u64) } unsafe fn (u: *unsafe mut U) change() { set *u = U::B(1); } unsafe fn run(u: *unsafe mut U) { match u { case U::A(p) => { u.change(); *p; } else => {} } }", |
|
| 6050 | 7779 | ]; |
|
| 6051 | 7780 | for program in programs { |
|
| 6052 | - | let mut a = testResolver(); |
|
| 6053 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6054 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
|
| 7781 | + | let mut testArena569 = testArena(); |
|
| 7782 | + | let testStorage569: 'test569 = &mut testArena569 in { |
|
| 7783 | + | let mut a = testResolver(testStorage569); |
|
| 7784 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7785 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
|
| 7786 | + | } |
|
| 6055 | 7787 | } |
|
| 6056 | 7788 | } |
|
| 6057 | 7789 | ||
| 6058 | 7790 | /// Binding mutability does not permit writes through immutable pointers. |
|
| 6059 | 7791 | @test unsafe fn testMutableBindingImmutableTargetRejected() throws (testing::TestError) { |
| 6065 | 7797 | "static DATA: [u8; 1] = [0]; fn run() { let mut p = &DATA; let q = &mut p[0]; }", |
|
| 6066 | 7798 | "record R: Copy { n: u8 } fn (r: &mut R) change() { set r.n = 1; } fn run(input: *R) { let mut p = input; p.change(); }", |
|
| 6067 | 7799 | "fn run(p: *mut u8, q: *mut u8) { set p = q; }", |
|
| 6068 | 7800 | ]; |
|
| 6069 | 7801 | for program in programs { |
|
| 6070 | - | let mut a = testResolver(); |
|
| 6071 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6072 | - | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 7802 | + | let mut testArena570 = testArena(); |
|
| 7803 | + | let testStorage570: 'test570 = &mut testArena570 in { |
|
| 7804 | + | let mut a = testResolver(testStorage570); |
|
| 7805 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7806 | + | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 7807 | + | } |
|
| 6073 | 7808 | } |
|
| 6074 | 7809 | } |
|
| 6075 | 7810 | ||
| 6076 | 7811 | /// Mutable targets support access through fixed and mutable pointer bindings. |
|
| 6077 | 7812 | @test unsafe fn testMutablePointerTargetsAllowed() throws (testing::TestError) { |
| 6087 | 7822 | "union U: Copy { A(u8), B } fn run(p: *unsafe U) { if let case U::A(n) = p { *n; } }", |
|
| 6088 | 7823 | "union U: Copy { A(u8), B } fn run(p: *unsafe U) { while let case U::A(n) = p { *n; break; } }", |
|
| 6089 | 7824 | "record R: Copy { n: u8 } fn run(p: *unsafe R) { let case R { n } = p else return; }", |
|
| 6090 | 7825 | ]; |
|
| 6091 | 7826 | for program in programs { |
|
| 6092 | - | let mut a = testResolver(); |
|
| 6093 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6094 | - | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7827 | + | let mut testArena571 = testArena(); |
|
| 7828 | + | let testStorage571: 'test571 = &mut testArena571 in { |
|
| 7829 | + | let mut a = testResolver(testStorage571); |
|
| 7830 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7831 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 7832 | + | } |
|
| 6095 | 7833 | } |
|
| 6096 | 7834 | } |
|
| 6097 | 7835 | ||
| 6098 | 7836 | /// Unsafe contexts permit pattern access through raw pointers. |
|
| 6099 | 7837 | @test unsafe fn testRawPointerPatternsAllowed() throws (testing::TestError) { |
| 6111 | 7849 | "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6112 | 7850 | "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6113 | 7851 | "union E: Copy { Bad } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 6114 | 7852 | ]; |
|
| 6115 | 7853 | for program in programs { |
|
| 6116 | - | let mut a = testResolver(); |
|
| 6117 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6118 | - | try expectErrorKind(&result, super::ErrorKind::InvalidSliceAllocator); |
|
| 7854 | + | let mut testArena572 = testArena(); |
|
| 7855 | + | let testStorage572: 'test572 = &mut testArena572 in { |
|
| 7856 | + | let mut a = testResolver(testStorage572); |
|
| 7857 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7858 | + | try expectErrorKind(&result, super::ErrorKind::InvalidSliceAllocator); |
|
| 7859 | + | } |
|
| 6119 | 7860 | } |
|
| 6120 | 7861 | } |
|
| 6121 | 7862 | ||
| 6122 | 7863 | /// Slice allocators can use raw context pointers. |
|
| 6123 | 7864 | @test unsafe fn testSliceAppendRawContextAllowed() throws (testing::TestError) { |
| 6131 | 7872 | "union U: Copy { A(*u8), B(u64) } fn run(p: *mut U) -> u8 { let q = p; match p { case U::A(n) => { set *q = U::B(1); return **n; } else => return 0, } }", |
|
| 6132 | 7873 | "union U: Copy { A(u8), B } fn change(p: &mut U) { set *p = U::B; } fn run(p: *mut U) { let q = p; if let case U::A(n) = p { change(q); *n; } }", |
|
| 6133 | 7874 | "union U: Copy { A(u8), B } fn run(p: *mut U) { let q = p; let r = q; while let case U::A(n) = p { set *r = U::B; *n; break; } }", |
|
| 6134 | 7875 | ]; |
|
| 6135 | 7876 | for program in programs { |
|
| 6136 | - | let mut a = testResolver(); |
|
| 6137 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6138 | - | let _ = try expectError(&result); |
|
| 7877 | + | let mut testArena573 = testArena(); |
|
| 7878 | + | let testStorage573: 'test573 = &mut testArena573 in { |
|
| 7879 | + | let mut a = testResolver(testStorage573); |
|
| 7880 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7881 | + | let _ = try expectError(&result); |
|
| 7882 | + | } |
|
| 6139 | 7883 | } |
|
| 6140 | 7884 | } |
|
| 6141 | 7885 | ||
| 6142 | 7886 | /// Every access to a moved safe mutable pointer is rejected. |
|
| 6143 | 7887 | @test unsafe fn testMutablePointerMovesRejected() throws (testing::TestError) { |
| 6153 | 7897 | "trait T { fn (&T) read(); } fn run(p: *mut opaque T) { let q = p; p.read(); }", |
|
| 6154 | 7898 | "fn run(p: *mut u8, flag: bool) { if flag { let q = p; } *p; }", |
|
| 6155 | 7899 | "fn run(p: *mut u8) { loop { let q = p; } }", |
|
| 6156 | 7900 | ]; |
|
| 6157 | 7901 | for program in programs { |
|
| 6158 | - | let mut a = testResolver(); |
|
| 6159 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6160 | - | let _ = try expectError(&result); |
|
| 7902 | + | let mut testArena574 = testArena(); |
|
| 7903 | + | let testStorage574: 'test574 = &mut testArena574 in { |
|
| 7904 | + | let mut a = testResolver(testStorage574); |
|
| 7905 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7906 | + | let _ = try expectError(&result); |
|
| 7907 | + | } |
|
| 6161 | 7908 | } |
|
| 6162 | 7909 | } |
|
| 6163 | 7910 | ||
| 6164 | 7911 | /// Copy composites cannot contain safe mutable owners or their containers. |
|
| 6165 | 7912 | @test unsafe fn testCopyMutablePointerFieldsRejected() throws (testing::TestError) { |
| 6171 | 7918 | "record R: Copy { p: *mut [u8] }", |
|
| 6172 | 7919 | "trait T {} record R: Copy { p: *mut opaque T }", |
|
| 6173 | 7920 | "record Inner { p: *mut u8 } record Outer: Copy { inner: Inner }", |
|
| 6174 | 7921 | ]; |
|
| 6175 | 7922 | for program in programs { |
|
| 6176 | - | let mut a = testResolver(); |
|
| 6177 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6178 | - | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
|
| 7923 | + | let mut testArena575 = testArena(); |
|
| 7924 | + | let testStorage575: 'test575 = &mut testArena575 in { |
|
| 7925 | + | let mut a = testResolver(testStorage575); |
|
| 7926 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7927 | + | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
|
| 7928 | + | } |
|
| 6179 | 7929 | } |
|
| 6180 | 7930 | } |
|
| 6181 | 7931 | ||
| 6182 | 7932 | /// Moves transfer safe mutable pointers, and calls can borrow them temporarily. |
|
| 6183 | 7933 | @test unsafe fn testMutablePointerMovesAllowed() throws (testing::TestError) { |
| 6188 | 7938 | try expectAnalyzeOk("record R: Copy { p: *unsafe mut u8 } unsafe fn run(p: *unsafe mut u8) { let q = p; set *p = 1; set *q = 2; }"); |
|
| 6189 | 7939 | try expectAnalyzeOk("record R: Copy { p: *unsafe mut [u8] } unsafe fn run(p: *unsafe mut [u8]) { let q = p; set p[0] = 1; set q[0] = 2; }"); |
|
| 6190 | 7940 | } |
|
| 6191 | 7941 | ||
| 6192 | 7942 | /// Function signature inspection uses immutable descriptors in safe code. |
|
| 6193 | - | fn expectImmutableFunctionSignatures(res: &super::Resolver, root: *ast::Node) throws (testing::TestError) { |
|
| 7943 | + | fn expectImmutableFunctionSignatures 'arena (res: &super::Resolver 'arena, root: *ast::Node) throws (testing::TestError) { |
|
| 6194 | 7944 | let first = try typeOf(res, try getBlockStmt(root, 0)); |
|
| 6195 | 7945 | let equivalent = try typeOf(res, try getBlockStmt(root, 1)); |
|
| 6196 | 7946 | let throwing = try typeOf(res, try getBlockStmt(root, 2)); |
|
| 6197 | 7947 | let unsafeCall = try typeOf(res, try getBlockStmt(root, 3)); |
|
| 6198 | 7948 | try testing::expect(super::typesEqual(first, equivalent)); |
| 6200 | 7950 | try testing::expect(not super::typesEqual(first, unsafeCall)); |
|
| 6201 | 7951 | } |
|
| 6202 | 7952 | ||
| 6203 | 7953 | /// Local analysis state does not change function signature identity. |
|
| 6204 | 7954 | @test unsafe fn testImmutableFunctionSignatures() throws (testing::TestError) { |
|
| 6205 | - | let mut res = testResolver(); |
|
| 6206 | - | let result = try resolveProgramStr(&mut res, |
|
| 6207 | - | "fn first(x: u8) -> u32 { return 0; } fn equivalent(y: u8) -> u32 { let a: u32 = 1; return a; } fn throwing(x: u8) -> u32 throws (u8) { throw x; } unsafe fn unsafeCall(x: u8) -> u32 { return 0; }"); |
|
| 6208 | - | try expectNoErrors(&result); |
|
| 6209 | - | try expectImmutableFunctionSignatures(&res, result.root); |
|
| 7955 | + | let mut testArena576 = testArena(); |
|
| 7956 | + | let testStorage576: 'test576 = &mut testArena576 in { |
|
| 7957 | + | let mut res = testResolver(testStorage576); |
|
| 7958 | + | let result = try resolveProgramStr(&mut res, |
|
| 7959 | + | "fn first(x: u8) -> u32 { return 0; } fn equivalent(y: u8) -> u32 { let a: u32 = 1; return a; } fn throwing(x: u8) -> u32 throws (u8) { throw x; } unsafe fn unsafeCall(x: u8) -> u32 { return 0; }"); |
|
| 7960 | + | try expectNoErrors(&result); |
|
| 7961 | + | try expectImmutableFunctionSignatures(&res, result.root); |
|
| 7962 | + | } |
|
| 6210 | 7963 | } |
|
| 6211 | 7964 | ||
| 6212 | 7965 | /// A diagnostic snapshot keeps its contents when the resolver buffer changes. |
|
| 6213 | 7966 | @test unsafe fn testDiagnosticSnapshotOwnsItsErrors() throws (testing::TestError) { |
|
| 6214 | - | let mut res = testResolver(); |
|
| 6215 | - | let result = try resolveProgramStr(&mut res, "fn run() { missing; }"); |
|
| 6216 | - | let snapshot = result.diagnostics; |
|
| 6217 | - | let original = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed; |
|
| 6218 | - | set res.errors.entries[0] = super::Error { |
|
| 6219 | - | kind: super::ErrorKind::Internal, node: nil, moduleId: 0, |
|
| 6220 | - | }; |
|
| 6221 | - | let later = super::diagnostics(&mut res); |
|
| 6222 | - | try testing::expect(snapshot.errors.len == 1); |
|
| 6223 | - | try testing::expect(later.errors.len == 1); |
|
| 6224 | - | let first = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed; |
|
| 6225 | - | try testing::expect(first.kind == original.kind); |
|
| 6226 | - | try testing::expect(later.errors[0].kind == super::ErrorKind::Internal); |
|
| 6227 | - | try testing::expect(super::errorAt(snapshot.errors, 1) == nil); |
|
| 7967 | + | let mut testArena577 = testArena(); |
|
| 7968 | + | let testStorage577: 'test577 = &mut testArena577 in { |
|
| 7969 | + | let mut res = testResolver(testStorage577); |
|
| 7970 | + | let result = try resolveProgramStr(&mut res, "fn run() { missing; }"); |
|
| 7971 | + | let snapshot = result.diagnostics; |
|
| 7972 | + | let original = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed; |
|
| 7973 | + | set res.errors.entries[0] = super::Error { |
|
| 7974 | + | kind: super::ErrorKind::Internal, node: nil, moduleId: 0, |
|
| 7975 | + | }; |
|
| 7976 | + | let later = super::diagnostics(&mut res); |
|
| 7977 | + | try testing::expect(snapshot.errors.len == 1); |
|
| 7978 | + | try testing::expect(later.errors.len == 1); |
|
| 7979 | + | let first = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed; |
|
| 7980 | + | try testing::expect(first.kind == original.kind); |
|
| 7981 | + | try testing::expect(later.errors[0].kind == super::ErrorKind::Internal); |
|
| 7982 | + | try testing::expect(super::errorAt(snapshot.errors, 1) == nil); |
|
| 7983 | + | } |
|
| 6228 | 7984 | } |
|
| 6229 | 7985 | ||
| 6230 | 7986 | /// Prefixes, equal fields, and uncertain element locations must conflict. |
|
| 6231 | 7987 | @test unsafe fn testOverlappingFieldBorrows() throws (testing::TestError) { |
|
| 6232 | 7988 | let programs = &[ |
| 6234 | 7990 | "record R: Copy { a: u32, b: u32 } fn take(a: &mut R, b: &u32) {} fn run(r: &mut R) { take(r, &r.b); }", |
|
| 6235 | 7991 | "record R: Copy { a: [u8; 2], b: [u8; 2] } fn take(a: &mut u8, b: &u8) {} fn run(r: &mut R) { take(&mut r.a[0], &r.a[1]); }", |
|
| 6236 | 7992 | "record R: Copy { a: u32, b: u32 } fn take(a: &mut u32, b: u32) {} fn run(r: &mut R) { take(&mut r.a, r.a); }", |
|
| 6237 | 7993 | ]; |
|
| 6238 | 7994 | for program in programs { |
|
| 6239 | - | let mut a = testResolver(); |
|
| 6240 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6241 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
|
| 7995 | + | let mut testArena578 = testArena(); |
|
| 7996 | + | let testStorage578: 'test578 = &mut testArena578 in { |
|
| 7997 | + | let mut a = testResolver(testStorage578); |
|
| 7998 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 7999 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
|
| 8000 | + | } |
|
| 6242 | 8001 | } |
|
| 6243 | 8002 | } |
|
| 6244 | 8003 | ||
| 6245 | 8004 | /// Local loans exclude writes, competing references, and source reads. |
|
| 6246 | 8005 | @test unsafe fn testLocalReferenceConflicts() throws (testing::TestError) { |
| 6254 | 8013 | "fn take(p: &mut u32) {} fn run() { let mut n: u32 = 4; let p = &n; take(&mut n); }", |
|
| 6255 | 8014 | "fn run() { let mut n: u32 = 4; let p = &n; unsafe { set n = 1; } }", |
|
| 6256 | 8015 | "fn run() { let mut n: u32 = 4; let p = &mut n; let q = &*p; let moved = p; }", |
|
| 6257 | 8016 | ]; |
|
| 6258 | 8017 | for program in programs { |
|
| 6259 | - | let mut a = testResolver(); |
|
| 6260 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6261 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n")); |
|
| 8018 | + | let mut testArena579 = testArena(); |
|
| 8019 | + | let testStorage579: 'test579 = &mut testArena579 in { |
|
| 8020 | + | let mut a = testResolver(testStorage579); |
|
| 8021 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 8022 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n")); |
|
| 8023 | + | } |
|
| 6262 | 8024 | } |
|
| 6263 | 8025 | } |
|
| 6264 | 8026 | ||
| 6265 | 8027 | /// Reference bindings cannot outlive temporary storage or change their source. |
|
| 6266 | 8028 | @test unsafe fn testLocalReferenceStorage() throws (testing::TestError) { |
|
| 6267 | 8029 | let programs = &[ |
|
| 8030 | + | "fn f 'r (p: &'r mut u32) { let mut cursor = p; }", |
|
| 8031 | + | "unsafe fn f 'r (p: &'r mut u32) { let mut cursor = p; }", |
|
| 6268 | 8032 | "fn run() { let n: u32 = 4; let mut p: &u32 = &n; }", |
|
| 6269 | 8033 | "record R: Copy { n: u32 } fn make() -> R { return R { n: 4 }; } fn run() { let p: &u32 = &make().n; }", |
|
| 6270 | 8034 | ]; |
|
| 6271 | 8035 | for program in programs { |
|
| 6272 | - | let mut a = testResolver(); |
|
| 6273 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6274 | - | try expectErrorKind(&result, super::ErrorKind::RefBinding); |
|
| 8036 | + | let mut testArena580 = testArena(); |
|
| 8037 | + | let testStorage580: 'test580 = &mut testArena580 in { |
|
| 8038 | + | let mut a = testResolver(testStorage580); |
|
| 8039 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 8040 | + | try expectErrorKind(&result, super::ErrorKind::RefBinding); |
|
| 8041 | + | } |
|
| 6275 | 8042 | } |
|
| 6276 | 8043 | } |
|
| 6277 | 8044 | ||
| 6278 | 8045 | /// Stored and returned values cannot contain a local reference. |
|
| 6279 | 8046 | @test unsafe fn testLocalReferenceEscapeRejected() throws (testing::TestError) { |
| 6282 | 8049 | "fn run() -> *u32 { let n: u32 = 1; let p = &n; return p as *u32; }", |
|
| 6283 | 8050 | "static DATA: u32 = 0; static P: *u32 = &DATA; fn run() { let n: u32 = 1; let p = &n; set P = p; }", |
|
| 6284 | 8051 | "record R: Copy { p: *u32 } fn run() { let n: u32 = 1; let p = &n; let r = R { p }; }", |
|
| 6285 | 8052 | ]; |
|
| 6286 | 8053 | for program in programs { |
|
| 6287 | - | let mut a = testResolver(); |
|
| 6288 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6289 | - | let _ = try expectError(&result); |
|
| 8054 | + | let mut testArena581 = testArena(); |
|
| 8055 | + | let testStorage581: 'test581 = &mut testArena581 in { |
|
| 8056 | + | let mut a = testResolver(testStorage581); |
|
| 8057 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 8058 | + | let _ = try expectError(&result); |
|
| 8059 | + | } |
|
| 6290 | 8060 | } |
|
| 6291 | 8061 | } |
|
| 6292 | 8062 | ||
| 6293 | 8063 | /// Pointer indirection cannot prove that sibling pointees are disjoint. |
|
| 6294 | 8064 | @test unsafe fn testIndirectFieldBorrowConflict() throws (testing::TestError) { |
|
| 6295 | 8065 | let programs = &[ |
|
| 6296 | 8066 | "record R: Copy { a: *unsafe mut u32, b: *unsafe mut u32 } fn take(a: &mut u32, b: &mut u32) {} unsafe fn run(r: &mut R) { take(&mut *r.a, &mut *r.b); }", |
|
| 6297 | 8067 | "record R: Copy { a: *unsafe mut u32, b: *unsafe mut u32 } unsafe fn run(r: &mut R) { let a: &mut u32 = &mut *r.a; let b: &mut u32 = &mut *r.b; }", |
|
| 6298 | 8068 | ]; |
|
| 6299 | 8069 | for program in programs { |
|
| 6300 | - | let mut a = testResolver(); |
|
| 6301 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6302 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
|
| 8070 | + | let mut testArena582 = testArena(); |
|
| 8071 | + | let testStorage582: 'test582 = &mut testArena582 in { |
|
| 8072 | + | let mut a = testResolver(testStorage582); |
|
| 8073 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 8074 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
|
| 8075 | + | } |
|
| 6303 | 8076 | } |
|
| 6304 | 8077 | } |
|
| 6305 | 8078 | ||
| 6306 | 8079 | /// Pattern references protect their field and permit writes to disjoint fields. |
|
| 6307 | 8080 | @test unsafe fn testDisjointPatternFieldBorrow() throws (testing::TestError) { |
|
| 6308 | - | let mut a = testResolver(); |
|
| 6309 | - | let result = try resolveProgramStr(&mut a, |
|
| 6310 | - | "union U: Copy { A(u32), B } record R: Copy { u: U, n: u32 } fn run(r: &mut R) { let alias = &mut r.u; match alias { case U::A(p) => { set *alias = U::B; *p; } else => {} } }"); |
|
| 6311 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
|
| 8081 | + | let mut testArena583 = testArena(); |
|
| 8082 | + | let testStorage583: 'test583 = &mut testArena583 in { |
|
| 8083 | + | let mut a = testResolver(testStorage583); |
|
| 8084 | + | let result = try resolveProgramStr(&mut a, |
|
| 8085 | + | "union U: Copy { A(u32), B } record R: Copy { u: U, n: u32 } fn run(r: &mut R) { let alias = &mut r.u; match alias { case U::A(p) => { set *alias = U::B; *p; } else => {} } }"); |
|
| 8086 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
|
| 8087 | + | } |
|
| 6312 | 8088 | } |
|
| 6313 | 8089 | ||
| 6314 | 8090 | /// Slice mutations must preserve storage held by a local reference. |
|
| 6315 | 8091 | @test unsafe fn testLocalReferenceSliceMutationRejected() throws (testing::TestError) { |
|
| 6316 | 8092 | let programs = &[ |
|
| 6317 | 8093 | "fn run(s: *mut [u8]) { let p: &u8 = &s[0]; s.delete(0); }", |
|
| 6318 | 8094 | "record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { let p: &u8 = &s[0]; s.append(1, a); }", |
|
| 6319 | 8095 | ]; |
|
| 6320 | 8096 | for program in programs { |
|
| 6321 | - | let mut a = testResolver(); |
|
| 6322 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6323 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("s")); |
|
| 8097 | + | let mut testArena584 = testArena(); |
|
| 8098 | + | let testStorage584: 'test584 = &mut testArena584 in { |
|
| 8099 | + | let mut a = testResolver(testStorage584); |
|
| 8100 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 8101 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("s")); |
|
| 8102 | + | } |
|
| 6324 | 8103 | } |
|
| 6325 | 8104 | } |
|
| 6326 | 8105 | ||
| 6327 | 8106 | /// Owner moves and mutable methods cannot invalidate a local loan. |
|
| 6328 | 8107 | @test unsafe fn testLocalReferenceOwnerMutationRejected() throws (testing::TestError) { |
|
| 6329 | 8108 | let programs = &[ |
|
| 6330 | 8109 | "fn run(p: *mut u32) { let r: &u32 = &*p; let moved = p; }", |
|
| 6331 | 8110 | "record R: Copy { a: u32 } fn (p: &mut R) change() { set p.a = 1; } fn run(p: &mut R) { let r = &p.a; p.change(); }", |
|
| 6332 | 8111 | ]; |
|
| 6333 | 8112 | for program in programs { |
|
| 6334 | - | let mut a = testResolver(); |
|
| 6335 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 6336 | - | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p")); |
|
| 8113 | + | let mut testArena585 = testArena(); |
|
| 8114 | + | let testStorage585: 'test585 = &mut testArena585 in { |
|
| 8115 | + | let mut a = testResolver(testStorage585); |
|
| 8116 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 8117 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p")); |
|
| 8118 | + | } |
|
| 6337 | 8119 | } |
|
| 6338 | 8120 | } |
|
| 6339 | 8121 | ||
| 6340 | 8122 | /// Reference locals require a function scope and a reachable initializer result. |
|
| 6341 | 8123 | @test unsafe fn testLocalReferenceDeclarationContext() throws (testing::TestError) { |
|
| 6342 | - | let mut a = testResolver(); |
|
| 6343 | - | let result = try resolveProgramStr(&mut a, "let n: u32 = 1; let p: &u32 = &n;"); |
|
| 6344 | - | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 8124 | + | let mut testArena586 = testArena(); |
|
| 8125 | + | let testStorage586: 'test586 = &mut testArena586 in { |
|
| 8126 | + | let mut a = testResolver(testStorage586); |
|
| 8127 | + | let result = try resolveProgramStr(&mut a, "let n: u32 = 1; let p: &u32 = &n;"); |
|
| 8128 | + | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 8129 | + | } |
|
| 8130 | + | } |
|
| 8131 | + | ||
| 8132 | + | /// Test-only imports do not require their modules in a normal build. |
|
| 8133 | + | @test unsafe fn testConditionalUse() throws (testing::TestError) { |
|
| 8134 | + | let mut testArena587 = testArena(); |
|
| 8135 | + | let testStorage587: 'test587 = &mut testArena587 in { |
|
| 8136 | + | let mut a = testResolver(testStorage587); |
|
| 8137 | + | set a.config.buildTest = false; |
|
| 8138 | + | let source = "@test use missing::testing; fn value() -> u32 { return 1; }"; |
|
| 8139 | + | let result = try resolveProgramStr(&mut a, source); |
|
| 8140 | + | try expectNoErrors(&result); |
|
| 8141 | + | set a.config.buildTest = true; |
|
| 8142 | + | let enabled = try resolveProgramStr(&mut a, source); |
|
| 8143 | + | try testing::expect(enabled.diagnostics.errors.len > 0); |
|
| 8144 | + | } |
|
| 8145 | + | } |
|
| 8146 | + | ||
| 8147 | + | /// Resolver metadata retains its caller's arena when the context moves. |
|
| 8148 | + | unsafe fn relocateResolver 'arena (context: super::Resolver 'arena) -> super::Resolver 'arena { |
|
| 8149 | + | return context; |
|
| 8150 | + | } |
|
| 8151 | + | ||
| 8152 | + | @test unsafe fn testResolverRelocation() throws (testing::TestError) { |
|
| 8153 | + | static DATA: [u8; 65536] = [0; 65536]; |
|
| 8154 | + | static AST_DATA: [u8; 8192] = [0; 8192]; |
|
| 8155 | + | static NODES: [super::NodeData; 128] = undefined; |
|
| 8156 | + | static ERRORS: [super::Error; 8] = undefined; |
|
| 8157 | + | static SCOPE: super::Scope = undefined; |
|
| 8158 | + | static POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 8159 | + | let mut arena = alloc::new(&mut DATA[..]); |
|
| 8160 | + | let mut astArena = ast::nodeArena(&mut AST_DATA[..]); |
|
| 8161 | + | let root = try! parser::parse(scanner::SourceLoc::String, |
|
| 8162 | + | "fn answer() -> u32 { return 42; }", &mut astArena, &mut POOL); |
|
| 8163 | + | let storage = super::ResolverStorage { |
|
| 8164 | + | nodeData: &mut NODES[..], pkgScope: &mut SCOPE, errors: &mut ERRORS[..], |
|
| 8165 | + | }; |
|
| 8166 | + | let arenaRef: 'arena = &mut arena in { |
|
| 8167 | + | let context = super::resolver(arenaRef, storage, super::Config { buildTest: false }); |
|
| 8168 | + | let initialized = alloc::used(context.arena); |
|
| 8169 | + | try testing::expect(initialized > 0); |
|
| 8170 | + | let mut moved = relocateResolver(context); |
|
| 8171 | + | let diagnostics = try! super::resolveModuleRoot(&mut moved, root); |
|
| 8172 | + | try testing::expect(super::success(&diagnostics)); |
|
| 8173 | + | try testing::expect(alloc::used(moved.arena) > initialized); |
|
| 8174 | + | try testing::expect(moved.arena.offset == alloc::used(moved.arena)); |
|
| 8175 | + | let output = try! alloc::alloc(&mut *moved.arena, @sizeOf(u32), @alignOf(u32)) as *mut u32; |
|
| 8176 | + | set *output = 42; |
|
| 8177 | + | try testing::expect(*output == 42); |
|
| 8178 | + | } |
|
| 6345 | 8179 | } |
lib/std/lang/resolver/tests/regions.rad
added
+1251 -0
| 1 | + | //! Lexical region resolver tests. |
|
| 2 | + | ||
| 3 | + | use std::mem; |
|
| 4 | + | use std::testing; |
|
| 5 | + | use std::lang::types; |
|
| 6 | + | use std::lang::resolver; |
|
| 7 | + | ||
| 8 | + | /// Equal region spellings in separate functions have distinct identities. |
|
| 9 | + | @test unsafe fn testRegionIdentities() throws (testing::TestError) { |
|
| 10 | + | let mut testArena1 = super::testArena(); |
|
| 11 | + | let testStorage1: 'test1 = &mut testArena1 in { |
|
| 12 | + | let mut res = super::testResolver(testStorage1); |
|
| 13 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 14 | + | "fn first 'r (p: &'r u8) -> u8 { return *p; }\nfn second 'r (p: &'r u8) -> u8 { return *p; }" |
|
| 15 | + | ); |
|
| 16 | + | try super::expectNoErrors(&result); |
|
| 17 | + | let first = try super::getBlockStmt(result.root, 0); |
|
| 18 | + | let second = try super::getBlockStmt(result.root, 1); |
|
| 19 | + | let firstType = resolver::typeFor(&res, first) else throw testing::TestError::Failed; |
|
| 20 | + | let secondType = resolver::typeFor(&res, second) else throw testing::TestError::Failed; |
|
| 21 | + | let case resolver::Type::Fn(a) = firstType else throw testing::TestError::Failed; |
|
| 22 | + | let case resolver::Type::Fn(b) = secondType else throw testing::TestError::Failed; |
|
| 23 | + | let case resolver::Type::Pointer { class: types::PointerClass::Region(ar), .. } = *a.paramTypes[0] |
|
| 24 | + | else throw testing::TestError::Failed; |
|
| 25 | + | let case resolver::Type::Pointer { class: types::PointerClass::Region(br), .. } = *b.paramTypes[0] |
|
| 26 | + | else throw testing::TestError::Failed; |
|
| 27 | + | try testing::expect(mem::eq(ar.name, br.name)); |
|
| 28 | + | try testing::expect(ar.id <> br.id); |
|
| 29 | + | try testing::expect(not resolver::typesEqual(*a.paramTypes[0], *b.paramTypes[0])); |
|
| 30 | + | try testing::expect(resolver::getTypeLayout(*a.paramTypes[0]).size == 8); |
|
| 31 | + | } |
|
| 32 | + | } |
|
| 33 | + | ||
| 34 | + | /// Parent links resolve forward declarations and preserve transitive ancestry. |
|
| 35 | + | @test unsafe fn testRegionParents() throws (testing::TestError) { |
|
| 36 | + | let mut testArena2 = super::testArena(); |
|
| 37 | + | let testStorage2: 'test2 = &mut testArena2 in { |
|
| 38 | + | let mut res = super::testResolver(testStorage2); |
|
| 39 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 40 | + | "fn read 'child 'middle 'root (p: &'child u8, q: &'root u8) where 'middle: 'child, 'root: 'middle {}" |
|
| 41 | + | ); |
|
| 42 | + | try super::expectNoErrors(&result); |
|
| 43 | + | let func = try super::getBlockStmt(result.root, 0); |
|
| 44 | + | let ty = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
|
| 45 | + | let case resolver::Type::Fn(info) = ty else throw testing::TestError::Failed; |
|
| 46 | + | let case resolver::Type::Pointer { class: types::PointerClass::Region(child), .. } = *info.paramTypes[0] |
|
| 47 | + | else throw testing::TestError::Failed; |
|
| 48 | + | let case resolver::Type::Pointer { class: types::PointerClass::Region(root), .. } = *info.paramTypes[1] |
|
| 49 | + | else throw testing::TestError::Failed; |
|
| 50 | + | try testing::expect(types::regionContains(root, child)); |
|
| 51 | + | try testing::expect(types::regionContains(child, child)); |
|
| 52 | + | try testing::expect(not types::regionContains(child, root)); |
|
| 53 | + | } |
|
| 54 | + | } |
|
| 55 | + | ||
| 56 | + | /// Region declarations reject duplicate names, missing parents, and cycles. |
|
| 57 | + | @test unsafe fn testInvalidRegionDeclarations() throws (testing::TestError) { |
|
| 58 | + | for program in [ |
|
| 59 | + | "fn f 'r 'r () {}", |
|
| 60 | + | "fn f 'r () where 'missing: 'r {}", |
|
| 61 | + | "fn f 'r () where 'r: 'r {}", |
|
| 62 | + | "fn f 'a 'b 'c () where 'b: 'a, 'c: 'b, 'a: 'c {}", |
|
| 63 | + | ] { |
|
| 64 | + | let mut testArena3 = super::testArena(); |
|
| 65 | + | let testStorage3: 'test3 = &mut testArena3 in { |
|
| 66 | + | let mut res = super::testResolver(testStorage3); |
|
| 67 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 68 | + | let _ = try super::expectError(&result); |
|
| 69 | + | } |
|
| 70 | + | } |
|
| 71 | + | } |
|
| 72 | + | ||
| 73 | + | /// Regions from another declaration cannot enter a function signature or body. |
|
| 74 | + | @test unsafe fn testUnknownRegionNames() throws (testing::TestError) { |
|
| 75 | + | for program in [ |
|
| 76 | + | "fn f(p: &'missing u8) {}", |
|
| 77 | + | "fn f 'r (p: &'r u8) {} fn g(p: &'r u8) {}", |
|
| 78 | + | "unsafe fn f(p: &'missing u8) {}", |
|
| 79 | + | "fn f 'r (p: &'r u8) { let q: &'missing u8 = p; }", |
|
| 80 | + | ] { |
|
| 81 | + | let mut testArena4 = super::testArena(); |
|
| 82 | + | let testStorage4: 'test4 = &mut testArena4 in { |
|
| 83 | + | let mut res = super::testResolver(testStorage4); |
|
| 84 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 85 | + | let err = try super::expectError(&result); |
|
| 86 | + | let case resolver::ErrorKind::UnknownRegion(_) = err.kind |
|
| 87 | + | else throw testing::TestError::Failed; |
|
| 88 | + | } |
|
| 89 | + | } |
|
| 90 | + | } |
|
| 91 | + | ||
| 92 | + | /// An ancestor relation does not make distinct reference regions assignable. |
|
| 93 | + | @test unsafe fn testRegionInvariance() throws (testing::TestError) { |
|
| 94 | + | for program in [ |
|
| 95 | + | "fn f 'a 'b (p: &'a u8) where 'a: 'b { let q: &'b u8 = p; }", |
|
| 96 | + | "unsafe fn f 'a 'b (p: &'b u8) where 'a: 'b { let q: &'a u8 = p; }", |
|
| 97 | + | ] { |
|
| 98 | + | let mut testArena5 = super::testArena(); |
|
| 99 | + | let testStorage5: 'test5 = &mut testArena5 in { |
|
| 100 | + | let mut res = super::testResolver(testStorage5); |
|
| 101 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 102 | + | let err = try super::expectError(&result); |
|
| 103 | + | let case resolver::ErrorKind::TypeMismatch(_) = err.kind |
|
| 104 | + | else throw testing::TestError::Failed; |
|
| 105 | + | } |
|
| 106 | + | } |
|
| 107 | + | } |
|
| 108 | + | ||
| 109 | + | /// Nominal region environments diagnose their own invalid parent relations. |
|
| 110 | + | @test unsafe fn testNominalRegionNames() throws (testing::TestError) { |
|
| 111 | + | for program in [ |
|
| 112 | + | "record R: 'r where 'missing: 'r { value: u8 }", |
|
| 113 | + | "union U: 'r where 'missing: 'r { Empty }", |
|
| 114 | + | "record R: 'r { value: &'other u8 }", |
|
| 115 | + | "union U: 'r { Value(&'other u8) }", |
|
| 116 | + | ] { |
|
| 117 | + | let mut testArena6 = super::testArena(); |
|
| 118 | + | let testStorage6: 'test6 = &mut testArena6 in { |
|
| 119 | + | let mut res = super::testResolver(testStorage6); |
|
| 120 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 121 | + | let err = try super::expectError(&result); |
|
| 122 | + | let case resolver::ErrorKind::UnknownRegion(_) = err.kind |
|
| 123 | + | else throw testing::TestError::Failed; |
|
| 124 | + | } |
|
| 125 | + | } |
|
| 126 | + | for program in [ |
|
| 127 | + | "record R: 'r + 'r { value: u8 }", |
|
| 128 | + | "union U: 'r + 'r { Empty }", |
|
| 129 | + | ] { |
|
| 130 | + | let mut testArena7 = super::testArena(); |
|
| 131 | + | let testStorage7: 'test7 = &mut testArena7 in { |
|
| 132 | + | let mut res = super::testResolver(testStorage7); |
|
| 133 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 134 | + | let _ = try super::expectErrorKind(&result, resolver::ErrorKind::DuplicateBinding("'r")); |
|
| 135 | + | } |
|
| 136 | + | } |
|
| 137 | + | } |
|
| 138 | + | ||
| 139 | + | /// Region declarations restore their environment after a signature error. |
|
| 140 | + | @test unsafe fn testRegionErrorScopeRestoration() throws (testing::TestError) { |
|
| 141 | + | let mut testArena8 = super::testArena(); |
|
| 142 | + | let testStorage8: 'test8 = &mut testArena8 in { |
|
| 143 | + | let mut res = super::testResolver(testStorage8); |
|
| 144 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 145 | + | "fn f 'r (p: &'missing u8) {} fn g 'other (p: &'r u8) {}" |
|
| 146 | + | ); |
|
| 147 | + | let err = try super::expectError(&result); |
|
| 148 | + | let case resolver::ErrorKind::UnknownRegion(name) = err.kind |
|
| 149 | + | else throw testing::TestError::Failed; |
|
| 150 | + | try testing::expect(mem::eq(name, "'missing")); |
|
| 151 | + | try testing::expect(res.regionScope == nil); |
|
| 152 | + | try testing::expect(res.currentFn == nil); |
|
| 153 | + | try testing::expect(res.currentFnNode == nil); |
|
| 154 | + | } |
|
| 155 | + | } |
|
| 156 | + | ||
| 157 | + | /// Regional exclusive references retain affine ownership and arithmetic checks. |
|
| 158 | + | @test unsafe fn testRegionExclusiveOwnership() throws (testing::TestError) { |
|
| 159 | + | for program in [ |
|
| 160 | + | "fn f 'r (p: &'r mut u8) { let q: &'r mut u8 = p; set *p = 3; }", |
|
| 161 | + | "unsafe fn f 'r (p: &'r mut u8) { let q: &'r mut u8 = p; set *p = 3; }", |
|
| 162 | + | "unsafe fn f 'r (p: &'r mut u8) { let q = p + 1; }", |
|
| 163 | + | "unsafe fn f 'r (p: &'r mut u8) { let q = p as *mut u8; }", |
|
| 164 | + | ] { |
|
| 165 | + | let mut testArena9 = super::testArena(); |
|
| 166 | + | let testStorage9: 'test9 = &mut testArena9 in { |
|
| 167 | + | let mut res = super::testResolver(testStorage9); |
|
| 168 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 169 | + | let _ = try super::expectError(&result); |
|
| 170 | + | } |
|
| 171 | + | } |
|
| 172 | + | } |
|
| 173 | + | ||
| 174 | + | /// Callback signatures preserve the same region substitution as data arguments. |
|
| 175 | + | @test unsafe fn testRegionCallbackSubstitution() throws (testing::TestError) { |
|
| 176 | + | let mut testArena10 = super::testArena(); |
|
| 177 | + | let testStorage10: 'test10 = &mut testArena10 in { |
|
| 178 | + | let mut res = super::testResolver(testStorage10); |
|
| 179 | + | let result = try super::resolveProgramStr(&mut res, "fn read 'r (p: &'r u32) -> u32 { return *p; } fn useOp 'r (p: &'r u32, op: fn(&'r u32) -> u32) -> u32 { return op(p); } fn relay 'a 'b (p: &'a u32) -> u32 { return useOp(p, read 'b); }"); |
|
| 180 | + | let err = try super::expectError(&result); |
|
| 181 | + | let case resolver::ErrorKind::RegionInference(_) = err.kind else throw testing::TestError::Failed; |
|
| 182 | + | } |
|
| 183 | + | } |
|
| 184 | + | ||
| 185 | + | /// A single region parameter requires one consistent named argument. |
|
| 186 | + | @test unsafe fn testRegionCallInferenceFailures() throws (testing::TestError) { |
|
| 187 | + | for program in [ |
|
| 188 | + | "fn pair 'r (a: &'r u32, b: &'r u32) {} fn relay 'a 'b (a: &'a u32, b: &'b u32) { pair(a, b); }", |
|
| 189 | + | "fn read 'r (p: &'r u32) {} fn relay(p: &u32) { read(p); }", |
|
| 190 | + | "unsafe fn read 'r (p: &'r u32) {} unsafe fn relay(p: *unsafe u32) { read(p); }", |
|
| 191 | + | "fn answer 'r () -> u32 { return 42; } fn relay() -> u32 { return answer(); }", |
|
| 192 | + | "fn read 'r (p: ?&'r u32) {} fn relay() { read(nil); }", |
|
| 193 | + | "unsafe fn read 'r (p: ?&'r u32) {} unsafe fn relay() { read(nil); }", |
|
| 194 | + | ] { |
|
| 195 | + | let mut testArena11 = super::testArena(); |
|
| 196 | + | let testStorage11: 'test11 = &mut testArena11 in { |
|
| 197 | + | let mut res = super::testResolver(testStorage11); |
|
| 198 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 199 | + | let err = try super::expectError(&result); |
|
| 200 | + | let case resolver::ErrorKind::RegionInference(_) = err.kind else throw testing::TestError::Failed; |
|
| 201 | + | } |
|
| 202 | + | } |
|
| 203 | + | } |
|
| 204 | + | ||
| 205 | + | /// Inferred and explicit region arguments must satisfy declared parent relations. |
|
| 206 | + | @test unsafe fn testRegionCallParents() throws (testing::TestError) { |
|
| 207 | + | for program in [ |
|
| 208 | + | "fn pair 'p 'c (p: &'p u32, c: &'c u32) where 'p: 'c {} fn relay 'a 'b (a: &'a u32, b: &'b u32) { pair(a, b); }", |
|
| 209 | + | "fn pair 'p 'c (p: &'p u32, c: &'c u32) where 'p: 'c {} unsafe fn relay 'a 'b (a: &'a u32, b: &'b u32) where 'a: 'b { pair 'b 'a (b, a); }", |
|
| 210 | + | ] { |
|
| 211 | + | let mut testArena12 = super::testArena(); |
|
| 212 | + | let testStorage12: 'test12 = &mut testArena12 in { |
|
| 213 | + | let mut res = super::testResolver(testStorage12); |
|
| 214 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 215 | + | let err = try super::expectError(&result); |
|
| 216 | + | let case resolver::ErrorKind::RegionParent(_) = err.kind else throw testing::TestError::Failed; |
|
| 217 | + | } |
|
| 218 | + | } |
|
| 219 | + | } |
|
| 220 | + | ||
| 221 | + | /// Region applications require the declared arity and accessible names. |
|
| 222 | + | @test unsafe fn testExplicitRegionApplicationErrors() throws (testing::TestError) { |
|
| 223 | + | for program in [ |
|
| 224 | + | "fn read 'r (p: &'r u32) {} fn relay 'a 'b (p: &'a u32) { read 'a 'b (p); }", |
|
| 225 | + | "fn read(p: &u32) {} fn relay 'a (p: &'a u32) { read 'a (p); }", |
|
| 226 | + | ] { |
|
| 227 | + | let mut testArena13 = super::testArena(); |
|
| 228 | + | let testStorage13: 'test13 = &mut testArena13 in { |
|
| 229 | + | let mut res = super::testResolver(testStorage13); |
|
| 230 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 231 | + | let err = try super::expectError(&result); |
|
| 232 | + | let case resolver::ErrorKind::RegionArgumentCount(_) = err.kind else throw testing::TestError::Failed; |
|
| 233 | + | } |
|
| 234 | + | } |
|
| 235 | + | let mut testArena14 = super::testArena(); |
|
| 236 | + | let testStorage14: 'test14 = &mut testArena14 in { |
|
| 237 | + | let mut res = super::testResolver(testStorage14); |
|
| 238 | + | let result = try super::resolveProgramStr(&mut res, "fn read 'r (p: &'r u32) {} fn relay 'a (p: &'a u32) { read 'missing (p); }"); |
|
| 239 | + | let err = try super::expectError(&result); |
|
| 240 | + | let case resolver::ErrorKind::UnknownRegion(_) = err.kind else throw testing::TestError::Failed; |
|
| 241 | + | } |
|
| 242 | + | } |
|
| 243 | + | ||
| 244 | + | /// Region header loans stay active through moves and nested exclusive reborrows. |
|
| 245 | + | @test unsafe fn testConcreteBorrowConflicts() throws (testing::TestError) { |
|
| 246 | + | for program in [ |
|
| 247 | + | "fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { set x = 2; } }", |
|
| 248 | + | "unsafe fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { set x = 2; } }", |
|
| 249 | + | "fn run() { let mut x: u32 = 1; let p: 'r = &mut x, q = &mut x in {} }", |
|
| 250 | + | "fn run() { let mut x: u32 = 1; let p: 'r = &x, q = &mut x in {} }", |
|
| 251 | + | "fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { { let q = p; set *q = 2; } set x = 3; } }", |
|
| 252 | + | "fn run() { let mut x: u32 = 1; let p: 'a = &mut x in { let q: 'b = &mut *p in { set *p = 3; } } }", |
|
| 253 | + | "fn run(i: u32, j: u32) { let mut x: [u32; 2] = [1, 2]; let p: 'r = &mut x[i], q = &mut x[j] in {} }", |
|
| 254 | + | ] { |
|
| 255 | + | let mut testArena15 = super::testArena(); |
|
| 256 | + | let testStorage15: 'test15 = &mut testArena15 in { |
|
| 257 | + | let mut res = super::testResolver(testStorage15); |
|
| 258 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 259 | + | let err = try super::expectError(&result); |
|
| 260 | + | let case resolver::ErrorKind::BorrowConflict(_) = err.kind else throw testing::TestError::Failed; |
|
| 261 | + | } |
|
| 262 | + | } |
|
| 263 | + | } |
|
| 264 | + | ||
| 265 | + | /// Borrow headers require existing places and accessible, distinct region names. |
|
| 266 | + | @test unsafe fn testConcreteRegionNamesAndSources() throws (testing::TestError) { |
|
| 267 | + | for program in [ |
|
| 268 | + | "fn run() { let p: 'r = &1 in {} }", |
|
| 269 | + | "fn value() -> u32 { return 1; } fn run() { let p: 'r = &value() in {} }", |
|
| 270 | + | "fn run() { let x: u32 = 1; let p: 'r = &x in { let q: 'r = &x in {} } }", |
|
| 271 | + | "fn run() { let x: u32 = 1; let p: 'r = &x in {} let q: &'r u32 = &x; }", |
|
| 272 | + | "fn run 'a (p: &'a mut u32) { let q: 'b = &mut *p in {} }", |
|
| 273 | + | ] { |
|
| 274 | + | let mut testArena16 = super::testArena(); |
|
| 275 | + | let testStorage16: 'test16 = &mut testArena16 in { |
|
| 276 | + | let mut res = super::testResolver(testStorage16); |
|
| 277 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 278 | + | let _ = try super::expectError(&result); |
|
| 279 | + | } |
|
| 280 | + | } |
|
| 281 | + | ||
| 282 | + | } |
|
| 283 | + | ||
| 284 | + | /// A block-local value cannot supply storage for the whole enclosing region. |
|
| 285 | + | @test unsafe fn testRegionLocalStorageBoundary() throws (testing::TestError) { |
|
| 286 | + | for program in [ |
|
| 287 | + | "fn run() { let a: u32 = 1; let p: 'outer = &a in { let mut cursor = p; let b: u32 = 2; let q: 'inner = &b where 'outer: 'inner in { set cursor = q; } } }", |
|
| 288 | + | "unsafe fn run() { let a: u32 = 1; let p: 'outer = &a in { let mut cursor = p; let b: u32 = 2; let q: 'inner = &b where 'outer: 'inner in { set cursor = q; } } }", |
|
| 289 | + | "fn run() { let x: u32 = 1; let p: 'r = &x in { let y: u32 = 2; let q: &'r u32 = &y; } }", |
|
| 290 | + | "unsafe fn run() { let x: u32 = 1; let p: 'r = &x in { let y: u32 = 2; let q: &'r u32 = &y; } }", |
|
| 291 | + | "fn run() { let mut x: u32 = 1; let p: 'a = &mut x in { let q: 'b = &mut *p in { let outer: &'a mut u32 = q; } } }", |
|
| 292 | + | ] { |
|
| 293 | + | let mut testArena17 = super::testArena(); |
|
| 294 | + | let testStorage17: 'test17 = &mut testArena17 in { |
|
| 295 | + | let mut res = super::testResolver(testStorage17); |
|
| 296 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 297 | + | let err = try super::expectError(&result); |
|
| 298 | + | let case resolver::ErrorKind::TypeMismatch(_) = err.kind else throw testing::TestError::Failed; |
|
| 299 | + | } |
|
| 300 | + | } |
|
| 301 | + | ||
| 302 | + | } |
|
| 303 | + | ||
| 304 | + | /// Generic calls keep the exclusive-argument overlap check after substitution. |
|
| 305 | + | @test unsafe fn testRegionCallBorrowOverlap() throws (testing::TestError) { |
|
| 306 | + | let mut testArena18 = super::testArena(); |
|
| 307 | + | let testStorage18: 'test18 = &mut testArena18 in { |
|
| 308 | + | let mut res = super::testResolver(testStorage18); |
|
| 309 | + | let result = try super::resolveProgramStr(&mut res, "fn pair 'r (a: &'r mut u32, b: &'r mut u32) {} fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { pair(p, p); } }"); |
|
| 310 | + | let err = try super::expectError(&result); |
|
| 311 | + | let case resolver::ErrorKind::BorrowConflict(_) = err.kind else throw testing::TestError::Failed; |
|
| 312 | + | } |
|
| 313 | + | } |
|
| 314 | + | ||
| 315 | + | /// Exact arguments share one descriptor and all applications share the source layout. |
|
| 316 | + | @test unsafe fn testNominalRegionApplications() throws (testing::TestError) { |
|
| 317 | + | let mut testArena19 = super::testArena(); |
|
| 318 | + | let testStorage19: 'test19 = &mut testArena19 in { |
|
| 319 | + | let mut res = super::testResolver(testStorage19); |
|
| 320 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 321 | + | "record N: 'r + Copy { value: u32 } fn f 'a 'b (x: N 'a, y: N 'a, z: N 'b) {}" |
|
| 322 | + | ); |
|
| 323 | + | try super::expectNoErrors(&result); |
|
| 324 | + | let func = try super::getBlockStmt(result.root, 1); |
|
| 325 | + | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
|
| 326 | + | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
|
| 327 | + | let case resolver::Type::Nominal(x) = *info.paramTypes[0] else throw testing::TestError::Failed; |
|
| 328 | + | let case resolver::Type::Nominal(y) = *info.paramTypes[1] else throw testing::TestError::Failed; |
|
| 329 | + | let case resolver::Type::Nominal(z) = *info.paramTypes[2] else throw testing::TestError::Failed; |
|
| 330 | + | try testing::expect(x == y); |
|
| 331 | + | try testing::expect(x <> z); |
|
| 332 | + | try testing::expect(not resolver::typesEqual(*info.paramTypes[0], *info.paramTypes[2])); |
|
| 333 | + | let a = resolver::nominalApplication(x) else throw testing::TestError::Failed; |
|
| 334 | + | let b = resolver::nominalApplication(z) else throw testing::TestError::Failed; |
|
| 335 | + | try testing::expect(a.base == b.base); |
|
| 336 | + | try testing::expect(a.arguments[0].id <> b.arguments[0].id); |
|
| 337 | + | let case resolver::NominalType::Record(base) = *a.base else throw testing::TestError::Failed; |
|
| 338 | + | let case resolver::NominalType::Record(first) = *x else throw testing::TestError::Failed; |
|
| 339 | + | let case resolver::NominalType::Record(second) = *z else throw testing::TestError::Failed; |
|
| 340 | + | try testing::expect(base.layout == first.layout); |
|
| 341 | + | try testing::expect(first.layout == second.layout); |
|
| 342 | + | try testing::expect(first.layout.size == 4); |
|
| 343 | + | } |
|
| 344 | + | } |
|
| 345 | + | ||
| 346 | + | /// Recursive fields refer to their own exact applied descriptor. |
|
| 347 | + | @test unsafe fn testRecursiveNominalApplications() throws (testing::TestError) { |
|
| 348 | + | let mut testArena20 = super::testArena(); |
|
| 349 | + | let testStorage20: 'test20 = &mut testArena20 in { |
|
| 350 | + | let mut res = super::testResolver(testStorage20); |
|
| 351 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 352 | + | "record N: 'r + Copy { next: ?*N 'r } fn f 'a 'b (x: N 'a, y: N 'b) {}" |
|
| 353 | + | ); |
|
| 354 | + | try super::expectNoErrors(&result); |
|
| 355 | + | let func = try super::getBlockStmt(result.root, 1); |
|
| 356 | + | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
|
| 357 | + | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
|
| 358 | + | for parameter in info.paramTypes { |
|
| 359 | + | let case resolver::Type::Nominal(descriptor) = *parameter else throw testing::TestError::Failed; |
|
| 360 | + | let case resolver::NominalType::Record(body) = *descriptor else throw testing::TestError::Failed; |
|
| 361 | + | let case resolver::Type::Optional(inner) = body.fields[0].fieldType else throw testing::TestError::Failed; |
|
| 362 | + | let case resolver::Type::Pointer { target, .. } = *inner else throw testing::TestError::Failed; |
|
| 363 | + | try testing::expect(resolver::typesEqual(*target, *parameter)); |
|
| 364 | + | try testing::expect(body.layout.size == 8); |
|
| 365 | + | } |
|
| 366 | + | } |
|
| 367 | + | } |
|
| 368 | + | ||
| 369 | + | /// An inline union payload retains its enclosing application's region arguments. |
|
| 370 | + | @test unsafe fn testNominalPayloadRegionCapture() throws (testing::TestError) { |
|
| 371 | + | let mut testArena21 = super::testArena(); |
|
| 372 | + | let testStorage21: 'test21 = &mut testArena21 in { |
|
| 373 | + | let mut res = super::testResolver(testStorage21); |
|
| 374 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 375 | + | "record N: 'r + Copy { value: u32 } union U: 'r + Copy { Link { node: *N 'r }, Empty } fn f 'a (u: U 'a, n: N 'a) {}" |
|
| 376 | + | ); |
|
| 377 | + | try super::expectNoErrors(&result); |
|
| 378 | + | let func = try super::getBlockStmt(result.root, 2); |
|
| 379 | + | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
|
| 380 | + | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
|
| 381 | + | let case resolver::Type::Nominal(resolver::NominalType::Union(body)) = *info.paramTypes[0] |
|
| 382 | + | else throw testing::TestError::Failed; |
|
| 383 | + | let case resolver::Type::Nominal(resolver::NominalType::Record(payload)) = body.variants[0].valueType |
|
| 384 | + | else throw testing::TestError::Failed; |
|
| 385 | + | let case resolver::Type::Pointer { target, .. } = payload.fields[0].fieldType |
|
| 386 | + | else throw testing::TestError::Failed; |
|
| 387 | + | try testing::expect(resolver::typesEqual(*target, *info.paramTypes[1])); |
|
| 388 | + | let applied = body.application else throw testing::TestError::Failed; |
|
| 389 | + | let case resolver::NominalType::Union(base) = *applied.base else throw testing::TestError::Failed; |
|
| 390 | + | try testing::expect(body.layout == base.layout); |
|
| 391 | + | } |
|
| 392 | + | } |
|
| 393 | + | ||
| 394 | + | /// Nominal applications require exactly the declared number of region arguments. |
|
| 395 | + | @test unsafe fn testNominalRegionArgumentCount() throws (testing::TestError) { |
|
| 396 | + | for program in [ |
|
| 397 | + | "record N: 'r { value: u32 } fn f(n: N) {}", |
|
| 398 | + | "record N { value: u32 } fn f 'r (n: N 'r) {}", |
|
| 399 | + | "record N: 'r + 's { value: u32 } fn f 'a (n: N 'a) {}", |
|
| 400 | + | "record N: 'r { value: u32 } fn f 'a 'b (n: N 'a 'b) {}", |
|
| 401 | + | "record N: 'r { value: u32 } fn f() { let n = N { value: 1 }; }", |
|
| 402 | + | "union U: 'r { Empty } fn f() { let u = U::Empty; }", |
|
| 403 | + | ] { |
|
| 404 | + | let mut testArena22 = super::testArena(); |
|
| 405 | + | let testStorage22: 'test22 = &mut testArena22 in { |
|
| 406 | + | let mut res = super::testResolver(testStorage22); |
|
| 407 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 408 | + | let error = try super::expectError(&result); |
|
| 409 | + | let case resolver::ErrorKind::RegionArgumentCount(_) = error.kind else throw testing::TestError::Failed; |
|
| 410 | + | } |
|
| 411 | + | } |
|
| 412 | + | } |
|
| 413 | + | ||
| 414 | + | /// Type arguments are invariant in values, constructor hints, and patterns. |
|
| 415 | + | @test unsafe fn testNominalRegionInvariance() throws (testing::TestError) { |
|
| 416 | + | for program in [ |
|
| 417 | + | "record N: 'r + Copy { value: u32 } fn f 'a 'b (n: N 'a) { let x: N 'b = n; }", |
|
| 418 | + | "record N: 'r + Copy { value: u32 } unsafe fn f 'a 'b (n: N 'a) { let x: N 'b = n; }", |
|
| 419 | + | "record N: 'r + Copy { value: u32 } fn f 'a 'b () { let n: N 'a = N 'b { value: 1 }; }", |
|
| 420 | + | "record N: 'r + Copy { value: u32 } fn f 'a 'b (n: N 'a) { let case N 'b { value } = n else panic; }", |
|
| 421 | + | "record N: 'r + Copy (u32); fn f 'a 'b (n: N 'a) { let case N 'b (value) = n else panic; }", |
|
| 422 | + | "record N: 'r + Copy { value: u32 } record M: 'r + Copy { value: u32 } fn f 'a (n: N 'a) { let case M 'a { value } = n else panic; }", |
|
| 423 | + | ] { |
|
| 424 | + | let mut testArena23 = super::testArena(); |
|
| 425 | + | let testStorage23: 'test23 = &mut testArena23 in { |
|
| 426 | + | let mut res = super::testResolver(testStorage23); |
|
| 427 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 428 | + | let error = try super::expectError(&result); |
|
| 429 | + | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
|
| 430 | + | } |
|
| 431 | + | } |
|
| 432 | + | } |
|
| 433 | + | ||
| 434 | + | /// Region inference must agree between an outer reference and its embedded nominal type. |
|
| 435 | + | @test unsafe fn testNominalRegionInferenceConflict() throws (testing::TestError) { |
|
| 436 | + | for program in [ |
|
| 437 | + | "record N: 'r + Copy { value: u32 } fn take 'r (n: &'r N 'r) {} fn f 'a 'b (n: &'a N 'b) { take(n); }", |
|
| 438 | + | "record N: 'r + Copy { value: u32 } fn take 'r (n: N 'r, m: N 'r) {} fn f 'a 'b (n: N 'a, m: N 'b) { take(n, m); }", |
|
| 439 | + | ] { |
|
| 440 | + | let mut testArena24 = super::testArena(); |
|
| 441 | + | let testStorage24: 'test24 = &mut testArena24 in { |
|
| 442 | + | let mut res = super::testResolver(testStorage24); |
|
| 443 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 444 | + | let error = try super::expectError(&result); |
|
| 445 | + | let case resolver::ErrorKind::RegionInference(_) = error.kind else throw testing::TestError::Failed; |
|
| 446 | + | } |
|
| 447 | + | } |
|
| 448 | + | } |
|
| 449 | + | ||
| 450 | + | /// Applications must satisfy the source declaration's explicit parent relations. |
|
| 451 | + | @test unsafe fn testNominalRegionParentConstraint() throws (testing::TestError) { |
|
| 452 | + | let mut testArena25 = super::testArena(); |
|
| 453 | + | let testStorage25: 'test25 = &mut testArena25 in { |
|
| 454 | + | let mut res = super::testResolver(testStorage25); |
|
| 455 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 456 | + | "record N: 'r + 's where 'r: 's { value: u32 } fn f 'a 'b (n: N 'a 'b) {}" |
|
| 457 | + | ); |
|
| 458 | + | let error = try super::expectError(&result); |
|
| 459 | + | let case resolver::ErrorKind::RegionParent(_) = error.kind else throw testing::TestError::Failed; |
|
| 460 | + | } |
|
| 461 | + | } |
|
| 462 | + | ||
| 463 | + | /// An applied affine type retains the source declaration's move rule. |
|
| 464 | + | @test unsafe fn testNominalRegionMoveRule() throws (testing::TestError) { |
|
| 465 | + | let mut testArena26 = super::testArena(); |
|
| 466 | + | let testStorage26: 'test26 = &mut testArena26 in { |
|
| 467 | + | let mut res = super::testResolver(testStorage26); |
|
| 468 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 469 | + | "record N: 'r { value: u32 } fn f 'a (n: N 'a) { let first = n; let second = n; }" |
|
| 470 | + | ); |
|
| 471 | + | let error = try super::expectError(&result); |
|
| 472 | + | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
|
| 473 | + | } |
|
| 474 | + | } |
|
| 475 | + | ||
| 476 | + | /// By-value cycles have no layout, including cycles through exact applications. |
|
| 477 | + | @test unsafe fn testNominalRegionValueCycle() throws (testing::TestError) { |
|
| 478 | + | for program in [ |
|
| 479 | + | "record N: 'r { next: N 'r }", |
|
| 480 | + | "record N: 'r { next: ?N 'r }", |
|
| 481 | + | "union U: 'r { More(U 'r), Empty }", |
|
| 482 | + | "record N: 'r { m: M 'r } record M: 'r { n: N 'r }", |
|
| 483 | + | ] { |
|
| 484 | + | let mut testArena27 = super::testArena(); |
|
| 485 | + | let testStorage27: 'test27 = &mut testArena27 in { |
|
| 486 | + | let mut res = super::testResolver(testStorage27); |
|
| 487 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 488 | + | let error = try super::expectError(&result); |
|
| 489 | + | let case resolver::ErrorKind::RecursiveType = error.kind else throw testing::TestError::Failed; |
|
| 490 | + | } |
|
| 491 | + | } |
|
| 492 | + | } |
|
| 493 | + | ||
| 494 | + | /// Recursive substitutions can permute arguments without expanding the type graph. |
|
| 495 | + | @test unsafe fn testNominalRegionPermutation() throws (testing::TestError) { |
|
| 496 | + | let mut testArena28 = super::testArena(); |
|
| 497 | + | let testStorage28: 'test28 = &mut testArena28 in { |
|
| 498 | + | let mut res = super::testResolver(testStorage28); |
|
| 499 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 500 | + | "record N: 'r + 's + Copy { next: ?*N 's 'r } fn f 'a 'b (x: N 'a 'b, y: N 'b 'a) {}" |
|
| 501 | + | ); |
|
| 502 | + | try super::expectNoErrors(&result); |
|
| 503 | + | let func = try super::getBlockStmt(result.root, 1); |
|
| 504 | + | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
|
| 505 | + | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
|
| 506 | + | for parameter, i in info.paramTypes { |
|
| 507 | + | let case resolver::Type::Nominal(resolver::NominalType::Record(body)) = *parameter |
|
| 508 | + | else throw testing::TestError::Failed; |
|
| 509 | + | let case resolver::Type::Optional(inner) = body.fields[0].fieldType else throw testing::TestError::Failed; |
|
| 510 | + | let case resolver::Type::Pointer { target, .. } = *inner else throw testing::TestError::Failed; |
|
| 511 | + | try testing::expect(resolver::typesEqual(*target, *info.paramTypes[1 - i])); |
|
| 512 | + | } |
|
| 513 | + | } |
|
| 514 | + | } |
|
| 515 | + | ||
| 516 | + | /// Calls transfer named exclusive references on normal and error paths. |
|
| 517 | + | @test unsafe fn testRegionalCallOwnershipTransfer() throws (testing::TestError) { |
|
| 518 | + | for program in [ |
|
| 519 | + | "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32) { take(p); take(p); }", |
|
| 520 | + | "fn take 'r (p: &'r mut u32) {} unsafe fn run 'r (p: &'r mut u32) { take(p); set *p = 1; }", |
|
| 521 | + | "fn take 'r (p: &'r mut [u32]) {} fn run 'r (p: &'r mut [u32]) { take(p); p[0]; }", |
|
| 522 | + | "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32) { let f = take 'r; f(p); set *p = 1; }", |
|
| 523 | + | "union E { Bad } fn take 'r (p: &'r mut u32) throws (E) { throw E::Bad; } fn run 'r (p: &'r mut u32) { try take(p) catch {}; set *p = 1; }", |
|
| 524 | + | "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32, c: bool) { if c { take(p); } set *p = 1; }", |
|
| 525 | + | ] { |
|
| 526 | + | let mut testArena29 = super::testArena(); |
|
| 527 | + | let testStorage29: 'test29 = &mut testArena29 in { |
|
| 528 | + | let mut res = super::testResolver(testStorage29); |
|
| 529 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 530 | + | let error = try super::expectError(&result); |
|
| 531 | + | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
|
| 532 | + | } |
|
| 533 | + | } |
|
| 534 | + | } |
|
| 535 | + | ||
| 536 | + | /// Full-region projections protect their source after a local binding leaves scope. |
|
| 537 | + | @test unsafe fn testRegionalProjectionLifetime() throws (testing::TestError) { |
|
| 538 | + | for program in [ |
|
| 539 | + | "fn f 'r (p: &'r mut u32) { let shared = &*p; let copy = shared; set *p = 1; }", |
|
| 540 | + | "unsafe fn f 'r (p: &'r mut u32) { let shared = &*p; let copy = shared; set *p = 1; }", |
|
| 541 | + | "fn f 'r (p: &'r mut u32, q: &'r u32) { let mut cursor = q; set cursor = &*p; set *p = 1; }", |
|
| 542 | + | "unsafe fn f 'r (p: &'r mut u32, q: &'r u32) { let mut cursor = q; set cursor = &*p; set *p = 1; }", |
|
| 543 | + | "fn f 'r (p: &'r mut u32) { { let q = &mut *p; set *q = 1; } set *p = 2; }", |
|
| 544 | + | "fn f 'r (p: &'r mut u32) { { let q = &*p; } set *p = 2; }", |
|
| 545 | + | "unsafe fn f 'r (p: &'r mut u32) { { let q = &*p; } set *p = 2; }", |
|
| 546 | + | "fn f 'r (p: &'r mut u32) { { let q = &mut *p; let moved = q; } *p; }", |
|
| 547 | + | "record P { a: u32, b: u32 } fn f 'r (p: &'r mut P) { { let q = &mut p.a; } p.a; }", |
|
| 548 | + | "record P { a: u32, b: u32 } fn f 'r (p: &'r mut P) { { let q = &p.a; } set p.a = 2; }", |
|
| 549 | + | ] { |
|
| 550 | + | let mut testArena30 = super::testArena(); |
|
| 551 | + | let testStorage30: 'test30 = &mut testArena30 in { |
|
| 552 | + | let mut res = super::testResolver(testStorage30); |
|
| 553 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 554 | + | let error = try super::expectError(&result); |
|
| 555 | + | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
|
| 556 | + | } |
|
| 557 | + | } |
|
| 558 | + | } |
|
| 559 | + | ||
| 560 | + | /// A regional loan on any live branch remains active at the branch join. |
|
| 561 | + | @test unsafe fn testRegionalProjectionBranchJoin() throws (testing::TestError) { |
|
| 562 | + | for program in [ |
|
| 563 | + | "fn f 'r (p: &'r mut u32, c: bool) { if c { let q = &mut *p; } set *p = 2; }", |
|
| 564 | + | "fn f 'r (p: &'r mut u32, c: bool) { if c {} else { let q = &*p; } set *p = 2; }", |
|
| 565 | + | "fn f 'r (p: &'r mut u32, c: bool) { match c { case true => { let q = &mut *p; } else => {} } *p; }", |
|
| 566 | + | "fn f 'r (p: &'r mut u32, c: ?u32) { if let n = c { let q = &*p; } set *p = 2; }", |
|
| 567 | + | ] { |
|
| 568 | + | let mut testArena31 = super::testArena(); |
|
| 569 | + | let testStorage31: 'test31 = &mut testArena31 in { |
|
| 570 | + | let mut res = super::testResolver(testStorage31); |
|
| 571 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 572 | + | let error = try super::expectError(&result); |
|
| 573 | + | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
|
| 574 | + | } |
|
| 575 | + | } |
|
| 576 | + | } |
|
| 577 | + | ||
| 578 | + | /// Loop back edges carry regional loans into later iterations. |
|
| 579 | + | @test unsafe fn testRegionalProjectionLoopBackEdge() throws (testing::TestError) { |
|
| 580 | + | for program in [ |
|
| 581 | + | "fn f 'r (p: &'r mut u32) { for i in 0..2 { let q = &mut *p; } }", |
|
| 582 | + | "fn f 'r (p: &'r mut u32) { for i in 0..2 { set *p = 1; let q = &*p; } }", |
|
| 583 | + | "fn f 'r (p: &'r mut u32) { for i in 0..2 { set *p = 1; let q = &*p; continue; } }", |
|
| 584 | + | "fn f 'r (p: &'r mut u32, c: bool) { while c { let q = &mut *p; } }", |
|
| 585 | + | "fn f 'r (p: &'r mut u32, c: ?u32) { while let n = c { let q = &mut *p; } }", |
|
| 586 | + | "fn f 'r (p: &'r mut u32) { loop { let q = &mut *p; } }", |
|
| 587 | + | "fn f 'r (p: &'r mut u32, c: bool) { loop { if c { set *p = 1; break; } let q = &*p; } }", |
|
| 588 | + | ] { |
|
| 589 | + | let mut testArena32 = super::testArena(); |
|
| 590 | + | let testStorage32: 'test32 = &mut testArena32 in { |
|
| 591 | + | let mut res = super::testResolver(testStorage32); |
|
| 592 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 593 | + | let error = try super::expectError(&result); |
|
| 594 | + | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
|
| 595 | + | } |
|
| 596 | + | } |
|
| 597 | + | } |
|
| 598 | + | ||
| 599 | + | /// Loans in caller-supplied regions survive loop and nested-region exits. |
|
| 600 | + | @test unsafe fn testRegionalProjectionLoopExit() throws (testing::TestError) { |
|
| 601 | + | for program in [ |
|
| 602 | + | "fn f 'r (p: &'r mut u32) { loop { let q = &mut *p; break; } set *p = 2; }", |
|
| 603 | + | "fn f 'r (p: &'r mut u32, c: bool) { while c { let q = &*p; break; } else {} set *p = 2; }", |
|
| 604 | + | "fn f 'r (p: &'r mut u32) { for i in 0..2 { let q = &*p; break; } else {} set *p = 2; }", |
|
| 605 | + | "fn f 'r (p: &'r mut u32) { let x: u32 = 0; let v: 'child = &x where 'r: 'child in { let q = &*p; } set *p = 2; }", |
|
| 606 | + | ] { |
|
| 607 | + | let mut testArena33 = super::testArena(); |
|
| 608 | + | let testStorage33: 'test33 = &mut testArena33 in { |
|
| 609 | + | let mut res = super::testResolver(testStorage33); |
|
| 610 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 611 | + | let error = try super::expectError(&result); |
|
| 612 | + | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
|
| 613 | + | } |
|
| 614 | + | } |
|
| 615 | + | } |
|
| 616 | + | ||
| 617 | + | /// Region erasure cannot merge distinct error alternatives in one signature. |
|
| 618 | + | @test unsafe fn testRegionalErrorAmbiguity() throws (testing::TestError) { |
|
| 619 | + | for program in [ |
|
| 620 | + | "record E: 'r + Copy {} fn f 'a 'b () throws (E 'a, E 'b) {}", |
|
| 621 | + | "record E: 'r + Copy {} unsafe fn f 'a 'b () throws (E 'a, E 'b) {}", |
|
| 622 | + | "record E: 'r + Copy {} record Callback: 'a + 'b { f: fn() throws (E 'a, E 'b) }", |
|
| 623 | + | "fn f 'a 'b () throws (&'a u32, &'b u32) {}", |
|
| 624 | + | ] { |
|
| 625 | + | let mut testArena34 = super::testArena(); |
|
| 626 | + | let testStorage34: 'test34 = &mut testArena34 in { |
|
| 627 | + | let mut res = super::testResolver(testStorage34); |
|
| 628 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 629 | + | try super::expectErrorKind(&result, resolver::ErrorKind::AmbiguousRegionalError); |
|
| 630 | + | } |
|
| 631 | + | } |
|
| 632 | + | } |
|
| 633 | + | ||
| 634 | + | /// Moving an exclusive reference into an aggregate transfers its ownership. |
|
| 635 | + | @test unsafe fn testRegionalAggregateMove() throws (testing::TestError) { |
|
| 636 | + | for program in [ |
|
| 637 | + | "record W: 'r { p: &'r mut u32 } fn f 'r (p: &'r mut u32) { let w = W 'r { p }; set *p = 1; }", |
|
| 638 | + | "record W: 'r { p: &'r mut u32 } unsafe fn f 'r (p: &'r mut u32) { let w = W 'r { p }; set *p = 1; }", |
|
| 639 | + | "record W: 'r { p: &'r mut u32 } fn f 'r (w: W 'r) { let case W { p } = w else panic; let again = w; }", |
|
| 640 | + | "union W: 'r { P { p: &'r mut u32 } } fn f 'r (p: &'r mut u32) { let w = W 'r::P { p }; set *p = 1; }", |
|
| 641 | + | "fn f 'r (p: &'r mut u32) { let a: [&'r mut u32; 1] = [p]; set *p = 1; }", |
|
| 642 | + | "fn f 'r (p: &'r mut u32) { let a: ?&'r mut u32 = p; set *p = 1; }", |
|
| 643 | + | ] { |
|
| 644 | + | let mut testArena35 = super::testArena(); |
|
| 645 | + | let testStorage35: 'test35 = &mut testArena35 in { |
|
| 646 | + | let mut res = super::testResolver(testStorage35); |
|
| 647 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 648 | + | let error = try super::expectError(&result); |
|
| 649 | + | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
|
| 650 | + | } |
|
| 651 | + | } |
|
| 652 | + | } |
|
| 653 | + | ||
| 654 | + | /// Aggregate transfers do not end a projection loan on its source. |
|
| 655 | + | @test unsafe fn testRegionalAggregateLoan() throws (testing::TestError) { |
|
| 656 | + | for program in [ |
|
| 657 | + | "record W: 'r { p: &'r mut u32 } fn f 'r (p: &'r mut u32) { { let w = W 'r { p: &mut *p }; } set *p = 1; }", |
|
| 658 | + | "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r mut u32) { { let r = R 'r { p: &*p }; } set *p = 1; }", |
|
| 659 | + | "record R: 'r + Copy { p: &'r u32 } fn take 'r (r: R 'r) {} fn f 'r (p: &'r mut u32) { take(R 'r { p: &*p }); set *p = 1; }", |
|
| 660 | + | "fn id 'r (p: &'r mut u32) -> &'r mut u32 { return p; } fn f 'r (p: &'r mut u32) { let q = id(p); { let other = &mut *q; } set *q = 1; }", |
|
| 661 | + | ] { |
|
| 662 | + | let mut testArena36 = super::testArena(); |
|
| 663 | + | let testStorage36: 'test36 = &mut testArena36 in { |
|
| 664 | + | let mut res = super::testResolver(testStorage36); |
|
| 665 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 666 | + | let error = try super::expectError(&result); |
|
| 667 | + | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
|
| 668 | + | } |
|
| 669 | + | } |
|
| 670 | + | } |
|
| 671 | + | ||
| 672 | + | /// Local storage cannot acquire a caller region through returns or aggregate hints. |
|
| 673 | + | @test unsafe fn testRegionalStorageEscape() throws (testing::TestError) { |
|
| 674 | + | for program in [ |
|
| 675 | + | "fn f 'r () -> &'r u32 { let x: u32 = 1; return &x; }", |
|
| 676 | + | "unsafe fn f 'r () -> &'r u32 { let x: u32 = 1; return &x; }", |
|
| 677 | + | "record R: 'r + Copy { p: &'r u32 } fn f 'r () -> R 'r { let x: u32 = 1; return R 'r { p: &x }; }", |
|
| 678 | + | "record R: 'r + Copy { p: &'r u32 } fn f 'r () -> R 'r { let x: u32 = 1; let p: 'short = &x in { return R 'short { p }; } }", |
|
| 679 | + | "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r u32) -> &'r R 'r { let r = R 'r { p }; return &r; }", |
|
| 680 | + | "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r u32) { let mut outer = R 'r { p }; let x: u32 = 1; let q: 'short = &x in { set outer = R 'short { p: q }; } }", |
|
| 681 | + | ] { |
|
| 682 | + | let mut testArena37 = super::testArena(); |
|
| 683 | + | let testStorage37: 'test37 = &mut testArena37 in { |
|
| 684 | + | let mut res = super::testResolver(testStorage37); |
|
| 685 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 686 | + | let error = try super::expectError(&result); |
|
| 687 | + | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
|
| 688 | + | } |
|
| 689 | + | } |
|
| 690 | + | } |
|
| 691 | + | ||
| 692 | + | /// Reference fields retain their ownership and storage restrictions. |
|
| 693 | + | @test unsafe fn testRegionalFieldRestrictions() throws (testing::TestError) { |
|
| 694 | + | { |
|
| 695 | + | let mut testArena38 = super::testArena(); |
|
| 696 | + | let testStorage38: 'test38 = &mut testArena38 in { |
|
| 697 | + | let mut res = super::testResolver(testStorage38); |
|
| 698 | + | let result = try super::resolveProgramStr(&mut res, "record W: 'r { p: &'r mut u32 } fn f 'r (w: W 'r) { let p = w.p; }"); |
|
| 699 | + | try super::expectErrorKind(&result, resolver::ErrorKind::LinearPartialMove); |
|
| 700 | + | } |
|
| 701 | + | } |
|
| 702 | + | { |
|
| 703 | + | let mut testArena39 = super::testArena(); |
|
| 704 | + | let testStorage39: 'test39 = &mut testArena39 in { |
|
| 705 | + | let mut res = super::testResolver(testStorage39); |
|
| 706 | + | let result = try super::resolveProgramStr(&mut res, "record R: 'r { p: &u32 }"); |
|
| 707 | + | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition); |
|
| 708 | + | } |
|
| 709 | + | } |
|
| 710 | + | { |
|
| 711 | + | let mut testArena40 = super::testArena(); |
|
| 712 | + | let testStorage40: 'test40 = &mut testArena40 in { |
|
| 713 | + | let mut res = super::testResolver(testStorage40); |
|
| 714 | + | let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { static r: &'r u32 = undefined; }"); |
|
| 715 | + | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition); |
|
| 716 | + | } |
|
| 717 | + | } |
|
| 718 | + | } |
|
| 719 | + | ||
| 720 | + | /// Regional projection tracking limits simultaneous live projections. |
|
| 721 | + | @test unsafe fn testRegionalLoanCapacity() throws (testing::TestError) { |
|
| 722 | + | let mut reuseArena = super::testArena(); |
|
| 723 | + | let reuseStorage: 'reuse = &mut reuseArena in { |
|
| 724 | + | let mut res = super::testResolver(reuseStorage); |
|
| 725 | + | let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { let outer: 'outer = &*p where 'r: 'outer in { let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } } }"); |
|
| 726 | + | try super::expectNoErrors(&result); |
|
| 727 | + | } |
|
| 728 | + | let mut testArena41 = super::testArena(); |
|
| 729 | + | let testStorage41: 'test41 = &mut testArena41 in { |
|
| 730 | + | let mut res = super::testResolver(testStorage41); |
|
| 731 | + | let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { [&*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p]; }"); |
|
| 732 | + | try super::expectErrorKind(&result, resolver::ErrorKind::RegionalLoanOverflow); |
|
| 733 | + | } |
|
| 734 | + | } |
|
| 735 | + | ||
| 736 | + | /// Shared regional parameters consume exclusive arguments that can be retained. |
|
| 737 | + | @test unsafe fn testRegionalSharedCallTransfer() throws (testing::TestError) { |
|
| 738 | + | for program in [ |
|
| 739 | + | "fn read 'r (p: &'r u32) -> &'r u32 { return p; } fn f 'r (p: &'r mut u32) -> u32 { let q = read(p); set *p = 2; return *q; }", |
|
| 740 | + | "fn read 'r (p: &'r u32) -> &'r u32 { return p; } unsafe fn f 'r (p: &'r mut u32) { let q = read(p); set *p = 2; }", |
|
| 741 | + | "fn read 'r (p: &'r [u32]) -> &'r [u32] { return p; } fn f 'r (p: &'r mut [u32]) { let q = read(p); set p[0] = 2; }", |
|
| 742 | + | "fn read 'r (p: &'r u32) -> &'r u32 { return p; } fn f 'r (p: &'r mut u32) { let callback = read 'r; let q = callback(p); set *p = 2; }", |
|
| 743 | + | ] { |
|
| 744 | + | let mut testArena42 = super::testArena(); |
|
| 745 | + | let testStorage42: 'test42 = &mut testArena42 in { |
|
| 746 | + | let mut res = super::testResolver(testStorage42); |
|
| 747 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 748 | + | let error = try super::expectError(&result); |
|
| 749 | + | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
|
| 750 | + | } |
|
| 751 | + | } |
|
| 752 | + | } |
|
| 753 | + | ||
| 754 | + | /// Values stored in a regional destination must cover its full lifetime. |
|
| 755 | + | @test unsafe fn testRegionalStoreLifetime() throws (testing::TestError) { |
|
| 756 | + | for program in [ |
|
| 757 | + | "fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) { set *dst = p; }", |
|
| 758 | + | "unsafe fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) { set *dst = p; }", |
|
| 759 | + | "fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) where 'a: 'b { set *dst = p; }", |
|
| 760 | + | "record R: 'r + Copy { p: &'r u32 } fn f 'a 'b (dst: &'a mut R 'b, p: &'b u32) { set *dst = R 'b { p }; }", |
|
| 761 | + | "fn f 'a 'b (dst: &'a mut [&'b u32], p: &'b u32) { set dst[0] = p; }", |
|
| 762 | + | "fn f 'a 'b (dst: &'a mut [&'b u32], p: &'b u32) { set dst[..] = p; }", |
|
| 763 | + | "fn f 'a 'b (dst: &'a mut fn(&'b u32), callback: fn(&'b u32)) { set *dst = callback; }", |
|
| 764 | + | ] { |
|
| 765 | + | let mut testArena43 = super::testArena(); |
|
| 766 | + | let testStorage43: 'test43 = &mut testArena43 in { |
|
| 767 | + | let mut res = super::testResolver(testStorage43); |
|
| 768 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 769 | + | let error = try super::expectError(&result); |
|
| 770 | + | let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed; |
|
| 771 | + | } |
|
| 772 | + | } |
|
| 773 | + | } |
|
| 774 | + | ||
| 775 | + | /// A session holds the arena loan after the interface is moved or discarded. |
|
| 776 | + | @test unsafe fn testSessionArenaLoan() throws (testing::TestError) { |
|
| 777 | + | for program in [ |
|
| 778 | + | "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { alloc::reset(arena); } }", |
|
| 779 | + | "use std::lang::alloc; unsafe fn f(arena: &mut alloc::Arena) { use *arena as s in { alloc::reset(arena); } }", |
|
| 780 | + | "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { let moved = s; alloc::reset(arena); } }", |
|
| 781 | + | "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { let _ = s; set arena.offset = 0; } }", |
|
| 782 | + | "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { use *arena as t in {} } }", |
|
| 783 | + | ] { |
|
| 784 | + | let mut testArena44 = super::testArena(); |
|
| 785 | + | let testStorage44: 'test44 = &mut testArena44 in { |
|
| 786 | + | let mut res = super::testResolver(testStorage44); |
|
| 787 | + | let result = try super::resolveSessionProgramStr(&mut res, program); |
|
| 788 | + | let error = try super::expectError(&result); |
|
| 789 | + | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
|
| 790 | + | } |
|
| 791 | + | } |
|
| 792 | + | } |
|
| 793 | + | ||
| 794 | + | /// Session interfaces have affine ownership and invariant region arguments. |
|
| 795 | + | @test unsafe fn testSessionOwnership() throws (testing::TestError) { |
|
| 796 | + | for program in [ |
|
| 797 | + | "fn f 'r (s: Session 'r) { let moved = s; let again = s; }", |
|
| 798 | + | "unsafe fn f 'r (s: Session 'r) { let moved = s; let again = s; }", |
|
| 799 | + | "fn take 'r (s: Session 'r) {} fn f 'r (s: Session 'r) { take(s); take(s); }", |
|
| 800 | + | ] { |
|
| 801 | + | let mut testArena45 = super::testArena(); |
|
| 802 | + | let testStorage45: 'test45 = &mut testArena45 in { |
|
| 803 | + | let mut res = super::testResolver(testStorage45); |
|
| 804 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 805 | + | let error = try super::expectError(&result); |
|
| 806 | + | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
|
| 807 | + | } |
|
| 808 | + | } |
|
| 809 | + | { |
|
| 810 | + | let mut testArena46 = super::testArena(); |
|
| 811 | + | let testStorage46: 'test46 = &mut testArena46 in { |
|
| 812 | + | let mut res = super::testResolver(testStorage46); |
|
| 813 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 814 | + | "fn f 'r 's (s: Session 's) -> Session 'r where 'r: 's { return s; }"); |
|
| 815 | + | let error = try super::expectError(&result); |
|
| 816 | + | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
|
| 817 | + | } |
|
| 818 | + | } |
|
| 819 | + | } |
|
| 820 | + | ||
| 821 | + | /// Regional `use` blocks require a mutable allocation trait implementer. |
|
| 822 | + | @test unsafe fn testSessionSource() throws (testing::TestError) { |
|
| 823 | + | for program in [ |
|
| 824 | + | "record Arena { data: *mut [u8], offset: u32 } fn f(arena: &mut Arena) { use *arena as s in {} }", |
|
| 825 | + | "fn f(value: &mut u32) { use *value as s in {} }", |
|
| 826 | + | ] { |
|
| 827 | + | let mut testArena47 = super::testArena(); |
|
| 828 | + | let testStorage47: 'test47 = &mut testArena47 in { |
|
| 829 | + | let mut res = super::testResolver(testStorage47); |
|
| 830 | + | let result = try super::resolveSessionProgramStr(&mut res, program); |
|
| 831 | + | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidSessionSource); |
|
| 832 | + | } |
|
| 833 | + | } |
|
| 834 | + | let mut customArena = super::testArena(); |
|
| 835 | + | let customStorage: 'custom = &mut customArena in { |
|
| 836 | + | let mut res = super::testResolver(customStorage); |
|
| 837 | + | let result = try super::resolveSessionProgramStr(&mut res, |
|
| 838 | + | "use std::lang::alloc; record Wrapper: 'r { arena: &'r mut alloc::Arena } instance alloc::Alloc for Wrapper 'r { unsafe fn (wrapper: &mut Wrapper 'r) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (alloc::AllocError) { panic; } unsafe fn (wrapper: &mut Wrapper 'r) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (alloc::AllocError) { panic; } } fn f 'r (wrapper: &'r mut Wrapper 'r) { use *wrapper as session in { try! session.new(1 as u32); } }"); |
|
| 839 | + | try super::expectNoErrors(&result); |
|
| 840 | + | } |
|
| 841 | + | } |
|
| 842 | + | ||
| 843 | + | /// Region-dependent values cannot be manufactured from uninitialized storage. |
|
| 844 | + | @test unsafe fn testRegionalUndefined() throws (testing::TestError) { |
|
| 845 | + | for program in [ |
|
| 846 | + | "unsafe fn f 'r () -> Session 'r { return undefined; }", |
|
| 847 | + | "record R: 'r { s: Session 'r } unsafe fn f 'r () -> R 'r { return undefined; }", |
|
| 848 | + | "unsafe fn f 'r () -> *unsafe Session 'r { return undefined; }", |
|
| 849 | + | "unsafe fn f 'r () -> &'r u32 { return undefined; }", |
|
| 850 | + | ] { |
|
| 851 | + | let mut testArena48 = super::testArena(); |
|
| 852 | + | let testStorage48: 'test48 = &mut testArena48 in { |
|
| 853 | + | let mut res = super::testResolver(testStorage48); |
|
| 854 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 855 | + | let error = try super::expectError(&result); |
|
| 856 | + | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
|
| 857 | + | } |
|
| 858 | + | } |
|
| 859 | + | } |
|
| 860 | + | ||
| 861 | + | /// Pointer casts cannot introduce session or aggregate region dependencies. |
|
| 862 | + | @test unsafe fn testRegionalCastForgery() throws (testing::TestError) { |
|
| 863 | + | for program in [ |
|
| 864 | + | "unsafe fn f 'r (p: *unsafe opaque) -> *unsafe Session 'r { return p as *unsafe Session 'r; }", |
|
| 865 | + | "unsafe fn f 'r (p: *unsafe [opaque]) -> *unsafe [Session 'r] { return p as *unsafe [Session 'r]; }", |
|
| 866 | + | "record R: 'r { s: Session 'r } unsafe fn f 'r (p: *unsafe opaque) -> *unsafe R 'r { return p as *unsafe R 'r; }", |
|
| 867 | + | "record R: 'r + Copy { p: &'r u32 } unsafe fn f 'r 's (p: *unsafe R 'r) -> *unsafe R 's { return p as *unsafe R 's; }", |
|
| 868 | + | ] { |
|
| 869 | + | let mut testArena49 = super::testArena(); |
|
| 870 | + | let testStorage49: 'test49 = &mut testArena49 in { |
|
| 871 | + | let mut res = super::testResolver(testStorage49); |
|
| 872 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 873 | + | let error = try super::expectError(&result); |
|
| 874 | + | let case resolver::ErrorKind::InvalidAsCast(_) = error.kind else throw testing::TestError::Failed; |
|
| 875 | + | } |
|
| 876 | + | } |
|
| 877 | + | } |
|
| 878 | + | ||
| 879 | + | /// Bulk allocation rejects resources and values that need ownership cleanup. |
|
| 880 | + | @test unsafe fn testSessionAllocationValue() throws (testing::TestError) { |
|
| 881 | + | for program in [ |
|
| 882 | + | "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
|
| 883 | + | "record R: Once { n: u32 } unsafe fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
|
| 884 | + | "record R: Once { n: u32 } record Outer { value: R } fn f 'r (s: &Session 'r, value: Outer) { try! s.new(value); }", |
|
| 885 | + | "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: ?R) { try! s.new(value); }", |
|
| 886 | + | "fn f 'r (s: &Session 'r, value: Session 'r) { try! s.new(value); }", |
|
| 887 | + | "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: [R; 1]) { try! s.new(value); }", |
|
| 888 | + | "fn f 'r (s: &Session 'r, value: &'r mut u32) { try! s.fill(value, 2); }", |
|
| 889 | + | "fn f 'r (s: &Session 'r, value: &[&'r mut u32]) { try! s.copy(value); }", |
|
| 890 | + | ] { |
|
| 891 | + | let mut testArena50 = super::testArena(); |
|
| 892 | + | let testStorage50: 'test50 = &mut testArena50 in { |
|
| 893 | + | let mut res = super::testResolver(testStorage50); |
|
| 894 | + | let result = try super::resolveSessionProgramStr(&mut res, program); |
|
| 895 | + | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidAllocationValue); |
|
| 896 | + | } |
|
| 897 | + | } |
|
| 898 | + | } |
|
| 899 | + | ||
| 900 | + | /// Allocated values must retain dependencies that cover the destination session. |
|
| 901 | + | @test unsafe fn testSessionAllocationDependency() throws (testing::TestError) { |
|
| 902 | + | for program in [ |
|
| 903 | + | "fn f 'r 's (a: &Session 'r, p: &'s u32) where 'r: 's { try! a.new(p); }", |
|
| 904 | + | "unsafe fn f 'r 's (a: &Session 'r, p: &'s u32) where 'r: 's { try! a.new(p); }", |
|
| 905 | + | "record R: 's + Copy { p: &'s u32 } fn f 'r 's (a: &Session 'r, p: R 's) where 'r: 's { try! a.new(p); }", |
|
| 906 | + | "fn f 'r 's (a: &Session 'r, p: &[&'s u32]) where 'r: 's { try! a.copy(p); }", |
|
| 907 | + | ] { |
|
| 908 | + | let mut testArena51 = super::testArena(); |
|
| 909 | + | let testStorage51: 'test51 = &mut testArena51 in { |
|
| 910 | + | let mut res = super::testResolver(testStorage51); |
|
| 911 | + | let result = try super::resolveSessionProgramStr(&mut res, program); |
|
| 912 | + | let error = try super::expectError(&result); |
|
| 913 | + | let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed; |
|
| 914 | + | } |
|
| 915 | + | } |
|
| 916 | + | } |
|
| 917 | + | ||
| 918 | + | /// Allocation operations require error handling and exact source region results. |
|
| 919 | + | @test unsafe fn testSessionAllocationContract() throws (testing::TestError) { |
|
| 920 | + | { |
|
| 921 | + | let mut testArena52 = super::testArena(); |
|
| 922 | + | let testStorage52: 'test52 = &mut testArena52 in { |
|
| 923 | + | let mut res = super::testResolver(testStorage52); |
|
| 924 | + | let result = try super::resolveSessionProgramStr(&mut res, |
|
| 925 | + | "fn f 'r (s: &Session 'r) { s.new(1 as u32); }"); |
|
| 926 | + | try super::expectErrorKind(&result, resolver::ErrorKind::MissingTry); |
|
| 927 | + | } |
|
| 928 | + | } |
|
| 929 | + | { |
|
| 930 | + | let mut testArena53 = super::testArena(); |
|
| 931 | + | let testStorage53: 'test53 = &mut testArena53 in { |
|
| 932 | + | let mut res = super::testResolver(testStorage53); |
|
| 933 | + | let result = try super::resolveSessionProgramStr(&mut res, |
|
| 934 | + | "fn f 'r 's (a: &Session 'r) -> &'s mut u32 { return try! a.new(1 as u32); }"); |
|
| 935 | + | let error = try super::expectError(&result); |
|
| 936 | + | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
|
| 937 | + | } |
|
| 938 | + | } |
|
| 939 | + | { |
|
| 940 | + | let mut testArena54 = super::testArena(); |
|
| 941 | + | let testStorage54: 'test54 = &mut testArena54 in { |
|
| 942 | + | let mut res = super::testResolver(testStorage54); |
|
| 943 | + | let result = try super::resolveSessionProgramStr(&mut res, |
|
| 944 | + | "fn f 'r (a: &Session 'r, p: &u32) { try! a.new(p); }"); |
|
| 945 | + | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition); |
|
| 946 | + | } |
|
| 947 | + | } |
|
| 948 | + | } |
|
| 949 | + | ||
| 950 | + | /// Typed reservations reject array products, aggregate sums, and field-offset overflow. |
|
| 951 | + | @test unsafe fn testSessionAllocationLayout() throws (testing::TestError) { |
|
| 952 | + | for program in [ |
|
| 953 | + | "fn f 'r (s: &Session 'r, value: [u64; 536870913]) { try! s.new(value); }", |
|
| 954 | + | "unsafe fn f 'r (s: &Session 'r, value: [u64; 536870913]) { try! s.new(value); }", |
|
| 955 | + | "record R { a: [u8; 4294967290], b: u64 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
|
| 956 | + | "record R { a: [u8; 2147483648], b: u8 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
|
| 957 | + | "fn f 'r (s: &Session 'r, value: ?[u8; 4294967295]) { try! s.new(value); }", |
|
| 958 | + | "union R { Data([u8; 4294967295]) } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
|
| 959 | + | ] { |
|
| 960 | + | let mut testArena55 = super::testArena(); |
|
| 961 | + | let testStorage55: 'test55 = &mut testArena55 in { |
|
| 962 | + | let mut res = super::testResolver(testStorage55); |
|
| 963 | + | let result = try super::resolveSessionProgramStr(&mut res, program); |
|
| 964 | + | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidAllocationLayout); |
|
| 965 | + | } |
|
| 966 | + | } |
|
| 967 | + | } |
|
| 968 | + | ||
| 969 | + | /// Cell payloads must permit plain value copies. |
|
| 970 | + | @test unsafe fn testCellPointerPayload() throws (testing::TestError) { |
|
| 971 | + | for program in [ |
|
| 972 | + | "union State: 'r { Next(&'r cell State 'r) }", |
|
| 973 | + | "union State: 'r { Ready(&'r u32) } fn f 'r (state: &'r cell State 'r) {}", |
|
| 974 | + | "union State: 'r { Ready(&'r u32) } unsafe fn f 'r (state: &'r cell State 'r) {}", |
|
| 975 | + | "fn f(p: *cell *mut u32) {}", |
|
| 976 | + | "unsafe fn f(p: *cell *mut u32) {}", |
|
| 977 | + | "record R: Once { n: u32 } fn f(p: *cell R) {}", |
|
| 978 | + | "record R { n: u32 } fn f(p: *cell R) {}", |
|
| 979 | + | "fn f 'r (p: &'r cell &'r mut u32) {}", |
|
| 980 | + | ] { |
|
| 981 | + | let mut testArena56 = super::testArena(); |
|
| 982 | + | let testStorage56: 'test56 = &mut testArena56 in { |
|
| 983 | + | let mut res = super::testResolver(testStorage56); |
|
| 984 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 985 | + | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload); |
|
| 986 | + | } |
|
| 987 | + | } |
|
| 988 | + | } |
|
| 989 | + | ||
| 990 | + | /// Cell access cannot expose payload references or restore exclusive pointers. |
|
| 991 | + | @test unsafe fn testCellPointerAccess() throws (testing::TestError) { |
|
| 992 | + | for program in [ |
|
| 993 | + | "fn f(p: *cell u32) { let r = &*p; }", |
|
| 994 | + | "unsafe fn f(p: *cell u32) { let r = &*p; }", |
|
| 995 | + | "record R: Copy { n: u32 } fn f(p: *cell R) { let r = &(*p).n; }", |
|
| 996 | + | "fn f(p: *cell [u32; 2]) { let r = &(*p)[0]; }", |
|
| 997 | + | "record R: Copy { n: u32 } fn f(p: *cell R) { set (*p).n = 1; }", |
|
| 998 | + | "fn f(p: *cell [u32; 2]) { set (*p)[0] = 1; }", |
|
| 999 | + | ] { |
|
| 1000 | + | let mut testArena57 = super::testArena(); |
|
| 1001 | + | let testStorage57: 'test57 = &mut testArena57 in { |
|
| 1002 | + | let mut res = super::testResolver(testStorage57); |
|
| 1003 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1004 | + | try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding); |
|
| 1005 | + | } |
|
| 1006 | + | } |
|
| 1007 | + | for program in [ |
|
| 1008 | + | "fn f(p: *cell u32) -> *mut u32 { return p as *mut u32; }", |
|
| 1009 | + | "unsafe fn f(p: *cell u32) -> *unsafe mut u32 { return p as *unsafe mut u32; }", |
|
| 1010 | + | "fn f(p: *u32) -> *cell u32 { return p as *cell u32; }", |
|
| 1011 | + | "unsafe fn f(p: *unsafe mut u32) -> *cell u32 { return p as *cell u32; }", |
|
| 1012 | + | "fn f(p: *mut u64) -> *cell u32 { return p as *cell u32; }", |
|
| 1013 | + | "fn f 'r (p: &'r mut u32) -> *cell u32 { return p as *cell u32; }", |
|
| 1014 | + | ] { |
|
| 1015 | + | let mut testArena58 = super::testArena(); |
|
| 1016 | + | let testStorage58: 'test58 = &mut testArena58 in { |
|
| 1017 | + | let mut res = super::testResolver(testStorage58); |
|
| 1018 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1019 | + | let error = try super::expectError(&result); |
|
| 1020 | + | let case resolver::ErrorKind::InvalidAsCast(_) = error.kind else throw testing::TestError::Failed; |
|
| 1021 | + | } |
|
| 1022 | + | } |
|
| 1023 | + | } |
|
| 1024 | + | ||
| 1025 | + | /// An exclusive payload borrow cannot be made from shared cell access. |
|
| 1026 | + | @test unsafe fn testCellPointerMutablePayload() throws (testing::TestError) { |
|
| 1027 | + | let mut testArena59 = super::testArena(); |
|
| 1028 | + | let testStorage59: 'test59 = &mut testArena59 in { |
|
| 1029 | + | let mut res = super::testResolver(testStorage59); |
|
| 1030 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 1031 | + | "fn f(p: *cell u32) { let r = &mut *p; }"); |
|
| 1032 | + | try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding); |
|
| 1033 | + | } |
|
| 1034 | + | } |
|
| 1035 | + | ||
| 1036 | + | /// Cell conversion consumes exclusive access, including call arguments. |
|
| 1037 | + | @test unsafe fn testCellPointerOwnership() throws (testing::TestError) { |
|
| 1038 | + | for program in [ |
|
| 1039 | + | "fn f(p: *mut u32) { let c = p as *cell u32; set *p = 1; }", |
|
| 1040 | + | "unsafe fn f(p: *mut u32) { let c = p as *cell u32; set *p = 1; }", |
|
| 1041 | + | "fn read(p: &cell u32) {} fn f(p: &mut u32) { read(p as &cell u32); set *p = 1; }", |
|
| 1042 | + | ] { |
|
| 1043 | + | let mut testArena60 = super::testArena(); |
|
| 1044 | + | let testStorage60: 'test60 = &mut testArena60 in { |
|
| 1045 | + | let mut res = super::testResolver(testStorage60); |
|
| 1046 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1047 | + | let error = try super::expectError(&result); |
|
| 1048 | + | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
|
| 1049 | + | } |
|
| 1050 | + | } |
|
| 1051 | + | for program in [ |
|
| 1052 | + | "fn f() { let mut n: u32 = 0; let c = &mut n as &cell u32; let r = &n; set *c = 1; }", |
|
| 1053 | + | "unsafe fn f() { let mut n: u32 = 0; let c = &mut n as &cell u32; let r = &n; set *c = 1; }", |
|
| 1054 | + | ] { |
|
| 1055 | + | let mut testArena61 = super::testArena(); |
|
| 1056 | + | let testStorage61: 'test61 = &mut testArena61 in { |
|
| 1057 | + | let mut res = super::testResolver(testStorage61); |
|
| 1058 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1059 | + | let error = try super::expectError(&result); |
|
| 1060 | + | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
|
| 1061 | + | } |
|
| 1062 | + | } |
|
| 1063 | + | } |
|
| 1064 | + | ||
| 1065 | + | /// Cell writes preserve exact payload regions and destination lifetimes. |
|
| 1066 | + | @test unsafe fn testCellPointerRegion() throws (testing::TestError) { |
|
| 1067 | + | for program in [ |
|
| 1068 | + | "fn f 'r 's (p: &'r cell &'r u32, value: &'s u32) { set *p = value; }", |
|
| 1069 | + | "unsafe fn f 'r 's (p: &'r cell &'r u32, value: &'s u32) { set *p = value; }", |
|
| 1070 | + | "fn f 'r 's (p: &'r cell u32) -> &'s cell u32 { return p; }", |
|
| 1071 | + | ] { |
|
| 1072 | + | let mut testArena62 = super::testArena(); |
|
| 1073 | + | let testStorage62: 'test62 = &mut testArena62 in { |
|
| 1074 | + | let mut res = super::testResolver(testStorage62); |
|
| 1075 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1076 | + | let error = try super::expectError(&result); |
|
| 1077 | + | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
|
| 1078 | + | } |
|
| 1079 | + | } |
|
| 1080 | + | for program in [ |
|
| 1081 | + | "fn f 'r 's (p: &'r cell &'s u32, value: &'s u32) where 'r: 's { set *p = value; }", |
|
| 1082 | + | "unsafe fn f 'r 's (p: &'r cell &'s u32, value: &'s u32) where 'r: 's { set *p = value; }", |
|
| 1083 | + | ] { |
|
| 1084 | + | let mut testArena63 = super::testArena(); |
|
| 1085 | + | let testStorage63: 'test63 = &mut testArena63 in { |
|
| 1086 | + | let mut res = super::testResolver(testStorage63); |
|
| 1087 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1088 | + | let error = try super::expectError(&result); |
|
| 1089 | + | let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed; |
|
| 1090 | + | } |
|
| 1091 | + | } |
|
| 1092 | + | } |
|
| 1093 | + | ||
| 1094 | + | /// Raw cell operations require an unsafe execution context. |
|
| 1095 | + | @test unsafe fn testCellPointerRawAccess() throws (testing::TestError) { |
|
| 1096 | + | for program in [ |
|
| 1097 | + | "fn f(p: *unsafe cell u32) -> u32 { return *p; }", |
|
| 1098 | + | "fn f(p: *unsafe cell u32) { set *p = 1; }", |
|
| 1099 | + | "fn f(p: &*unsafe cell u32) { set **p = 1; }", |
|
| 1100 | + | ] { |
|
| 1101 | + | let mut testArena64 = super::testArena(); |
|
| 1102 | + | let testStorage64: 'test64 = &mut testArena64 in { |
|
| 1103 | + | let mut res = super::testResolver(testStorage64); |
|
| 1104 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1105 | + | try super::expectErrorKind(&result, resolver::ErrorKind::UnsafeOperation); |
|
| 1106 | + | } |
|
| 1107 | + | } |
|
| 1108 | + | } |
|
| 1109 | + | ||
| 1110 | + | /// A checked buffer context cannot be copied while its exclusive storage is live. |
|
| 1111 | + | @test unsafe fn testCheckedBufferOwnership() throws (testing::TestError) { |
|
| 1112 | + | for program in [ |
|
| 1113 | + | "record Buffer: 'r { words: &'r mut [u32] } fn observe 'r (b: &Buffer 'r) {} fn f 'r (b: Buffer 'r) { let moved = b; observe(&b); }", |
|
| 1114 | + | "record Buffer: 'r { words: &'r mut [u32] } unsafe fn observe 'r (b: &Buffer 'r) {} unsafe fn f 'r (b: Buffer 'r) { let moved = b; observe(&b); }", |
|
| 1115 | + | ] { |
|
| 1116 | + | let mut testArena65 = super::testArena(); |
|
| 1117 | + | let testStorage65: 'test65 = &mut testArena65 in { |
|
| 1118 | + | let mut res = super::testResolver(testStorage65); |
|
| 1119 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1120 | + | let error = try super::expectError(&result); |
|
| 1121 | + | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
|
| 1122 | + | } |
|
| 1123 | + | } |
|
| 1124 | + | } |
|
| 1125 | + | ||
| 1126 | + | /// An iterator's stored shared borrow excludes writes through the buffer owner. |
|
| 1127 | + | @test unsafe fn testCheckedIteratorLoan() throws (testing::TestError) { |
|
| 1128 | + | for program in [ |
|
| 1129 | + | "record Buffer: 'r { words: &'r mut [u32] } record Iter: 'r + 'v + Copy where 'r: 'v { source: &'v Buffer 'r } fn f 'r (b: Buffer 'r) { let mut owner = b; let view: 'scan = &owner where 'r: 'scan in { let iterator: Iter 'r 'scan = Iter { source: view }; set owner.words[0] = 1; } }", |
|
| 1130 | + | "record Buffer: 'r { words: &'r mut [u32] } record Iter: 'r + 'v + Copy where 'r: 'v { source: &'v Buffer 'r } unsafe fn f 'r (b: Buffer 'r) { let mut owner = b; let view: 'scan = &owner where 'r: 'scan in { let iterator: Iter 'r 'scan = Iter { source: view }; set owner.words[0] = 1; } }", |
|
| 1131 | + | ] { |
|
| 1132 | + | let mut testArena66 = super::testArena(); |
|
| 1133 | + | let testStorage66: 'test66 = &mut testArena66 in { |
|
| 1134 | + | let mut res = super::testResolver(testStorage66); |
|
| 1135 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1136 | + | let error = try super::expectError(&result); |
|
| 1137 | + | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
|
| 1138 | + | } |
|
| 1139 | + | } |
|
| 1140 | + | } |
|
| 1141 | + | ||
| 1142 | + | /// An inferred raw pointer argument requires an unsafe address operation. |
|
| 1143 | + | @test unsafe fn testRegionCallRawAddress() throws (testing::TestError) { |
|
| 1144 | + | let mut arena = super::testArena(); |
|
| 1145 | + | let storage: 'arena = &mut arena in { |
|
| 1146 | + | let mut res = super::testResolver(storage); |
|
| 1147 | + | let result = try super::resolveProgramStr(&mut res, |
|
| 1148 | + | "record View: 'r + Copy { value: &'r u32 } fn read 'r (view: *unsafe View 'r) {} fn f 'r (view: View 'r) { read(&view); }"); |
|
| 1149 | + | try super::expectErrorKind(&result, resolver::ErrorKind::UnsafeOperation); |
|
| 1150 | + | } |
|
| 1151 | + | } |
|
| 1152 | + | ||
| 1153 | + | /// Shared aggregate access cannot expose mutable access to an exclusive field. |
|
| 1154 | + | @test unsafe fn testSharedExclusiveFieldAccess() throws (testing::TestError) { |
|
| 1155 | + | for program in [ |
|
| 1156 | + | "record H: 'r { items: &'r mut [u32] } fn f 'r (h: &H 'r) { set h.items[0] = 42; }", |
|
| 1157 | + | "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r (h: &H 'r) { set h.items[0] = 42; }", |
|
| 1158 | + | "record H: 'r { items: &'r mut [u32] } fn f 'r (h: &'r H 'r) { let p = &mut h.items[0]; }", |
|
| 1159 | + | "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r (h: &'r H 'r) { let p = &mut h.items[0]; }", |
|
| 1160 | + | "record H: 'r { items: &'r mut [u32] } fn write(p: &mut [u32]) {} fn f 'r (h: &H 'r) { write(h.items); }", |
|
| 1161 | + | "record H: 'r { items: &'r mut [u32] } fn write(p: &mut [u32]) {} unsafe fn f 'r (h: &H 'r) { write(h.items); }", |
|
| 1162 | + | "record H: 'r { item: &'r mut u32 } fn f 'r (h: &H 'r) { let p: &mut u32 = h.item; }", |
|
| 1163 | + | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r (h: &H 'r) { let p: &mut u32 = h.item; }", |
|
| 1164 | + | "record H { item: *mut u32 } fn f(h: &H) { set *h.item = 42; }", |
|
| 1165 | + | "record H { item: *mut u32 } unsafe fn f(h: &H) { set *h.item = 42; }", |
|
| 1166 | + | "record H: 'r { items: [&'r mut u32; 1] } fn f 'r (h: &H 'r) { set *h.items[0] = 42; }", |
|
| 1167 | + | "record H: 'r { items: [&'r mut u32; 1] } unsafe fn f 'r (h: &H 'r) { set *h.items[0] = 42; }", |
|
| 1168 | + | "fn f 'r (items: &'r [&'r mut u32]) { set *items[0] = 42; }", |
|
| 1169 | + | "unsafe fn f 'r (items: &'r [&'r mut u32]) { set *items[0] = 42; }", |
|
| 1170 | + | "fn f 'r (p: &'r &'r mut u32) { set **p = 42; }", |
|
| 1171 | + | "unsafe fn f 'r (p: &'r &'r mut u32) { set **p = 42; }", |
|
| 1172 | + | "record H: 'r { item: &'r mut u32 } fn write(p: &mut u32) {} fn f 'r (h: &H 'r, flag: bool) { write(h.item if flag else h.item); }", |
|
| 1173 | + | "record H: 'r { item: &'r mut u32 } fn write(p: &mut u32) {} unsafe fn f 'r (h: &H 'r, flag: bool) { write(h.item if flag else h.item); }", |
|
| 1174 | + | "record V { n: u32 } record H { item: *mut V } fn (v: &mut V) write() { set v.n = 42; } fn f(h: &H) { h.item.write(); }", |
|
| 1175 | + | "record V { n: u32 } record H { item: *mut V } fn (v: &mut V) write() { set v.n = 42; } unsafe fn f(h: &H) { h.item.write(); }", |
|
| 1176 | + | "trait W { fn (&mut W) write(); } record H: 'r { object: &'r mut opaque W } fn f 'r (h: &H 'r) { h.object.write(); }", |
|
| 1177 | + | "trait W { fn (&mut W) write(); } record H: 'r { object: &'r mut opaque W } unsafe fn f 'r (h: &H 'r) { h.object.write(); }", |
|
| 1178 | + | ] { |
|
| 1179 | + | let mut arena = super::testArena(); |
|
| 1180 | + | let storage: 'test = &mut arena in { |
|
| 1181 | + | let mut res = super::testResolver(storage); |
|
| 1182 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1183 | + | try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding); |
|
| 1184 | + | } |
|
| 1185 | + | } |
|
| 1186 | + | } |
|
| 1187 | + | ||
| 1188 | + | /// A reference through an exclusive field cannot outlive the owner's borrow. |
|
| 1189 | + | @test unsafe fn testExclusiveOwnerBorrowLifetimes() throws (testing::TestError) { |
|
| 1190 | + | for program in [ |
|
| 1191 | + | "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { let p: &'r u32 = h.item; return p; }", |
|
| 1192 | + | "record H: 'r { item: ?&'r mut u32 } fn f 'r 's (h: &'s H 'r) -> ?&'r u32 where 'r: 's { let p: ?&'r u32 = h.item; return p; }", |
|
| 1193 | + | "record H: 'r { item: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { let p: &'r [u32] = h.item; return p; }", |
|
| 1194 | + | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { let p: &'r u32 = h.item; return p; }", |
|
| 1195 | + | "record H: 'r { item: ?&'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> ?&'r u32 where 'r: 's { let p: ?&'r u32 = h.item; return p; }", |
|
| 1196 | + | "record H: 'r { item: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { let p: &'r [u32] = h.item; return p; }", |
|
| 1197 | + | "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &*h.item; }", |
|
| 1198 | + | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &*h.item; }", |
|
| 1199 | + | "record H: 'r { items: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &h.items[0]; }", |
|
| 1200 | + | "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &h.items[0]; }", |
|
| 1201 | + | "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s mut H 'r) -> &'r mut u32 where 'r: 's { return &mut *h.item; }", |
|
| 1202 | + | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s mut H 'r) -> &'r mut u32 where 'r: 's { return &mut *h.item; }", |
|
| 1203 | + | "record H { item: *mut u32 } fn f(h: &H) -> *u32 { return &*h.item; }", |
|
| 1204 | + | "record H { item: *mut u32 } unsafe fn f(h: &H) -> *u32 { return &*h.item; }", |
|
| 1205 | + | "record H: 'r { items: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { return &h.items[..]; }", |
|
| 1206 | + | "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { return &h.items[..]; }", |
|
| 1207 | + | ] { |
|
| 1208 | + | let mut arena = super::testArena(); |
|
| 1209 | + | let storage: 'test = &mut arena in { |
|
| 1210 | + | let mut res = super::testResolver(storage); |
|
| 1211 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1212 | + | let error = try super::expectError(&result); |
|
| 1213 | + | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
|
| 1214 | + | } |
|
| 1215 | + | } |
|
| 1216 | + | } |
|
| 1217 | + | ||
| 1218 | + | /// A shared binding cannot hide a move from an exclusive field. |
|
| 1219 | + | @test unsafe fn testSharedBindingPreservesSourceOwnership() throws (testing::TestError) { |
|
| 1220 | + | for program in [ |
|
| 1221 | + | "record H { item: *mut u32 } fn f(h: H) -> *u32 { let p: *u32 = h.item; return p; }", |
|
| 1222 | + | "record H { item: ?*mut u32 } fn f(h: H) -> ?*u32 { let p: ?*u32 = h.item; return p; }", |
|
| 1223 | + | "record H: 'r { item: &'r mut u32 } fn f 'r (h: &'r H 'r) -> &'r u32 { let p: &'r u32 = h.item; return p; }", |
|
| 1224 | + | "record H { item: *mut u32 } unsafe fn f(h: H) -> *u32 { let p: *u32 = h.item; return p; }", |
|
| 1225 | + | "record H { item: ?*mut u32 } unsafe fn f(h: H) -> ?*u32 { let p: ?*u32 = h.item; return p; }", |
|
| 1226 | + | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r (h: &'r H 'r) -> &'r u32 { let p: &'r u32 = h.item; return p; }", |
|
| 1227 | + | ] { |
|
| 1228 | + | let mut arena = super::testArena(); |
|
| 1229 | + | let storage: 'test = &mut arena in { |
|
| 1230 | + | let mut res = super::testResolver(storage); |
|
| 1231 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1232 | + | try super::expectErrorKind(&result, resolver::ErrorKind::LinearPartialMove); |
|
| 1233 | + | } |
|
| 1234 | + | } |
|
| 1235 | + | } |
|
| 1236 | + | ||
| 1237 | + | /// Methods and trait instances resolve declaration region parameters. |
|
| 1238 | + | @test unsafe fn testRegionalMethods() throws (testing::TestError) { |
|
| 1239 | + | for program in [ |
|
| 1240 | + | "record View: 'r { value: &'r u32 } fn (view: &View 'r) get 'r () -> &'r u32 { return view.value; } fn read 'x (view: &View 'x) -> u32 { return *view.get(); }", |
|
| 1241 | + | "trait Pick { fn (&Pick) pick 'r (value: &'r u32) -> &'r u32; } record Picker {} instance Pick for Picker { fn (picker: &Picker) pick 'r (value: &'r u32) -> &'r u32 { return value; } } fn choose 'x (picker: &opaque Pick, value: &'x u32) -> &'x u32 { return picker.pick(value); }", |
|
| 1242 | + | "trait Read { fn (&Read) read() -> u32; } record Regional: 'r { value: &'r u32 } instance Read for Regional 'r { fn (value: &Regional 'r) read() -> u32 { return *value.value; } } fn inspect(value: &opaque Read) -> u32 { return value.read(); } fn call 'x (value: &Regional 'x) -> u32 { return inspect(value); }", |
|
| 1243 | + | ] { |
|
| 1244 | + | let mut arena = super::testArena(); |
|
| 1245 | + | let storage: 'method = &mut arena in { |
|
| 1246 | + | let mut res = super::testResolver(storage); |
|
| 1247 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 1248 | + | try super::expectNoErrors(&result); |
|
| 1249 | + | } |
|
| 1250 | + | } |
|
| 1251 | + | } |
lib/std/lang/scanner.rad
+15 -3
| 71 | 71 | ||
| 72 | 72 | /// Eg. `fnord` |
|
| 73 | 73 | Ident, |
|
| 74 | 74 | /// Eg. `@default` |
|
| 75 | 75 | AtIdent, |
|
| 76 | + | /// Region name with an apostrophe prefix. |
|
| 77 | + | Region, |
|
| 76 | 78 | /// The `log` keyword. |
|
| 77 | 79 | Log, |
|
| 78 | 80 | ||
| 79 | 81 | // Literals. |
|
| 80 | 82 | String, // "fnord" |
| 103 | 105 | // Trait-related tokens. |
|
| 104 | 106 | Trait, Instance, |
|
| 105 | 107 | ||
| 106 | 108 | // Type-related tokens. |
|
| 107 | 109 | I8, I16, I32, I64, U8, U16, U32, U64, |
|
| 108 | - | Opaque, Fn, Bool, Union, Record, As, Unsafe |
|
| 110 | + | Opaque, Fn, Bool, Union, Record, As, Unsafe, Where |
|
| 109 | 111 | } |
|
| 110 | 112 | ||
| 111 | 113 | /// A reserved keyword. |
|
| 112 | 114 | record Keyword: Copy { |
|
| 113 | 115 | /// Keyword string. |
| 115 | 117 | /// Corresponding token. |
|
| 116 | 118 | tok: TokenKind, |
|
| 117 | 119 | } |
|
| 118 | 120 | ||
| 119 | 121 | /// Sorted keyword table for binary search. |
|
| 120 | - | constant KEYWORDS: [Keyword; 52] = [ |
|
| 122 | + | constant KEYWORDS: [Keyword; 53] = [ |
|
| 121 | 123 | { name: "align", tok: TokenKind::Align }, |
|
| 122 | 124 | { name: "and", tok: TokenKind::And }, |
|
| 123 | 125 | { name: "as", tok: TokenKind::As }, |
|
| 124 | 126 | { name: "assert", tok: TokenKind::Assert }, |
|
| 125 | 127 | { name: "bool", tok: TokenKind::Bool }, |
| 167 | 169 | { name: "u8", tok: TokenKind::U8 }, |
|
| 168 | 170 | { name: "undefined", tok: TokenKind::Undefined }, |
|
| 169 | 171 | { name: "union", tok: TokenKind::Union }, |
|
| 170 | 172 | { name: "unsafe", tok: TokenKind::Unsafe }, |
|
| 171 | 173 | { name: "use", tok: TokenKind::Use }, |
|
| 174 | + | { name: "where", tok: TokenKind::Where }, |
|
| 172 | 175 | { name: "while", tok: TokenKind::While }, |
|
| 173 | 176 | ]; |
|
| 174 | 177 | ||
| 175 | 178 | /// Describes where source code originated from. |
|
| 176 | 179 | export union SourceLoc: Copy { |
| 371 | 374 | return tok; |
|
| 372 | 375 | } |
|
| 373 | 376 | return invalid(s.token, "unterminated string"); |
|
| 374 | 377 | } |
|
| 375 | 378 | ||
| 376 | - | /// Scan character literal enclosed in single quotes. |
|
| 379 | + | /// Scan an apostrophe-prefixed region name or a quoted character literal. |
|
| 377 | 380 | fn scanChar(s: &mut Scanner) -> Token { |
|
| 381 | + | if let ch = current(s); char::isAlpha(ch) or ch == '_' { |
|
| 382 | + | while let ch = current(s); char::isAlpha(ch) or ch == '_' or char::isDigit(ch) { |
|
| 383 | + | advance(s); |
|
| 384 | + | } |
|
| 385 | + | if consume(s, '\'') { |
|
| 386 | + | return tok(s, TokenKind::Char); |
|
| 387 | + | } |
|
| 388 | + | return tok(s, TokenKind::Region); |
|
| 389 | + | } |
|
| 378 | 390 | if let tok = scanDelimited(s, '\'', TokenKind::Char) { |
|
| 379 | 391 | return tok; |
|
| 380 | 392 | } |
|
| 381 | 393 | return invalid(s.token, "unterminated character"); |
|
| 382 | 394 | } |
lib/std/lang/scanner/tests.rad
+30 -1
| 219 | 219 | try testing::expect(super::next(&mut s).kind == expectedKind); |
|
| 220 | 220 | } |
|
| 221 | 221 | } |
|
| 222 | 222 | ||
| 223 | 223 | @test unsafe fn testScanKeywords() throws (testing::TestError) { |
|
| 224 | - | let mut s = testScanner("nil mod not static unsafe"); |
|
| 224 | + | let mut s = testScanner("nil mod not static unsafe where"); |
|
| 225 | 225 | let tok1: super::Token = super::next(&mut s); |
|
| 226 | 226 | ||
| 227 | 227 | try testing::expect(tok1.kind == super::TokenKind::Nil); |
|
| 228 | 228 | try testing::expect(tok1.source.len == 3); |
|
| 229 | 229 |
| 240 | 240 | try testing::expect(tok4.source.len == 6); |
|
| 241 | 241 | ||
| 242 | 242 | let tok5: super::Token = super::next(&mut s); |
|
| 243 | 243 | try testing::expect(tok5.kind == super::TokenKind::Unsafe); |
|
| 244 | 244 | try testing::expect(tok5.source.len == 6); |
|
| 245 | + | ||
| 246 | + | let tok6: super::Token = super::next(&mut s); |
|
| 247 | + | try testing::expect(tok6.kind == super::TokenKind::Where); |
|
| 248 | + | try testing::expect(tok6.source.len == 5); |
|
| 245 | 249 | } |
|
| 246 | 250 | ||
| 247 | 251 | @test unsafe fn testScanVoidAsIdent() throws (testing::TestError) { |
|
| 248 | 252 | let mut s = testScanner("void"); |
|
| 249 | 253 | let tok: super::Token = super::next(&mut s); |
| 318 | 322 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Ident); |
|
| 319 | 323 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Semicolon); |
|
| 320 | 324 | try testing::expect(super::next(&mut s).kind == super::TokenKind::RBrace); |
|
| 321 | 325 | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 322 | 326 | } |
|
| 327 | + | ||
| 328 | + | /// Region tokens preserve their names and do not consume character literals. |
|
| 329 | + | @test unsafe fn testScanRegions() throws (testing::TestError) { |
|
| 330 | + | let mut s = testScanner("'x 'long_2 'x' '\\n' 'outer<'inner"); |
|
| 331 | + | let first = super::next(&mut s); |
|
| 332 | + | try testing::expect(first.kind == super::TokenKind::Region); |
|
| 333 | + | try testing::expect(mem::eq(first.source, "'x")); |
|
| 334 | + | let second = super::next(&mut s); |
|
| 335 | + | try testing::expect(second.kind == super::TokenKind::Region); |
|
| 336 | + | try testing::expect(mem::eq(second.source, "'long_2")); |
|
| 337 | + | try testing::expect(super::next(&mut s).kind == super::TokenKind::Char); |
|
| 338 | + | try testing::expect(super::next(&mut s).kind == super::TokenKind::Char); |
|
| 339 | + | try testing::expect(super::next(&mut s).kind == super::TokenKind::Region); |
|
| 340 | + | try testing::expect(super::next(&mut s).kind == super::TokenKind::Lt); |
|
| 341 | + | try testing::expect(super::next(&mut s).kind == super::TokenKind::Region); |
|
| 342 | + | try testing::expect(super::next(&mut s).kind == super::TokenKind::Eof); |
|
| 343 | + | } |
|
| 344 | + | ||
| 345 | + | /// An apostrophe without a name or a closing quote is invalid. |
|
| 346 | + | @test unsafe fn testScanMalformedRegions() throws (testing::TestError) { |
|
| 347 | + | for text in ["'", "'1", "'\\", "'\n"] { |
|
| 348 | + | let mut s = testScanner(text); |
|
| 349 | + | try testing::expect(super::next(&mut s).kind == super::TokenKind::Invalid); |
|
| 350 | + | } |
|
| 351 | + | } |
lib/std/lang/types.rad
+43 -0
| 4 | 4 | export union PointerClass: Copy { |
|
| 5 | 5 | /// Owned pointer, eg. `*T`. |
|
| 6 | 6 | Owned, |
|
| 7 | 7 | /// Reference, borrowed pointer, eg. `&T`. |
|
| 8 | 8 | Ref, |
|
| 9 | + | /// Reference retained by one resolved lexical region. |
|
| 10 | + | Region(*unsafe Region), |
|
| 9 | 11 | /// Unsafe, raw pointer, eg. `*unsafe T`. |
|
| 10 | 12 | Unsafe, |
|
| 11 | 13 | } |
|
| 14 | + | ||
| 15 | + | /// Source construct that introduces a region identity. |
|
| 16 | + | export union RegionOrigin: Copy { |
|
| 17 | + | /// Caller-supplied declaration parameter. |
|
| 18 | + | Parameter, |
|
| 19 | + | /// Concrete region delimited by a lexical block. |
|
| 20 | + | Block, |
|
| 21 | + | } |
|
| 22 | + | ||
| 23 | + | /// Semantic identity and ancestry of a source region. |
|
| 24 | + | export record Region: Copy { |
|
| 25 | + | /// Globally unique AST node ID of the region declaration. |
|
| 26 | + | id: u32, |
|
| 27 | + | /// Source construct that supplies the region lifetime. |
|
| 28 | + | origin: RegionOrigin, |
|
| 29 | + | /// Source spelling used in diagnostics. |
|
| 30 | + | name: *[u8], |
|
| 31 | + | /// Proven enclosing region, if one is declared. |
|
| 32 | + | parent: ?*unsafe Region, |
|
| 33 | + | } |
|
| 34 | + | ||
| 35 | + | /// Return whether a pointer class denotes checked borrowed storage. |
|
| 36 | + | export fn isReference(class: PointerClass) -> bool { |
|
| 37 | + | match class { |
|
| 38 | + | case PointerClass::Ref, PointerClass::Region(_) => return true, |
|
| 39 | + | else => return false, |
|
| 40 | + | } |
|
| 41 | + | } |
|
| 42 | + | ||
| 43 | + | /// Return whether a region is equal to, or contains, another region. |
|
| 44 | + | /// Parent links must form an acyclic graph within one resolver. |
|
| 45 | + | export unsafe fn regionContains(parent: *unsafe Region, child: *unsafe Region) -> bool { |
|
| 46 | + | let mut current: ?*unsafe Region = child; |
|
| 47 | + | while let region = current { |
|
| 48 | + | if region.id == parent.id { |
|
| 49 | + | return true; |
|
| 50 | + | } |
|
| 51 | + | set current = region.parent; |
|
| 52 | + | } |
|
| 53 | + | return false; |
|
| 54 | + | } |
seed/radiance.rv64
+0 -0
Binary file changed.
seed/radiance.rv64.git
+1 -1
| 1 | - | cbc895eb5c6c569c7648e51c5e4ff0579de8b925415ceb368100d07370b9fd7e |
|
| 1 | + | 4a24fa5e2f0b82d0aa1f922caa85dcf831d22cb8828365820197f8ebb13f55b4 |
std.lib
+1 -0
| 51 | 51 | lib/std/lang/gen/regalloc.rad |
|
| 52 | 52 | lib/std/lang/gen/regalloc/liveness.rad |
|
| 53 | 53 | lib/std/lang/gen/regalloc/spill.rad |
|
| 54 | 54 | lib/std/lang/gen/regalloc/assign.rad |
|
| 55 | 55 | lib/std/arch/rv64/shared.rad |
|
| 56 | + | lib/std/arch/rv64/shared/catalog.rad |
|
| 56 | 57 | lib/std/arch/rv64/atomics.rad |
std.lib.test
+4 -1
| 3 | 3 | lib/std/char/tests.rad |
|
| 4 | 4 | lib/std/arch/rv64/tests.rad |
|
| 5 | 5 | lib/std/arch/rv64/asm/tests.rad |
|
| 6 | 6 | lib/std/arch/rv64/asm/scanner/tests.rad |
|
| 7 | 7 | lib/std/lang/alloc/tests.rad |
|
| 8 | + | lib/std/lang/gen/bitset/tests.rad |
|
| 9 | + | lib/std/lang/gen/regalloc/liveness/tests.rad |
|
| 10 | + | lib/std/lang/il/tests.rad |
|
| 8 | 11 | lib/std/lang/parser/tests.rad |
|
| 9 | 12 | lib/std/lang/module/tests.rad |
|
| 10 | 13 | lib/std/lang/scanner/tests.rad |
|
| 11 | 14 | lib/std/lang/resolver/tests.rad |
|
| 12 | - | lib/std/lang/gen/bitset/tests.rad |
|
| 13 | 15 | lib/std/lang/il/binary/tests.rad |
|
| 14 | 16 | lib/std/lang/il/binary/decodeTests.rad |
|
| 15 | 17 | lib/std/arch/rv64/image/tests.rad |
|
| 16 | 18 | lib/std/arch/rv64/shared/tests.rad |
|
| 17 | 19 | lib/std/arch/rv64/bounds.rad |
|
| 18 | 20 | lib/std/arch/rv64/atomicTests.rad |
|
| 21 | + | lib/std/lang/resolver/tests/regions.rad |
test/runner.rad
+30 -28
| 259 | 259 | io::printLn("error: parsing failed"); |
|
| 260 | 260 | return false; |
|
| 261 | 261 | }; |
|
| 262 | 262 | ||
| 263 | 263 | // Run resolver. |
|
| 264 | + | let mut resolverArena = alloc::new(&mut RESOLVER_ARENA_STORAGE[..]); |
|
| 264 | 265 | let storage = resolver::ResolverStorage { |
|
| 265 | - | arena: alloc::new(&mut RESOLVER_ARENA_STORAGE[..]), |
|
| 266 | 266 | nodeData: &mut NODE_DATA_STORAGE[..], |
|
| 267 | 267 | pkgScope: &mut pkgScope, |
|
| 268 | 268 | errors: &mut ERROR_STORAGE[..], |
|
| 269 | 269 | }; |
|
| 270 | 270 | let config = resolver::Config { buildTest: false }; |
|
| 271 | - | let mut res = resolver::resolver(storage, config); |
|
| 272 | - | let diag = try resolver::resolveModuleRoot(&mut res, root) catch { |
|
| 273 | - | io::printLn("error: resolver failed"); |
|
| 274 | - | return false; |
|
| 275 | - | }; |
|
| 276 | - | if not resolver::success(&diag) { |
|
| 277 | - | io::printLn("error: resolver failed"); |
|
| 278 | - | return false; |
|
| 279 | - | } |
|
| 271 | + | let arenaRef: 'arena = &mut resolverArena in { |
|
| 272 | + | let mut res = resolver::resolver(arenaRef, storage, config); |
|
| 273 | + | let diag = try resolver::resolveModuleRoot(&mut res, root) catch { |
|
| 274 | + | io::printLn("error: resolver failed"); |
|
| 275 | + | return false; |
|
| 276 | + | }; |
|
| 277 | + | if not resolver::success(&diag) { |
|
| 278 | + | io::printLn("error: resolver failed"); |
|
| 279 | + | return false; |
|
| 280 | + | } |
|
| 280 | 281 | ||
| 281 | - | // Lower to IL. |
|
| 282 | - | let mut ilArena = alloc::new(&mut IL_ARENA_STORAGE[..]); |
|
| 283 | - | let program = try lower::lower(&res, root, "test", &mut ilArena) catch err { |
|
| 284 | - | io::print("error: lowering failed: "); |
|
| 285 | - | lower::printError(err); |
|
| 286 | - | io::printLn(""); |
|
| 287 | - | return false; |
|
| 288 | - | }; |
|
| 282 | + | // Lower to IL. |
|
| 283 | + | let mut ilArena = alloc::new(&mut IL_ARENA_STORAGE[..]); |
|
| 284 | + | let program = try lower::lower(&res, root, "test", &mut ilArena) catch err { |
|
| 285 | + | io::print("error: lowering failed: "); |
|
| 286 | + | lower::printError(err); |
|
| 287 | + | io::printLn(""); |
|
| 288 | + | return false; |
|
| 289 | + | }; |
|
| 289 | 290 | ||
| 290 | - | // Print IL to buffer. |
|
| 291 | - | let actual = printer::printProgramToBuffer(&program, &mut OUTPUT_BUF[..]); |
|
| 291 | + | // Print IL to buffer. |
|
| 292 | + | let actual = printer::printProgramToBuffer(&program, &mut OUTPUT_BUF[..]); |
|
| 292 | 293 | ||
| 293 | - | // Compare ignoring comments. |
|
| 294 | - | if not stringsEqual(actual, expected) { |
|
| 295 | - | io::printLn("FAILED"); |
|
| 296 | - | printDiff(expected, actual); |
|
| 297 | - | return false; |
|
| 298 | - | } |
|
| 299 | - | io::printLn("ok"); |
|
| 294 | + | // Compare ignoring comments. |
|
| 295 | + | if not stringsEqual(actual, expected) { |
|
| 296 | + | io::printLn("FAILED"); |
|
| 297 | + | printDiff(expected, actual); |
|
| 298 | + | return false; |
|
| 299 | + | } |
|
| 300 | + | io::printLn("ok"); |
|
| 300 | 301 | ||
| 301 | - | return true; |
|
| 302 | + | return true; |
|
| 303 | + | } |
|
| 302 | 304 | } |
|
| 303 | 305 | ||
| 304 | 306 | /// Run a single test specified as an argument. |
|
| 305 | 307 | @default unsafe fn main(env: *sys::Env) -> i32 { |
|
| 306 | 308 | let args = env.args; |
test/tests/const.record.mutcopy.ril
+5 -2
| 18 | 18 | @then1 |
|
| 19 | 19 | copy %6 $SCRATCH2; |
|
| 20 | 20 | blit %2 %6 1; |
|
| 21 | 21 | jmp @merge2; |
|
| 22 | 22 | @merge2 |
|
| 23 | - | load w64 %8 %2 0; |
|
| 24 | - | ret %8; |
|
| 23 | + | reserve %8 8 8; |
|
| 24 | + | store w64 0 %8 0; |
|
| 25 | + | blit %8 %2 1; |
|
| 26 | + | load w64 %9 %8 0; |
|
| 27 | + | ret %9; |
|
| 25 | 28 | } |
test/tests/field.aggregate.ril
+5 -2
| 19 | 19 | store w8 1 %2 0; |
|
| 20 | 20 | add w64 %3 %2 4; |
|
| 21 | 21 | blit %3 %1 4; |
|
| 22 | 22 | blit %0 %2 8; |
|
| 23 | 23 | store w32 99 %0 8; |
|
| 24 | - | load w64 %4 %0 0; |
|
| 25 | - | ret %4; |
|
| 24 | + | reserve %4 8 8; |
|
| 25 | + | store w64 0 %4 0; |
|
| 26 | + | blit %4 %0 8; |
|
| 27 | + | load w64 %5 %4 0; |
|
| 28 | + | ret %5; |
|
| 26 | 29 | } |
|
| 27 | 30 | ||
| 28 | 31 | fn w32 $accessSliceField() { |
|
| 29 | 32 | @entry0 |
|
| 30 | 33 | reserve %0 12 4; |
test/tests/let.copy.semantics.ril
+5 -2
| 1 | 1 | fn w64 $makePoint() { |
|
| 2 | 2 | @entry0 |
|
| 3 | 3 | reserve %0 8 4; |
|
| 4 | 4 | store w32 1 %0 0; |
|
| 5 | 5 | store w32 2 %0 4; |
|
| 6 | - | load w64 %1 %0 0; |
|
| 7 | - | ret %1; |
|
| 6 | + | reserve %1 8 8; |
|
| 7 | + | store w64 0 %1 0; |
|
| 8 | + | blit %1 %0 8; |
|
| 9 | + | load w64 %2 %1 0; |
|
| 10 | + | ret %2; |
|
| 8 | 11 | } |
|
| 9 | 12 | ||
| 10 | 13 | fn w32 $letFromCall() { |
|
| 11 | 14 | @entry0 |
|
| 12 | 15 | call w64 %0 $makePoint(); |
test/tests/record.ctor.tuple.ril
+5 -2
| 1 | 1 | fn w64 $make() { |
|
| 2 | 2 | @entry0 |
|
| 3 | 3 | reserve %0 8 4; |
|
| 4 | 4 | store w32 1 %0 0; |
|
| 5 | 5 | store w32 2 %0 4; |
|
| 6 | - | load w64 %1 %0 0; |
|
| 7 | - | ret %1; |
|
| 6 | + | reserve %1 8 8; |
|
| 7 | + | store w64 0 %1 0; |
|
| 8 | + | blit %1 %0 8; |
|
| 9 | + | load w64 %2 %1 0; |
|
| 10 | + | ret %2; |
|
| 8 | 11 | } |
test/tests/slice.append.rad
+1 -1
| 9 | 9 | ||
| 10 | 10 | fn newArena(data: *mut [u8]) -> Arena { |
|
| 11 | 11 | return Arena { data, offset: 0 }; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn arenaAlloc(arena: &mut Arena, size: u32, al: u32) -> *mut opaque { |
|
| 14 | + | unsafe fn arenaAlloc(arena: *unsafe mut Arena, size: u32, al: u32) -> *mut opaque { |
|
| 15 | 15 | let aligned = (arena.offset + al - 1) / al * al; |
|
| 16 | 16 | let newOffset = aligned + size; |
|
| 17 | 17 | ||
| 18 | 18 | assert newOffset <= arena.data.len as u32; |
|
| 19 | 19 |
test/tests/union.ctor.ril
+10 -4
| 14 | 14 | store w32 7 %0 0; |
|
| 15 | 15 | reserve %1 8 4; |
|
| 16 | 16 | store w8 1 %1 0; |
|
| 17 | 17 | add w64 %2 %1 4; |
|
| 18 | 18 | blit %2 %0 4; |
|
| 19 | - | load w64 %3 %1 0; |
|
| 20 | - | ret %3; |
|
| 19 | + | reserve %3 8 8; |
|
| 20 | + | store w64 0 %3 0; |
|
| 21 | + | blit %3 %1 8; |
|
| 22 | + | load w64 %4 %3 0; |
|
| 23 | + | ret %4; |
|
| 21 | 24 | } |
|
| 22 | 25 | ||
| 23 | 26 | fn w64 $makeNone() { |
|
| 24 | 27 | @entry0 |
|
| 25 | 28 | reserve %0 8 4; |
|
| 26 | 29 | store w8 0 %0 0; |
|
| 27 | - | load w64 %1 %0 0; |
|
| 28 | - | ret %1; |
|
| 30 | + | reserve %1 8 8; |
|
| 31 | + | store w64 0 %1 0; |
|
| 32 | + | blit %1 %0 8; |
|
| 33 | + | load w64 %2 %1 0; |
|
| 34 | + | ret %2; |
|
| 29 | 35 | } |
test/tests/union.variant.access.ril
+15 -6
| 10 | 10 | ||
| 11 | 11 | fn w64 $makeNone() { |
|
| 12 | 12 | @entry0 |
|
| 13 | 13 | reserve %0 8 4; |
|
| 14 | 14 | store w8 0 %0 0; |
|
| 15 | - | load w64 %1 %0 0; |
|
| 16 | - | ret %1; |
|
| 15 | + | reserve %1 8 8; |
|
| 16 | + | store w64 0 %1 0; |
|
| 17 | + | blit %1 %0 8; |
|
| 18 | + | load w64 %2 %1 0; |
|
| 19 | + | ret %2; |
|
| 17 | 20 | } |
|
| 18 | 21 | ||
| 19 | 22 | fn w64 $makeSome(w32 %0) { |
|
| 20 | 23 | @entry0 |
|
| 21 | 24 | reserve %1 4 4; |
|
| 22 | 25 | store w32 %0 %1 0; |
|
| 23 | 26 | reserve %2 8 4; |
|
| 24 | 27 | store w8 1 %2 0; |
|
| 25 | 28 | add w64 %3 %2 4; |
|
| 26 | 29 | blit %3 %1 4; |
|
| 27 | - | load w64 %4 %2 0; |
|
| 28 | - | ret %4; |
|
| 30 | + | reserve %4 8 8; |
|
| 31 | + | store w64 0 %4 0; |
|
| 32 | + | blit %4 %2 8; |
|
| 33 | + | load w64 %5 %4 0; |
|
| 34 | + | ret %5; |
|
| 29 | 35 | } |
|
| 30 | 36 | ||
| 31 | 37 | fn w64 $assignNone() { |
|
| 32 | 38 | @entry0 |
|
| 33 | 39 | reserve %0 8 4; |
|
| 34 | 40 | store w8 0 %0 0; |
|
| 35 | - | load w64 %1 %0 0; |
|
| 36 | - | ret %1; |
|
| 41 | + | reserve %1 8 8; |
|
| 42 | + | store w64 0 %1 0; |
|
| 43 | + | blit %1 %0 8; |
|
| 44 | + | load w64 %2 %1 0; |
|
| 45 | + | ret %2; |
|
| 37 | 46 | } |
vim/radiance.vim
+7 -2
| 16 | 16 | ||
| 17 | 17 | " Keywords |
|
| 18 | 18 | syntax keyword radianceKeyword mod fn return if else while true false and or not case align static |
|
| 19 | 19 | syntax keyword radianceKeyword export break continue use loop in for match nil undefined |
|
| 20 | 20 | syntax keyword radianceKeyword let mut set as register device constant log record union trait instance |
|
| 21 | - | syntax keyword radianceKeyword throws throw try catch panic assert super unsafe |
|
| 21 | + | syntax keyword radianceKeyword throws throw try catch panic assert super unsafe where |
|
| 22 | 22 | syntax keyword radianceType i8 i16 i32 i64 u8 u16 u32 u64 f32 void bool bit opaque Copy Once |
|
| 23 | 23 | ||
| 24 | 24 | " Double-quoted strings |
|
| 25 | 25 | syntax region radianceString start=/"/ skip=/\\"/ end=/"/ contains=radianceEscape |
|
| 26 | 26 | " Characters |
|
| 27 | - | syntax region radianceCharacter start=/'/ skip=/\\'|\\\\/ end=/'/ oneline contains=radianceEscape |
|
| 27 | + | syntax match radianceCharacter /'\h\w*'/ contains=radianceEscape |
|
| 28 | + | syntax region radianceCharacter start=/'\%(\h\)\@!/ skip=/\\'|\\\\/ end=/'/ oneline contains=radianceEscape |
|
| 29 | + | " Lexical regions |
|
| 30 | + | syntax match radianceRegion "'\h\w*\%('\)\@!" |
|
| 28 | 31 | " String escapes |
|
| 29 | 32 | syntax match radianceEscape contained /\\["'nrtvfab\\]/ |
|
| 30 | 33 | " Compiler built-in |
|
| 31 | 34 | syntax match radianceBuiltin "@\h\w\+" |
|
| 32 | 35 |
| 36 | 39 | syntax match radianceNumber "\<\d\+\>" |
|
| 37 | 40 | syntax match radiancePlaceholder "\<_\>" |
|
| 38 | 41 | ||
| 39 | 42 | " Function names |
|
| 40 | 43 | syntax match radianceFunction "\<\h\w*\>\s*?(" |
|
| 44 | + | syntax match radianceFunction "\<\h\w*\>\ze\s*\%('\h\w*\s*\)*(" |
|
| 41 | 45 | ||
| 42 | 46 | " Operators |
|
| 43 | 47 | syntax match radianceOperator "[:!?&\-+*/=<>]" |
|
| 44 | 48 | syntax match radianceOperator "/" contains=radianceComment |
|
| 45 | 49 |
| 66 | 70 | highlight default link radianceBraces Delimiter |
|
| 67 | 71 | highlight default link radianceParens Delimiter |
|
| 68 | 72 | highlight default link radianceBrackets Delimiter |
|
| 69 | 73 | highlight default link radianceString String |
|
| 70 | 74 | highlight default link radianceCharacter Character |
|
| 75 | + | highlight default link radianceRegion Type |
|
| 71 | 76 | highlight default link radianceEscape SpecialChar |
|
| 72 | 77 | highlight default link radianceNamespaceAccess Normal |
|
| 73 | 78 | ||
| 74 | 79 | let b:current_syntax = "radiance" |