lang: enforce unsafe memory access
01c735add52799e08abd60fc88afe9714a85bec7122d67d7e88d7ef10022ab3b
Require unsafe context for raw pointer operations, mutable global state, and unsafe bindings. Track loans across expressions and reject conflicting borrows, invalid escapes, and ownership conversions. Add scoped unsafe blocks, harden arena arithmetic, use call-scoped callback references, and update the compiler, standard library, bootstrap image, and test fixtures for the enforced safety rules.
1 parent
1e2fdea9
Makefile
+2 -2
| 36 | 36 | endif |
|
| 37 | 37 | ||
| 38 | 38 | # Compiler build |
|
| 39 | 39 | ||
| 40 | 40 | SEED := seed/radiance.rv64 |
|
| 41 | - | SEED_OPTS := $(STD) -pkg radiance -mod compiler/radiance.rad -entry radiance |
|
| 41 | + | SEED_OPTS := $(STD) -pkg radiance -mod compiler/radiance.rad -mod compiler/radiance/codegenSink.rad -entry radiance |
|
| 42 | 42 | ||
| 43 | - | $(RAD_BIN): $(STD_LIB) compiler/radiance.rad | $(BIN_DIR) |
|
| 43 | + | $(RAD_BIN): $(STD_LIB) compiler/radiance.rad compiler/radiance/codegenSink.rad | $(BIN_DIR) |
|
| 44 | 44 | @echo "radiance $(SEED) => $@" |
|
| 45 | 45 | @$(EMU) $(EMU_FLAGS) -run $(SEED) $(SEED_OPTS) -o $@ |
|
| 46 | 46 | ||
| 47 | 47 | $(BIN_DIR): |
|
| 48 | 48 | @mkdir -p $@ |
compiler/radiance.rad
+57 -83
| 42 | 42 | /// Main arena size (96 MB) - lives throughout compilation. |
|
| 43 | 43 | /// Used for: resolver data, types, symbols, global IL data, and codegen output. |
|
| 44 | 44 | constant MAIN_ARENA_SIZE: u32 = 100663296; |
|
| 45 | 45 | ||
| 46 | 46 | /// AST storage arena. |
|
| 47 | - | static TEMP_ARENA: [u8; TEMP_ARENA_SIZE] = undefined; |
|
| 47 | + | unsafe static TEMP_ARENA: [u8; TEMP_ARENA_SIZE] = undefined; |
|
| 48 | 48 | /// Scratch storage reclaimed after each generated function. |
|
| 49 | - | static FN_ARENA: [u8; FN_ARENA_SIZE] = undefined; |
|
| 49 | + | unsafe static FN_ARENA: [u8; FN_ARENA_SIZE] = undefined; |
|
| 50 | 50 | /// Main storage arena - persists throughout compilation. |
|
| 51 | - | static MAIN_ARENA: [u8; MAIN_ARENA_SIZE] = undefined; |
|
| 51 | + | unsafe static MAIN_ARENA: [u8; MAIN_ARENA_SIZE] = undefined; |
|
| 52 | 52 | ||
| 53 | 53 | /// Module source code. |
|
| 54 | - | static MODULE_SOURCES: [u8; MAX_SOURCES_SIZE] = undefined; |
|
| 54 | + | unsafe static MODULE_SOURCES: [u8; MAX_SOURCES_SIZE] = undefined; |
|
| 55 | 55 | /// Module entries for all packages. |
|
| 56 | - | static MODULE_ENTRIES: [module::ModuleEntry; MAX_TOTAL_MODULES] = undefined; |
|
| 56 | + | unsafe static MODULE_ENTRIES: [module::ModuleEntry; MAX_TOTAL_MODULES] = undefined; |
|
| 57 | 57 | /// String pool. |
|
| 58 | - | static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 58 | + | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 59 | 59 | ||
| 60 | 60 | /// Package scope. |
|
| 61 | - | static RESOLVER_PKG_SCOPE: resolver::Scope = undefined; |
|
| 61 | + | unsafe static RESOLVER_PKG_SCOPE: resolver::Scope = undefined; |
|
| 62 | 62 | /// Errors emitted by resolver. |
|
| 63 | - | static RESOLVER_ERRORS: [resolver::Error; resolver::MAX_ERRORS] = undefined; |
|
| 63 | + | unsafe static RESOLVER_ERRORS: [resolver::Error; resolver::MAX_ERRORS] = undefined; |
|
| 64 | 64 | ||
| 65 | 65 | /// Code generation storage. |
|
| 66 | - | static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined; |
|
| 66 | + | unsafe static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined; |
|
| 67 | 67 | /// Hash table entries for data symbol lookup. |
|
| 68 | - | static CODEGEN_DATA_SYM_ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined; |
|
| 68 | + | unsafe static CODEGEN_DATA_SYM_ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined; |
|
| 69 | 69 | ||
| 70 | 70 | /// Debug info file extension. |
|
| 71 | - | constant DEBUG_EXT: *[u8] = ".debug"; |
|
| 71 | + | unsafe constant DEBUG_EXT: *[u8] = ".debug"; |
|
| 72 | 72 | ||
| 73 | 73 | /// Maximum rodata size (4MB). |
|
| 74 | 74 | constant MAX_RO_DATA_SIZE: u32 = 4194304; |
|
| 75 | 75 | /// Maximum rwdata size (4MB). |
|
| 76 | 76 | constant MAX_RW_DATA_SIZE: u32 = 4194304; |
|
| 77 | 77 | /// Maximum path length. |
|
| 78 | 78 | constant MAX_PATH_LEN: u32 = 256; |
|
| 79 | 79 | /// Read-only data buffer. |
|
| 80 | - | static RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined; |
|
| 80 | + | unsafe static RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined; |
|
| 81 | 81 | /// Read-write data buffer. |
|
| 82 | - | static RW_DATA_BUF: [u8; MAX_RW_DATA_SIZE] = undefined; |
|
| 82 | + | unsafe static RW_DATA_BUF: [u8; MAX_RW_DATA_SIZE] = undefined; |
|
| 83 | 83 | /// Assembly module source buffer. |
|
| 84 | - | static ASM_SOURCE_BUF: [u8; MAX_SOURCES_SIZE] = undefined; |
|
| 84 | + | unsafe static ASM_SOURCE_BUF: [u8; MAX_SOURCES_SIZE] = undefined; |
|
| 85 | 85 | /// Temporary assembly text buffer. |
|
| 86 | - | static ASM_TEXT_BUF: [u32; 262144] = undefined; |
|
| 86 | + | unsafe static ASM_TEXT_BUF: [u32; 262144] = undefined; |
|
| 87 | 87 | /// Temporary assembly data buffer. |
|
| 88 | - | static ASM_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined; |
|
| 88 | + | unsafe static ASM_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined; |
|
| 89 | 89 | /// Accumulated assembly read-only data. |
|
| 90 | - | static ASM_RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined; |
|
| 90 | + | unsafe static ASM_RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined; |
|
| 91 | 91 | ||
| 92 | 92 | /// Assembly source file extension. |
|
| 93 | - | constant ASM_SOURCE_EXT: *[u8] = ".ras"; |
|
| 93 | + | unsafe constant ASM_SOURCE_EXT: *[u8] = ".ras"; |
|
| 94 | 94 | /// Symbol name exported for startup code to call the semantic entry function. |
|
| 95 | - | constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default"; |
|
| 95 | + | unsafe constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default"; |
|
| 96 | 96 | ||
| 97 | 97 | /// Usage string. |
|
| 98 | - | constant USAGE: *[u8] = |
|
| 98 | + | unsafe constant USAGE: *[u8] = |
|
| 99 | 99 | "usage: radiance -pkg <name> [-start <input.ras>] -mod <input>.. [-pkg <name> -mod <input>..] -entry <pkg> -o <output>\n"; |
|
| 100 | 100 | ||
| 101 | 101 | /// Compiler error. |
|
| 102 | 102 | union Error { |
|
| 103 | 103 | Other, |
| 167 | 167 | record RootModule { |
|
| 168 | 168 | entry: *module::ModuleEntry, |
|
| 169 | 169 | ast: *mut ast::Node, |
|
| 170 | 170 | } |
|
| 171 | 171 | ||
| 172 | - | /// State carried by the streaming lowerer/codegen callback. |
|
| 173 | - | record CodegenSinkContext { |
|
| 174 | - | /// RV64 generator receiving lowered functions. |
|
| 175 | - | generator: *mut rv64::Generator, |
|
| 176 | - | /// Arena holding the current function's lowered IL. |
|
| 177 | - | fnArena: *mut alloc::Arena, |
|
| 178 | - | } |
|
| 172 | + | /// Trusted implementation for streamed code generation. |
|
| 173 | + | unsafe mod codegenSink; |
|
| 179 | 174 | ||
| 180 | 175 | /// Entry handling for streamed code generation. |
|
| 181 | 176 | union CodegenEntryMode { |
|
| 182 | 177 | /// Do not reserve an entry jump. |
|
| 183 | 178 | None, |
| 194 | 189 | /// How the generated program should handle entry. |
|
| 195 | 190 | entryMode: CodegenEntryMode, |
|
| 196 | 191 | } |
|
| 197 | 192 | ||
| 198 | 193 | /// Print a driver error line. |
|
| 199 | - | fn error(msg: *[*[u8]]) -> Error { |
|
| 194 | + | unsafe fn error(msg: *[*[u8]]) -> Error { |
|
| 200 | 195 | io::printError("radiance: "); |
|
| 201 | 196 | ||
| 202 | 197 | for part, i in msg { |
|
| 203 | 198 | io::printError(part); |
|
| 204 | 199 | if i < msg.len - 1 { |
| 208 | 203 | io::printError("\n"); |
|
| 209 | 204 | return Error::Other; |
|
| 210 | 205 | } |
|
| 211 | 206 | ||
| 212 | 207 | /// Print a log line for the given package. |
|
| 213 | - | fn pkgLog(pkg: *package::Package, msg: *[*[u8]]) { |
|
| 208 | + | unsafe fn pkgLog(pkg: *package::Package, msg: *[*[u8]]) { |
|
| 214 | 209 | io::printError("radiance: "); |
|
| 215 | 210 | io::printError(pkg.name); |
|
| 216 | 211 | io::printError(": "); |
|
| 217 | 212 | ||
| 218 | 213 | for part, i in msg { |
| 223 | 218 | } |
|
| 224 | 219 | io::printError("\n"); |
|
| 225 | 220 | } |
|
| 226 | 221 | ||
| 227 | 222 | /// Return `true` when `path` ends with `ext`. |
|
| 228 | - | fn hasExtension(path: *[u8], ext: *[u8]) -> bool { |
|
| 223 | + | unsafe fn hasExtension(path: *[u8], ext: *[u8]) -> bool { |
|
| 229 | 224 | if path.len < ext.len { |
|
| 230 | 225 | return false; |
|
| 231 | 226 | } |
|
| 232 | 227 | let start = path.len - ext.len; |
|
| 233 | 228 | return mem::eq(&path[start..], ext); |
|
| 234 | 229 | } |
|
| 235 | 230 | ||
| 236 | 231 | /// Create an empty source input set for one package. |
|
| 237 | - | fn packageInput(name: *[u8]) -> PackageInput { |
|
| 232 | + | unsafe fn packageInput(name: *[u8]) -> PackageInput { |
|
| 238 | 233 | return PackageInput { |
|
| 239 | 234 | name, |
|
| 240 | 235 | startupPath: nil, |
|
| 241 | 236 | radPaths: undefined, |
|
| 242 | 237 | radPathCount: 0, |
| 244 | 239 | asmPathCount: 0, |
|
| 245 | 240 | }; |
|
| 246 | 241 | } |
|
| 247 | 242 | ||
| 248 | 243 | /// Register, load, and parse `path` within `pkg`. |
|
| 249 | - | fn processModule( |
|
| 244 | + | unsafe fn processModule( |
|
| 250 | 245 | pkg: *mut package::Package, |
|
| 251 | 246 | graph: *mut module::ModuleGraph, |
|
| 252 | 247 | path: *[u8], |
|
| 253 | 248 | nodeArena: *mut ast::NodeArena, |
|
| 254 | 249 | sourceArena: *mut alloc::Arena |
| 282 | 277 | throw error(&["error setting source"]); |
|
| 283 | 278 | }; |
|
| 284 | 279 | } |
|
| 285 | 280 | ||
| 286 | 281 | /// Consume the next argument, or print an error and throw. |
|
| 287 | - | fn nextArg(args: *[*[u8]], idx: *mut u32, msg: *[*[u8]]) -> *[u8] throws (Error) { |
|
| 282 | + | unsafe fn nextArg(args: *[*[u8]], idx: *mut u32, msg: *[*[u8]]) -> *[u8] throws (Error) { |
|
| 288 | 283 | set *idx += 1; |
|
| 289 | 284 | if *idx >= args.len { |
|
| 290 | 285 | throw error(msg); |
|
| 291 | 286 | } |
|
| 292 | 287 | return args[*idx]; |
|
| 293 | 288 | } |
|
| 294 | 289 | ||
| 295 | 290 | /// Parse CLI arguments and return compilation context. |
|
| 296 | - | fn processCommand( |
|
| 291 | + | unsafe fn processCommand( |
|
| 297 | 292 | args: *[*[u8]], |
|
| 298 | 293 | arena: *mut ast::NodeArena |
|
| 299 | 294 | ) -> CompileContext throws (Error) { |
|
| 300 | 295 | let mut buildTest = false; |
|
| 301 | 296 | let mut debugEnabled = false; |
| 446 | 441 | } |
|
| 447 | 442 | return ctx; |
|
| 448 | 443 | } |
|
| 449 | 444 | ||
| 450 | 445 | /// Get the entry package from the context. |
|
| 451 | - | fn getEntryPackage(ctx: *CompileContext) -> *package::Package throws (Error) { |
|
| 446 | + | unsafe fn getEntryPackage(ctx: *CompileContext) -> *package::Package throws (Error) { |
|
| 452 | 447 | let entryIdx = ctx.entryPkgIdx else { |
|
| 453 | 448 | throw error(&["no entry package specified"]); |
|
| 454 | 449 | }; |
|
| 455 | 450 | return &ctx.packages[entryIdx]; |
|
| 456 | 451 | } |
|
| 457 | 452 | ||
| 458 | 453 | /// Return the startup assembly path for the entry package, if one was supplied. |
|
| 459 | - | fn getEntryStartupPath(ctx: *CompileContext) -> ?*[u8] { |
|
| 454 | + | unsafe fn getEntryStartupPath(ctx: *CompileContext) -> ?*[u8] { |
|
| 460 | 455 | let entryIdx = ctx.entryPkgIdx else { |
|
| 461 | 456 | panic "getEntryStartupPath: no entry package"; |
|
| 462 | 457 | }; |
|
| 463 | 458 | return ctx.inputs[entryIdx].startupPath; |
|
| 464 | 459 | } |
|
| 465 | 460 | ||
| 466 | 461 | /// Get root module info from a package. |
|
| 467 | - | fn getRootModule(pkg: *package::Package, graph: *module::ModuleGraph) -> RootModule throws (Error) { |
|
| 462 | + | unsafe fn getRootModule(pkg: *package::Package, graph: *module::ModuleGraph) -> RootModule throws (Error) { |
|
| 468 | 463 | let rootId = pkg.rootModuleId else { |
|
| 469 | 464 | throw error(&["no root module found"]); |
|
| 470 | 465 | }; |
|
| 471 | 466 | let rootEntry = module::get(graph, rootId) else { |
|
| 472 | 467 | throw error(&["root module entry not found"]); |
| 476 | 471 | }; |
|
| 477 | 472 | return RootModule { entry: rootEntry, ast: rootAst }; |
|
| 478 | 473 | } |
|
| 479 | 474 | ||
| 480 | 475 | /// Dump the module graph. |
|
| 481 | - | fn dumpGraph(ctx: *CompileContext) { |
|
| 476 | + | unsafe fn dumpGraph(ctx: *CompileContext) { |
|
| 482 | 477 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 483 | 478 | module::printer::printGraph(&ctx.graph, &mut arena); |
|
| 484 | 479 | } |
|
| 485 | 480 | ||
| 486 | 481 | /// Dump the parsed AST. |
|
| 487 | - | fn dumpAst(ctx: *CompileContext) throws (Error) { |
|
| 482 | + | unsafe fn dumpAst(ctx: *CompileContext) throws (Error) { |
|
| 488 | 483 | let pkg = try getEntryPackage(ctx); |
|
| 489 | 484 | let root = try getRootModule(pkg, &ctx.graph); |
|
| 490 | 485 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 491 | 486 | ||
| 492 | 487 | ast::printer::printTree(root.ast, &mut arena); |
|
| 493 | 488 | } |
|
| 494 | 489 | ||
| 495 | 490 | /// Lower all packages into a single IL program. |
|
| 496 | 491 | /// Dependencies are lowered first, then the entry package. |
|
| 497 | - | fn lowerAllPackages( |
|
| 492 | + | unsafe fn lowerAllPackages( |
|
| 498 | 493 | ctx: *mut CompileContext, |
|
| 499 | 494 | res: *mut resolver::Resolver |
|
| 500 | 495 | ) -> il::Program throws (Error) { |
|
| 501 | 496 | let entryIdx = ctx.entryPkgIdx else { |
|
| 502 | 497 | panic "lowerAllPackages: no entry package"; |
| 513 | 508 | // Finalize and return the unified program. |
|
| 514 | 509 | return lower::finalize(&low); |
|
| 515 | 510 | } |
|
| 516 | 511 | ||
| 517 | 512 | /// Lower all packages into an existing lowerer. |
|
| 518 | - | fn lowerAllPackagesInto( |
|
| 513 | + | unsafe fn lowerAllPackagesInto( |
|
| 519 | 514 | ctx: *mut CompileContext, |
|
| 520 | 515 | res: *mut resolver::Resolver, |
|
| 521 | 516 | low: *mut lower::Lowerer |
|
| 522 | 517 | ) throws (Error) { |
|
| 523 | 518 | let entryIdx = ctx.entryPkgIdx else { |
| 532 | 527 | // Lower entry package. |
|
| 533 | 528 | try lowerPackage(ctx, res, low, &mut ctx.packages[entryIdx], true); |
|
| 534 | 529 | } |
|
| 535 | 530 | ||
| 536 | 531 | /// Lower all modules in a package into the lowerer accumulator. |
|
| 537 | - | fn lowerPackage( |
|
| 532 | + | unsafe fn lowerPackage( |
|
| 538 | 533 | ctx: *CompileContext, |
|
| 539 | 534 | res: *mut resolver::Resolver, |
|
| 540 | 535 | low: *mut lower::Lowerer, |
|
| 541 | 536 | pkg: *mut package::Package, |
|
| 542 | 537 | isEntry: bool |
| 550 | 545 | ||
| 551 | 546 | try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg); |
|
| 552 | 547 | } |
|
| 553 | 548 | ||
| 554 | 549 | /// Recursively lower a module and all its children into the accumulator. |
|
| 555 | - | fn lowerModuleTreeInto( |
|
| 550 | + | unsafe fn lowerModuleTreeInto( |
|
| 556 | 551 | ctx: *CompileContext, |
|
| 557 | 552 | low: *mut lower::Lowerer, |
|
| 558 | 553 | graph: *module::ModuleGraph, |
|
| 559 | 554 | modId: u16, |
|
| 560 | 555 | isRoot: bool, |
| 582 | 577 | try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg); |
|
| 583 | 578 | } |
|
| 584 | 579 | } |
|
| 585 | 580 | ||
| 586 | 581 | /// Build a scope access chain: a::b::c from a slice of identifiers. |
|
| 587 | - | fn synthScopeAccess(arena: *mut ast::NodeArena, path: *[*[u8]]) -> *ast::Node { |
|
| 582 | + | unsafe fn synthScopeAccess(arena: *mut ast::NodeArena, path: *[*[u8]]) -> *ast::Node { |
|
| 588 | 583 | let mut result = ast::synthNode( |
|
| 589 | 584 | arena, |
|
| 590 | 585 | ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, path[0])) |
|
| 591 | 586 | ); |
|
| 592 | 587 | for i in 1..path.len { |
| 600 | 595 | } |
|
| 601 | 596 | return result; |
|
| 602 | 597 | } |
|
| 603 | 598 | ||
| 604 | 599 | /// Check if a function declaration has the `@test` attribute and return its name if so. |
|
| 605 | - | fn getTestFnName(decl: *ast::FnDecl) -> ?*[u8] { |
|
| 600 | + | unsafe fn getTestFnName(decl: *ast::FnDecl) -> ?*[u8] { |
|
| 606 | 601 | let attrs = decl.attrs else { return nil; }; |
|
| 607 | 602 | if not ast::attributesContains(&attrs, ast::Attribute::Test) { |
|
| 608 | 603 | return nil; |
|
| 609 | 604 | } |
|
| 610 | 605 | let case ast::NodeValue::Ident(name) = decl.name.value |
| 612 | 607 | ||
| 613 | 608 | return name; |
|
| 614 | 609 | } |
|
| 615 | 610 | ||
| 616 | 611 | /// Scan a single module's AST for `@test` functions and append them to `tests`. |
|
| 617 | - | fn collectModuleTests( |
|
| 612 | + | unsafe fn collectModuleTests( |
|
| 618 | 613 | entry: *module::ModuleEntry, |
|
| 619 | 614 | tests: *mut [TestDesc], |
|
| 620 | 615 | testCount: *mut u32 |
|
| 621 | 616 | ) { |
|
| 622 | 617 | let modAst = entry.ast else { |
| 640 | 635 | } |
|
| 641 | 636 | } |
|
| 642 | 637 | } |
|
| 643 | 638 | ||
| 644 | 639 | /// Synthesize a `testing::test("mod", "name", mod::fn)` call for one test. |
|
| 645 | - | fn synthTestCall(arena: *mut ast::NodeArena, desc: *TestDesc) -> *ast::Node { |
|
| 640 | + | unsafe fn synthTestCall(arena: *mut ast::NodeArena, desc: *TestDesc) -> *ast::Node { |
|
| 646 | 641 | let callee = synthScopeAccess(arena, &["testing", "test"]); |
|
| 647 | 642 | let modStr = il::formatQualifiedName( |
|
| 648 | 643 | &mut arena.arena, |
|
| 649 | 644 | &desc.modPath[..desc.modPath.len - 1], |
|
| 650 | 645 | desc.modPath[desc.modPath.len - 1] |
| 682 | 677 | /// ]); |
|
| 683 | 678 | /// } |
|
| 684 | 679 | /// ``` |
|
| 685 | 680 | /// |
|
| 686 | 681 | /// Uses `#`-prefixed names to avoid conflicts with user code. |
|
| 687 | - | fn generateTestRunner( |
|
| 682 | + | unsafe fn generateTestRunner( |
|
| 688 | 683 | ctx: *mut CompileContext, |
|
| 689 | 684 | arena: *mut ast::NodeArena |
|
| 690 | 685 | ) throws (Error) { |
|
| 691 | 686 | let entryPkg = try getEntryPackage(ctx); |
|
| 692 | 687 | let root = try getRootModule(entryPkg, &ctx.graph); |
| 712 | 707 | ||
| 713 | 708 | injectIntoBlock(root.ast, arena, fnDecl); |
|
| 714 | 709 | } |
|
| 715 | 710 | ||
| 716 | 711 | /// Synthesize the test entry point. |
|
| 717 | - | fn synthTestMainFn(arena: *mut ast::NodeArena, tests: *[TestDesc]) -> *ast::Node { |
|
| 712 | + | unsafe fn synthTestMainFn(arena: *mut ast::NodeArena, tests: *[TestDesc]) -> *ast::Node { |
|
| 718 | 713 | // Build array literal: `[testing::test(...), ...]`. |
|
| 719 | 714 | let a = alloc::arenaAllocator(&mut arena.arena); |
|
| 720 | 715 | let mut elements = ast::nodeSlice(arena, tests.len as u32); |
|
| 721 | 716 | for i in 0..tests.len { |
|
| 722 | 717 | elements.append(synthTestCall(arena, &tests[i]), a); |
| 760 | 755 | name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs, |
|
| 761 | 756 | })); |
|
| 762 | 757 | } |
|
| 763 | 758 | ||
| 764 | 759 | /// Append a declaration to a block node's statement list. |
|
| 765 | - | fn injectIntoBlock( |
|
| 760 | + | unsafe fn injectIntoBlock( |
|
| 766 | 761 | blockNode: *mut ast::Node, |
|
| 767 | 762 | arena: *mut ast::NodeArena, |
|
| 768 | 763 | decl: *ast::Node |
|
| 769 | 764 | ) { |
|
| 770 | 765 | let case ast::NodeValue::Block(block) = blockNode.value else { |
| 773 | 768 | let stmts = block.statements.append(decl, alloc::arenaAllocator(&mut arena.arena)); |
|
| 774 | 769 | set blockNode.value = ast::NodeValue::Block(ast::Block { statements: stmts }); |
|
| 775 | 770 | } |
|
| 776 | 771 | ||
| 777 | 772 | /// Write a self-contained RV64 image containing text and data sections. |
|
| 778 | - | fn writeImage( |
|
| 773 | + | unsafe fn writeImage( |
|
| 779 | 774 | code: *[u32], |
|
| 780 | 775 | roData: *[u8], |
|
| 781 | 776 | rwData: *[u8], |
|
| 782 | 777 | path: *[u8] |
|
| 783 | 778 | ) -> bool { |
| 789 | 784 | return unix::writeFileParts(path, &[headerBytes, codeBytes, roData, rwData]); |
|
| 790 | 785 | } |
|
| 791 | 786 | ||
| 792 | 787 | /// Write a data section to a file at `basePath` + `ext`. |
|
| 793 | 788 | /// Empty data truncates any stale sidecar left by an earlier build. |
|
| 794 | - | fn writeDataWithExt( |
|
| 789 | + | unsafe fn writeDataWithExt( |
|
| 795 | 790 | data: *[u8], |
|
| 796 | 791 | basePath: *[u8], |
|
| 797 | 792 | ext: *[u8] |
|
| 798 | 793 | ) throws (Error) { |
|
| 799 | 794 | let mut path: [u8; MAX_PATH_LEN] = undefined; |
| 809 | 804 | } |
|
| 810 | 805 | ||
| 811 | 806 | /// Serialize debug entries and write the `.debug` file. |
|
| 812 | 807 | /// Resolves module IDs to file paths via the module graph. |
|
| 813 | 808 | /// Format per entry is `{pc: u32, offset: u32, filePath: [u8], NULL}`. |
|
| 814 | - | fn writeDebugInfo( |
|
| 809 | + | unsafe fn writeDebugInfo( |
|
| 815 | 810 | entries: *[types::DebugEntry], |
|
| 816 | 811 | graph: *module::ModuleGraph, |
|
| 817 | 812 | basePath: *[u8], |
|
| 818 | 813 | arena: *mut alloc::Arena |
|
| 819 | 814 | ) throws (Error) { |
| 838 | 833 | } |
|
| 839 | 834 | try writeDataWithExt(&buf[..pos], basePath, DEBUG_EXT); |
|
| 840 | 835 | } |
|
| 841 | 836 | ||
| 842 | 837 | /// Run the resolver on the parsed modules. |
|
| 843 | - | fn runResolver(ctx: *mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) { |
|
| 838 | + | unsafe fn runResolver(ctx: *mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) { |
|
| 844 | 839 | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 845 | 840 | let entryPkg = try getEntryPackage(ctx); |
|
| 846 | 841 | ||
| 847 | 842 | pkgLog(entryPkg, &["resolving", ".."]); |
|
| 848 | 843 |
| 885 | 880 | throw error(&["failed:", countStr, "errors"]); |
|
| 886 | 881 | } |
|
| 887 | 882 | return res; |
|
| 888 | 883 | } |
|
| 889 | 884 | ||
| 890 | - | /// Emit one lowered function to machine code and reclaim its IL arena. |
|
| 891 | - | fn generateLoweredFn(ctxPtr: *mut opaque, func: *il::Fn, role: lower::FnRole) { |
|
| 892 | - | let ctx = ctxPtr as *mut CodegenSinkContext; |
|
| 893 | - | ||
| 894 | - | match role { |
|
| 895 | - | case lower::FnRole::Default => { |
|
| 896 | - | rv64::recordFunctionAlias(ctx.generator, DEFAULT_ENTRY_SYMBOL); |
|
| 897 | - | match ctx.generator.entryPatch { |
|
| 898 | - | case rv64::EntryPatch::Reserved(_) => { |
|
| 899 | - | set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name); |
|
| 900 | - | } |
|
| 901 | - | // No entry jump was reserved: startup assembly calls the |
|
| 902 | - | // default function through `DEFAULT_ENTRY_SYMBOL` instead. |
|
| 903 | - | case rv64::EntryPatch::None => {} |
|
| 904 | - | } |
|
| 905 | - | } |
|
| 906 | - | else => {} |
|
| 907 | - | } |
|
| 908 | - | rv64::generateFunction(ctx.generator, func, ctx.fnArena); |
|
| 909 | - | alloc::reset(ctx.fnArena); |
|
| 910 | - | } |
|
| 911 | 885 | ||
| 912 | 886 | /// Assemble one `.ras` input and merge it into the active code generator. |
|
| 913 | 887 | /// |
|
| 914 | 888 | /// Text symbols are appended to `generator`. Data emitted by the assembler is |
|
| 915 | 889 | /// copied into `ASM_RO_DATA_BUF` at `*asmDataLen`, and `*asmDataLen` is advanced |
|
| 916 | 890 | /// so the next assembly module receives the correct rodata base address. |
|
| 917 | - | fn assembleAsmModule( |
|
| 891 | + | unsafe fn assembleAsmModule( |
|
| 918 | 892 | generator: *mut rv64::Generator, |
|
| 919 | 893 | pkg: *package::Package, |
|
| 920 | 894 | path: *[u8], |
|
| 921 | 895 | asmDataLen: *mut u32, |
|
| 922 | 896 | arena: *mut alloc::Arena |
| 948 | 922 | ||
| 949 | 923 | rv64::addAssembly(generator, program); |
|
| 950 | 924 | } |
|
| 951 | 925 | ||
| 952 | 926 | /// Assemble all inputs collected in the package inputs. |
|
| 953 | - | fn assembleAsmInputs( |
|
| 927 | + | unsafe fn assembleAsmInputs( |
|
| 954 | 928 | ctx: *CompileContext, |
|
| 955 | 929 | generator: *mut rv64::Generator, |
|
| 956 | 930 | asmDataLen: *mut u32, |
|
| 957 | 931 | arena: *mut alloc::Arena |
|
| 958 | 932 | ) -> *[u8] throws (Error) { |
| 970 | 944 | } |
|
| 971 | 945 | return &ASM_RO_DATA_BUF[..*asmDataLen]; |
|
| 972 | 946 | } |
|
| 973 | 947 | ||
| 974 | 948 | /// Lower all packages while streaming each lowered function into RV64 codegen. |
|
| 975 | - | fn lowerAndGenerateAllPackages( |
|
| 949 | + | unsafe fn lowerAndGenerateAllPackages( |
|
| 976 | 950 | ctx: *mut CompileContext, |
|
| 977 | 951 | res: *mut resolver::Resolver, |
|
| 978 | 952 | fnArena: *mut alloc::Arena, |
|
| 979 | 953 | codegenOptions: CodegenOptions |
|
| 980 | 954 | ) -> rv64::Program throws (Error) { |
| 997 | 971 | } |
|
| 998 | 972 | let mut generator = rv64::beginProgram( |
|
| 999 | 973 | rv64::ProgramOptions { entryPatch, debug: codegenOptions.debug }, |
|
| 1000 | 974 | &mut res.arena |
|
| 1001 | 975 | ); |
|
| 1002 | - | let mut codegenCtx = CodegenSinkContext { |
|
| 976 | + | let mut codegenCtx = codegenSink::Context { |
|
| 1003 | 977 | generator: &mut generator, |
|
| 1004 | 978 | fnArena, |
|
| 1005 | 979 | }; |
|
| 1006 | 980 | let mut low = lower::lowerer( |
|
| 1007 | 981 | res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena, options |
|
| 1008 | 982 | ); |
|
| 1009 | 983 | set low.output = lower::FnOutput::Stream(lower::FnSink { |
|
| 1010 | - | ctx: &mut codegenCtx as *mut opaque, |
|
| 1011 | - | emitFn: generateLoweredFn, |
|
| 984 | + | ctx: codegenSink::eraseContext(&mut codegenCtx), |
|
| 985 | + | emitFn: codegenSink::generate, |
|
| 1012 | 986 | }); |
|
| 1013 | 987 | let mut asmDataLen: u32 = 0; |
|
| 1014 | 988 | if let path = startupPath { |
|
| 1015 | 989 | try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, &mut res.arena); |
|
| 1016 | 990 | } |
| 1030 | 1004 | } |
|
| 1031 | 1005 | return rv64::finishProgram(&mut generator, &low.data[..], storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]); |
|
| 1032 | 1006 | } |
|
| 1033 | 1007 | ||
| 1034 | 1008 | /// Lower, optionally dump, and optionally generate binary output. |
|
| 1035 | - | fn compile( |
|
| 1009 | + | unsafe fn compile( |
|
| 1036 | 1010 | ctx: *mut CompileContext, |
|
| 1037 | 1011 | res: *mut resolver::Resolver, |
|
| 1038 | 1012 | fnArena: *mut alloc::Arena |
|
| 1039 | 1013 | ) throws (Error) { |
|
| 1040 | 1014 | let entryPkg = try getEntryPackage(ctx); |
| 1086 | 1060 | try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, &mut res.arena); |
|
| 1087 | 1061 | } |
|
| 1088 | 1062 | pkgLog(entryPkg, &["ok", "(", outPath, ")"]); |
|
| 1089 | 1063 | } |
|
| 1090 | 1064 | ||
| 1091 | - | @default fn main(env: *sys::Env) -> i32 { |
|
| 1065 | + | @default unsafe fn main(env: *sys::Env) -> i32 { |
|
| 1092 | 1066 | let mut arena = ast::nodeArena(&mut TEMP_ARENA[..]); |
|
| 1093 | 1067 | let mut ctx = try processCommand(env.args, &mut arena) catch { |
|
| 1094 | 1068 | return 1; |
|
| 1095 | 1069 | }; |
|
| 1096 | 1070 | match ctx.dump { |
compiler/radiance/codegenSink.rad
added
+49 -0
| 1 | + | //! Trusted streamed code generation implementation. |
|
| 2 | + | use std::lang::alloc; |
|
| 3 | + | use std::lang::il; |
|
| 4 | + | use std::lang::lower; |
|
| 5 | + | use std::arch::rv64; |
|
| 6 | + | ||
| 7 | + | /// Symbol name exported for startup code to call the semantic entry function. |
|
| 8 | + | unsafe constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default"; |
|
| 9 | + | ||
| 10 | + | /// State carried by the streaming lowerer/codegen callback. |
|
| 11 | + | export record Context { |
|
| 12 | + | /// RV64 generator receiving lowered functions. |
|
| 13 | + | generator: *mut rv64::Generator, |
|
| 14 | + | /// Arena holding the current function's lowered IL. |
|
| 15 | + | fnArena: *mut alloc::Arena, |
|
| 16 | + | } |
|
| 17 | + | ||
| 18 | + | /// Erase a streamed code generation context for storage in a function sink. |
|
| 19 | + | /// |
|
| 20 | + | /// The caller must keep `ctx` alive and exclusively accessible until the sink |
|
| 21 | + | /// stops invoking its callback. |
|
| 22 | + | export unsafe fn eraseContext(ctx: &mut Context) -> *unsafe mut u8 { |
|
| 23 | + | return ((ctx as &mut opaque) as &mut u8) as *unsafe mut u8; |
|
| 24 | + | } |
|
| 25 | + | ||
| 26 | + | /// Generate machine code for a lowered function and reclaim its IL arena. |
|
| 27 | + | export fn generate(ctx: &mut opaque, func: *il::Fn, role: lower::FnRole) { |
|
| 28 | + | generateWithContext(ctx as &mut Context, func, role); |
|
| 29 | + | } |
|
| 30 | + | ||
| 31 | + | /// Generate machine code with the typed streamed-lowering context. |
|
| 32 | + | fn generateWithContext(ctx: &mut Context, func: *il::Fn, role: lower::FnRole) { |
|
| 33 | + | match role { |
|
| 34 | + | case lower::FnRole::Default => { |
|
| 35 | + | rv64::recordFunctionAlias(ctx.generator, DEFAULT_ENTRY_SYMBOL); |
|
| 36 | + | match ctx.generator.entryPatch { |
|
| 37 | + | case rv64::EntryPatch::Reserved(_) => { |
|
| 38 | + | set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name); |
|
| 39 | + | } |
|
| 40 | + | // No entry jump was reserved: startup assembly calls the |
|
| 41 | + | // default function through `DEFAULT_ENTRY_SYMBOL` instead. |
|
| 42 | + | case rv64::EntryPatch::None => {} |
|
| 43 | + | } |
|
| 44 | + | } |
|
| 45 | + | else => {} |
|
| 46 | + | } |
|
| 47 | + | rv64::generateFunction(ctx.generator, func, ctx.fnArena); |
|
| 48 | + | alloc::reset(ctx.fnArena); |
|
| 49 | + | } |
lib/std.rad
+11 -11
| 1 | 1 | //! The Radiance Standard Library. |
|
| 2 | 2 | ||
| 3 | - | export mod io; |
|
| 4 | - | export mod collections; |
|
| 3 | + | export unsafe mod io; |
|
| 4 | + | export unsafe mod collections; |
|
| 5 | 5 | export mod char; |
|
| 6 | - | export mod lang; |
|
| 7 | - | export mod sys; |
|
| 8 | - | export mod arch; |
|
| 9 | - | export mod fmt; |
|
| 10 | - | export mod mem; |
|
| 11 | - | export mod vec; |
|
| 12 | - | export mod intrinsics; |
|
| 6 | + | export unsafe mod lang; |
|
| 7 | + | export unsafe mod sys; |
|
| 8 | + | export unsafe mod arch; |
|
| 9 | + | export unsafe mod fmt; |
|
| 10 | + | export unsafe mod mem; |
|
| 11 | + | export unsafe mod vec; |
|
| 12 | + | export unsafe mod intrinsics; |
|
| 13 | 13 | ||
| 14 | 14 | // Test modules. |
|
| 15 | - | @test export mod testing; |
|
| 16 | - | @test export mod tests; |
|
| 15 | + | @test export unsafe mod testing; |
|
| 16 | + | @test export unsafe mod tests; |
lib/std/arch/rv64/asm.rad
+4 -4
| 260 | 260 | export constant SHIFT_LIMIT: i32 = 64; |
|
| 261 | 261 | /// Largest `lui` or `auipc` immediate. |
|
| 262 | 262 | export constant UPPER_IMM_MAX_VALUE: i64 = 0xFFFFF; |
|
| 263 | 263 | ||
| 264 | 264 | /// Sorted instruction descriptor table used by the assembler parser. |
|
| 265 | - | export constant INSTRUCTIONS: [InstructionEntry; 88] = [ |
|
| 265 | + | export unsafe constant INSTRUCTIONS: [InstructionEntry; 88] = [ |
|
| 266 | 266 | { name: "add", encoder: InstructionEncoder::RRR { enc: encode::add } }, |
|
| 267 | 267 | { name: "addi", encoder: InstructionEncoder::RRI { enc: encode::addi } }, |
|
| 268 | 268 | { name: "addiw", encoder: InstructionEncoder::RRI { enc: encode::addiw } }, |
|
| 269 | 269 | { name: "addw", encoder: InstructionEncoder::RRR { enc: encode::addw } }, |
|
| 270 | 270 | { name: "and", encoder: InstructionEncoder::RRR { enc: encode::and_ } }, |
| 352 | 352 | { name: "xor", encoder: InstructionEncoder::RRR { enc: encode::xor } }, |
|
| 353 | 353 | { name: "xori", encoder: InstructionEncoder::RRI { enc: encode::xori } }, |
|
| 354 | 354 | ]; |
|
| 355 | 355 | ||
| 356 | 356 | /// Sorted directive lookup table used by the assembler parser. |
|
| 357 | - | export constant DIRECTIVES: [DirectiveEntry; 10] = [ |
|
| 357 | + | export unsafe constant DIRECTIVES: [DirectiveEntry; 10] = [ |
|
| 358 | 358 | { name: "align", kind: DirectiveKind::Align }, |
|
| 359 | 359 | { name: "ascii", kind: DirectiveKind::Ascii }, |
|
| 360 | 360 | { name: "byte", kind: DirectiveKind::Byte }, |
|
| 361 | 361 | { name: "constant", kind: DirectiveKind::Constant }, |
|
| 362 | 362 | { name: "data", kind: DirectiveKind::Data }, |
| 366 | 366 | { name: "text", kind: DirectiveKind::Text }, |
|
| 367 | 367 | { name: "word", kind: DirectiveKind::Word }, |
|
| 368 | 368 | ]; |
|
| 369 | 369 | ||
| 370 | 370 | /// Sorted register-name lookup table used by the assembler parser. |
|
| 371 | - | export constant REGISTERS: [RegisterEntry; 33] = [ |
|
| 371 | + | export unsafe constant REGISTERS: [RegisterEntry; 33] = [ |
|
| 372 | 372 | { name: "a0", reg: rv64::A0 }, |
|
| 373 | 373 | { name: "a1", reg: rv64::A1 }, |
|
| 374 | 374 | { name: "a2", reg: rv64::A2 }, |
|
| 375 | 375 | { name: "a3", reg: rv64::A3 }, |
|
| 376 | 376 | { name: "a4", reg: rv64::A4 }, |
| 403 | 403 | { name: "tp", reg: rv64::TP }, |
|
| 404 | 404 | { name: "zero", reg: rv64::ZERO }, |
|
| 405 | 405 | ]; |
|
| 406 | 406 | ||
| 407 | 407 | /// Sorted CSR-name lookup table used by the assembler parser. |
|
| 408 | - | export constant CSRS: [CsrEntry; 9] = [ |
|
| 408 | + | export unsafe constant CSRS: [CsrEntry; 9] = [ |
|
| 409 | 409 | { name: "mcause", csr: 0x342 }, |
|
| 410 | 410 | { name: "mepc", csr: 0x341 }, |
|
| 411 | 411 | { name: "mhartid", csr: 0xF14 }, |
|
| 412 | 412 | { name: "mie", csr: 0x304 }, |
|
| 413 | 413 | { name: "mip", csr: 0x344 }, |
lib/std/arch/rv64/asm/scanner/tests.rad
+1 -1
| 1 | 1 | use std::mem; |
|
| 2 | 2 | use std::testing; |
|
| 3 | 3 | use std::lang::strings; |
|
| 4 | 4 | ||
| 5 | 5 | /// String pool used by assembler scanner tests. |
|
| 6 | - | static TEST_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 6 | + | unsafe static TEST_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 7 | 7 | ||
| 8 | 8 | /// Create a scanner for test input. |
|
| 9 | 9 | fn testScanner(source: *[u8]) -> super::Scanner { |
|
| 10 | 10 | return super::scanner(super::SourceKind::String, source, &mut TEST_STRING_POOL); |
|
| 11 | 11 | } |
lib/std/arch/rv64/asm/tests.rad
+6 -6
| 9 | 9 | use std::arch::rv64::encode; |
|
| 10 | 10 | use std::arch::rv64::printer; |
|
| 11 | 11 | ||
| 12 | 12 | use super::scanner; |
|
| 13 | 13 | ||
| 14 | - | static ASM_ARENA_STORAGE: [u8; 65536] = undefined; |
|
| 15 | - | static ASM_TEXT_STORAGE: [u32; 256] = undefined; |
|
| 16 | - | static ASM_DATA_STORAGE: [u8; 1024] = undefined; |
|
| 17 | - | static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 18 | - | static PRINT_ARENA_STORAGE: [u8; 1024] = undefined; |
|
| 19 | - | static PRINT_BUFFER: [u8; 128] = undefined; |
|
| 14 | + | unsafe static ASM_ARENA_STORAGE: [u8; 65536] = undefined; |
|
| 15 | + | unsafe static ASM_TEXT_STORAGE: [u32; 256] = undefined; |
|
| 16 | + | unsafe static ASM_DATA_STORAGE: [u8; 1024] = undefined; |
|
| 17 | + | unsafe static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 18 | + | unsafe static PRINT_ARENA_STORAGE: [u8; 1024] = undefined; |
|
| 19 | + | unsafe static PRINT_BUFFER: [u8; 128] = undefined; |
|
| 20 | 20 | ||
| 21 | 21 | fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) { |
|
| 22 | 22 | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
|
| 23 | 23 | return try super::assemble( |
|
| 24 | 24 | scanner::SourceKind::String, |
lib/std/arch/rv64/printer.rad
+1 -1
| 15 | 15 | ///////////////////// |
|
| 16 | 16 | // Register Names // |
|
| 17 | 17 | ///////////////////// |
|
| 18 | 18 | ||
| 19 | 19 | /// ABI register names. |
|
| 20 | - | constant REG_NAMES: [*[u8]; 32] = [ |
|
| 20 | + | unsafe constant REG_NAMES: [*[u8]; 32] = [ |
|
| 21 | 21 | "%zero", "%ra", "%sp", "%gp", "%tp", "%t0", "%t1", "%t2", |
|
| 22 | 22 | "%fp", "%s1", "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", |
|
| 23 | 23 | "%a6", "%a7", "%s2", "%s3", "%s4", "%s5", "%s6", "%s7", |
|
| 24 | 24 | "%s8", "%s9", "%s10", "%s11", "%t3", "%t4", "%t5", "%t6" |
|
| 25 | 25 | ]; |
lib/std/intrinsics.rad
+6 -3
| 1 | 1 | //! Compiler intrinsics. |
|
| 2 | 2 | ||
| 3 | - | /// Environment call. |
|
| 3 | + | /// Issue a system call with the given number and arguments. |
|
| 4 | 4 | /// |
|
| 5 | - | /// Issues a system call with the given number and arguments. |
|
| 6 | 5 | /// Arguments and the return value are `i64` to support both 32-bit |
|
| 7 | 6 | /// emulator addresses and 64-bit native (AMD64) pointers. |
|
| 8 | - | @intrinsic export fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64; |
|
| 7 | + | /// |
|
| 8 | + | /// The caller must satisfy the target system call's ABI. Every argument that |
|
| 9 | + | /// the kernel interprets as an address must remain valid and readable or |
|
| 10 | + | /// writable for the full extent and duration required by that system call. |
|
| 11 | + | @intrinsic export unsafe fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64; |
|
| 9 | 12 | ||
| 10 | 13 | /// Break out of program. |
|
| 11 | 14 | @intrinsic export fn ebreak(); |
|
| 12 | 15 | ||
| 13 | 16 | /// Full acquire/release memory fence. |
lib/std/io.rad
+5 -3
| 1 | 1 | //! Input/output utilities. |
|
| 2 | 2 | use std::fmt; |
|
| 3 | 3 | use std::intrinsics; |
|
| 4 | 4 | ||
| 5 | 5 | export fn print(str: *[u8]) { |
|
| 6 | - | intrinsics::ecall(64, 1, str.ptr as i64, str.len as i64, 0); |
|
| 6 | + | unsafe { intrinsics::ecall(64, 1, str.ptr as i64, str.len as i64, 0); } |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | export fn printError(str: *[u8]) { |
|
| 10 | - | intrinsics::ecall(64, 2, str.ptr as i64, str.len as i64, 0); |
|
| 10 | + | unsafe { intrinsics::ecall(64, 2, str.ptr as i64, str.len as i64, 0); } |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | export fn printLn(str: *[u8]) { |
|
| 14 | 14 | print(str); |
|
| 15 | 15 | print("\n"); |
| 32 | 32 | let result: *[u8] = fmt::formatBool(val, &mut buffer[..]); |
|
| 33 | 33 | print(result); |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | 36 | export fn read(buf: *mut [u8]) -> u32 { |
|
| 37 | - | return intrinsics::ecall(63, 0, buf.ptr as i64, buf.len as i64, 0) as u32; |
|
| 37 | + | let mut result: i64 = 0; |
|
| 38 | + | unsafe { set result = intrinsics::ecall(63, 0, buf.ptr as i64, buf.len as i64, 0); } |
|
| 39 | + | return result as u32; |
|
| 38 | 40 | } |
|
| 39 | 41 | ||
| 40 | 42 | export fn readToEnd(buf: *mut [u8]) -> *[u8] { |
|
| 41 | 43 | let mut total: u32 = 0; |
|
| 42 | 44 |
lib/std/lang.rad
+12 -12
| 1 | 1 | //! Radiance language implementation. |
|
| 2 | - | export mod alloc; |
|
| 3 | - | export mod sexpr; |
|
| 2 | + | export unsafe mod alloc; |
|
| 3 | + | export unsafe mod sexpr; |
|
| 4 | 4 | export mod types; |
|
| 5 | - | export mod strings; |
|
| 6 | - | export mod scanner; |
|
| 7 | - | export mod ast; |
|
| 8 | - | export mod parser; |
|
| 9 | - | export mod module; |
|
| 10 | - | export mod resolver; |
|
| 11 | - | export mod package; |
|
| 12 | - | export mod il; |
|
| 13 | - | export mod lower; |
|
| 14 | - | export mod gen; |
|
| 5 | + | export unsafe mod strings; |
|
| 6 | + | export unsafe mod scanner; |
|
| 7 | + | export unsafe mod ast; |
|
| 8 | + | export unsafe mod parser; |
|
| 9 | + | export unsafe mod module; |
|
| 10 | + | export unsafe mod resolver; |
|
| 11 | + | export unsafe mod package; |
|
| 12 | + | export unsafe mod il; |
|
| 13 | + | export unsafe mod lower; |
|
| 14 | + | export unsafe mod gen; |
lib/std/lang/alloc.rad
+40 -15
| 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; |
|
| 9 | + | /// Largest byte extent representable by allocator APIs. |
|
| 10 | + | constant MAX_EXTENT: u32 = 0xFFFFFFFF; |
|
| 10 | 11 | ||
| 11 | 12 | /// Error thrown by allocator. |
|
| 12 | 13 | export union AllocError { |
|
| 13 | 14 | /// Allocator is out of memory. |
|
| 14 | 15 | OutOfMemory, |
| 30 | 31 | return Arena { data, offset: 0 }; |
|
| 31 | 32 | } |
|
| 32 | 33 | ||
| 33 | 34 | /// Allocate `size` bytes with the given alignment. |
|
| 34 | 35 | /// |
|
| 36 | + | /// `size` must be nonzero and `alignment` must be a nonzero power of two. |
|
| 35 | 37 | /// Returns an opaque pointer to the allocated memory. Throws `AllocError` if |
|
| 36 | - | /// the arena is exhausted. The caller is responsible for casting to the |
|
| 37 | - | /// appropriate type and initializing the memory. |
|
| 38 | + | /// the arena is exhausted or its offset lies beyond the backing storage. The |
|
| 39 | + | /// caller is responsible for casting to the appropriate type and initializing |
|
| 40 | + | /// the memory. |
|
| 38 | 41 | export fn alloc(arena: *mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
|
| 39 | 42 | assert alignment > 0; |
|
| 43 | + | assert (alignment & (alignment - 1)) == 0; |
|
| 40 | 44 | assert size > 0; |
|
| 41 | 45 | ||
| 42 | - | let aligned = mem::alignUp(arena.offset, alignment); |
|
| 43 | - | let newOffset = aligned + size; |
|
| 44 | - | ||
| 45 | - | if newOffset > arena.data.len as u32 { |
|
| 46 | + | let capacity = arena.data.len as u32; |
|
| 47 | + | if arena.offset > capacity { |
|
| 48 | + | throw AllocError::OutOfMemory; |
|
| 49 | + | } |
|
| 50 | + | let available = capacity - arena.offset; |
|
| 51 | + | let padding = (0 - arena.offset) & (alignment - 1); |
|
| 52 | + | if padding > available { |
|
| 53 | + | throw AllocError::OutOfMemory; |
|
| 54 | + | } |
|
| 55 | + | let alignedAvailable = available - padding; |
|
| 56 | + | if size > alignedAvailable { |
|
| 46 | 57 | throw AllocError::OutOfMemory; |
|
| 47 | 58 | } |
|
| 59 | + | let aligned = arena.offset + padding; |
|
| 60 | + | let newOffset = aligned + size; |
|
| 48 | 61 | let base: *mut u8 = &mut arena.data[aligned]; |
|
| 49 | 62 | set arena.offset = newOffset; |
|
| 50 | 63 | ||
| 51 | 64 | return base as *mut opaque; |
|
| 52 | 65 | } |
|
| 53 | 66 | ||
| 54 | 67 | /// Reset the arena, allowing all memory to be reused. |
|
| 55 | 68 | /// |
|
| 69 | + | /// The caller must not use any allocation from this arena after the reset. |
|
| 56 | 70 | /// Does not zero the memory. |
|
| 57 | - | export fn reset(arena: *mut Arena) { |
|
| 71 | + | export unsafe fn reset(arena: *mut Arena) { |
|
| 58 | 72 | set arena.offset = 0; |
|
| 59 | 73 | } |
|
| 60 | 74 | ||
| 61 | 75 | /// Save the current arena state for later restoration. |
|
| 62 | 76 | export fn save(arena: *Arena) -> u32 { |
|
| 63 | 77 | return arena.offset; |
|
| 64 | 78 | } |
|
| 65 | 79 | ||
| 66 | 80 | /// Restore the arena to a previously saved state, reclaiming all |
|
| 67 | 81 | /// allocations made since that point. |
|
| 68 | - | export fn restore(arena: *mut Arena, savedOffset: u32) { |
|
| 82 | + | /// |
|
| 83 | + | /// The caller must not use an allocation reclaimed by this operation. |
|
| 84 | + | export unsafe fn restore(arena: *mut Arena, savedOffset: u32) { |
|
| 69 | 85 | set arena.offset = savedOffset; |
|
| 70 | 86 | } |
|
| 71 | 87 | ||
| 72 | 88 | /// Returns the number of bytes currently allocated. |
|
| 73 | 89 | export fn used(arena: *Arena) -> u32 { |
| 77 | 93 | /// Returns the number of bytes remaining in the arena. |
|
| 78 | 94 | export fn remaining(arena: *Arena) -> u32 { |
|
| 79 | 95 | return arena.data.len as u32 - arena.offset; |
|
| 80 | 96 | } |
|
| 81 | 97 | ||
| 82 | - | /// Returns the remaining buffer as a mutable slice. |
|
| 83 | - | export fn remainingBuf(arena: *mut Arena) -> *mut [u8] { |
|
| 98 | + | /// Return an exclusive view of the remaining buffer. |
|
| 99 | + | /// |
|
| 100 | + | /// The caller must end access through the view before another arena operation. |
|
| 101 | + | export unsafe fn remainingBuf(arena: *mut Arena) -> *mut [u8] { |
|
| 84 | 102 | return &mut arena.data[arena.offset..]; |
|
| 85 | 103 | } |
|
| 86 | 104 | ||
| 87 | - | /// Commits `size` bytes of allocation, advancing the offset. |
|
| 88 | - | /// Use after writing to the buffer returned by [`remainingBuf`]. |
|
| 89 | - | export fn commit(arena: *mut Arena, size: u32) { |
|
| 105 | + | /// Commit `size` bytes of allocation and advance the offset. |
|
| 106 | + | /// |
|
| 107 | + | /// The caller must provide a size within the view returned by [`remainingBuf`] |
|
| 108 | + | /// and must end access through that view before another arena operation. |
|
| 109 | + | export unsafe fn commit(arena: *mut Arena, size: u32) { |
|
| 110 | + | assert size <= remaining(arena), "alloc::commit: size exceeds remaining capacity"; |
|
| 90 | 111 | set arena.offset += size; |
|
| 91 | 112 | } |
|
| 92 | 113 | ||
| 93 | 114 | /// Allocate a slice of `count` elements, each of `size` bytes with given alignment. |
|
| 94 | 115 | /// |
|
| 95 | 116 | /// Returns a type-erased slice that should be cast to the appropriate `*[T]`. |
|
| 96 | 117 | /// The slice length is set to `count` (element count, not bytes). |
|
| 97 | - | /// Throws `AllocError` if the arena is exhausted. |
|
| 118 | + | /// Throws `AllocError` if the arena is exhausted or the total byte extent |
|
| 119 | + | /// cannot be represented by `u32`. |
|
| 98 | 120 | export fn allocSlice(arena: *mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
|
| 99 | 121 | if count == 0 { |
|
| 100 | 122 | return &mut []; |
|
| 101 | 123 | } |
|
| 124 | + | if size > MAX_EXTENT / count { |
|
| 125 | + | throw AllocError::OutOfMemory; |
|
| 126 | + | } |
|
| 102 | 127 | let ptr = try alloc(arena, size * count, alignment); |
|
| 103 | 128 | ||
| 104 | 129 | return @sliceOf(ptr, count); |
|
| 105 | 130 | } |
|
| 106 | 131 |
lib/std/lang/alloc/tests.rad
+29 -0
| 102 | 102 | set failed = true; |
|
| 103 | 103 | }; |
|
| 104 | 104 | try testing::expect(failed); |
|
| 105 | 105 | } |
|
| 106 | 106 | ||
| 107 | + | /// Test that a forged arena offset cannot wrap allocation arithmetic. |
|
| 108 | + | @test fn testAllocRejectsInvalidOffset() throws (testing::TestError) { |
|
| 109 | + | static STORAGE: [u8; 16] = undefined; |
|
| 110 | + | let mut arena = super::Arena { data: &mut STORAGE[..], offset: 0xFFFFFFFF }; |
|
| 111 | + | let mut failed = false; |
|
| 112 | + | ||
| 113 | + | try super::alloc(&mut arena, 1, 1) catch { |
|
| 114 | + | set failed = true; |
|
| 115 | + | }; |
|
| 116 | + | ||
| 117 | + | try testing::expect(failed); |
|
| 118 | + | try testing::expect(super::used(&arena) == 0xFFFFFFFF); |
|
| 119 | + | } |
|
| 120 | + | ||
| 121 | + | /// Test that slice byte extents cannot overflow u32. |
|
| 122 | + | @test fn testAllocSliceExtentOverflow() throws (testing::TestError) { |
|
| 123 | + | static STORAGE: [u8; 16] = undefined; |
|
| 124 | + | let mut arena = super::new(&mut STORAGE[..]); |
|
| 125 | + | let count: u32 = 0xFFFFFFFF / 3 + 1; |
|
| 126 | + | let mut failed = false; |
|
| 127 | + | ||
| 128 | + | try super::allocSlice(&mut arena, 3, 1, count) catch { |
|
| 129 | + | set failed = true; |
|
| 130 | + | }; |
|
| 131 | + | ||
| 132 | + | try testing::expect(failed); |
|
| 133 | + | try testing::expect(super::used(&arena) == 0); |
|
| 134 | + | } |
|
| 135 | + | ||
| 107 | 136 | /// Test the Allocator interface backed by an arena. |
|
| 108 | 137 | @test fn testAllocator() throws (testing::TestError) { |
|
| 109 | 138 | static STORAGE: [u8; 256] = undefined; |
|
| 110 | 139 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 111 | 140 | let a = super::arenaAllocator(&mut arena); |
lib/std/lang/ast.rad
+2 -0
| 630 | 630 | /// Argument list. |
|
| 631 | 631 | args: *mut [*Node], |
|
| 632 | 632 | }, |
|
| 633 | 633 | /// Block expression or statement body. |
|
| 634 | 634 | Block(Block), |
|
| 635 | + | /// Scoped unsafe block (`unsafe { ... }`). |
|
| 636 | + | Unsafe(*Node), |
|
| 635 | 637 | /// Call expression, eg. `f(x)`. |
|
| 636 | 638 | Call(Call), |
|
| 637 | 639 | /// Field access expression (e.g. `foo.bar`). |
|
| 638 | 640 | FieldAccess(Access), |
|
| 639 | 641 | /// Scope access expression (e.g. `foo::bar`). |
lib/std/lang/ast/printer.rad
+2 -0
| 367 | 367 | } |
|
| 368 | 368 | case super::NodeValue::Block(blk) => { |
|
| 369 | 369 | let children = nodeListToExprs(a, &blk.statements[..]); |
|
| 370 | 370 | return sexpr::block(a, "block", &[], children); |
|
| 371 | 371 | } |
|
| 372 | + | case super::NodeValue::Unsafe(body) => |
|
| 373 | + | return sexpr::list(a, "unsafe", &[toExpr(a, body)]), |
|
| 372 | 374 | case super::NodeValue::Let(decl) => { |
|
| 373 | 375 | let mut head = "let"; |
|
| 374 | 376 | if decl.mutable { set head = "let-mut"; } |
|
| 375 | 377 | return sexpr::list(a, head, &[ |
|
| 376 | 378 | toExpr(a, decl.ident), |
lib/std/lang/gen/regalloc/assign.rad
+22 -12
| 158 | 158 | allocatable, |
|
| 159 | 159 | calleeSaved: config.calleeSaved, |
|
| 160 | 160 | assignments, |
|
| 161 | 161 | spillInfo, |
|
| 162 | 162 | }; |
|
| 163 | - | il::forEachReg(instr, processInstrRegCb, &mut ctx as *mut opaque); |
|
| 163 | + | il::forEachReg(instr, processInstrRegCb, &mut ctx); |
|
| 164 | 164 | ||
| 165 | 165 | // Allocate destination. |
|
| 166 | 166 | if let dst = il::instrDst(instr) { |
|
| 167 | 167 | if dst.n < maxReg and not spill::isSpilled(spillInfo, dst) { |
|
| 168 | 168 | set assignments[dst.n] = rallocReg(&mut current, &mut usedRegs, dst.n, allocatable, config.calleeSaved, spillInfo); |
| 278 | 278 | } |
|
| 279 | 279 | panic "rallocReg: no free register, spilling fault"; |
|
| 280 | 280 | } |
|
| 281 | 281 | ||
| 282 | 282 | /// Callback for [`il::forEachReg`]: free last uses and allocate missing uses. |
|
| 283 | - | fn processInstrRegCb(reg: il::Reg, ctxPtr: *mut opaque) { |
|
| 284 | - | let ctx = ctxPtr as *mut InstrCtx; |
|
| 285 | - | if not liveness::hasLaterUse(ctx.live, ctx.func, ctx.blockIdx, ctx.instrIdx, reg) { |
|
| 286 | - | if let phys = rmapRemove(ctx.current, reg.n) { |
|
| 287 | - | bitset::clear(ctx.usedRegs, *phys as u32); |
|
| 283 | + | fn processInstrRegCb(reg: il::Reg, ctx: &mut opaque) { |
|
| 284 | + | if not liveness::hasLaterUse( |
|
| 285 | + | (ctx as &mut InstrCtx).live, |
|
| 286 | + | (ctx as &mut InstrCtx).func, |
|
| 287 | + | (ctx as &mut InstrCtx).blockIdx, |
|
| 288 | + | (ctx as &mut InstrCtx).instrIdx, |
|
| 289 | + | reg |
|
| 290 | + | ) { |
|
| 291 | + | if let phys = rmapRemove((ctx as &mut InstrCtx).current, reg.n) { |
|
| 292 | + | bitset::clear((ctx as &mut InstrCtx).usedRegs, *phys as u32); |
|
| 288 | 293 | } |
|
| 289 | 294 | } |
|
| 290 | - | assert reg.n < ctx.assignments.len, "processInstrRegCb: register out of bounds"; |
|
| 291 | - | if spill::isSpilled(ctx.spillInfo, reg) { |
|
| 295 | + | assert reg.n < (ctx as &mut InstrCtx).assignments.len, |
|
| 296 | + | "processInstrRegCb: register out of bounds"; |
|
| 297 | + | if spill::isSpilled((ctx as &mut InstrCtx).spillInfo, reg) { |
|
| 292 | 298 | return; // Spilled values don't get physical registers. |
|
| 293 | 299 | } |
|
| 294 | - | if ctx.assignments[reg.n] == nil { |
|
| 295 | - | set ctx.assignments[reg.n] = rallocReg( |
|
| 296 | - | ctx.current, ctx.usedRegs, reg.n, ctx.allocatable, |
|
| 297 | - | ctx.calleeSaved, ctx.spillInfo |
|
| 300 | + | if (ctx as &mut InstrCtx).assignments[reg.n] == nil { |
|
| 301 | + | set (ctx as &mut InstrCtx).assignments[reg.n] = rallocReg( |
|
| 302 | + | (ctx as &mut InstrCtx).current, |
|
| 303 | + | (ctx as &mut InstrCtx).usedRegs, |
|
| 304 | + | reg.n, |
|
| 305 | + | (ctx as &mut InstrCtx).allocatable, |
|
| 306 | + | (ctx as &mut InstrCtx).calleeSaved, |
|
| 307 | + | (ctx as &mut InstrCtx).spillInfo |
|
| 298 | 308 | ); |
|
| 299 | 309 | } |
|
| 300 | 310 | } |
lib/std/lang/gen/regalloc/liveness.rad
+16 -14
| 82 | 82 | let block = &func.blocks[b]; |
|
| 83 | 83 | for p in block.params { |
|
| 84 | 84 | set maxReg = maxRegNum(p.value.n, maxReg); |
|
| 85 | 85 | } |
|
| 86 | 86 | for i in 0..block.instrs.len { |
|
| 87 | - | il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as *mut opaque); |
|
| 87 | + | il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg); |
|
| 88 | 88 | if let dst = il::instrDst(block.instrs[i]) { |
|
| 89 | 89 | set maxReg = maxRegNum(dst.n, maxReg); |
|
| 90 | 90 | } |
|
| 91 | 91 | } |
|
| 92 | 92 | } |
| 163 | 163 | bitset::put(defs, p.value.n); |
|
| 164 | 164 | } |
|
| 165 | 165 | for i in 0..block.instrs.len { |
|
| 166 | 166 | let instr = block.instrs[i]; |
|
| 167 | 167 | let mut ctx = DefsUses { defs, uses }; |
|
| 168 | - | il::forEachReg(instr, addUseCallback, &mut ctx as *mut opaque); |
|
| 168 | + | il::forEachReg(instr, addUseCallback, &mut ctx); |
|
| 169 | 169 | ||
| 170 | 170 | if let dst = il::instrDst(instr) { |
|
| 171 | 171 | bitset::put(defs, dst.n); |
|
| 172 | 172 | } |
|
| 173 | 173 | } |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Callback for [`il::forEachReg`]: adds register to uses if not already defined. |
|
| 177 | - | fn addUseCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 178 | - | let c = ctx as *mut DefsUses; |
|
| 179 | - | if not bitset::contains(c.defs, reg.n) { |
|
| 180 | - | bitset::put(c.uses, reg.n); |
|
| 177 | + | fn addUseCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 178 | + | if not bitset::contains((ctx as &mut DefsUses).defs, reg.n) { |
|
| 179 | + | bitset::put((ctx as &mut DefsUses).uses, reg.n); |
|
| 181 | 180 | } |
|
| 182 | 181 | } |
|
| 183 | 182 | ||
| 184 | 183 | /// Callback for [`il::forEachReg`]: updates max register number. |
|
| 185 | - | fn maxRegCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 186 | - | let max = ctx as *mut u32; |
|
| 187 | - | set *max = maxRegNum(reg.n, *max); |
|
| 184 | + | fn maxRegCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 185 | + | set *(ctx as &mut u32) = maxRegNum(reg.n, *(ctx as &mut u32)); |
|
| 188 | 186 | } |
|
| 189 | 187 | ||
| 190 | 188 | /// Return the larger of n+1 and current. |
|
| 191 | 189 | fn maxRegNum(n: u32, current: u32) -> u32 { |
|
| 192 | 190 | if n + 1 > current { |
| 240 | 238 | } |
|
| 241 | 239 | ||
| 242 | 240 | /// Check if an instruction uses a specific register. |
|
| 243 | 241 | fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool { |
|
| 244 | 242 | let mut ctx = FindCtx { target: reg.n, found: false }; |
|
| 245 | - | il::forEachReg(instr, findRegCallback, &mut ctx as *mut opaque); |
|
| 243 | + | il::forEachReg(instr, findRegCallback, &mut ctx); |
|
| 246 | 244 | return ctx.found; |
|
| 247 | 245 | } |
|
| 248 | 246 | ||
| 249 | 247 | /// Callback for [`il::forEachReg`]: sets found if register matches target. |
|
| 250 | - | fn findRegCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 251 | - | let c = ctx as *mut FindCtx; |
|
| 252 | - | if reg.n == c.target { |
|
| 253 | - | set c.found = true; |
|
| 248 | + | fn findRegCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 249 | + | if reg.n == (ctx as &mut FindCtx).target { |
|
| 250 | + | markFound(ctx as &mut FindCtx); |
|
| 254 | 251 | } |
|
| 255 | 252 | } |
|
| 253 | + | ||
| 254 | + | /// Mark a register search context as found. |
|
| 255 | + | fn markFound(ctx: &mut FindCtx) { |
|
| 256 | + | set ctx.found = true; |
|
| 257 | + | } |
lib/std/lang/gen/regalloc/spill.rad
+14 -8
| 140 | 140 | // Remove definition from live set. |
|
| 141 | 141 | if let dst = il::instrDst(instr) { |
|
| 142 | 142 | bitset::clear(&mut scratch, dst.n); |
|
| 143 | 143 | } |
|
| 144 | 144 | // Add uses to live set. |
|
| 145 | - | il::forEachReg(instr, addRegToSetCallback, &mut scratch as *mut opaque); |
|
| 145 | + | il::forEachReg(instr, addRegToSetCallback, &mut scratch); |
|
| 146 | 146 | } |
|
| 147 | 147 | // Also limit pressure at block entry. |
|
| 148 | 148 | limitPressure(&mut scratch, &mut spilled, costs, numRegs); |
|
| 149 | 149 | } |
|
| 150 | 150 |
| 199 | 199 | set costs[dst.n].defs = costs[dst.n].defs + weight; |
|
| 200 | 200 | } |
|
| 201 | 201 | } |
|
| 202 | 202 | // Count uses. |
|
| 203 | 203 | let mut ctx = CountCtx { costs, weight }; |
|
| 204 | - | il::forEachReg(instr, countRegUseCallback, &mut ctx as *mut opaque); |
|
| 204 | + | il::forEachReg(instr, countRegUseCallback, &mut ctx); |
|
| 205 | 205 | } |
|
| 206 | 206 | } |
|
| 207 | 207 | } |
|
| 208 | 208 | ||
| 209 | 209 | /// Sort candidates by cost (ascending) using insertion sort, then spill |
| 307 | 307 | } |
|
| 308 | 308 | } |
|
| 309 | 309 | } |
|
| 310 | 310 | ||
| 311 | 311 | /// Callback for [`il::forEachReg`]: increments use count for register. |
|
| 312 | - | fn countRegUseCallback(reg: il::Reg, ctxPtr: *mut opaque) { |
|
| 313 | - | let ctx = ctxPtr as *mut CountCtx; |
|
| 314 | - | assert reg.n < ctx.costs.len, "countRegUseCallback: register out of bounds"; |
|
| 315 | - | set ctx.costs[reg.n].uses = ctx.costs[reg.n].uses + ctx.weight; |
|
| 312 | + | fn countRegUseCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 313 | + | assert reg.n < (ctx as &mut CountCtx).costs.len, |
|
| 314 | + | "countRegUseCallback: register out of bounds"; |
|
| 315 | + | set (ctx as &mut CountCtx).costs[reg.n].uses = |
|
| 316 | + | (ctx as &mut CountCtx).costs[reg.n].uses + (ctx as &mut CountCtx).weight; |
|
| 316 | 317 | } |
|
| 317 | 318 | ||
| 318 | 319 | /// Callback for [`il::forEachReg`]: adds register to live set. |
|
| 319 | - | fn addRegToSetCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 320 | - | bitset::put(ctx as *mut bitset::Bitset, reg.n); |
|
| 320 | + | fn addRegToSetCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 321 | + | if reg.n >= (ctx as &mut bitset::Bitset).len { |
|
| 322 | + | return; |
|
| 323 | + | } |
|
| 324 | + | let word = reg.n / 32; |
|
| 325 | + | let bit = reg.n % 32; |
|
| 326 | + | set (ctx as &mut bitset::Bitset).bits[word] |= (1 << bit); |
|
| 321 | 327 | } |
|
| 322 | 328 | ||
| 323 | 329 | /// Check if a register is spilled. |
|
| 324 | 330 | export fn isSpilled(info: *SpillInfo, reg: il::Reg) -> bool { |
|
| 325 | 331 | if reg.n >= info.maxReg { |
lib/std/lang/il.rad
+6 -3
| 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 | ||
| 63 | + | /// Unit tests for intermediate language traversal. |
|
| 64 | + | @test mod tests; |
|
| 65 | + | ||
| 63 | 66 | use std::mem; |
|
| 64 | 67 | use std::lang::alloc; |
|
| 65 | 68 | ||
| 66 | 69 | /// Source location for debug info. |
|
| 67 | 70 | /// |
| 77 | 80 | /////////////////////// |
|
| 78 | 81 | // Name Formatting // |
|
| 79 | 82 | /////////////////////// |
|
| 80 | 83 | ||
| 81 | 84 | /// Separator for qualified symbol names. |
|
| 82 | - | export constant PATH_SEPARATOR: *[u8] = "::"; |
|
| 85 | + | export unsafe constant PATH_SEPARATOR: *[u8] = "::"; |
|
| 83 | 86 | ||
| 84 | 87 | /// Format a qualified symbol name: `pkg::mod::path::name`. |
|
| 85 | 88 | export fn formatQualifiedName(arena: *mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] { |
|
| 86 | 89 | let mut totalLen: u32 = name.len; |
|
| 87 | 90 | for segment in path { |
| 425 | 428 | } |
|
| 426 | 429 | } |
|
| 427 | 430 | ||
| 428 | 431 | /// Call a function for each register used by an instruction. |
|
| 429 | 432 | /// This is called by the register allocator to analyze register usage. |
|
| 430 | - | export fn forEachReg(instr: Instr, f: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 433 | + | export fn forEachReg(instr: Instr, f: fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 431 | 434 | match instr { |
|
| 432 | 435 | case Instr::Reserve { size, .. } => |
|
| 433 | 436 | withReg(size, f, ctx), |
|
| 434 | 437 | case Instr::Load { src, .. } => f(src, ctx), |
|
| 435 | 438 | case Instr::Sload { src, .. } => f(src, ctx), |
| 503 | 506 | Instr::MemoryFence => {}, |
|
| 504 | 507 | } |
|
| 505 | 508 | } |
|
| 506 | 509 | ||
| 507 | 510 | /// Call callback if value is a register. |
|
| 508 | - | fn withReg(val: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 511 | + | fn withReg(val: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 509 | 512 | if let case Val::Reg(r) = val { |
|
| 510 | 513 | callback(r, ctx); |
|
| 511 | 514 | } |
|
| 512 | 515 | } |
lib/std/lang/il/tests.rad
added
+21 -0
| 1 | + | //! Intermediate language tests. |
|
| 2 | + | ||
| 3 | + | use std::testing; |
|
| 4 | + | ||
| 5 | + | /// Add one register number through a call-scoped context loan. |
|
| 6 | + | fn observeReg(reg: super::Reg, ctx: &mut opaque) { |
|
| 7 | + | unsafe { set *(ctx as &mut u32) += reg.n; } |
|
| 8 | + | } |
|
| 9 | + | ||
| 10 | + | /// Register traversal may reuse one mutable callback context loan. |
|
| 11 | + | @test fn testForEachRegCallbackLoan() throws (testing::TestError) { |
|
| 12 | + | let mut context: u32 = 0; |
|
| 13 | + | let instr = super::Instr::Store { |
|
| 14 | + | typ: super::Type::W32, |
|
| 15 | + | src: super::Val::Reg(super::Reg { n: 1 }), |
|
| 16 | + | dst: super::Reg { n: 2 }, |
|
| 17 | + | offset: 0, |
|
| 18 | + | }; |
|
| 19 | + | super::forEachReg(instr, observeReg, &mut context); |
|
| 20 | + | assert context == 3; |
|
| 21 | + | } |
lib/std/lang/lower.rad
+11 -5
| 264 | 264 | Default, |
|
| 265 | 265 | } |
|
| 266 | 266 | ||
| 267 | 267 | /// Function sink used by lowerers that consume functions as they are produced. |
|
| 268 | 268 | export record FnSink { |
|
| 269 | - | /// Opaque context passed to the sink callback. |
|
| 270 | - | ctx: *mut opaque, |
|
| 269 | + | /// Raw storage address reborrowed only for the duration of each callback. |
|
| 270 | + | ctx: *unsafe mut u8, |
|
| 271 | 271 | /// Callback invoked for each lowered function. |
|
| 272 | - | emitFn: fn(*mut opaque, *il::Fn, FnRole), |
|
| 272 | + | emitFn: fn(&mut opaque, *il::Fn, FnRole), |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// Destination for functions produced by the lowerer. |
|
| 276 | 276 | export union FnOutput { |
|
| 277 | 277 | /// Store lowered functions in the provided slice. |
| 363 | 363 | let mut fns = accumulated; |
|
| 364 | 364 | fns.append(func, self.allocator); |
|
| 365 | 365 | set self.output = FnOutput::Accumulate(fns); |
|
| 366 | 366 | } |
|
| 367 | 367 | case FnOutput::Stream(sink) => { |
|
| 368 | - | sink.emitFn(sink.ctx, func, role); |
|
| 368 | + | sink.emitFn(&mut *sink.ctx as &mut opaque, func, role); |
|
| 369 | 369 | } |
|
| 370 | 370 | } |
|
| 371 | 371 | } |
|
| 372 | 372 | ||
| 373 | 373 | /// Get the function role for a top-level function declaration. |
| 3937 | 3937 | } |
|
| 3938 | 3938 | match node.value { |
|
| 3939 | 3939 | case ast::NodeValue::Block(_) => { |
|
| 3940 | 3940 | try lowerBlock(self, node); |
|
| 3941 | 3941 | } |
|
| 3942 | + | case ast::NodeValue::Unsafe(body) => { |
|
| 3943 | + | try lowerNode(self, body); |
|
| 3944 | + | } |
|
| 3942 | 3945 | case ast::NodeValue::Return { value } => { |
|
| 3943 | 3946 | try lowerReturnStmt(self, node, value); |
|
| 3944 | 3947 | } |
|
| 3945 | 3948 | case ast::NodeValue::Throw { expr } => { |
|
| 3946 | 3949 | try lowerThrowStmt(self, expr); |
| 6903 | 6906 | } else { |
|
| 6904 | 6907 | throw LowerError::UnknownIntrinsic; |
|
| 6905 | 6908 | } |
|
| 6906 | 6909 | } |
|
| 6907 | 6910 | ||
| 6908 | - | /// Lower an ecall intrinsic: `ecall(num, a0, a1, a2, a3) -> i32`. |
|
| 6911 | + | /// Lower an ecall intrinsic: `ecall(num, a0, a1, a2, a3) -> i64`. |
|
| 6909 | 6912 | fn lowerEcall(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6910 | 6913 | if call.args.len <> 5 { |
|
| 6911 | 6914 | throw LowerError::InvalidArgCount; |
|
| 6912 | 6915 | } |
|
| 6913 | 6916 | let num = try lowerExpr(self, call.args[0]); |
| 7266 | 7269 | } |
|
| 7267 | 7270 | case ast::NodeValue::Block(_) => { |
|
| 7268 | 7271 | try lowerBlock(self, node); |
|
| 7269 | 7272 | set val = il::Val::Undef; |
|
| 7270 | 7273 | } |
|
| 7274 | + | case ast::NodeValue::Unsafe(body) => { |
|
| 7275 | + | set val = try lowerExpr(self, body); |
|
| 7276 | + | } |
|
| 7271 | 7277 | case ast::NodeValue::ExprStmt(expr) => { |
|
| 7272 | 7278 | let _ = expr; |
|
| 7273 | 7279 | set val = il::Val::Undef; |
|
| 7274 | 7280 | } |
|
| 7275 | 7281 | // Lower these as statements. |
lib/std/lang/module.rad
+1 -1
| 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. |
|
| 23 | 23 | constant PATH_SEP: u8 = '/'; |
|
| 24 | 24 | /// Source file extension handled by the loader. |
|
| 25 | - | constant SOURCE_EXT: *[u8] = ".rad"; |
|
| 25 | + | unsafe constant SOURCE_EXT: *[u8] = ".rad"; |
|
| 26 | 26 | ||
| 27 | 27 | /// Lifecycle state for modules in the dependency graph. |
|
| 28 | 28 | export union ModuleState { |
|
| 29 | 29 | /// Slot unused or yet to be initialized. |
|
| 30 | 30 | Vacant, |
lib/std/lang/module/tests.rad
+1 -1
| 6 | 6 | use std::lang::strings; |
|
| 7 | 7 | ||
| 8 | 8 | /// Test arena backing storage. |
|
| 9 | 9 | static TEST_ARENA: [u8; 4096] = undefined; |
|
| 10 | 10 | /// Interned string pool. |
|
| 11 | - | static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 11 | + | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 12 | 12 | ||
| 13 | 13 | fn expectSliceEq(actual: *[u8], expected: *[u8]) |
|
| 14 | 14 | throws (testing::TestError) |
|
| 15 | 15 | { |
|
| 16 | 16 | if not mem::eq(actual, expected) { |
lib/std/lang/parser.rad
+31 -1
| 568 | 568 | ast::NodeValue::WhileLet(_), |
|
| 569 | 569 | ast::NodeValue::For(_), |
|
| 570 | 570 | ast::NodeValue::Loop { .. }, |
|
| 571 | 571 | ast::NodeValue::Match(_), |
|
| 572 | 572 | ast::NodeValue::Block(_), |
|
| 573 | + | ast::NodeValue::Unsafe(_), |
|
| 573 | 574 | ast::NodeValue::FnDecl(_), |
|
| 574 | 575 | ast::NodeValue::RecordDecl(_), |
|
| 575 | 576 | ast::NodeValue::UnionDecl(_), |
|
| 576 | 577 | ast::NodeValue::TraitDecl { .. }, |
|
| 577 | 578 | ast::NodeValue::InstanceDecl { .. }, |
|
| 578 | 579 | ast::NodeValue::MethodDecl { .. } => return false, |
|
| 579 | 580 | else => return true, |
|
| 580 | 581 | } |
|
| 581 | 582 | } |
|
| 582 | 583 | ||
| 584 | + | /// Speculatively parse a scoped unsafe block. |
|
| 585 | + | /// |
|
| 586 | + | /// Restores the parser when `unsafe` starts a declaration instead. |
|
| 587 | + | fn tryParseUnsafeBlock(p: *mut Parser) -> ?*ast::Node |
|
| 588 | + | throws (ParseError) |
|
| 589 | + | { |
|
| 590 | + | let saved = saveState(p); |
|
| 591 | + | if not consume(p, scanner::TokenKind::Unsafe) { |
|
| 592 | + | return nil; |
|
| 593 | + | } |
|
| 594 | + | if not check(p, scanner::TokenKind::LBrace) { |
|
| 595 | + | restoreState(p, &saved); |
|
| 596 | + | return nil; |
|
| 597 | + | } |
|
| 598 | + | let body = try parseBlock(p); |
|
| 599 | + | return node(p, ast::NodeValue::Unsafe(body)); |
|
| 600 | + | } |
|
| 601 | + | ||
| 583 | 602 | /// Parse a primary leaf expression without postfix operators. |
|
| 584 | 603 | fn parseLeaf(p: *mut Parser) -> *ast::Node |
|
| 585 | 604 | throws (ParseError) |
|
| 586 | 605 | { |
|
| 587 | 606 | match p.current.kind { |
| 591 | 610 | } |
|
| 592 | 611 | case scanner::TokenKind::False => { |
|
| 593 | 612 | advance(p); |
|
| 594 | 613 | return nodeBool(p, false); |
|
| 595 | 614 | } |
|
| 615 | + | case scanner::TokenKind::Unsafe => { |
|
| 616 | + | let unsafeBlock = try tryParseUnsafeBlock(p) |
|
| 617 | + | else throw failParsing(p, "expected `{` after `unsafe`"); |
|
| 618 | + | return unsafeBlock; |
|
| 619 | + | } |
|
| 596 | 620 | case scanner::TokenKind::Ident => { |
|
| 597 | 621 | advance(p); |
|
| 598 | 622 | return node(p, ast::NodeValue::Ident(p.previous.source)); |
|
| 599 | 623 | } |
|
| 600 | 624 | case scanner::TokenKind::Super => { |
| 830 | 854 | /// |
|
| 831 | 855 | /// Dispatches to the appropriate statement parser based on the current token. |
|
| 832 | 856 | export fn parseStmt(p: *mut Parser) -> *ast::Node |
|
| 833 | 857 | throws (ParseError) |
|
| 834 | 858 | { |
|
| 859 | + | if let unsafeBlock = try tryParseUnsafeBlock(p) { |
|
| 860 | + | return unsafeBlock; |
|
| 861 | + | } |
|
| 862 | + | ||
| 835 | 863 | // TODO: Why is `parseStmt` checking for attributes? |
|
| 836 | 864 | // We should have a `parseDecl` which is top-level, and `parseStmt` which |
|
| 837 | 865 | // is inside functions. |
|
| 838 | 866 | let attrs = parseAttributes(p); |
|
| 839 | 867 | if let list = attrs { |
|
| 840 | 868 | if ast::attributesContains(&list, ast::Attribute::Unsafe) |
|
| 841 | 869 | and p.current.kind <> scanner::TokenKind::Fn |
|
| 842 | 870 | and p.current.kind <> scanner::TokenKind::Mod |
|
| 871 | + | and p.current.kind <> scanner::TokenKind::Static |
|
| 872 | + | and p.current.kind <> scanner::TokenKind::Constant |
|
| 843 | 873 | { |
|
| 844 | - | throw failParsing(p, "`unsafe` is only allowed on functions and modules"); |
|
| 874 | + | throw failParsing(p, "`unsafe` is only allowed on functions, modules, statics, and constants"); |
|
| 845 | 875 | } |
|
| 846 | 876 | let allowed: bool = |
|
| 847 | 877 | p.current.kind == scanner::TokenKind::Fn or |
|
| 848 | 878 | p.current.kind == scanner::TokenKind::Union or |
|
| 849 | 879 | p.current.kind == scanner::TokenKind::Record or |
lib/std/lang/parser/tests.rad
+63 -4
| 14 | 14 | /// Unified arena size. |
|
| 15 | 15 | constant ARENA_SIZE: u32 = 2097152; |
|
| 16 | 16 | /// Unified arena storage for all AST allocations. |
|
| 17 | 17 | static ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 18 | 18 | /// String pool. |
|
| 19 | - | static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 19 | + | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 20 | 20 | ||
| 21 | 21 | /// Assert that a node is an identifier with the given name. |
|
| 22 | 22 | fn expectIdent(node: *ast::Node, name: *[u8]) |
|
| 23 | 23 | throws (testing::TestError) |
|
| 24 | 24 | { |
| 94 | 94 | ||
| 95 | 95 | return root; |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | 98 | /// Parse a single expression from a string. |
|
| 99 | - | export fn parseExprStr(input: *[u8]) -> *ast::Node |
|
| 99 | + | fn parseExprStr(input: *[u8]) -> *ast::Node |
|
| 100 | 100 | throws (super::ParseError) |
|
| 101 | 101 | { |
|
| 102 | 102 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 103 | 103 | let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL); |
|
| 104 | 104 | super::advance(&mut parser); |
| 270 | 270 | let r2 = try! parseExprStr("false"); |
|
| 271 | 271 | let case ast::NodeValue::Bool(v2) = r2.value if not v2 |
|
| 272 | 272 | else throw testing::TestError::Failed; |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | + | /// Verify why the static-arena expression parser stays module-private. |
|
| 276 | + | @test fn testParseExprStrResetsSharedArena() throws (testing::TestError) { |
|
| 277 | + | let first = try! parseExprStr("first"); |
|
| 278 | + | let second = try! parseExprStr("second"); |
|
| 279 | + | ||
| 280 | + | try testing::expect(first == second); |
|
| 281 | + | try expectIdent(first, "second"); |
|
| 282 | + | } |
|
| 283 | + | ||
| 275 | 284 | /// Test parsing number literals. |
|
| 276 | 285 | @test fn testParseNumber() throws (testing::TestError) { |
|
| 277 | 286 | let r1 = try! parseExprStr("4519"); |
|
| 278 | 287 | try expectNumber(r1, "4519"); |
|
| 279 | 288 | } |
| 542 | 551 | let case ast::NodeValue::ExprStmt(thirdExpr) = thirdStmt.value |
|
| 543 | 552 | else throw testing::TestError::Failed; |
|
| 544 | 553 | try expectIdent(thirdExpr, "third"); |
|
| 545 | 554 | } |
|
| 546 | 555 | ||
| 556 | + | /// Test parsing an unsafe block and its child block AST. |
|
| 557 | + | @test fn testParseUnsafeBlock() throws (testing::TestError) { |
|
| 558 | + | let root = try! parseStmtStr("unsafe { operation }"); |
|
| 559 | + | let case ast::NodeValue::Unsafe(body) = root.value |
|
| 560 | + | else throw testing::TestError::Failed; |
|
| 561 | + | ||
| 562 | + | try expectBlockExprStmt(body, ast::NodeValue::Ident("operation")); |
|
| 563 | + | } |
|
| 564 | + | ||
| 565 | + | /// Test parsing an unsafe block as one statement in an enclosing block. |
|
| 566 | + | @test fn testParseUnsafeBlockStatement() throws (testing::TestError) { |
|
| 567 | + | let root = try! parseStmtStr("{ before; unsafe { operation; } after; }"); |
|
| 568 | + | let case ast::NodeValue::Block(block) = root.value |
|
| 569 | + | else throw testing::TestError::Failed; |
|
| 570 | + | try testing::expect(block.statements.len == 3); |
|
| 571 | + | ||
| 572 | + | let firstStmt = block.statements[0]; |
|
| 573 | + | let case ast::NodeValue::ExprStmt(firstExpr) = firstStmt.value |
|
| 574 | + | else throw testing::TestError::Failed; |
|
| 575 | + | try expectIdent(firstExpr, "before"); |
|
| 576 | + | ||
| 577 | + | let unsafeStmt = block.statements[1]; |
|
| 578 | + | let case ast::NodeValue::Unsafe(body) = unsafeStmt.value |
|
| 579 | + | else throw testing::TestError::Failed; |
|
| 580 | + | try expectBlockExprStmt(body, ast::NodeValue::Ident("operation")); |
|
| 581 | + | ||
| 582 | + | let lastStmt = block.statements[2]; |
|
| 583 | + | let case ast::NodeValue::ExprStmt(lastExpr) = lastStmt.value |
|
| 584 | + | else throw testing::TestError::Failed; |
|
| 585 | + | try expectIdent(lastExpr, "after"); |
|
| 586 | + | } |
|
| 587 | + | ||
| 547 | 588 | /// Test parsing a block that keeps a trailing `;` delimiter. |
|
| 548 | 589 | @test fn testParseBlockTrailingSemicolon() throws (testing::TestError) { |
|
| 549 | 590 | let root = try! parseStmtStr("if cond { only; }") catch { |
|
| 550 | 591 | throw testing::TestError::Failed; |
|
| 551 | 592 | }; |
| 1141 | 1182 | ||
| 1142 | 1183 | try testing::expect(attrs.list.len == 1); |
|
| 1143 | 1184 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Unsafe)); |
|
| 1144 | 1185 | } |
|
| 1145 | 1186 | ||
| 1187 | + | /// Test parsing an unsafe static initializer. |
|
| 1188 | + | @test fn testParseUnsafeStaticDecl() throws (testing::TestError) { |
|
| 1189 | + | let node = try! parseStmtStr("unsafe static BUFFER: [u8; 4] = undefined;"); |
|
| 1190 | + | let case ast::NodeValue::StaticDecl(decl) = node.value |
|
| 1191 | + | else throw testing::TestError::Failed; |
|
| 1192 | + | let attrs = decl.attrs |
|
| 1193 | + | else throw testing::TestError::Failed; |
|
| 1194 | + | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Unsafe)); |
|
| 1195 | + | } |
|
| 1196 | + | ||
| 1197 | + | /// Test parsing an unsafe constant declaration. |
|
| 1198 | + | @test fn testParseUnsafeConstDecl() throws (testing::TestError) { |
|
| 1199 | + | let node = try! parseStmtStr("unsafe constant TEXT: *[u8] = \"text\";"); |
|
| 1200 | + | let case ast::NodeValue::ConstDecl(decl) = node.value |
|
| 1201 | + | else throw testing::TestError::Failed; |
|
| 1202 | + | let attrs = decl.attrs |
|
| 1203 | + | else throw testing::TestError::Failed; |
|
| 1204 | + | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Unsafe)); |
|
| 1205 | + | } |
|
| 1206 | + | ||
| 1146 | 1207 | /// Test rejecting `unsafe` on declarations where it has no semantics. |
|
| 1147 | 1208 | @test fn testParseUnsafeUnsupportedDecl() throws (testing::TestError) { |
|
| 1148 | 1209 | let recordDecl: ?*ast::Node = try? parseStmtStr("unsafe record R {}"); |
|
| 1149 | 1210 | try testing::expect(recordDecl == nil); |
|
| 1150 | - | let constDecl: ?*ast::Node = try? parseStmtStr("unsafe constant X = 1;"); |
|
| 1151 | - | try testing::expect(constDecl == nil); |
|
| 1152 | 1211 | } |
|
| 1153 | 1212 | ||
| 1154 | 1213 | /// Test `unsafe` on the other declaration forms that support it. |
|
| 1155 | 1214 | @test fn testParseUnsafeMethodAndModule() throws (testing::TestError) { |
|
| 1156 | 1215 | let moduleNode = try! parseStmtStr("unsafe mod io;"); |
lib/std/lang/resolver.rad
+469 -105
| 23 | 23 | ||
| 24 | 24 | /// Maximum number of diagnostics recorded. |
|
| 25 | 25 | export constant MAX_ERRORS: u32 = 64; |
|
| 26 | 26 | ||
| 27 | 27 | /// Synthetic function name used when wrapping a bare expression for analysis. |
|
| 28 | - | export constant ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__"; |
|
| 28 | + | export unsafe constant ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__"; |
|
| 29 | 29 | /// Synthetic function name used when wrapping a block for analysis. |
|
| 30 | - | export constant ANALYZE_BLOCK_FN_NAME: *[u8] = "__block__"; |
|
| 30 | + | export unsafe constant ANALYZE_BLOCK_FN_NAME: *[u8] = "__block__"; |
|
| 31 | 31 | ||
| 32 | 32 | /// Maximum number of symbols stored within a module scope. |
|
| 33 | 33 | export constant MAX_MODULE_SYMBOLS: u32 = 512; |
|
| 34 | 34 | /// Maximum number of symbols stored within a local scope. |
|
| 35 | 35 | export constant MAX_LOCAL_SYMBOLS: u32 = 32; |
| 107 | 107 | /// Symbol for the method. |
|
| 108 | 108 | symbol: *mut Symbol, |
|
| 109 | 109 | } |
|
| 110 | 110 | ||
| 111 | 111 | /// Identifier for the synthetic `len` field. |
|
| 112 | - | export constant LEN_FIELD: *[u8] = "len"; |
|
| 112 | + | export unsafe constant LEN_FIELD: *[u8] = "len"; |
|
| 113 | 113 | /// Identifier for the synthetic `ptr` field. |
|
| 114 | - | export constant PTR_FIELD: *[u8] = "ptr"; |
|
| 114 | + | export unsafe constant PTR_FIELD: *[u8] = "ptr"; |
|
| 115 | 115 | /// Identifier for the synthetic `cap` field. |
|
| 116 | - | export constant CAP_FIELD: *[u8] = "cap"; |
|
| 116 | + | export unsafe constant CAP_FIELD: *[u8] = "cap"; |
|
| 117 | 117 | ||
| 118 | 118 | /// Maximum `u16` value. |
|
| 119 | 119 | constant U16_MAX: u16 = 0xFFFF; |
|
| 120 | 120 | /// Maximum `u8` value. |
|
| 121 | 121 | constant U8_MAX: u16 = 0xFF; |
| 519 | 519 | FnMissingBody, |
|
| 520 | 520 | /// Function body is not expected. |
|
| 521 | 521 | FnUnexpectedBody, |
|
| 522 | 522 | /// Intrinsic function must not have a body. |
|
| 523 | 523 | IntrinsicUnexpectedBody, |
|
| 524 | + | /// Ecall intrinsic declaration does not match the canonical unsafe ABI. |
|
| 525 | + | InvalidEcallIntrinsicSignature, |
|
| 524 | 526 | /// Encountered loop control outside of a loop construct. |
|
| 525 | 527 | InvalidLoopControl, |
|
| 526 | 528 | /// `try` used when the enclosing function does not declare throws. |
|
| 527 | 529 | TryRequiresThrows, |
|
| 528 | 530 | /// `try` used to propagate an error not declared by the enclosing function. |
| 695 | 697 | constValue: ?ConstValue, |
|
| 696 | 698 | /// Lexical scope owned by this node. |
|
| 697 | 699 | scope: ?*mut Scope, |
|
| 698 | 700 | /// Node-specific extra data. |
|
| 699 | 701 | extra: NodeExtra, |
|
| 702 | + | /// Whether the declaration body belongs to a trusted unsafe module. |
|
| 703 | + | trustedBody: bool, |
|
| 700 | 704 | } |
|
| 701 | 705 | ||
| 702 | 706 | /// Table storing all resolver metadata indexed by node ID. |
|
| 703 | 707 | record NodeDataTable { |
|
| 704 | 708 | entries: *mut [NodeData], |
| 796 | 800 | len: u32, |
|
| 797 | 801 | /// Whether this control-flow path has terminated. |
|
| 798 | 802 | terminated: bool, |
|
| 799 | 803 | } |
|
| 800 | 804 | ||
| 805 | + | /// Loans kept alive while later call arguments are evaluated. |
|
| 806 | + | record LinearLoans { |
|
| 807 | + | /// Enclosing call's active loans. |
|
| 808 | + | parent: ?*LinearLoans, |
|
| 809 | + | /// Root symbol for each active loan. |
|
| 810 | + | roots: [?*mut Symbol; MAX_FN_PARAMS + 1], |
|
| 811 | + | /// Whether each loan excludes every other access. |
|
| 812 | + | exclusive: [bool; MAX_FN_PARAMS + 1], |
|
| 813 | + | /// Number of active entries. |
|
| 814 | + | len: u32, |
|
| 815 | + | } |
|
| 816 | + | ||
| 801 | 817 | /// Function-local exact-use checker state. |
|
| 802 | 818 | record LinearChecker { |
|
| 803 | 819 | /// Resolver that owns the symbols and diagnostics. |
|
| 804 | 820 | resolver: *mut Resolver, |
|
| 821 | + | /// Active loans from enclosing and earlier call arguments. |
|
| 822 | + | loans: ?*LinearLoans, |
|
| 805 | 823 | /// Binding count at entry to each active loop. |
|
| 806 | 824 | loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH], |
|
| 807 | 825 | /// Available bindings at entry to each active loop. |
|
| 808 | 826 | loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH], |
|
| 809 | 827 | /// Available bindings shared by the exits from each active loop. |
| 981 | 999 | coercion: Coercion::Identity, |
|
| 982 | 1000 | sym: nil, |
|
| 983 | 1001 | constValue: nil, |
|
| 984 | 1002 | scope: nil, |
|
| 985 | 1003 | extra: NodeExtra::None, |
|
| 1004 | + | trustedBody: false, |
|
| 986 | 1005 | }; |
|
| 987 | 1006 | } |
|
| 988 | 1007 | ||
| 989 | 1008 | let mut moduleScopes: [?*mut Scope; module::MAX_MODULES] = undefined; |
|
| 990 | 1009 | // TODO: Simplify. |
| 1756 | 1775 | /// Return whether pointer classes are compatible. |
|
| 1757 | 1776 | fn pointerClassesAssignable( |
|
| 1758 | 1777 | to: types::PointerClass, |
|
| 1759 | 1778 | from: types::PointerClass, |
|
| 1760 | 1779 | ) -> bool { |
|
| 1761 | - | return to == from or ( |
|
| 1762 | - | to == types::PointerClass::Owned |
|
| 1763 | - | and from == types::PointerClass::Ref |
|
| 1764 | - | ); |
|
| 1780 | + | return to == from; |
|
| 1765 | 1781 | } |
|
| 1766 | 1782 | ||
| 1767 | 1783 | /// Check if the `from` type is assignable to the `to` type, and return a |
|
| 1768 | 1784 | /// coercion plan if so. |
|
| 1769 | 1785 | fn isAssignable(self: *mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion { |
| 2679 | 2695 | if self.unsafeDepth == 0 { |
|
| 2680 | 2696 | throw emitError(self, node, ErrorKind::UnsafeOperation); |
|
| 2681 | 2697 | } |
|
| 2682 | 2698 | } |
|
| 2683 | 2699 | ||
| 2700 | + | /// Require unsafe context when reading an unsafe binding that is not a function declaration. |
|
| 2701 | + | fn checkUnsafeBindingAccess( |
|
| 2702 | + | self: *mut Resolver, |
|
| 2703 | + | node: *ast::Node, |
|
| 2704 | + | sym: *mut Symbol, |
|
| 2705 | + | ) throws (ResolveError) { |
|
| 2706 | + | if not ast::hasAttribute(sym.attrs, ast::Attribute::Unsafe) { |
|
| 2707 | + | return; |
|
| 2708 | + | } |
|
| 2709 | + | if let case ast::NodeValue::FnDecl(_) = sym.node.value { |
|
| 2710 | + | return; |
|
| 2711 | + | } |
|
| 2712 | + | match sym.data { |
|
| 2713 | + | case SymbolData::Value { .. }, |
|
| 2714 | + | SymbolData::Constant { .. } => try requireUnsafe(self, node), |
|
| 2715 | + | else => {} |
|
| 2716 | + | } |
|
| 2717 | + | } |
|
| 2718 | + | ||
| 2684 | 2719 | /// Reject calls from safe code through unsafe function types. |
|
| 2685 | 2720 | fn checkUnsafeCall(self: *mut Resolver, node: *ast::Node, info: *FnType) |
|
| 2686 | 2721 | throws (ResolveError) |
|
| 2687 | 2722 | { |
|
| 2688 | 2723 | if info.isUnsafe and self.unsafeDepth == 0 { |
| 2809 | 2844 | } |
|
| 2810 | 2845 | match node.value { |
|
| 2811 | 2846 | case ast::NodeValue::Ident(name) => { |
|
| 2812 | 2847 | let sym = findAnySymbol(self.scope, name) |
|
| 2813 | 2848 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 2849 | + | try checkUnsafeBindingAccess(self, node, sym); |
|
| 2814 | 2850 | setNodeSymbol(self, node, sym); |
|
| 2815 | 2851 | match sym.data { |
|
| 2816 | 2852 | case SymbolData::Value { type, .. } => |
|
| 2817 | 2853 | return setNodeType(self, node, type), |
|
| 2818 | 2854 | case SymbolData::Constant { type, value } => { |
| 2833 | 2869 | }, |
|
| 2834 | 2870 | case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal), |
|
| 2835 | 2871 | case ast::NodeValue::FieldAccess(access) => return try resolveFieldAccess(self, node, access), |
|
| 2836 | 2872 | case ast::NodeValue::BinOp(binop) => return try resolveBinOp(self, node, binop), |
|
| 2837 | 2873 | case ast::NodeValue::Block(block) => return try resolveBlock(self, node, block), |
|
| 2874 | + | case ast::NodeValue::Unsafe(body) => { |
|
| 2875 | + | set self.unsafeDepth += 1; |
|
| 2876 | + | let bodyTy = try visit(self, body, hint) catch e { |
|
| 2877 | + | set self.unsafeDepth -= 1; |
|
| 2878 | + | throw e; |
|
| 2879 | + | }; |
|
| 2880 | + | set self.unsafeDepth -= 1; |
|
| 2881 | + | return setNodeType(self, node, bodyTy); |
|
| 2882 | + | }, |
|
| 2838 | 2883 | case ast::NodeValue::Let(decl) => return try resolveLet(self, node, decl), |
|
| 2839 | 2884 | case ast::NodeValue::ConstDecl(decl) => return try resolveConstOrStatic( |
|
| 2840 | 2885 | self, node, decl.ident, decl.type, decl.value, decl.attrs, true |
|
| 2841 | 2886 | ), |
|
| 2842 | 2887 | case ast::NodeValue::StaticDecl(decl) => return try resolveConstOrStatic( |
| 2915 | 2960 | return setNodeType(self, node, hint); |
|
| 2916 | 2961 | } |
|
| 2917 | 2962 | return setNodeType(self, node, Type::Nil); |
|
| 2918 | 2963 | }, |
|
| 2919 | 2964 | case ast::NodeValue::Undef => { |
|
| 2965 | + | try requireUnsafe(self, node); |
|
| 2920 | 2966 | return setNodeType(self, node, Type::Undefined); |
|
| 2921 | 2967 | }, |
|
| 2922 | 2968 | case ast::NodeValue::Bool(value) => { |
|
| 2923 | 2969 | setNodeConstValue(self, node, ConstValue::Bool(value)); |
|
| 2924 | 2970 | return setNodeType(self, node, Type::Bool); |
| 3175 | 3221 | }, |
|
| 3176 | 3222 | case ast::NodeValue::As(expr) => { |
|
| 3177 | 3223 | // Cast expressions are constant if the source value is constant. |
|
| 3178 | 3224 | return isConstExpr(self, expr.value); |
|
| 3179 | 3225 | }, |
|
| 3226 | + | case ast::NodeValue::Unsafe(body) => { |
|
| 3227 | + | return isConstExpr(self, body); |
|
| 3228 | + | }, |
|
| 3180 | 3229 | else => { |
|
| 3181 | 3230 | return false; |
|
| 3182 | 3231 | } |
|
| 3183 | 3232 | } |
|
| 3184 | 3233 | } |
| 3280 | 3329 | isConst: bool |
|
| 3281 | 3330 | ) -> Type throws (ResolveError) { |
|
| 3282 | 3331 | let attrs = resolveAttributes(self, attrList); |
|
| 3283 | 3332 | let bindingTy = try infer(self, typeNode); |
|
| 3284 | 3333 | try ensureStorableType(self, typeNode, bindingTy); |
|
| 3285 | - | let valueTy = try checkAssignable(self, valueNode, bindingTy); |
|
| 3334 | + | try ensureTypeResolved(self, bindingTy, typeNode); |
|
| 3335 | + | let unsafeGlobal = ast::hasAttribute(attrs, ast::Attribute::Unsafe); |
|
| 3336 | + | if isLinear(bindingTy) and not unsafeGlobal { |
|
| 3337 | + | throw emitError(self, typeNode, ErrorKind::LinearDiscard); |
|
| 3338 | + | } |
|
| 3339 | + | let unsafeInitializer = unsafeGlobal; |
|
| 3340 | + | if unsafeInitializer { |
|
| 3341 | + | set self.unsafeDepth += 1; |
|
| 3342 | + | } |
|
| 3343 | + | let valueTy = try checkAssignable(self, valueNode, bindingTy) catch e { |
|
| 3344 | + | if unsafeInitializer { |
|
| 3345 | + | set self.unsafeDepth -= 1; |
|
| 3346 | + | } |
|
| 3347 | + | throw e; |
|
| 3348 | + | }; |
|
| 3349 | + | if unsafeInitializer { |
|
| 3350 | + | set self.unsafeDepth -= 1; |
|
| 3351 | + | } |
|
| 3286 | 3352 | ||
| 3287 | 3353 | if isConst { |
|
| 3288 | 3354 | let mut constVal = constValueEntry(self, valueNode); |
|
| 3289 | 3355 | if constVal == nil and not isConstExpr(self, valueNode) { |
|
| 3290 | 3356 | throw emitError(self, valueNode, ErrorKind::ConstExprRequired); |
| 3304 | 3370 | setNodeType(self, valueNode, bindingTy); |
|
| 3305 | 3371 | ||
| 3306 | 3372 | return Type::Void; |
|
| 3307 | 3373 | } |
|
| 3308 | 3374 | ||
| 3375 | + | /// Return whether a function type matches the compiler's ecall ABI. |
|
| 3376 | + | fn isCanonicalEcallType(info: *FnType) -> bool { |
|
| 3377 | + | if not info.isUnsafe |
|
| 3378 | + | or info.paramTypes.len <> 5 |
|
| 3379 | + | or info.throwList.len <> 0 |
|
| 3380 | + | or *info.returnType <> Type::I64 |
|
| 3381 | + | { |
|
| 3382 | + | return false; |
|
| 3383 | + | } |
|
| 3384 | + | return *info.paramTypes[0] == Type::U32 |
|
| 3385 | + | and *info.paramTypes[1] == Type::I64 |
|
| 3386 | + | and *info.paramTypes[2] == Type::I64 |
|
| 3387 | + | and *info.paramTypes[3] == Type::I64 |
|
| 3388 | + | and *info.paramTypes[4] == Type::I64; |
|
| 3389 | + | } |
|
| 3390 | + | ||
| 3309 | 3391 | /// Analyze a function declaration signature and bind the function name. |
|
| 3310 | 3392 | fn resolveFnDecl(self: *mut Resolver, node: *ast::Node, decl: ast::FnDecl) -> Type |
|
| 3311 | 3393 | throws (ResolveError) |
|
| 3312 | 3394 | { |
|
| 3395 | + | set self.nodeData.entries[node.id].trustedBody = self.unsafeDepth > 0; |
|
| 3313 | 3396 | let attrMask = resolveAttributes(self, decl.attrs); |
|
| 3314 | 3397 | let mut retTy = Type::Void; |
|
| 3315 | 3398 | if let retNode = decl.sig.returnType { |
|
| 3316 | 3399 | set retTy = try infer(self, retNode); |
|
| 3317 | 3400 | try ensureStorableType(self, retNode, retTy); |
| 3360 | 3443 | try ensureStorableType(self, throwNode, throwTy); |
|
| 3361 | 3444 | } |
|
| 3362 | 3445 | exitFn(self); |
|
| 3363 | 3446 | set fnType.paramTypes = ¶mTypes[..]; |
|
| 3364 | 3447 | set fnType.throwList = &throwList[..]; |
|
| 3448 | + | if ast::hasAttribute(attrMask, ast::Attribute::Intrinsic) { |
|
| 3449 | + | let name = try nodeName(self, decl.name); |
|
| 3450 | + | if mem::eq(name, "ecall") and not isCanonicalEcallType(&fnType) { |
|
| 3451 | + | throw emitError( |
|
| 3452 | + | self, |
|
| 3453 | + | node, |
|
| 3454 | + | ErrorKind::InvalidEcallIntrinsicSignature, |
|
| 3455 | + | ); |
|
| 3456 | + | } |
|
| 3457 | + | } |
|
| 3365 | 3458 | ||
| 3366 | 3459 | // Bind the function name. |
|
| 3367 | 3460 | let ty = Type::Fn(allocFnType(self, fnType)); |
|
| 3368 | 3461 | let sym = try bindValueIdent(self, decl.name, node, ty, false, 0, attrMask) |
|
| 3369 | 3462 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
| 3383 | 3476 | }; |
|
| 3384 | 3477 | let retTy = *fnType.returnType; |
|
| 3385 | 3478 | let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern); |
|
| 3386 | 3479 | let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic); |
|
| 3387 | 3480 | let isUnsafe = fnType.isUnsafe; |
|
| 3481 | + | let trustedBody = nodeData(self, node).trustedBody; |
|
| 3388 | 3482 | ||
| 3389 | 3483 | if let body = decl.body { |
|
| 3390 | 3484 | if isIntrinsic { |
|
| 3391 | 3485 | throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody); |
|
| 3392 | 3486 | } |
|
| 3393 | 3487 | if isExtern { |
|
| 3394 | 3488 | throw emitError(self, node, ErrorKind::FnUnexpectedBody); |
|
| 3395 | 3489 | } |
|
| 3396 | - | if isUnsafe { |
|
| 3490 | + | if isUnsafe or trustedBody { |
|
| 3397 | 3491 | set self.unsafeDepth += 1; |
|
| 3398 | 3492 | } |
|
| 3399 | 3493 | enterFn(self, node, fnType); // Enter function scope for body analysis. |
|
| 3400 | 3494 | ||
| 3401 | 3495 | let bodyTy = try checkAssignable(self, body, Type::Void) catch e { |
|
| 3402 | 3496 | exitFn(self); |
|
| 3403 | - | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 3497 | + | if isUnsafe or trustedBody { set self.unsafeDepth -= 1; } |
|
| 3404 | 3498 | throw e; |
|
| 3405 | 3499 | }; |
|
| 3406 | 3500 | if retTy <> Type::Void and bodyTy <> Type::Never { |
|
| 3407 | 3501 | exitFn(self); |
|
| 3408 | - | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 3502 | + | if isUnsafe or trustedBody { set self.unsafeDepth -= 1; } |
|
| 3409 | 3503 | throw emitError(self, body, ErrorKind::FnMissingReturn); |
|
| 3410 | 3504 | } |
|
| 3505 | + | if self.unsafeDepth == 0 { |
|
| 3506 | + | try checkLinearFn(self, nil, decl.sig.params, body) catch e { |
|
| 3507 | + | exitFn(self); |
|
| 3508 | + | throw e; |
|
| 3509 | + | }; |
|
| 3510 | + | } |
|
| 3411 | 3511 | exitFn(self); |
|
| 3412 | - | if isUnsafe { |
|
| 3512 | + | if isUnsafe or trustedBody { |
|
| 3413 | 3513 | set self.unsafeDepth -= 1; |
|
| 3414 | 3514 | } |
|
| 3415 | 3515 | } else if not isExtern { |
|
| 3416 | 3516 | throw emitError(self, node, ErrorKind::FnMissingBody); |
|
| 3417 | 3517 | } |
| 3788 | 3888 | // Match each instance method to a trait method. |
|
| 3789 | 3889 | for methodNode in methods { |
|
| 3790 | 3890 | let case ast::NodeValue::MethodDecl { |
|
| 3791 | 3891 | name, receiverName, receiverType, sig, body, attrs, |
|
| 3792 | 3892 | } = methodNode.value else continue; |
|
| 3893 | + | set self.nodeData.entries[methodNode.id].trustedBody = self.unsafeDepth > 0; |
|
| 3793 | 3894 | ||
| 3794 | 3895 | let methodName = try nodeName(self, name); |
|
| 3795 | 3896 | let attrMask = resolveAttributes(self, attrs); |
|
| 3796 | 3897 | ||
| 3797 | 3898 | // Find the matching trait method. |
| 3976 | 4077 | let sym = symbolFor(self, node) |
|
| 3977 | 4078 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 3978 | 4079 | let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data |
|
| 3979 | 4080 | else panic "resolveMethodBody: expected value symbol"; |
|
| 3980 | 4081 | let isUnsafe = fnType.isUnsafe; |
|
| 3981 | - | if isUnsafe { |
|
| 4082 | + | let trustedBody = nodeData(self, node).trustedBody; |
|
| 4083 | + | if isUnsafe or trustedBody { |
|
| 3982 | 4084 | set self.unsafeDepth += 1; |
|
| 3983 | 4085 | } |
|
| 3984 | 4086 | ||
| 3985 | 4087 | // Enter function scope. |
|
| 3986 | 4088 | enterFn(self, node, fnType); |
|
| 3987 | 4089 | ||
| 3988 | 4090 | // Bind the receiver parameter. |
|
| 3989 | 4091 | let receiverTy = *fnType.paramTypes[0]; |
|
| 3990 | 4092 | try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch e { |
|
| 3991 | 4093 | exitFn(self); |
|
| 3992 | - | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 4094 | + | if isUnsafe or trustedBody { set self.unsafeDepth -= 1; } |
|
| 3993 | 4095 | throw e; |
|
| 3994 | 4096 | }; |
|
| 3995 | 4097 | // Bind the remaining parameters from the signature. |
|
| 3996 | 4098 | for paramNode in sig.params { |
|
| 3997 | 4099 | let paramTy = try infer(self, paramNode) catch e { |
|
| 3998 | 4100 | exitFn(self); |
|
| 3999 | - | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 4101 | + | if isUnsafe or trustedBody { set self.unsafeDepth -= 1; } |
|
| 4000 | 4102 | throw e; |
|
| 4001 | 4103 | }; |
|
| 4002 | 4104 | } |
|
| 4003 | 4105 | ||
| 4004 | 4106 | // Resolve the body. |
|
| 4005 | 4107 | let retTy = *fnType.returnType; |
|
| 4006 | 4108 | let bodyTy = try checkAssignable(self, body, Type::Void) catch e { |
|
| 4007 | 4109 | exitFn(self); |
|
| 4008 | - | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 4110 | + | if isUnsafe or trustedBody { set self.unsafeDepth -= 1; } |
|
| 4009 | 4111 | throw e; |
|
| 4010 | 4112 | }; |
|
| 4011 | 4113 | if retTy <> Type::Void and bodyTy <> Type::Never { |
|
| 4012 | 4114 | exitFn(self); |
|
| 4013 | - | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 4115 | + | if isUnsafe or trustedBody { set self.unsafeDepth -= 1; } |
|
| 4014 | 4116 | throw emitError(self, body, ErrorKind::FnMissingReturn); |
|
| 4015 | 4117 | } |
|
| 4118 | + | if self.unsafeDepth == 0 { |
|
| 4119 | + | try checkLinearFn(self, receiverName, sig.params, body) catch e { |
|
| 4120 | + | exitFn(self); |
|
| 4121 | + | throw e; |
|
| 4122 | + | }; |
|
| 4123 | + | } |
|
| 4016 | 4124 | exitFn(self); |
|
| 4017 | - | if isUnsafe { |
|
| 4125 | + | if isUnsafe or trustedBody { |
|
| 4018 | 4126 | set self.unsafeDepth -= 1; |
|
| 4019 | 4127 | } |
|
| 4020 | 4128 | } |
|
| 4021 | 4129 | ||
| 4022 | 4130 | /// Resolve a standalone method declaration (signature only). |
| 4046 | 4154 | receiverName: *ast::Node, |
|
| 4047 | 4155 | receiverType: *ast::Node, |
|
| 4048 | 4156 | sig: ast::FnSig, |
|
| 4049 | 4157 | attrs: ?ast::Attributes, |
|
| 4050 | 4158 | ) throws (ResolveError) { |
|
| 4159 | + | set self.nodeData.entries[node.id].trustedBody = self.unsafeDepth > 0; |
|
| 4051 | 4160 | // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a |
|
| 4052 | 4161 | // nominal type. |
|
| 4053 | 4162 | let fullReceiverTy = try infer(self, receiverType); |
|
| 4054 | 4163 | let case Type::Pointer { |
|
| 4055 | 4164 | class: receiverClass, target: receiverTarget, mutable: receiverMut, |
| 4293 | 4402 | // Find module under the current module. |
|
| 4294 | 4403 | let modName = try nodeName(self, decl.name); |
|
| 4295 | 4404 | let submod = try enterSubModule(self, modName, node); |
|
| 4296 | 4405 | let case ast::NodeValue::Block(block) = submod.root.value |
|
| 4297 | 4406 | else panic "resolveModDecl: expected block for module root"; |
|
| 4298 | - | try resolveModuleDecls(self, &block); |
|
| 4299 | - | ||
| 4407 | + | let mut isUnsafe = false; |
|
| 4408 | + | if let attrs = decl.attrs { |
|
| 4409 | + | set isUnsafe = ast::attributesContains(&attrs, ast::Attribute::Unsafe); |
|
| 4410 | + | } |
|
| 4411 | + | if isUnsafe { |
|
| 4412 | + | set self.unsafeDepth += 1; |
|
| 4413 | + | } |
|
| 4414 | + | try resolveModuleDecls(self, &block) catch e { |
|
| 4415 | + | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 4416 | + | exitModuleScope(self, submod); |
|
| 4417 | + | throw e; |
|
| 4418 | + | }; |
|
| 4419 | + | if isUnsafe { |
|
| 4420 | + | set self.unsafeDepth -= 1; |
|
| 4421 | + | } |
|
| 4300 | 4422 | exitModuleScope(self, submod); |
|
| 4301 | 4423 | } |
|
| 4302 | 4424 | ||
| 4303 | 4425 | /// Analyze a `use` statement and create a symbol for the imported module. |
|
| 4304 | 4426 | fn resolveUse(self: *mut Resolver, node: *ast::Node, decl: ast::Use) -> Type |
| 5323 | 5445 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
|
| 5324 | 5446 | expected: 2, |
|
| 5325 | 5447 | actual: args.len as u32, |
|
| 5326 | 5448 | })); |
|
| 5327 | 5449 | } |
|
| 5450 | + | try requireUnsafe(self, node); |
|
| 5328 | 5451 | let ptrType = try visit(self, args[0], Type::Unknown); |
|
| 5329 | 5452 | let case Type::Pointer { class, target, mutable } = ptrType else { |
|
| 5330 | 5453 | throw emitError(self, node, ErrorKind::ExpectedPointer); |
|
| 5331 | 5454 | }; |
|
| 5332 | 5455 | let _ = try checkAssignable(self, args[1], Type::U32); |
| 5508 | 5631 | ||
| 5509 | 5632 | // Associate return type to call. |
|
| 5510 | 5633 | return setNodeType(self, node, *info.returnType); |
|
| 5511 | 5634 | } |
|
| 5512 | 5635 | ||
| 5636 | + | /// Return whether a type has the exact allocator representation used by slice append lowering. |
|
| 5637 | + | fn isSliceAllocatorType(ty: Type) -> bool { |
|
| 5638 | + | let case Type::Nominal(NominalType::Record(recInfo)) = ty |
|
| 5639 | + | else return false; |
|
| 5640 | + | if recInfo.fields.len <> 2 |
|
| 5641 | + | or recInfo.layout.size <> PTR_SIZE * 2 |
|
| 5642 | + | or recInfo.layout.alignment <> PTR_SIZE |
|
| 5643 | + | or recInfo.fields[0].offset <> 0 |
|
| 5644 | + | or recInfo.fields[1].offset <> PTR_SIZE as i32 |
|
| 5645 | + | { |
|
| 5646 | + | return false; |
|
| 5647 | + | } |
|
| 5648 | + | ||
| 5649 | + | let case Type::Fn(callback) = recInfo.fields[0].fieldType |
|
| 5650 | + | else return false; |
|
| 5651 | + | if callback.isUnsafe |
|
| 5652 | + | or callback.paramTypes.len <> 3 |
|
| 5653 | + | or callback.throwList.len <> 0 |
|
| 5654 | + | or *callback.paramTypes[1] <> Type::U32 |
|
| 5655 | + | or *callback.paramTypes[2] <> Type::U32 |
|
| 5656 | + | { |
|
| 5657 | + | return false; |
|
| 5658 | + | } |
|
| 5659 | + | let case Type::Pointer { |
|
| 5660 | + | class: types::PointerClass::Owned, |
|
| 5661 | + | target: callbackCtx, |
|
| 5662 | + | mutable: true, |
|
| 5663 | + | } = *callback.paramTypes[0] else return false; |
|
| 5664 | + | if *callbackCtx <> Type::Opaque { |
|
| 5665 | + | return false; |
|
| 5666 | + | } |
|
| 5667 | + | let case Type::Pointer { |
|
| 5668 | + | class: types::PointerClass::Owned, |
|
| 5669 | + | target: result, |
|
| 5670 | + | mutable: true, |
|
| 5671 | + | } = *callback.returnType else return false; |
|
| 5672 | + | if *result <> Type::Opaque { |
|
| 5673 | + | return false; |
|
| 5674 | + | } |
|
| 5675 | + | ||
| 5676 | + | return typesEqual(recInfo.fields[1].fieldType, *callback.paramTypes[0]); |
|
| 5677 | + | } |
|
| 5678 | + | ||
| 5513 | 5679 | /// Resolve `slice.append(val, allocator)`. |
|
| 5514 | 5680 | fn resolveSliceAppend( |
|
| 5515 | 5681 | self: *mut Resolver, |
|
| 5516 | 5682 | node: *ast::Node, |
|
| 5517 | 5683 | parent: *ast::Node, |
| 5527 | 5693 | throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 5528 | 5694 | expected: 2, |
|
| 5529 | 5695 | actual: args.len as u32, |
|
| 5530 | 5696 | })); |
|
| 5531 | 5697 | } |
|
| 5698 | + | try requireUnsafe(self, node); |
|
| 5532 | 5699 | // First argument must be assignable to the element type. |
|
| 5533 | 5700 | try checkAssignable(self, args[0], *elemType); |
|
| 5534 | - | // Second argument: the allocator. We accept any type -- the lowerer |
|
| 5535 | - | // reads `.func` and `.ctx` at fixed offsets. |
|
| 5536 | - | try visit(self, args[1], Type::Unknown); |
|
| 5701 | + | // The lowerer loads the allocator callback and context from fixed offsets. |
|
| 5702 | + | let allocatorType = try visit(self, args[1], Type::Unknown); |
|
| 5703 | + | try ensureTypeResolved(self, allocatorType, args[1]); |
|
| 5704 | + | if not isSliceAllocatorType(allocatorType) { |
|
| 5705 | + | throw emitTypeMismatch(self, args[1], TypeMismatch { |
|
| 5706 | + | expected: Type::Unknown, |
|
| 5707 | + | actual: allocatorType, |
|
| 5708 | + | }); |
|
| 5709 | + | } |
|
| 5537 | 5710 | set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType }; |
|
| 5538 | 5711 | ||
| 5539 | 5712 | // Return the parent's type so the caller can rebind: |
|
| 5540 | 5713 | return setNodeType(self, node, parentType); |
|
| 5541 | 5714 | } |
| 6024 | 6197 | /// Analyze a scope access expression. |
|
| 6025 | 6198 | fn resolveScopeAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type |
|
| 6026 | 6199 | throws (ResolveError) |
|
| 6027 | 6200 | { |
|
| 6028 | 6201 | let sym = try resolveAccess(self, node, access, self.scope); |
|
| 6202 | + | try checkUnsafeBindingAccess(self, node, sym); |
|
| 6029 | 6203 | let mut ty: Type = undefined; |
|
| 6030 | 6204 | ||
| 6031 | 6205 | match sym.data { |
|
| 6032 | 6206 | case SymbolData::Value { type, .. } => { |
|
| 6033 | 6207 | setNodeSymbol(self, node, sym); |
| 6243 | 6417 | ||
| 6244 | 6418 | /// Analyze an address-of expression. |
|
| 6245 | 6419 | fn resolveAddressOf(self: *mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type |
|
| 6246 | 6420 | throws (ResolveError) |
|
| 6247 | 6421 | { |
|
| 6248 | - | // Checked and unsafe pointer contexts require a reference source. |
|
| 6249 | - | let class = types::PointerClass::Ref |
|
| 6250 | - | if isRefType(hint) or isUnsafePointerType(hint) |
|
| 6251 | - | else types::PointerClass::Owned; |
|
| 6422 | + | // Safe address-of expressions always create call-scoped loans. Trusted |
|
| 6423 | + | // implementations may create owners when no loan or raw type is required. |
|
| 6424 | + | let class = types::PointerClass::Owned |
|
| 6425 | + | if self.unsafeDepth > 0 and not isRefType(hint) and not isUnsafePointerType(hint) |
|
| 6426 | + | else types::PointerClass::Ref; |
|
| 6252 | 6427 | if addr.mutable { |
|
| 6253 | 6428 | if not try canBorrowMutFrom(self, addr.target) { |
|
| 6254 | 6429 | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
|
| 6255 | 6430 | } |
|
| 6256 | 6431 | } |
| 6450 | 6625 | assert sourceTy <> Type::Unknown; |
|
| 6451 | 6626 | assert targetTy <> Type::Unknown; |
|
| 6452 | 6627 | ||
| 6453 | 6628 | let mut valid = isValidCast(sourceTy, targetTy); |
|
| 6454 | 6629 | if let case Type::Pointer { |
|
| 6455 | - | class: sourceClass, target: sourceTarget, mutable: sourceMutable, |
|
| 6630 | + | class: sourceClass, mutable: sourceMutable, .. |
|
| 6456 | 6631 | } = sourceTy { |
|
| 6457 | 6632 | if let case Type::Pointer { |
|
| 6458 | - | class: targetClass, target: targetTarget, mutable: targetMutable, |
|
| 6633 | + | class: targetClass, mutable: targetMutable, .. |
|
| 6459 | 6634 | } = targetTy { |
|
| 6460 | - | if sourceClass == types::PointerClass::Ref and |
|
| 6461 | - | targetClass == types::PointerClass::Unsafe and |
|
| 6462 | - | (not targetMutable or sourceMutable) and |
|
| 6463 | - | isValidCast(*sourceTarget, *targetTarget) |
|
| 6464 | - | { |
|
| 6635 | + | let compatibleClass = sourceClass == targetClass or ( |
|
| 6636 | + | sourceClass == types::PointerClass::Ref and |
|
| 6637 | + | targetClass == types::PointerClass::Unsafe |
|
| 6638 | + | ); |
|
| 6639 | + | if compatibleClass and (not targetMutable or sourceMutable) { |
|
| 6465 | 6640 | set valid = true; |
|
| 6466 | 6641 | } |
|
| 6467 | 6642 | } |
|
| 6468 | 6643 | } |
|
| 6469 | 6644 | if let case Type::Slice { |
|
| 6470 | - | class: sourceClass, item: sourceItem, mutable: sourceMutable, |
|
| 6645 | + | class: sourceClass, mutable: sourceMutable, .. |
|
| 6471 | 6646 | } = sourceTy { |
|
| 6472 | 6647 | if let case Type::Slice { |
|
| 6473 | - | class: targetClass, item: targetItem, mutable: targetMutable, |
|
| 6648 | + | class: targetClass, mutable: targetMutable, .. |
|
| 6474 | 6649 | } = targetTy { |
|
| 6475 | - | if sourceClass == types::PointerClass::Ref and |
|
| 6476 | - | targetClass == types::PointerClass::Unsafe and |
|
| 6477 | - | (not targetMutable or sourceMutable) and |
|
| 6478 | - | isValidCast(*sourceItem, *targetItem) |
|
| 6479 | - | { |
|
| 6650 | + | let compatibleClass = sourceClass == targetClass or ( |
|
| 6651 | + | sourceClass == types::PointerClass::Ref and |
|
| 6652 | + | targetClass == types::PointerClass::Unsafe |
|
| 6653 | + | ); |
|
| 6654 | + | if compatibleClass and (not targetMutable or sourceMutable) { |
|
| 6480 | 6655 | set valid = true; |
|
| 6481 | 6656 | } |
|
| 6482 | 6657 | } |
|
| 6483 | 6658 | } |
|
| 6484 | 6659 | if valid { |
|
| 6660 | + | let mut changesRepresentation = false; |
|
| 6661 | + | match sourceTy { |
|
| 6662 | + | case Type::Pointer { target: sourceTarget, .. } => { |
|
| 6663 | + | if let case Type::Pointer { target: targetTarget, .. } = targetTy { |
|
| 6664 | + | set changesRepresentation = not typesEqual(*sourceTarget, *targetTarget); |
|
| 6665 | + | } |
|
| 6666 | + | } |
|
| 6667 | + | case Type::Slice { item: sourceItem, .. } => { |
|
| 6668 | + | if let case Type::Slice { item: targetItem, .. } = targetTy { |
|
| 6669 | + | set changesRepresentation = not typesEqual(*sourceItem, *targetItem); |
|
| 6670 | + | } |
|
| 6671 | + | } |
|
| 6672 | + | else => {} |
|
| 6673 | + | } |
|
| 6674 | + | if changesRepresentation { |
|
| 6675 | + | try requireUnsafe(self, node); |
|
| 6676 | + | } |
|
| 6485 | 6677 | // Propagate the constant value after applying the cast's target-width |
|
| 6486 | 6678 | // truncation and signed interpretation. |
|
| 6487 | 6679 | if let value = constValueEntry(self, expr.value) { |
|
| 6488 | 6680 | if let case ConstValue::Int(i) = value { |
|
| 6489 | 6681 | setNodeConstValue(self, node, castConstInt(i, targetTy)); |
| 6967 | 7159 | throw emitError(self, node, ErrorKind::OpaquePointerArithmetic); |
|
| 6968 | 7160 | } |
|
| 6969 | 7161 | if leftClass <> types::PointerClass::Ref |
|
| 6970 | 7162 | and isNumericType(rightTy) |
|
| 6971 | 7163 | { |
|
| 6972 | - | if leftClass == types::PointerClass::Unsafe { |
|
| 6973 | - | try requireUnsafe(self, node); |
|
| 6974 | - | } |
|
| 7164 | + | try requireUnsafe(self, node); |
|
| 6975 | 7165 | return setNodeType(self, node, leftTy); |
|
| 6976 | 7166 | } |
|
| 6977 | 7167 | } |
|
| 6978 | 7168 | if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy { |
|
| 6979 | 7169 | if *rightTarget == Type::Opaque { |
| 6981 | 7171 | } |
|
| 6982 | 7172 | if binop.op == ast::BinaryOp::Add |
|
| 6983 | 7173 | and rightClass <> types::PointerClass::Ref |
|
| 6984 | 7174 | and isNumericType(leftTy) |
|
| 6985 | 7175 | { |
|
| 6986 | - | if rightClass == types::PointerClass::Unsafe { |
|
| 6987 | - | try requireUnsafe(self, node); |
|
| 6988 | - | } |
|
| 7176 | + | try requireUnsafe(self, node); |
|
| 6989 | 7177 | return setNodeType(self, node, rightTy); |
|
| 6990 | 7178 | } |
|
| 6991 | 7179 | } |
|
| 6992 | 7180 | } |
|
| 6993 | 7181 | let leftTy = try checkNumeric(self, binop.left); |
| 7418 | 7606 | } |
|
| 7419 | 7607 | } |
|
| 7420 | 7608 | set env.len = start; |
|
| 7421 | 7609 | } |
|
| 7422 | 7610 | ||
| 7423 | - | /// Consume a tracked identifier exactly once. |
|
| 7424 | - | fn consumeLinearIdent( |
|
| 7611 | + | /// Mark a tracked identifier as consumed. |
|
| 7612 | + | fn consumeLinearIdent(env: *mut LinearEnv, index: u32) { |
|
| 7613 | + | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7614 | + | } |
|
| 7615 | + | ||
| 7616 | + | /// Check that a tracked identifier is available for its requested use. |
|
| 7617 | + | fn checkLinearIdent( |
|
| 7425 | 7618 | checker: *mut LinearChecker, |
|
| 7426 | 7619 | env: *mut LinearEnv, |
|
| 7427 | 7620 | node: *ast::Node, |
|
| 7621 | + | usage: LinearUse, |
|
| 7428 | 7622 | ) throws (ResolveError) { |
|
| 7623 | + | if usage == LinearUse::Place { |
|
| 7624 | + | return; |
|
| 7625 | + | } |
|
| 7429 | 7626 | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7430 | 7627 | let index = findLinearBinding(env, sym) else return; |
|
| 7431 | 7628 | if not linearBindingAvailable(env, index) { |
|
| 7432 | 7629 | throw emitError( |
|
| 7433 | 7630 | checker.resolver, |
|
| 7434 | 7631 | node, |
|
| 7435 | 7632 | ErrorKind::LinearUseAfterConsume(sym.name), |
|
| 7436 | 7633 | ); |
|
| 7437 | 7634 | } |
|
| 7438 | - | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7635 | + | if usage == LinearUse::Consume { |
|
| 7636 | + | consumeLinearIdent(env, index); |
|
| 7637 | + | } |
|
| 7439 | 7638 | } |
|
| 7440 | 7639 | ||
| 7441 | 7640 | /// Verify that two live branches agree on every outer binding. |
|
| 7442 | 7641 | fn joinLinearBranches( |
|
| 7443 | 7642 | checker: *mut LinearChecker, |
| 7489 | 7688 | } |
|
| 7490 | 7689 | } |
|
| 7491 | 7690 | set env.terminated = true; |
|
| 7492 | 7691 | } |
|
| 7493 | 7692 | ||
| 7494 | - | /// Find the local root borrowed or consumed by an argument expression. |
|
| 7693 | + | /// Find the root borrowed or consumed by an argument expression. |
|
| 7495 | 7694 | fn linearRootSymbol(self: *mut Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 7496 | 7695 | match node.value { |
|
| 7497 | - | case ast::NodeValue::Ident(_) => return symbolFor(self, node), |
|
| 7696 | + | case ast::NodeValue::Ident(_), |
|
| 7697 | + | ast::NodeValue::ScopeAccess(_) => return symbolFor(self, node), |
|
| 7498 | 7698 | case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target), |
|
| 7499 | 7699 | case ast::NodeValue::FieldAccess(access) => |
|
| 7500 | 7700 | return linearRootSymbol(self, access.parent), |
|
| 7501 | 7701 | case ast::NodeValue::Subscript { container, .. } => |
|
| 7502 | 7702 | return linearRootSymbol(self, container), |
|
| 7503 | 7703 | case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target), |
|
| 7704 | + | case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value), |
|
| 7705 | + | case ast::NodeValue::Unsafe(body) => return linearRootSymbol(self, body), |
|
| 7504 | 7706 | else => return nil, |
|
| 7505 | 7707 | } |
|
| 7506 | 7708 | } |
|
| 7507 | 7709 | ||
| 7710 | + | /// Reject an access that overlaps a loan active in an enclosing call. |
|
| 7711 | + | fn checkCallLoanConflicts( |
|
| 7712 | + | checker: *mut LinearChecker, |
|
| 7713 | + | node: *ast::Node, |
|
| 7714 | + | exclusive: bool, |
|
| 7715 | + | ) throws (ResolveError) { |
|
| 7716 | + | let root = linearRootSymbol(checker.resolver, node) else return; |
|
| 7717 | + | let mut cursor = checker.loans; |
|
| 7718 | + | while let loans = cursor { |
|
| 7719 | + | for i in 0..loans.len { |
|
| 7720 | + | if let previous = loans.roots[i]; |
|
| 7721 | + | previous == root and (loans.exclusive[i] or exclusive) |
|
| 7722 | + | { |
|
| 7723 | + | throw emitError( |
|
| 7724 | + | checker.resolver, |
|
| 7725 | + | node, |
|
| 7726 | + | ErrorKind::BorrowConflict(root.name), |
|
| 7727 | + | ); |
|
| 7728 | + | } |
|
| 7729 | + | } |
|
| 7730 | + | set cursor = loans.parent; |
|
| 7731 | + | } |
|
| 7732 | + | } |
|
| 7733 | + | ||
| 7508 | 7734 | /// Add the value identifiers introduced by a pattern. |
|
| 7509 | 7735 | fn addLinearPatternBindings( |
|
| 7510 | 7736 | checker: *mut LinearChecker, |
|
| 7511 | 7737 | env: *mut LinearEnv, |
|
| 7512 | 7738 | pattern: *ast::Node, |
| 7712 | 7938 | if haveResult { |
|
| 7713 | 7939 | set *env = result; |
|
| 7714 | 7940 | } |
|
| 7715 | 7941 | } |
|
| 7716 | 7942 | ||
| 7943 | + | /// Check one call argument and retain its loan through all later arguments. |
|
| 7944 | + | fn checkLinearCallArg( |
|
| 7945 | + | checker: *mut LinearChecker, |
|
| 7946 | + | env: *mut LinearEnv, |
|
| 7947 | + | loans: *mut LinearLoans, |
|
| 7948 | + | arg: *ast::Node, |
|
| 7949 | + | expected: Type, |
|
| 7950 | + | ) throws (ResolveError) { |
|
| 7951 | + | let mut exclusive = isLinear(expected); |
|
| 7952 | + | if let case Type::Pointer { |
|
| 7953 | + | class: types::PointerClass::Ref, |
|
| 7954 | + | mutable, |
|
| 7955 | + | .. |
|
| 7956 | + | } = expected { |
|
| 7957 | + | set exclusive = mutable; |
|
| 7958 | + | } else if let case Type::Slice { |
|
| 7959 | + | class: types::PointerClass::Ref, |
|
| 7960 | + | mutable, |
|
| 7961 | + | .. |
|
| 7962 | + | } = expected { |
|
| 7963 | + | set exclusive = mutable; |
|
| 7964 | + | } else if let case Type::TraitObject { |
|
| 7965 | + | class: types::PointerClass::Ref, |
|
| 7966 | + | mutable, |
|
| 7967 | + | .. |
|
| 7968 | + | } = expected { |
|
| 7969 | + | set exclusive = mutable; |
|
| 7970 | + | } |
|
| 7971 | + | if not isUnsafePointerType(expected) { |
|
| 7972 | + | try checkCallLoanConflicts(checker, arg, exclusive); |
|
| 7973 | + | } |
|
| 7974 | + | if isRefType(expected) { |
|
| 7975 | + | try checkLinearNode(checker, env, arg, LinearUse::Borrow); |
|
| 7976 | + | } else { |
|
| 7977 | + | try checkLinearNode(checker, env, arg, LinearUse::Consume); |
|
| 7978 | + | } |
|
| 7979 | + | if (isRefType(expected) or isLinear(expected)) and not isUnsafePointerType(expected) { |
|
| 7980 | + | if let root = linearRootSymbol(checker.resolver, arg) { |
|
| 7981 | + | set loans.roots[loans.len] = root; |
|
| 7982 | + | set loans.exclusive[loans.len] = exclusive; |
|
| 7983 | + | set loans.len += 1; |
|
| 7984 | + | } |
|
| 7985 | + | } |
|
| 7986 | + | } |
|
| 7987 | + | ||
| 7988 | + | /// Check a syntactic slice append or delete call without a callee function type. |
|
| 7989 | + | fn checkLinearSliceCall( |
|
| 7990 | + | checker: *mut LinearChecker, |
|
| 7991 | + | env: *mut LinearEnv, |
|
| 7992 | + | node: *ast::Node, |
|
| 7993 | + | call: ast::Call, |
|
| 7994 | + | elemType: ?*Type, |
|
| 7995 | + | ) throws (ResolveError) { |
|
| 7996 | + | let case ast::NodeValue::FieldAccess(access) = call.callee.value else { |
|
| 7997 | + | throw emitError(checker.resolver, call.callee, ErrorKind::Internal); |
|
| 7998 | + | }; |
|
| 7999 | + | let receiverTy = typeFor(checker.resolver, access.parent) else { |
|
| 8000 | + | throw emitError(checker.resolver, access.parent, ErrorKind::Internal); |
|
| 8001 | + | }; |
|
| 8002 | + | let case Type::Slice { class: receiverClass, .. } = autoDeref(receiverTy) else { |
|
| 8003 | + | throw emitError(checker.resolver, access.parent, ErrorKind::Internal); |
|
| 8004 | + | }; |
|
| 8005 | + | let mut loans = LinearLoans { |
|
| 8006 | + | parent: checker.loans, |
|
| 8007 | + | roots: [nil; MAX_FN_PARAMS + 1], |
|
| 8008 | + | exclusive: [false; MAX_FN_PARAMS + 1], |
|
| 8009 | + | len: 0, |
|
| 8010 | + | }; |
|
| 8011 | + | set checker.loans = &loans; |
|
| 8012 | + | ||
| 8013 | + | if receiverClass <> types::PointerClass::Unsafe { |
|
| 8014 | + | try checkCallLoanConflicts(checker, access.parent, true); |
|
| 8015 | + | } |
|
| 8016 | + | if receiverClass == types::PointerClass::Owned and elemType <> nil { |
|
| 8017 | + | try checkLinearNode(checker, env, access.parent, LinearUse::Consume); |
|
| 8018 | + | } else if receiverClass <> types::PointerClass::Unsafe { |
|
| 8019 | + | try checkLinearNode(checker, env, access.parent, LinearUse::Borrow); |
|
| 8020 | + | } else { |
|
| 8021 | + | try checkLinearNode(checker, env, access.parent, LinearUse::Observe); |
|
| 8022 | + | } |
|
| 8023 | + | if receiverClass <> types::PointerClass::Unsafe { |
|
| 8024 | + | if let root = linearRootSymbol(checker.resolver, access.parent) { |
|
| 8025 | + | set loans.roots[loans.len] = root; |
|
| 8026 | + | set loans.exclusive[loans.len] = true; |
|
| 8027 | + | set loans.len += 1; |
|
| 8028 | + | } |
|
| 8029 | + | } |
|
| 8030 | + | ||
| 8031 | + | if let item = elemType { |
|
| 8032 | + | if call.args.len <> 2 { |
|
| 8033 | + | throw emitError(checker.resolver, node, ErrorKind::Internal); |
|
| 8034 | + | } |
|
| 8035 | + | try checkLinearCallArg(checker, env, &mut loans, call.args[0], *item); |
|
| 8036 | + | let allocatorTy = typeFor(checker.resolver, call.args[1]) else { |
|
| 8037 | + | throw emitError(checker.resolver, call.args[1], ErrorKind::Internal); |
|
| 8038 | + | }; |
|
| 8039 | + | try checkLinearCallArg( |
|
| 8040 | + | checker, |
|
| 8041 | + | env, |
|
| 8042 | + | &mut loans, |
|
| 8043 | + | call.args[1], |
|
| 8044 | + | allocatorTy, |
|
| 8045 | + | ); |
|
| 8046 | + | } else { |
|
| 8047 | + | if call.args.len <> 1 { |
|
| 8048 | + | throw emitError(checker.resolver, node, ErrorKind::Internal); |
|
| 8049 | + | } |
|
| 8050 | + | try checkLinearCallArg( |
|
| 8051 | + | checker, |
|
| 8052 | + | env, |
|
| 8053 | + | &mut loans, |
|
| 8054 | + | call.args[0], |
|
| 8055 | + | Type::U32, |
|
| 8056 | + | ); |
|
| 8057 | + | } |
|
| 8058 | + | set checker.loans = loans.parent; |
|
| 8059 | + | } |
|
| 8060 | + | ||
| 7717 | 8061 | /// Check call-scoped loans and argument ownership transfers. |
|
| 7718 | 8062 | fn checkLinearCall( |
|
| 7719 | 8063 | checker: *mut LinearChecker, |
|
| 7720 | 8064 | env: *mut LinearEnv, |
|
| 7721 | 8065 | node: *ast::Node, |
|
| 7722 | 8066 | call: ast::Call, |
|
| 7723 | 8067 | ) throws (ResolveError) { |
|
| 8068 | + | match checker.resolver.nodeData.entries[node.id].extra { |
|
| 8069 | + | case NodeExtra::SliceAppend { elemType } => { |
|
| 8070 | + | try checkLinearSliceCall(checker, env, node, call, elemType); |
|
| 8071 | + | return; |
|
| 8072 | + | } |
|
| 8073 | + | case NodeExtra::SliceDelete { .. } => { |
|
| 8074 | + | try checkLinearSliceCall(checker, env, node, call, nil); |
|
| 8075 | + | return; |
|
| 8076 | + | } |
|
| 8077 | + | else => {} |
|
| 8078 | + | } |
|
| 7724 | 8079 | try checkLinearNode(checker, env, call.callee, LinearUse::Observe); |
|
| 7725 | 8080 | let calleeTy = typeFor(checker.resolver, call.callee) else { |
|
| 7726 | 8081 | throw emitError(checker.resolver, call.callee, ErrorKind::Internal); |
|
| 7727 | 8082 | }; |
|
| 7728 | 8083 | let case Type::Fn(info) = calleeTy else { |
|
| 7729 | 8084 | for arg in call.args { |
|
| 7730 | 8085 | try checkLinearNode(checker, env, arg, LinearUse::Consume); |
|
| 7731 | 8086 | } |
|
| 7732 | 8087 | return; |
|
| 7733 | 8088 | }; |
|
| 7734 | - | let mut roots: [?*mut Symbol; MAX_FN_PARAMS + 1] = undefined; |
|
| 7735 | - | let mut exclusive: [bool; MAX_FN_PARAMS + 1] = undefined; |
|
| 7736 | - | let mut rootsLen: u32 = 0; |
|
| 8089 | + | let mut loans = LinearLoans { |
|
| 8090 | + | parent: checker.loans, |
|
| 8091 | + | roots: [nil; MAX_FN_PARAMS + 1], |
|
| 8092 | + | exclusive: [false; MAX_FN_PARAMS + 1], |
|
| 8093 | + | len: 0, |
|
| 8094 | + | }; |
|
| 8095 | + | set checker.loans = &loans; |
|
| 7737 | 8096 | ||
| 7738 | 8097 | // Method function types exclude their implicit receiver. Account for it |
|
| 7739 | 8098 | // explicitly so owning receivers are consumed and reference receivers |
|
| 7740 | 8099 | // participate in call-scoped loan conflict checks. |
|
| 7741 | 8100 | if let case ast::NodeValue::FieldAccess(access) = call.callee.value { |
| 7755 | 8114 | set haveReceiver = true; |
|
| 7756 | 8115 | } |
|
| 7757 | 8116 | else => {} |
|
| 7758 | 8117 | } |
|
| 7759 | 8118 | if haveReceiver { |
|
| 8119 | + | let receiverExclusive = |
|
| 8120 | + | receiverClass == types::PointerClass::Owned or receiverMutable; |
|
| 7760 | 8121 | if receiverClass <> types::PointerClass::Unsafe { |
|
| 7761 | - | let root = linearRootSymbol(checker.resolver, access.parent); |
|
| 7762 | - | if let rootSym = root { |
|
| 7763 | - | set roots[rootsLen] = rootSym; |
|
| 7764 | - | set exclusive[rootsLen] = |
|
| 7765 | - | receiverClass == types::PointerClass::Owned or receiverMutable; |
|
| 7766 | - | set rootsLen += 1; |
|
| 7767 | - | } |
|
| 8122 | + | try checkCallLoanConflicts(checker, access.parent, receiverExclusive); |
|
| 7768 | 8123 | } |
|
| 7769 | 8124 | if receiverClass == types::PointerClass::Ref { |
|
| 7770 | 8125 | try checkLinearNode(checker, env, access.parent, LinearUse::Borrow); |
|
| 7771 | 8126 | } else if receiverClass == types::PointerClass::Owned { |
|
| 7772 | 8127 | try checkLinearNode(checker, env, access.parent, LinearUse::Consume); |
|
| 7773 | 8128 | } |
|
| 8129 | + | if receiverClass <> types::PointerClass::Unsafe { |
|
| 8130 | + | if let root = linearRootSymbol(checker.resolver, access.parent) { |
|
| 8131 | + | set loans.roots[loans.len] = root; |
|
| 8132 | + | set loans.exclusive[loans.len] = receiverExclusive; |
|
| 8133 | + | set loans.len += 1; |
|
| 8134 | + | } |
|
| 8135 | + | } |
|
| 7774 | 8136 | } |
|
| 7775 | 8137 | } |
|
| 7776 | 8138 | ||
| 7777 | 8139 | for arg, i in call.args { |
|
| 7778 | - | let expected = *info.paramTypes[i]; |
|
| 7779 | - | let root = linearRootSymbol(checker.resolver, arg); |
|
| 7780 | - | let mut argExclusive = isLinear(expected); |
|
| 7781 | - | if let case Type::Pointer { class: types::PointerClass::Ref, mutable, .. } = expected { |
|
| 7782 | - | set argExclusive = mutable; |
|
| 7783 | - | } else if let case Type::Slice { class: types::PointerClass::Ref, mutable, .. } = expected { |
|
| 7784 | - | set argExclusive = mutable; |
|
| 7785 | - | } else if let case Type::TraitObject { |
|
| 7786 | - | class: types::PointerClass::Ref, mutable, .. |
|
| 7787 | - | } = expected { |
|
| 7788 | - | set argExclusive = mutable; |
|
| 7789 | - | } |
|
| 7790 | - | if not isUnsafePointerType(expected) { |
|
| 7791 | - | if let rootSym = root { |
|
| 7792 | - | for j in 0..rootsLen { |
|
| 7793 | - | if let previous = roots[j] { |
|
| 7794 | - | if previous == rootSym and (exclusive[j] or argExclusive) { |
|
| 7795 | - | throw emitError( |
|
| 7796 | - | checker.resolver, |
|
| 7797 | - | arg, |
|
| 7798 | - | ErrorKind::BorrowConflict(rootSym.name), |
|
| 7799 | - | ); |
|
| 7800 | - | } |
|
| 7801 | - | } |
|
| 7802 | - | } |
|
| 7803 | - | set roots[rootsLen] = rootSym; |
|
| 7804 | - | set exclusive[rootsLen] = argExclusive; |
|
| 7805 | - | set rootsLen += 1; |
|
| 7806 | - | } |
|
| 7807 | - | } |
|
| 7808 | - | if isRefType(expected) { |
|
| 7809 | - | try checkLinearNode(checker, env, arg, LinearUse::Borrow); |
|
| 7810 | - | } else { |
|
| 7811 | - | try checkLinearNode(checker, env, arg, LinearUse::Consume); |
|
| 7812 | - | } |
|
| 8140 | + | try checkLinearCallArg( |
|
| 8141 | + | checker, |
|
| 8142 | + | env, |
|
| 8143 | + | &mut loans, |
|
| 8144 | + | arg, |
|
| 8145 | + | *info.paramTypes[i], |
|
| 8146 | + | ); |
|
| 7813 | 8147 | } |
|
| 8148 | + | set checker.loans = loans.parent; |
|
| 7814 | 8149 | } |
|
| 7815 | 8150 | ||
| 7816 | 8151 | /// Check a pattern conditional. Linear scrutinees require an exhaustive match. |
|
| 7817 | 8152 | fn checkLinearIfLet( |
|
| 7818 | 8153 | checker: *mut LinearChecker, |
| 7859 | 8194 | usage: LinearUse, |
|
| 7860 | 8195 | ) throws (ResolveError) { |
|
| 7861 | 8196 | if env.terminated { |
|
| 7862 | 8197 | return; |
|
| 7863 | 8198 | } |
|
| 7864 | - | match node.value { |
|
| 7865 | - | case ast::NodeValue::Ident(_) => { |
|
| 7866 | - | if usage == LinearUse::Consume { |
|
| 7867 | - | try consumeLinearIdent(checker, env, node); |
|
| 7868 | - | } |
|
| 8199 | + | let mut exclusive = usage == LinearUse::Place; |
|
| 8200 | + | if usage == LinearUse::Consume { |
|
| 8201 | + | if let ty = typeFor(checker.resolver, node) { |
|
| 8202 | + | set exclusive = isLinear(ty); |
|
| 7869 | 8203 | } |
|
| 8204 | + | } |
|
| 8205 | + | if let case ast::NodeValue::AddressOf(addr) = node.value; addr.mutable { |
|
| 8206 | + | set exclusive = true; |
|
| 8207 | + | } |
|
| 8208 | + | try checkCallLoanConflicts(checker, node, exclusive); |
|
| 8209 | + | match node.value { |
|
| 8210 | + | case ast::NodeValue::Ident(_) => |
|
| 8211 | + | try checkLinearIdent(checker, env, node, usage), |
|
| 7870 | 8212 | case ast::NodeValue::ExprStmt(expr) => { |
|
| 7871 | 8213 | if let exprTy = typeFor(checker.resolver, expr) { |
|
| 7872 | 8214 | if isLinear(exprTy) { |
|
| 7873 | 8215 | throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard); |
|
| 7874 | 8216 | } |
|
| 7875 | 8217 | } |
|
| 7876 | 8218 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
|
| 7877 | 8219 | } |
|
| 7878 | 8220 | case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node), |
|
| 8221 | + | case ast::NodeValue::Unsafe(body) => |
|
| 8222 | + | try checkLinearNode(checker, env, body, usage), |
|
| 7879 | 8223 | case ast::NodeValue::Let(binding) => { |
|
| 7880 | 8224 | if let case ast::NodeValue::Undef = binding.value.value { |
|
| 7881 | 8225 | if let bindingTy = typeFor(checker.resolver, binding.ident); |
|
| 7882 | 8226 | isLinear(bindingTy) |
|
| 7883 | 8227 | { |
| 7977 | 8321 | try checkLinearNode(checker, env, repeat.item, LinearUse::Consume); |
|
| 7978 | 8322 | try checkLinearNode(checker, env, repeat.count, LinearUse::Consume); |
|
| 7979 | 8323 | } |
|
| 7980 | 8324 | case ast::NodeValue::BinOp(op) => { |
|
| 7981 | 8325 | try checkLinearNode(checker, env, op.left, LinearUse::Consume); |
|
| 7982 | - | try checkLinearNode(checker, env, op.right, LinearUse::Consume); |
|
| 8326 | + | if op.op == ast::BinaryOp::And or op.op == ast::BinaryOp::Or { |
|
| 8327 | + | let skipped = *env; |
|
| 8328 | + | let mut evaluated = skipped; |
|
| 8329 | + | try checkLinearNode( |
|
| 8330 | + | checker, |
|
| 8331 | + | &mut evaluated, |
|
| 8332 | + | op.right, |
|
| 8333 | + | LinearUse::Consume, |
|
| 8334 | + | ); |
|
| 8335 | + | try joinLinearBranches(checker, env, skipped, evaluated, node); |
|
| 8336 | + | } else { |
|
| 8337 | + | try checkLinearNode(checker, env, op.right, LinearUse::Consume); |
|
| 8338 | + | } |
|
| 7983 | 8339 | } |
|
| 7984 | 8340 | case ast::NodeValue::UnOp(op) => { |
|
| 7985 | 8341 | try checkLinearNode(checker, env, op.value, LinearUse::Consume); |
|
| 7986 | 8342 | } |
|
| 7987 | 8343 | case ast::NodeValue::As(expr) => { |
| 8072 | 8428 | try checkLinearMatch(checker, env, node, matchExpr); |
|
| 8073 | 8429 | } |
|
| 8074 | 8430 | case ast::NodeValue::Try(tryExpr) => { |
|
| 8075 | 8431 | try checkLinearNode(checker, env, tryExpr.expr, usage); |
|
| 8076 | 8432 | let success = *env; |
|
| 8433 | + | if tryExpr.catches.len == 0 |
|
| 8434 | + | and not tryExpr.shouldPanic |
|
| 8435 | + | and not tryExpr.returnsOptional |
|
| 8436 | + | { |
|
| 8437 | + | let mut errorExit = success; |
|
| 8438 | + | try finishLinearExit(checker, &mut errorExit); |
|
| 8439 | + | } |
|
| 8077 | 8440 | for catchNode in tryExpr.catches { |
|
| 8078 | 8441 | let case ast::NodeValue::CatchClause(catchClause) = catchNode.value |
|
| 8079 | 8442 | else panic "checkLinearNode: expected catch"; |
|
| 8080 | 8443 | let mut branch = success; |
|
| 8081 | 8444 | let start = branch.len; |
| 8259 | 8622 | params: *mut [*ast::Node], |
|
| 8260 | 8623 | body: *ast::Node, |
|
| 8261 | 8624 | ) throws (ResolveError) { |
|
| 8262 | 8625 | let mut checker = LinearChecker { |
|
| 8263 | 8626 | resolver: self, |
|
| 8627 | + | loans: nil, |
|
| 8264 | 8628 | loopMarks: [0; MAX_LINEAR_LOOP_DEPTH], |
|
| 8265 | 8629 | loopAvailable: [0; MAX_LINEAR_LOOP_DEPTH], |
|
| 8266 | 8630 | loopExitAvailable: [0; MAX_LINEAR_LOOP_DEPTH], |
|
| 8267 | 8631 | loopHasNaturalExit: [false; MAX_LINEAR_LOOP_DEPTH], |
|
| 8268 | 8632 | loopBreakSeen: [false; MAX_LINEAR_LOOP_DEPTH], |
lib/std/lang/resolver/printer.rad
+3 -0
| 418 | 418 | io::print("function body is not expected"); |
|
| 419 | 419 | } |
|
| 420 | 420 | case super::ErrorKind::IntrinsicUnexpectedBody => { |
|
| 421 | 421 | io::print("intrinsic function must not have a body"); |
|
| 422 | 422 | } |
|
| 423 | + | case super::ErrorKind::InvalidEcallIntrinsicSignature => { |
|
| 424 | + | io::print("ecall intrinsic must be unsafe and use signature (u32, i64, i64, i64, i64) -> i64"); |
|
| 425 | + | } |
|
| 423 | 426 | case super::ErrorKind::InvalidLoopControl => { |
|
| 424 | 427 | io::print("loop control outside of a loop construct"); |
|
| 425 | 428 | } |
|
| 426 | 429 | case super::ErrorKind::TryRequiresThrows => { |
|
| 427 | 430 | io::print("try used when function does not declare throws"); |
lib/std/lang/resolver/tests.rad
+726 -149
| 9 | 9 | use std::lang::scanner; |
|
| 10 | 10 | use std::lang::module; |
|
| 11 | 11 | use std::lang::strings; |
|
| 12 | 12 | ||
| 13 | 13 | /// Synthetic file path used for resolver tests. |
|
| 14 | - | constant MODULE_PATH: *[u8] = "/dev/test.rad"; |
|
| 14 | + | unsafe constant MODULE_PATH: *[u8] = "/dev/test.rad"; |
|
| 15 | 15 | ||
| 16 | 16 | /// AST arena storage used by resolver tests. |
|
| 17 | - | static AST_ARENA: [u8; 2097152] = undefined; |
|
| 17 | + | unsafe static AST_ARENA: [u8; 2097152] = undefined; |
|
| 18 | 18 | ||
| 19 | 19 | /// Resolver arena storage used by resolver tests. |
|
| 20 | - | static ARENA_STORAGE: [u8; 2097152] = undefined; |
|
| 20 | + | unsafe static ARENA_STORAGE: [u8; 2097152] = undefined; |
|
| 21 | 21 | ||
| 22 | 22 | /// Node metadata storage used by resolver tests. |
|
| 23 | - | static NODE_DATA_STORAGE: [super::NodeData; 256] = undefined; |
|
| 23 | + | unsafe static NODE_DATA_STORAGE: [super::NodeData; 256] = undefined; |
|
| 24 | 24 | ||
| 25 | 25 | /// Diagnostic storage used by resolver tests. |
|
| 26 | - | static ERROR_STORAGE: [super::Error; 16] = undefined; |
|
| 26 | + | unsafe static ERROR_STORAGE: [super::Error; 16] = undefined; |
|
| 27 | 27 | ||
| 28 | 28 | /// Package scope used by resolver tests. |
|
| 29 | - | static PKG_SCOPE: super::Scope = undefined; |
|
| 29 | + | unsafe static PKG_SCOPE: super::Scope = undefined; |
|
| 30 | 30 | ||
| 31 | 31 | /// Module entries used by resolver tests. |
|
| 32 | - | static MODULE_ENTRIES: [module::ModuleEntry; 8] = undefined; |
|
| 32 | + | unsafe static MODULE_ENTRIES: [module::ModuleEntry; 8] = undefined; |
|
| 33 | 33 | ||
| 34 | 34 | /// Module graph used by resolver tests. |
|
| 35 | - | static MODULE_GRAPH: module::ModuleGraph = undefined; |
|
| 35 | + | unsafe static MODULE_GRAPH: module::ModuleGraph = undefined; |
|
| 36 | 36 | ||
| 37 | 37 | /// Module AST arena storage used by resolver tests. |
|
| 38 | - | static MODULE_ARENA_STORAGE: [u8; 4096] = undefined; |
|
| 38 | + | unsafe static MODULE_ARENA_STORAGE: [u8; 4096] = undefined; |
|
| 39 | 39 | ||
| 40 | 40 | /// Module AST arena used by resolver tests. |
|
| 41 | - | static MODULE_ARENA: ast::NodeArena = undefined; |
|
| 41 | + | unsafe static MODULE_ARENA: ast::NodeArena = undefined; |
|
| 42 | 42 | ||
| 43 | 43 | /// Interned string pool used by resolver tests. |
|
| 44 | - | static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 44 | + | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 45 | 45 | ||
| 46 | 46 | /// String literals used in tests. |
|
| 47 | - | constant LITERALS: [*[u8]; 15] = [ |
|
| 47 | + | unsafe constant LITERALS: [*[u8]; 15] = [ |
|
| 48 | 48 | "Ok", "Error", "R", "S", |
|
| 49 | 49 | "f", "Status", "Pending", |
|
| 50 | 50 | "Some", "None", "First", |
|
| 51 | 51 | "Second", "Opt", "x", |
|
| 52 | 52 | "value", "idx" |
| 344 | 344 | let mut a = testResolver(); |
|
| 345 | 345 | let result = try resolveProgramStr(&mut a, program); |
|
| 346 | 346 | try expectNoErrors(&result); |
|
| 347 | 347 | } |
|
| 348 | 348 | ||
| 349 | + | /// Require a linear use-after-consume error at the later identifier use. |
|
| 350 | + | fn expectLinearUseAfterConsume( |
|
| 351 | + | program: *[u8], |
|
| 352 | + | name: *[u8], |
|
| 353 | + | offset: u32, |
|
| 354 | + | ) throws (testing::TestError) { |
|
| 355 | + | let mut a = testResolver(); |
|
| 356 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 357 | + | let err = try expectErrorKind( |
|
| 358 | + | &result, |
|
| 359 | + | super::ErrorKind::LinearUseAfterConsume(name), |
|
| 360 | + | ); |
|
| 361 | + | let node = err.node else throw testing::TestError::Failed; |
|
| 362 | + | try testing::expect(node.span.offset == offset); |
|
| 363 | + | } |
|
| 364 | + | ||
| 349 | 365 | /// Require an inferred integer type mismatch. |
|
| 350 | 366 | fn expectIntMismatch(program: *[u8], expected: super::Type) |
|
| 351 | 367 | throws (testing::TestError) |
|
| 352 | 368 | { |
|
| 353 | 369 | let mut a = testResolver(); |
| 367 | 383 | throw testing::TestError::Failed; |
|
| 368 | 384 | } |
|
| 369 | 385 | return body.statements[index]; |
|
| 370 | 386 | } |
|
| 371 | 387 | ||
| 388 | + | /// Retrieve a declared function body by its statement index. |
|
| 389 | + | fn getFnDeclBody(root: *ast::Node, index: u32) -> ast::Block |
|
| 390 | + | throws (testing::TestError) |
|
| 391 | + | { |
|
| 392 | + | let stmt = try getBlockStmt(root, index); |
|
| 393 | + | let case ast::NodeValue::FnDecl(decl) = stmt.value |
|
| 394 | + | else throw testing::TestError::Failed; |
|
| 395 | + | let body = decl.body |
|
| 396 | + | else throw testing::TestError::Failed; |
|
| 397 | + | let case ast::NodeValue::Block(block) = body.value |
|
| 398 | + | else throw testing::TestError::Failed; |
|
| 399 | + | return block; |
|
| 400 | + | } |
|
| 401 | + | ||
| 372 | 402 | /// Retrieve a function body block by function name from the program scope. |
|
| 373 | 403 | fn getFnBody(a: *super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block |
|
| 374 | 404 | throws (testing::TestError) |
|
| 375 | 405 | { |
|
| 376 | 406 | let scope = super::scopeFor(a, root) |
| 513 | 543 | try expectType(&a, result.root, super::Type::Bool); |
|
| 514 | 544 | } |
|
| 515 | 545 | ||
| 516 | 546 | @test fn testResolveStringLiteralType() throws (testing::TestError) { |
|
| 517 | 547 | let mut a = testResolver(); |
|
| 518 | - | let result = try resolveExprStr(&mut a, "\"hello\""); |
|
| 548 | + | let result = try resolveBlockStr(&mut a, "panic \"hello\";"); |
|
| 519 | 549 | ||
| 520 | 550 | try expectNoErrors(&result); |
|
| 521 | - | let ty = try typeOf(&a, result.root); |
|
| 551 | + | let stmt = try getBlockStmt(result.root, 0); |
|
| 552 | + | let case ast::NodeValue::Panic { message } = stmt.value |
|
| 553 | + | else throw testing::TestError::Failed; |
|
| 554 | + | let literal = message |
|
| 555 | + | else throw testing::TestError::Failed; |
|
| 556 | + | let ty = try typeOf(&a, literal); |
|
| 522 | 557 | let elemTy = try expectSliceType(ty, false); |
|
| 523 | 558 | try testing::expect(elemTy == super::Type::U8); |
|
| 524 | 559 | } |
|
| 525 | 560 | ||
| 526 | 561 | @test fn testResolveAsNumeric() throws (testing::TestError) { |
| 661 | 696 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 662 | 697 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 663 | 698 | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern)); |
|
| 664 | 699 | } |
|
| 665 | 700 | ||
| 701 | + | /// The canonical unsafe ecall intrinsic declaration is accepted. |
|
| 702 | + | @test fn testResolveEcallIntrinsicCanonicalSignature() throws (testing::TestError) { |
|
| 703 | + | let mut a = testResolver(); |
|
| 704 | + | let program = "@intrinsic unsafe fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64;"; |
|
| 705 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 706 | + | try expectNoErrors(&result); |
|
| 707 | + | } |
|
| 708 | + | ||
| 709 | + | /// The ecall intrinsic cannot be exposed as a safe function. |
|
| 710 | + | @test fn testResolveEcallIntrinsicRequiresUnsafe() throws (testing::TestError) { |
|
| 711 | + | let mut a = testResolver(); |
|
| 712 | + | let program = "@intrinsic fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64;"; |
|
| 713 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 714 | + | try expectErrorKind( |
|
| 715 | + | &result, |
|
| 716 | + | super::ErrorKind::InvalidEcallIntrinsicSignature, |
|
| 717 | + | ); |
|
| 718 | + | } |
|
| 719 | + | ||
| 720 | + | /// The ecall intrinsic declaration must use its canonical ABI. |
|
| 721 | + | @test fn testResolveEcallIntrinsicRequiresCanonicalAbi() throws (testing::TestError) { |
|
| 722 | + | { |
|
| 723 | + | let mut a = testResolver(); |
|
| 724 | + | let program = "@intrinsic unsafe fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64) -> i64;"; |
|
| 725 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 726 | + | try expectErrorKind( |
|
| 727 | + | &result, |
|
| 728 | + | super::ErrorKind::InvalidEcallIntrinsicSignature, |
|
| 729 | + | ); |
|
| 730 | + | } { |
|
| 731 | + | let mut a = testResolver(); |
|
| 732 | + | let program = "@intrinsic unsafe fn ecall(number: u32, arg1: u64, arg2: i64, arg3: i64, arg4: i64) -> i64;"; |
|
| 733 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 734 | + | try expectErrorKind( |
|
| 735 | + | &result, |
|
| 736 | + | super::ErrorKind::InvalidEcallIntrinsicSignature, |
|
| 737 | + | ); |
|
| 738 | + | } { |
|
| 739 | + | let mut a = testResolver(); |
|
| 740 | + | let program = "@intrinsic unsafe fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i32;"; |
|
| 741 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 742 | + | try expectErrorKind( |
|
| 743 | + | &result, |
|
| 744 | + | super::ErrorKind::InvalidEcallIntrinsicSignature, |
|
| 745 | + | ); |
|
| 746 | + | } |
|
| 747 | + | } |
|
| 748 | + | ||
| 666 | 749 | @test fn testSymbolStoresRecordAttributes() throws (testing::TestError) { |
|
| 667 | 750 | let mut a = testResolver(); |
|
| 668 | 751 | let program = "export record S { value: i32 }"; |
|
| 669 | 752 | let result = try resolveProgramStr(&mut a, program); |
|
| 670 | 753 | try expectNoErrors(&result); |
| 843 | 926 | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 844 | 927 | } |
|
| 845 | 928 | ||
| 846 | 929 | @test fn testResolveSliceIndex() throws (testing::TestError) { |
|
| 847 | 930 | let mut a = testResolver(); |
|
| 848 | - | let program = "let xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];"; |
|
| 931 | + | let program = "fn run(slice: &[i32]) { slice[1]; }"; |
|
| 849 | 932 | let result = try resolveProgramStr(&mut a, program); |
|
| 850 | 933 | try expectNoErrors(&result); |
|
| 851 | 934 | ||
| 852 | - | let sliceStmt = try getBlockStmt(result.root, 1); |
|
| 853 | - | let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value |
|
| 854 | - | else throw testing::TestError::Failed; |
|
| 855 | - | let sliceTy = try typeOf(&a, sliceDecl.value); |
|
| 856 | - | let elemTy = try expectSliceType(sliceTy, false); |
|
| 857 | - | try testing::expect(elemTy == super::Type::I32); |
|
| 858 | - | ||
| 859 | - | let indexStmt = try getBlockStmt(result.root, 2); |
|
| 860 | - | try expectExprStmtType(&a, indexStmt, super::Type::I32); |
|
| 935 | + | let body = try getFnDeclBody(result.root, 0); |
|
| 936 | + | try testing::expect(body.statements.len == 1); |
|
| 937 | + | try expectExprStmtType(&a, body.statements[0], super::Type::I32); |
|
| 861 | 938 | } |
|
| 862 | 939 | ||
| 863 | 940 | @test fn testResolveSliceFields() throws (testing::TestError) { |
|
| 864 | 941 | let mut a = testResolver(); |
|
| 865 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; slice.ptr;"; |
|
| 942 | + | let program = "fn run(slice: &[i32]) { slice.len; slice.ptr; }"; |
|
| 866 | 943 | let result = try resolveProgramStr(&mut a, program); |
|
| 867 | 944 | try expectNoErrors(&result); |
|
| 868 | 945 | ||
| 869 | - | let lenStmt = try getBlockStmt(result.root, 2); |
|
| 870 | - | let case ast::NodeValue::ExprStmt(lenExpr) = lenStmt.value |
|
| 946 | + | let body = try getFnDeclBody(result.root, 0); |
|
| 947 | + | try testing::expect(body.statements.len == 2); |
|
| 948 | + | let case ast::NodeValue::ExprStmt(lenExpr) = body.statements[0].value |
|
| 871 | 949 | else throw testing::TestError::Failed; |
|
| 872 | 950 | let lenTy = try typeOf(&a, lenExpr); |
|
| 873 | 951 | try testing::expect(lenTy == super::Type::U32); |
|
| 874 | 952 | ||
| 875 | - | let ptrStmt = try getBlockStmt(result.root, 3); |
|
| 876 | - | let case ast::NodeValue::ExprStmt(ptrExpr) = ptrStmt.value |
|
| 953 | + | let case ast::NodeValue::ExprStmt(ptrExpr) = body.statements[1].value |
|
| 877 | 954 | else throw testing::TestError::Failed; |
|
| 878 | 955 | let ptrTy = try typeOf(&a, ptrExpr); |
|
| 879 | - | let targetTy = try expectPointerType(ptrTy, false); |
|
| 880 | - | try testing::expect(targetTy == super::Type::I32); |
|
| 956 | + | let case super::Type::Pointer { class, target, mutable } = ptrTy |
|
| 957 | + | else throw testing::TestError::Failed; |
|
| 958 | + | try testing::expect(class == types::PointerClass::Ref); |
|
| 959 | + | try testing::expect(not mutable); |
|
| 960 | + | try testing::expect(*target == super::Type::I32); |
|
| 881 | 961 | } |
|
| 882 | 962 | ||
| 883 | 963 | @test fn testResolveSliceLiteralImmutable() throws (testing::TestError) { |
|
| 884 | 964 | let mut a = testResolver(); |
|
| 885 | - | let program = "let slice: *[i32] = &[1, 2, 3];"; |
|
| 965 | + | let program = "unsafe fn run() { let slice: *[i32] = &[1, 2, 3]; }"; |
|
| 886 | 966 | let result = try resolveProgramStr(&mut a, program); |
|
| 887 | 967 | try expectNoErrors(&result); |
|
| 888 | 968 | } |
|
| 889 | 969 | ||
| 890 | 970 | /// Empty array literal infers element type from slice annotation. |
|
| 891 | 971 | @test fn testResolveSliceLiteralEmpty() throws (testing::TestError) { |
|
| 892 | 972 | let mut a = testResolver(); |
|
| 893 | - | let program = "let slice: *[i32] = &[];"; |
|
| 973 | + | let program = "unsafe fn run() { let slice: *[i32] = &[]; }"; |
|
| 894 | 974 | let result = try resolveProgramStr(&mut a, program); |
|
| 895 | 975 | try expectNoErrors(&result); |
|
| 896 | 976 | } |
|
| 897 | 977 | ||
| 898 | 978 | /// Nested array literal should infer inner element type from slice annotation. |
|
| 899 | 979 | @test fn testResolveSliceLiteralNestedArray() throws (testing::TestError) { |
|
| 900 | 980 | let mut a = testResolver(); |
|
| 901 | - | let program = "let slice: *[[i32; 2]] = &[[1, 2], [3, 4]];"; |
|
| 981 | + | let program = "unsafe fn run() { let slice: *[[i32; 2]] = &[[1, 2], [3, 4]]; }"; |
|
| 902 | 982 | let result = try resolveProgramStr(&mut a, program); |
|
| 903 | 983 | try expectNoErrors(&result); |
|
| 904 | 984 | } |
|
| 905 | 985 | ||
| 906 | 986 | @test fn testResolveSliceFromArray() throws (testing::TestError) { |
|
| 907 | 987 | { |
|
| 908 | 988 | let mut a = testResolver(); |
|
| 909 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];"; |
|
| 989 | + | let program = "unsafe fn run() { let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..]; }"; |
|
| 910 | 990 | let result = try resolveProgramStr(&mut a, program); |
|
| 911 | 991 | try expectNoErrors(&result); |
|
| 912 | 992 | } { |
|
| 913 | 993 | let mut a = testResolver(); |
|
| 914 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];"; |
|
| 994 | + | let program = "unsafe fn run() { let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3]; }"; |
|
| 915 | 995 | let result = try resolveProgramStr(&mut a, program); |
|
| 916 | 996 | try expectNoErrors(&result); |
|
| 917 | 997 | } { |
|
| 918 | 998 | let mut a = testResolver(); |
|
| 919 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];"; |
|
| 999 | + | let program = "unsafe fn run() { let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3]; }"; |
|
| 920 | 1000 | let result = try resolveProgramStr(&mut a, program); |
|
| 921 | 1001 | try expectNoErrors(&result); |
|
| 922 | 1002 | } { |
|
| 923 | 1003 | let mut a = testResolver(); |
|
| 924 | - | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];"; |
|
| 1004 | + | let program = "unsafe fn run() { let xs: [u8; 2] = [1, 2]; let slice = &xs[1..1]; }"; |
|
| 925 | 1005 | let result = try resolveProgramStr(&mut a, program); |
|
| 926 | 1006 | try expectNoErrors(&result); |
|
| 927 | 1007 | } |
|
| 928 | 1008 | } |
|
| 929 | 1009 |
| 936 | 1016 | else throw testing::TestError::Failed; |
|
| 937 | 1017 | } |
|
| 938 | 1018 | ||
| 939 | 1019 | @test fn testResolveSliceLiteralMutable() throws (testing::TestError) { |
|
| 940 | 1020 | let mut a = testResolver(); |
|
| 941 | - | let program = "let slice: *mut [i32] = &mut [1, 2, 3];"; |
|
| 1021 | + | let program = "unsafe fn run() { let slice: *mut [i32] = &mut [1, 2, 3]; }"; |
|
| 942 | 1022 | let result = try resolveProgramStr(&mut a, program); |
|
| 943 | 1023 | try expectNoErrors(&result); |
|
| 944 | 1024 | } |
|
| 945 | 1025 | ||
| 946 | 1026 | @test fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) { |
| 952 | 1032 | else throw testing::TestError::Failed; |
|
| 953 | 1033 | } |
|
| 954 | 1034 | ||
| 955 | 1035 | @test fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) { |
|
| 956 | 1036 | let mut a = testResolver(); |
|
| 957 | - | let program = "let mut x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;"; |
|
| 1037 | + | let program = "fn f(mptr: *mut i32) -> *i32 { return mptr; }"; |
|
| 958 | 1038 | let result = try resolveProgramStr(&mut a, program); |
|
| 959 | 1039 | try expectNoErrors(&result); |
|
| 960 | 1040 | } |
|
| 961 | 1041 | ||
| 962 | 1042 | @test fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) { |
|
| 963 | 1043 | { |
|
| 964 | 1044 | let mut a = testResolver(); |
|
| 965 | - | let program = "let x: i32 = 0; let ptr = &mut x;"; |
|
| 1045 | + | let program = "fn borrow(ptr: &mut i32) {} fn run() { let x: i32 = 0; borrow(&mut x); }"; |
|
| 966 | 1046 | let result = try resolveProgramStr(&mut a, program); |
|
| 967 | 1047 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 968 | 1048 | } { |
|
| 969 | 1049 | let mut a = testResolver(); |
|
| 970 | - | let program = "let mut x: i32 = 0; let ptr = &mut x;"; |
|
| 1050 | + | let program = "fn borrow(ptr: &mut i32) {} fn run() { let mut x: i32 = 0; borrow(&mut x); }"; |
|
| 971 | 1051 | let result = try resolveProgramStr(&mut a, program); |
|
| 972 | 1052 | try expectNoErrors(&result); |
|
| 973 | 1053 | } |
|
| 974 | 1054 | } |
|
| 975 | 1055 | ||
| 976 | 1056 | @test fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) { |
|
| 977 | 1057 | { |
|
| 978 | 1058 | let mut a = testResolver(); |
|
| 979 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];"; |
|
| 1059 | + | let program = "fn borrow(slice: &mut [i32]) {} fn run() { let xs: [i32; 3] = [1, 2, 3]; borrow(&mut xs[..]); }"; |
|
| 980 | 1060 | let result = try resolveProgramStr(&mut a, program); |
|
| 981 | 1061 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 982 | 1062 | } { |
|
| 983 | 1063 | let mut a = testResolver(); |
|
| 984 | - | let program = "let mut xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];"; |
|
| 1064 | + | let program = "fn borrow(slice: &mut [i32]) {} fn run() { let mut xs: [i32; 3] = [1, 2, 3]; borrow(&mut xs[..]); }"; |
|
| 985 | 1065 | let result = try resolveProgramStr(&mut a, program); |
|
| 986 | 1066 | try expectNoErrors(&result); |
|
| 987 | 1067 | } |
|
| 988 | 1068 | } |
|
| 989 | 1069 |
| 1804 | 1884 | let result = try resolveProgramStr(&mut a, program); |
|
| 1805 | 1885 | try expectNoErrors(&result); |
|
| 1806 | 1886 | } |
|
| 1807 | 1887 | { |
|
| 1808 | 1888 | let mut a = testResolver(); |
|
| 1809 | - | let program = "let mut xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;"; |
|
| 1889 | + | let program = "fn assign(slice: &mut [u8]) { set slice[0] = 1; } fn run() { let mut xs: [u8; 2] = [0, 1]; assign(&mut xs[..]); }"; |
|
| 1810 | 1890 | let result = try resolveProgramStr(&mut a, program); |
|
| 1811 | 1891 | try expectNoErrors(&result); |
|
| 1812 | 1892 | } |
|
| 1813 | 1893 | { |
|
| 1814 | 1894 | let mut a = testResolver(); |
|
| 1815 | - | let program = "let mut xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 1895 | + | let program = "fn f(input: *[u8]) { let mut slice: *[u8] = input; set slice[0] = 1; }"; |
|
| 1816 | 1896 | let result = try resolveProgramStr(&mut a, program); |
|
| 1817 | 1897 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1818 | 1898 | } |
|
| 1819 | 1899 | { |
|
| 1820 | 1900 | let mut a = testResolver(); |
| 1822 | 1902 | let result = try resolveProgramStr(&mut a, program); |
|
| 1823 | 1903 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1824 | 1904 | } |
|
| 1825 | 1905 | { |
|
| 1826 | 1906 | let mut a = testResolver(); |
|
| 1827 | - | let program = "let mut xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 1907 | + | let program = "fn f(slice: &[u8]) { set slice[0] = 1; }"; |
|
| 1828 | 1908 | let result = try resolveProgramStr(&mut a, program); |
|
| 1829 | 1909 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1830 | 1910 | } |
|
| 1831 | 1911 | } |
|
| 1832 | 1912 |
| 2146 | 2226 | } |
|
| 2147 | 2227 | ||
| 2148 | 2228 | @test fn testUndefinedCoercions() throws (testing::TestError) { |
|
| 2149 | 2229 | { |
|
| 2150 | 2230 | let mut a = testResolver(); |
|
| 2151 | - | let result = try resolveBlockStr(&mut a, "let count: i32 = undefined;"); |
|
| 2231 | + | let result = try resolveBlockStr(&mut a, "unsafe { let count: i32 = undefined; }"); |
|
| 2152 | 2232 | try expectNoErrors(&result); |
|
| 2153 | 2233 | } { |
|
| 2154 | 2234 | let mut a = testResolver(); |
|
| 2155 | - | let program = "let mut value: i32 = 0; set value = undefined;"; |
|
| 2235 | + | let program = "let mut value: i32 = 0; unsafe { set value = undefined; }"; |
|
| 2156 | 2236 | let result = try resolveProgramStr(&mut a, program); |
|
| 2157 | 2237 | try expectNoErrors(&result); |
|
| 2158 | 2238 | } { |
|
| 2159 | 2239 | let mut a = testResolver(); |
|
| 2160 | - | let program = "fn f(x: i32) {} fn g() { f(undefined); }"; |
|
| 2240 | + | let program = "fn f(x: i32) {} fn g() { unsafe { f(undefined); } }"; |
|
| 2161 | 2241 | let result = try resolveProgramStr(&mut a, program); |
|
| 2162 | 2242 | try expectNoErrors(&result); |
|
| 2163 | 2243 | } { |
|
| 2164 | 2244 | let mut a = testResolver(); |
|
| 2165 | - | let program = "fn fetch() -> i32 { return undefined; }"; |
|
| 2245 | + | let program = "fn fetch() -> i32 { unsafe { return undefined; } }"; |
|
| 2166 | 2246 | let result = try resolveProgramStr(&mut a, program); |
|
| 2167 | 2247 | try expectNoErrors(&result); |
|
| 2168 | 2248 | } |
|
| 2169 | 2249 | } |
|
| 2170 | 2250 |
| 2739 | 2819 | // Dereference tests ////////////////////////////////////////////////////////// |
|
| 2740 | 2820 | ||
| 2741 | 2821 | @test fn testResolveDeref() throws (testing::TestError) { |
|
| 2742 | 2822 | { |
|
| 2743 | 2823 | let mut a = testResolver(); |
|
| 2744 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 42; let ptr: *i32 = &x; *ptr;"); |
|
| 2824 | + | let result = try resolveProgramStr(&mut a, "fn f(ptr: &i32) { *ptr; }"); |
|
| 2745 | 2825 | try expectNoErrors(&result); |
|
| 2746 | - | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2826 | + | let body = try getFnBody(&a, result.root, "f"); |
|
| 2827 | + | if body.statements.len <> 1 { |
|
| 2828 | + | throw testing::TestError::Failed; |
|
| 2829 | + | } |
|
| 2830 | + | let stmt = body.statements[0]; |
|
| 2747 | 2831 | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2748 | 2832 | } { |
|
| 2749 | 2833 | let mut a = testResolver(); |
|
| 2750 | 2834 | let result = try resolveExprStr(&mut a, "*42"); |
|
| 2751 | 2835 | try expectErrorKind(&result, super::ErrorKind::ExpectedPointer); |
| 2757 | 2841 | } |
|
| 2758 | 2842 | ||
| 2759 | 2843 | @test fn testResolveAssignDeref() throws (testing::TestError) { |
|
| 2760 | 2844 | { |
|
| 2761 | 2845 | let mut a = testResolver(); |
|
| 2762 | - | let program = "let mut x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;"; |
|
| 2846 | + | let program = "fn f(ptr: &mut i32) { set *ptr = 42; }"; |
|
| 2763 | 2847 | let result = try resolveProgramStr(&mut a, program); |
|
| 2764 | 2848 | try expectNoErrors(&result); |
|
| 2765 | 2849 | } { |
|
| 2766 | 2850 | let mut a = testResolver(); |
|
| 2767 | - | let program = "let mut x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;"; |
|
| 2851 | + | let program = "fn f(ptr: &i32) { set *ptr = 42; }"; |
|
| 2768 | 2852 | let result = try resolveProgramStr(&mut a, program); |
|
| 2769 | 2853 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2770 | 2854 | } { |
|
| 2771 | 2855 | let mut a = testResolver(); |
|
| 2772 | - | let program = "let mut x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;"; |
|
| 2856 | + | let program = "fn f(input: *i32) { let mut ptr: *i32 = input; set *ptr = 42; }"; |
|
| 2773 | 2857 | let result = try resolveProgramStr(&mut a, program); |
|
| 2774 | 2858 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2775 | 2859 | } { |
|
| 2776 | 2860 | let mut a = testResolver(); |
|
| 2777 | - | let program = "let mut x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;"; |
|
| 2861 | + | let program = "fn f(ptr: &mut u8) { set *ptr = 255; }"; |
|
| 2778 | 2862 | let result = try resolveProgramStr(&mut a, program); |
|
| 2779 | 2863 | try expectNoErrors(&result); |
|
| 2780 | 2864 | } |
|
| 2781 | 2865 | } |
|
| 2782 | 2866 |
| 3440 | 3524 | } |
|
| 3441 | 3525 | ||
| 3442 | 3526 | /// Test that mutable pointer parameters can be borrowed mutably. |
|
| 3443 | 3527 | @test fn testMutableBorrowFromMutablePointer() throws (testing::TestError) { |
|
| 3444 | 3528 | let mut a = testResolver(); |
|
| 3445 | - | let program = "fn f(p: *mut i32) { let x: *mut i32 = &mut *p; }"; |
|
| 3529 | + | let program = "fn borrow(value: &mut i32) {} fn f(p: &mut i32) { borrow(&mut *p); }"; |
|
| 3446 | 3530 | let result = try resolveProgramStr(&mut a, program); |
|
| 3447 | 3531 | try expectNoErrors(&result); |
|
| 3448 | 3532 | } |
|
| 3449 | 3533 | ||
| 3450 | 3534 | /// Test that mutable slice parameters can be borrowed mutably. |
|
| 3451 | 3535 | @test fn testMutableBorrowFromMutableSlice() throws (testing::TestError) { |
|
| 3452 | 3536 | let mut a = testResolver(); |
|
| 3453 | - | let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }"; |
|
| 3537 | + | let program = "fn borrow(value: &mut i32) {} fn f(s: &mut [i32]) { borrow(&mut s[0]); }"; |
|
| 3454 | 3538 | let result = try resolveProgramStr(&mut a, program); |
|
| 3455 | 3539 | try expectNoErrors(&result); |
|
| 3456 | 3540 | } |
|
| 3457 | 3541 | ||
| 3458 | 3542 | /// Test borrowing mutably from a field access on a call returning `*mut`. |
|
| 3459 | 3543 | @test fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) { |
|
| 3460 | 3544 | let mut a = testResolver(); |
|
| 3461 | - | let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { let mut b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }"; |
|
| 3545 | + | let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } unsafe fn f() -> *mut i32 { let mut b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; return px; }"; |
|
| 3462 | 3546 | let result = try resolveProgramStr(&mut a, program); |
|
| 3463 | 3547 | try expectNoErrors(&result); |
|
| 3464 | 3548 | } |
|
| 3465 | 3549 | ||
| 3466 | 3550 | /// Test that calls returning immutable pointers cannot be mutably borrowed. |
|
| 3467 | 3551 | @test fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) { |
|
| 3468 | 3552 | let mut a = testResolver(); |
|
| 3469 | - | let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { let b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }"; |
|
| 3553 | + | let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn borrow(px: &mut i32) {} fn f(b: *Box) { borrow(&mut idBox(b).x); }"; |
|
| 3470 | 3554 | let result = try resolveProgramStr(&mut a, program); |
|
| 3471 | 3555 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3472 | 3556 | } |
|
| 3473 | 3557 | ||
| 3474 | 3558 | /// Test borrowing mutably from a public static through scope access. |
| 3476 | 3560 | let mut a = testResolver(); |
|
| 3477 | 3561 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3478 | 3562 | ||
| 3479 | 3563 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 3480 | 3564 | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena); |
|
| 3481 | - | 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); |
|
| 3565 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn assign(p: &mut i32) { set *p = 7; } fn main() { assign(&mut statics::COUNTER); }", &mut arena); |
|
| 3482 | 3566 | ||
| 3483 | 3567 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3484 | 3568 | try expectNoErrors(&result); |
|
| 3485 | 3569 | } |
|
| 3486 | 3570 |
| 3498 | 3582 | } |
|
| 3499 | 3583 | ||
| 3500 | 3584 | /// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer. |
|
| 3501 | 3585 | @test fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) { |
|
| 3502 | 3586 | let mut a = testResolver(); |
|
| 3503 | - | let program = "fn f() { let mut x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }"; |
|
| 3587 | + | let program = "fn borrow(value: &mut i32) {} fn f(input: *i32) { let mut p: *i32 = input; borrow(&mut *p); }"; |
|
| 3504 | 3588 | let result = try resolveProgramStr(&mut a, program); |
|
| 3505 | 3589 | let err = try expectError(&result); |
|
| 3506 | 3590 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3507 | 3591 | } |
|
| 3508 | 3592 |
| 3520 | 3604 | /// Test that mutable slice parameters can be assigned through index. |
|
| 3521 | 3605 | @test fn testAssignThroughMutableSliceParam() throws (testing::TestError) { |
|
| 3522 | 3606 | { |
|
| 3523 | 3607 | // Mutable slice param: direct assignment should work |
|
| 3524 | 3608 | let mut a = testResolver(); |
|
| 3525 | - | let program = "fn f(slice: *mut [i32]) { set slice[0] = 1; }"; |
|
| 3609 | + | let program = "fn f(slice: &mut [i32]) { set slice[0] = 1; }"; |
|
| 3526 | 3610 | let result = try resolveProgramStr(&mut a, program); |
|
| 3527 | 3611 | try expectNoErrors(&result); |
|
| 3528 | 3612 | } { |
|
| 3529 | 3613 | // Immutable slice param: direct assignment should fail |
|
| 3530 | 3614 | let mut a = testResolver(); |
|
| 3531 | - | let program = "fn f(slice: *[i32]) { set slice[0] = 1; }"; |
|
| 3615 | + | let program = "fn f(slice: &[i32]) { set slice[0] = 1; }"; |
|
| 3532 | 3616 | let result = try resolveProgramStr(&mut a, program); |
|
| 3533 | 3617 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3534 | 3618 | } |
|
| 3535 | 3619 | } |
|
| 3536 | 3620 |
| 3616 | 3700 | } |
|
| 3617 | 3701 | ||
| 3618 | 3702 | /// Test that record fields can be assigned through a mutable pointer. |
|
| 3619 | 3703 | @test fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) { |
|
| 3620 | 3704 | let mut a = testResolver(); |
|
| 3621 | - | let program = "record S { x: i32 } fn f(p: *mut S) { set p.x = 2; }"; |
|
| 3705 | + | let program = "record S { x: i32 } fn f(p: &mut S) { set p.x = 2; }"; |
|
| 3622 | 3706 | let result = try resolveProgramStr(&mut a, program); |
|
| 3623 | 3707 | try expectNoErrors(&result); |
|
| 3624 | 3708 | } |
|
| 3625 | 3709 | ||
| 3626 | 3710 | /// Test that record fields cannot be assigned through an immutable pointer. |
| 3635 | 3719 | // Opaque pointer tests. |
|
| 3636 | 3720 | ||
| 3637 | 3721 | /// You can assign any pointer (*T) to an opaque pointer (*opaque) without a cast. |
|
| 3638 | 3722 | @test fn testOpaquePointerAutoCoercion() throws (testing::TestError) { |
|
| 3639 | 3723 | let mut a = testResolver(); |
|
| 3640 | - | let result = try resolveProgramStr(&mut a, "fn f(x: i32) { let mut ptr: *i32 = &x; let o: *opaque = ptr; set ptr = o as *i32; }"); |
|
| 3724 | + | let result = try resolveProgramStr(&mut a, "fn f(ptr: *i32) -> *opaque { return ptr; }"); |
|
| 3641 | 3725 | try expectNoErrors(&result); |
|
| 3642 | 3726 | } |
|
| 3643 | 3727 | ||
| 3644 | 3728 | /// You cannot assign an opaque pointer to a non-opaque pointer without a cast. |
|
| 3645 | 3729 | @test fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) { |
|
| 3646 | 3730 | let mut a = testResolver(); |
|
| 3647 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let ptr: *i32 = o; }"); |
|
| 3731 | + | let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let ptr: *i32 = o; }"); |
|
| 3648 | 3732 | let err = try expectError(&result); |
|
| 3649 | 3733 | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
|
| 3650 | 3734 | else throw testing::TestError::Failed; |
|
| 3651 | 3735 | let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected |
|
| 3652 | 3736 | else throw testing::TestError::Failed; |
| 3664 | 3748 | let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}"); |
|
| 3665 | 3749 | let err = try expectError(&result); |
|
| 3666 | 3750 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 3667 | 3751 | } { |
|
| 3668 | 3752 | let mut a = testResolver(); |
|
| 3669 | - | let result = try resolveProgramStr(&mut a, "fn f() { let x: opaque = undefined; }"); |
|
| 3753 | + | let result = try resolveProgramStr(&mut a, "fn f() { unsafe { let x: opaque = undefined; } }"); |
|
| 3670 | 3754 | let err = try expectError(&result); |
|
| 3671 | 3755 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 3672 | 3756 | } { |
|
| 3673 | 3757 | let mut a = testResolver(); |
|
| 3674 | 3758 | let result = try resolveProgramStr(&mut a, "record R { x: opaque }"); |
| 3678 | 3762 | } |
|
| 3679 | 3763 | ||
| 3680 | 3764 | /// You cannot dereference an opaque pointer, you have to cast it first. |
|
| 3681 | 3765 | @test fn testOpaquePointerNoDereference() throws (testing::TestError) { |
|
| 3682 | 3766 | let mut a = testResolver(); |
|
| 3683 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = *o; }"); |
|
| 3767 | + | let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = *o; }"); |
|
| 3684 | 3768 | let err = try expectError(&result); |
|
| 3685 | 3769 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref); |
|
| 3686 | 3770 | } |
|
| 3687 | 3771 | ||
| 3688 | 3772 | /// Test that you can dereference after casting. |
|
| 3689 | 3773 | @test fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) { |
|
| 3690 | 3774 | let mut a = testResolver(); |
|
| 3691 | - | let result = try resolveProgramStr(&mut a, "fn f() { let o: *opaque = undefined; let x = *(o as *i32); }"); |
|
| 3775 | + | let result = try resolveProgramStr(&mut a, "unsafe fn f(o: *opaque) -> *i32 { let ptr = o as *i32; let x = *ptr; return ptr; }"); |
|
| 3692 | 3776 | try expectNoErrors(&result); |
|
| 3693 | 3777 | } |
|
| 3694 | 3778 | ||
| 3695 | 3779 | /// You cannot do pointer arithmetic with an opaque pointer. |
|
| 3696 | 3780 | @test fn testOpaquePointerNoArithmetic() throws (testing::TestError) { |
|
| 3697 | 3781 | { |
|
| 3698 | 3782 | let mut a = testResolver(); |
|
| 3699 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = o + 1; }"); |
|
| 3783 | + | let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = o + 1; }"); |
|
| 3700 | 3784 | let err = try expectError(&result); |
|
| 3701 | 3785 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 3702 | 3786 | } { |
|
| 3703 | 3787 | let mut a = testResolver(); |
|
| 3704 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = 1 + o; }"); |
|
| 3788 | + | let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = 1 + o; }"); |
|
| 3705 | 3789 | let err = try expectError(&result); |
|
| 3706 | 3790 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 3707 | 3791 | } { |
|
| 3708 | 3792 | let mut a = testResolver(); |
|
| 3709 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = o - 1; }"); |
|
| 3793 | + | let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = o - 1; }"); |
|
| 3710 | 3794 | let err = try expectError(&result); |
|
| 3711 | 3795 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 3712 | 3796 | } { |
|
| 3713 | 3797 | let mut a = testResolver(); |
|
| 3714 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = 1 - o; }"); |
|
| 3798 | + | let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = 1 - o; }"); |
|
| 3715 | 3799 | let err = try expectError(&result); |
|
| 3716 | 3800 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 3717 | 3801 | } |
|
| 3718 | 3802 | } |
|
| 3719 | 3803 |
| 3822 | 3906 | /// enabling compile-time evaluation of array sizes using imported constants. |
|
| 3823 | 3907 | @test fn testImportedConstantInArraySize() throws (testing::TestError) { |
|
| 3824 | 3908 | let mut a = testResolver(); |
|
| 3825 | 3909 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3826 | 3910 | ||
| 3827 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3911 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; unsafe mod app;", &mut arena); |
|
| 3828 | 3912 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena); |
|
| 3829 | 3913 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = undefined;", &mut arena); |
|
| 3830 | 3914 | ||
| 3831 | 3915 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3832 | 3916 | try expectNoErrors(&result); |
| 4129 | 4213 | /// Test @sliceOf with correct arguments succeeds. |
|
| 4130 | 4214 | @test fn testResolveSliceOfCorrect() throws (testing::TestError) { |
|
| 4131 | 4215 | // Immutable pointer. |
|
| 4132 | 4216 | { |
|
| 4133 | 4217 | let mut a = testResolver(); |
|
| 4134 | - | let program = "fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4218 | + | let program = "fn f(ptr: *u8, len: u32) -> *[u8] { unsafe { return @sliceOf(ptr, len); } }"; |
|
| 4135 | 4219 | let result = try resolveProgramStr(&mut a, program); |
|
| 4136 | 4220 | try expectNoErrors(&result); |
|
| 4137 | 4221 | } |
|
| 4138 | 4222 | // Mutable pointer produces mutable slice. |
|
| 4139 | 4223 | { |
|
| 4140 | 4224 | let mut a = testResolver(); |
|
| 4141 | - | let program = "fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }"; |
|
| 4225 | + | let program = "fn f(ptr: *mut u8, len: u32) -> *mut [u8] { unsafe { return @sliceOf(ptr, len); } }"; |
|
| 4142 | 4226 | let result = try resolveProgramStr(&mut a, program); |
|
| 4143 | 4227 | try expectNoErrors(&result); |
|
| 4144 | 4228 | } |
|
| 4145 | 4229 | } |
|
| 4146 | 4230 |
| 4184 | 4268 | /// Test @sliceOf with wrong argument types produces errors. |
|
| 4185 | 4269 | @test fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) { |
|
| 4186 | 4270 | // Non-pointer first argument. |
|
| 4187 | 4271 | { |
|
| 4188 | 4272 | let mut a = testResolver(); |
|
| 4189 | - | let program = "fn f(val: u32, len: u32) -> *[u8] { return @sliceOf(val, len); }"; |
|
| 4273 | + | let program = "fn f(val: u32, len: u32) -> *[u8] { return unsafe { @sliceOf(val, len) }; }"; |
|
| 4190 | 4274 | let result = try resolveProgramStr(&mut a, program); |
|
| 4191 | 4275 | let err = try expectError(&result); |
|
| 4192 | 4276 | let case super::ErrorKind::ExpectedPointer = err.kind |
|
| 4193 | 4277 | else throw testing::TestError::Failed; |
|
| 4194 | 4278 | } |
|
| 4195 | 4279 | // Array instead of pointer. |
|
| 4196 | 4280 | { |
|
| 4197 | 4281 | let mut a = testResolver(); |
|
| 4198 | - | let program = "fn f(arr: [u8; 4], len: u32) -> *[u8] { return @sliceOf(arr, len); }"; |
|
| 4282 | + | let program = "fn f(arr: [u8; 4], len: u32) -> *[u8] { return unsafe { @sliceOf(arr, len) }; }"; |
|
| 4199 | 4283 | let result = try resolveProgramStr(&mut a, program); |
|
| 4200 | 4284 | let err = try expectError(&result); |
|
| 4201 | 4285 | let case super::ErrorKind::ExpectedPointer = err.kind |
|
| 4202 | 4286 | else throw testing::TestError::Failed; |
|
| 4203 | 4287 | } |
|
| 4204 | 4288 | // Non-numeric second argument. |
|
| 4205 | 4289 | { |
|
| 4206 | 4290 | let mut a = testResolver(); |
|
| 4207 | - | let program = "fn f(ptr: *u8, len: bool) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4291 | + | let program = "fn f(ptr: *u8, len: bool) -> *[u8] { return unsafe { @sliceOf(ptr, len) }; }"; |
|
| 4208 | 4292 | let result = try resolveProgramStr(&mut a, program); |
|
| 4209 | 4293 | let err = try expectError(&result); |
|
| 4210 | 4294 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4211 | 4295 | else throw testing::TestError::Failed; |
|
| 4212 | 4296 | } |
|
| 4213 | 4297 | // Pointer second argument. |
|
| 4214 | 4298 | { |
|
| 4215 | 4299 | let mut a = testResolver(); |
|
| 4216 | - | let program = "fn f(ptr: *u8, len: *u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4300 | + | let program = "fn f(ptr: *u8, len: *u32) -> *[u8] { return unsafe { @sliceOf(ptr, len) }; }"; |
|
| 4217 | 4301 | let result = try resolveProgramStr(&mut a, program); |
|
| 4218 | 4302 | let err = try expectError(&result); |
|
| 4219 | 4303 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4220 | 4304 | else throw testing::TestError::Failed; |
|
| 4221 | 4305 | } |
| 4223 | 4307 | ||
| 4224 | 4308 | /// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds. |
|
| 4225 | 4309 | @test fn testResolveSliceOfWithCap() throws (testing::TestError) { |
|
| 4226 | 4310 | { |
|
| 4227 | 4311 | let mut a = testResolver(); |
|
| 4228 | - | let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4312 | + | let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { unsafe { return @sliceOf(ptr, len, cap); } }"; |
|
| 4229 | 4313 | let result = try resolveProgramStr(&mut a, program); |
|
| 4230 | 4314 | try expectNoErrors(&result); |
|
| 4231 | 4315 | } |
|
| 4232 | 4316 | // Mutable pointer produces mutable slice. |
|
| 4233 | 4317 | { |
|
| 4234 | 4318 | let mut a = testResolver(); |
|
| 4235 | - | let program = "fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4319 | + | let program = "fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { unsafe { return @sliceOf(ptr, len, cap); } }"; |
|
| 4236 | 4320 | let result = try resolveProgramStr(&mut a, program); |
|
| 4237 | 4321 | try expectNoErrors(&result); |
|
| 4238 | 4322 | } |
|
| 4239 | 4323 | } |
|
| 4240 | 4324 | ||
| 4241 | 4325 | /// Test @sliceOf with 3 arguments but wrong cap type. |
|
| 4242 | 4326 | @test fn testResolveSliceOfCapWrongType() throws (testing::TestError) { |
|
| 4243 | 4327 | let mut a = testResolver(); |
|
| 4244 | - | let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4328 | + | let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return unsafe { @sliceOf(ptr, len, cap) }; }"; |
|
| 4245 | 4329 | let result = try resolveProgramStr(&mut a, program); |
|
| 4246 | 4330 | let err = try expectError(&result); |
|
| 4247 | 4331 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4248 | 4332 | else throw testing::TestError::Failed; |
|
| 4249 | 4333 | } |
|
| 4250 | 4334 | ||
| 4251 | 4335 | /// Test .cap field access on slices resolves to u32. |
|
| 4252 | 4336 | @test fn testResolveSliceCapField() throws (testing::TestError) { |
|
| 4253 | 4337 | let mut a = testResolver(); |
|
| 4254 | - | let program = "fn f(s: *[u8]) -> u32 { return s.cap; }"; |
|
| 4338 | + | let program = "fn f(s: &[u8]) -> u32 { return s.cap; }"; |
|
| 4255 | 4339 | let result = try resolveProgramStr(&mut a, program); |
|
| 4256 | 4340 | try expectNoErrors(&result); |
|
| 4257 | 4341 | } |
|
| 4258 | 4342 | ||
| 4343 | + | /// Require a slice append allocator expression to produce a type mismatch. |
|
| 4344 | + | fn expectSliceAppendAllocatorError(program: *[u8]) throws (testing::TestError) { |
|
| 4345 | + | let mut a = testResolver(); |
|
| 4346 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4347 | + | let err = try expectError(&result); |
|
| 4348 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4349 | + | else throw testing::TestError::Failed; |
|
| 4350 | + | } |
|
| 4351 | + | ||
| 4259 | 4352 | /// Test `.append()` on immutable slice produces an error. |
|
| 4260 | 4353 | @test fn testResolveSliceAppendImmutable() throws (testing::TestError) { |
|
| 4261 | 4354 | let mut a = testResolver(); |
|
| 4262 | 4355 | 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); }"; |
|
| 4263 | 4356 | let result = try resolveProgramStr(&mut a, program); |
| 4292 | 4385 | } |
|
| 4293 | 4386 | } |
|
| 4294 | 4387 | ||
| 4295 | 4388 | /// Test `.append()` with correct arguments succeeds. |
|
| 4296 | 4389 | @test fn testResolveSliceAppendCorrect() throws (testing::TestError) { |
|
| 4390 | + | // A structurally matching allocator record is accepted. |
|
| 4391 | + | { |
|
| 4392 | + | let mut a = testResolver(); |
|
| 4393 | + | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } unsafe fn f(s: &mut [i32], a: A) { s.append(1, a); }"; |
|
| 4394 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4395 | + | try expectNoErrors(&result); |
|
| 4396 | + | } |
|
| 4397 | + | // Function structural matching and concrete-to-opaque pointer coercion are preserved. |
|
| 4398 | + | { |
|
| 4399 | + | let mut a = testResolver(); |
|
| 4400 | + | let program = "record C { value: u32 } record A { callback: fn(*mut opaque, u32, u32) -> *mut opaque, context: *mut opaque } fn allocate(ctx: *mut opaque, size: u32, alignment: u32) -> *mut opaque { return ctx; } unsafe fn f(s: &mut [i32], ctx: *mut C) { s.append(1, A { callback: allocate, context: ctx }); }"; |
|
| 4401 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4402 | + | try expectNoErrors(&result); |
|
| 4403 | + | } |
|
| 4404 | + | } |
|
| 4405 | + | ||
| 4406 | + | /// Test `.append()` requires an unsafe context even with a valid allocator. |
|
| 4407 | + | @test fn testResolveSliceAppendRequiresUnsafe() throws (testing::TestError) { |
|
| 4297 | 4408 | let mut a = testResolver(); |
|
| 4298 | - | 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); }"; |
|
| 4409 | + | 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); }"; |
|
| 4299 | 4410 | let result = try resolveProgramStr(&mut a, program); |
|
| 4300 | - | try expectNoErrors(&result); |
|
| 4411 | + | let err = try expectError(&result); |
|
| 4412 | + | let case super::ErrorKind::UnsafeOperation = err.kind |
|
| 4413 | + | else throw testing::TestError::Failed; |
|
| 4414 | + | } |
|
| 4415 | + | ||
| 4416 | + | /// Test `.append()` rejects allocator values without the exact structural contract. |
|
| 4417 | + | @test fn testResolveSliceAppendInvalidAllocator() throws (testing::TestError) { |
|
| 4418 | + | try expectSliceAppendAllocatorError( |
|
| 4419 | + | "unsafe fn f(s: *mut [i32]) { s.append(1, 0); }" |
|
| 4420 | + | ); |
|
| 4421 | + | try expectSliceAppendAllocatorError( |
|
| 4422 | + | "unsafe fn f(s: *mut [i32]) { s.append(1, undefined); }" |
|
| 4423 | + | ); |
|
| 4424 | + | try expectSliceAppendAllocatorError( |
|
| 4425 | + | "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque, extra: u32 } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }" |
|
| 4426 | + | ); |
|
| 4427 | + | try expectSliceAppendAllocatorError( |
|
| 4428 | + | "record A { ctx: *mut opaque, func: fn(*mut opaque, u32, u32) -> *mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }" |
|
| 4429 | + | ); |
|
| 4430 | + | try expectSliceAppendAllocatorError( |
|
| 4431 | + | "record A { func: fn(*mut opaque, u64, u32) -> *mut opaque, ctx: *mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }" |
|
| 4432 | + | ); |
|
| 4433 | + | try expectSliceAppendAllocatorError( |
|
| 4434 | + | "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }" |
|
| 4435 | + | ); |
|
| 4436 | + | try expectSliceAppendAllocatorError( |
|
| 4437 | + | "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }" |
|
| 4438 | + | ); |
|
| 4439 | + | try expectSliceAppendAllocatorError( |
|
| 4440 | + | "record A { func: fn(*unsafe mut opaque, u32, u32) -> *unsafe mut opaque, ctx: *unsafe mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }" |
|
| 4441 | + | ); |
|
| 4301 | 4442 | } |
|
| 4302 | 4443 | ||
| 4303 | 4444 | /// Test `.append()` with wrong element type produces an error. |
|
| 4304 | 4445 | @test fn testResolveSliceAppendWrongElemType() throws (testing::TestError) { |
|
| 4305 | 4446 | let mut a = testResolver(); |
|
| 4306 | - | 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); }"; |
|
| 4447 | + | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(true, a); }"; |
|
| 4307 | 4448 | let result = try resolveProgramStr(&mut a, program); |
|
| 4308 | 4449 | let err = try expectError(&result); |
|
| 4309 | 4450 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4310 | 4451 | else throw testing::TestError::Failed; |
|
| 4311 | 4452 | } |
| 4347 | 4488 | } |
|
| 4348 | 4489 | ||
| 4349 | 4490 | /// Test `.delete()` with correct arguments succeeds. |
|
| 4350 | 4491 | @test fn testResolveSliceDeleteCorrect() throws (testing::TestError) { |
|
| 4351 | 4492 | let mut a = testResolver(); |
|
| 4352 | - | let program = "fn f(s: *mut [i32]) { s.delete(0); }"; |
|
| 4493 | + | let program = "unsafe fn f(s: &mut [i32]) { s.delete(0); }"; |
|
| 4353 | 4494 | let result = try resolveProgramStr(&mut a, program); |
|
| 4354 | 4495 | try expectNoErrors(&result); |
|
| 4355 | 4496 | } |
|
| 4356 | 4497 | ||
| 4357 | 4498 | /// Test `.delete()` with wrong argument type produces an error. |
| 4362 | 4503 | let err = try expectError(&result); |
|
| 4363 | 4504 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4364 | 4505 | else throw testing::TestError::Failed; |
|
| 4365 | 4506 | } |
|
| 4366 | 4507 | ||
| 4508 | + | /// Safe slice delete borrows its receiver for the call and leaves it available. |
|
| 4509 | + | @test fn testLinearSliceDeletePreservesReceiver() throws (testing::TestError) { |
|
| 4510 | + | let program = "fn consumeSlice(slice: *mut [u32]) { consumeSlice(slice); } fn run(slice: *mut [u32]) { slice.delete(0); consumeSlice(slice); }"; |
|
| 4511 | + | try expectAnalyzeOk(program); |
|
| 4512 | + | } |
|
| 4513 | + | ||
| 4514 | + | /// Slice append consumes its receiver, element, and allocator before rebinding. |
|
| 4515 | + | @test fn testLinearSliceAppendInUnsafeBlock() throws (testing::TestError) { |
|
| 4516 | + | let program = "record Token: Linear {} record Allocator { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn consumeSlice(slice: *mut [Token]) { consumeSlice(slice); } fn run(slice: *mut [Token], token: Token, allocator: Allocator) { unsafe { set slice = slice.append(token, allocator); } consumeSlice(slice); }"; |
|
| 4517 | + | try expectAnalyzeOk(program); |
|
| 4518 | + | } |
|
| 4519 | + | ||
| 4367 | 4520 | /// Test `match &opt` produces immutable pointer bindings. |
|
| 4368 | 4521 | @test fn testResolveMatchRefUnionBinding() throws (testing::TestError) { |
|
| 4369 | 4522 | let mut a = testResolver(); |
|
| 4370 | 4523 | let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4371 | 4524 | let result = try resolveProgramStr(&mut a, program); |
| 4480 | 4633 | } |
|
| 4481 | 4634 | ||
| 4482 | 4635 | /// A mutable slice pointer should be assignable to an immutable slice pointer. |
|
| 4483 | 4636 | @test fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) { |
|
| 4484 | 4637 | let mut a = testResolver(); |
|
| 4485 | - | let result = try resolveBlockStr(&mut a, "let mut arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;"); |
|
| 4638 | + | let result = try resolveProgramStr(&mut a, "fn f(slice: *mut [i32]) -> *[i32] { return slice; }"); |
|
| 4486 | 4639 | try expectNoErrors(&result); |
|
| 4487 | 4640 | } |
|
| 4488 | 4641 | ||
| 4489 | - | /// Comprehensive tests for `as` cast expressions. |
|
| 4642 | + | /// Safe `as` casts preserve pointer representation or discard mutability. |
|
| 4490 | 4643 | @test fn testResolveAsCasts() throws (testing::TestError) { |
|
| 4491 | 4644 | { // Pointer to numeric. |
|
| 4492 | 4645 | let mut a = testResolver(); |
|
| 4493 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 0; let p = &x; p as u32;"); |
|
| 4646 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *i32) { p as u64; }"); |
|
| 4494 | 4647 | try expectNoErrors(&result); |
|
| 4495 | 4648 | } { // Function pointer to numeric. |
|
| 4496 | 4649 | let mut a = testResolver(); |
|
| 4497 | - | let result = try resolveBlockStr(&mut a, "let f: fn() = undefined; f as u32;"); |
|
| 4650 | + | let result = try resolveProgramStr(&mut a, "fn target() {} fn f() { target as u32; }"); |
|
| 4498 | 4651 | try expectNoErrors(&result); |
|
| 4499 | - | } { // *u8 to *i32 (u8 to i32 is valid). |
|
| 4652 | + | } { // Identity cast: *mut [i32] to *mut [i32]. |
|
| 4500 | 4653 | let mut a = testResolver(); |
|
| 4501 | - | let result = try resolveBlockStr(&mut a, "let p: *u8 = undefined; p as *i32;"); |
|
| 4654 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *mut [i32]) -> *mut [i32] { return s as *mut [i32]; }"); |
|
| 4502 | 4655 | try expectNoErrors(&result); |
|
| 4503 | - | } { // **u8 to **i32 (*u8 to *i32 is valid). |
|
| 4656 | + | } { // Identity cast: *i32 to *i32. |
|
| 4504 | 4657 | let mut a = testResolver(); |
|
| 4505 | - | let result = try resolveBlockStr(&mut a, "let p: **u8 = undefined; p as **i32;"); |
|
| 4658 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *i32) -> *i32 { return p as *i32; }"); |
|
| 4506 | 4659 | try expectNoErrors(&result); |
|
| 4507 | - | } |
|
| 4508 | - | ||
| 4509 | - | { // *[i32] to *[opaque]. |
|
| 4660 | + | } { // Identity cast: i32 to i32. |
|
| 4510 | 4661 | let mut a = testResolver(); |
|
| 4511 | - | let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as *[opaque];"); |
|
| 4662 | + | let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;"); |
|
| 4512 | 4663 | try expectNoErrors(&result); |
|
| 4513 | - | } { // *[opaque] to *[i32]. |
|
| 4664 | + | } { // Mutable pointer to immutable pointer. |
|
| 4514 | 4665 | let mut a = testResolver(); |
|
| 4515 | - | let result = try resolveBlockStr(&mut a, "let s: *[opaque] = undefined; s as *[i32];"); |
|
| 4666 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *mut i32) -> *i32 { return p as *i32; }"); |
|
| 4667 | + | try expectNoErrors(&result); |
|
| 4668 | + | } { // Mutable slice to immutable slice. |
|
| 4669 | + | let mut a = testResolver(); |
|
| 4670 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *mut [i32]) -> *[i32] { return s as *[i32]; }"); |
|
| 4516 | 4671 | try expectNoErrors(&result); |
|
| 4517 | 4672 | } |
|
| 4673 | + | } |
|
| 4518 | 4674 | ||
| 4519 | - | { // *[i32] to *[u8]. |
|
| 4675 | + | /// Representation-changing pointer and slice casts require an unsafe context. |
|
| 4676 | + | @test fn testResolveAsCastsRequireUnsafe() throws (testing::TestError) { |
|
| 4677 | + | { // Pointer pointee representation. |
|
| 4520 | 4678 | let mut a = testResolver(); |
|
| 4521 | - | let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as *[u8];"); |
|
| 4522 | - | try expectNoErrors(&result); |
|
| 4523 | - | } { // *[record] to *[u8]. |
|
| 4679 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *u8) -> *i32 { return p as *i32; }"); |
|
| 4680 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4681 | + | } { // Nested pointer pointee representation. |
|
| 4524 | 4682 | let mut a = testResolver(); |
|
| 4525 | - | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) { s as *[u8]; }"); |
|
| 4526 | - | try expectNoErrors(&result); |
|
| 4683 | + | let result = try resolveProgramStr(&mut a, "fn f(p: **u8) -> **i32 { return p as **i32; }"); |
|
| 4684 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4685 | + | } { // Concrete pointer to opaque pointer. |
|
| 4686 | + | let mut a = testResolver(); |
|
| 4687 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *i32) -> *opaque { return p as *opaque; }"); |
|
| 4688 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4689 | + | } { // Opaque pointer to concrete pointer. |
|
| 4690 | + | let mut a = testResolver(); |
|
| 4691 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *opaque) -> *i32 { return p as *i32; }"); |
|
| 4692 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4693 | + | } { // Concrete slice to opaque slice. |
|
| 4694 | + | let mut a = testResolver(); |
|
| 4695 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[i32]) -> *[opaque] { return s as *[opaque]; }"); |
|
| 4696 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4697 | + | } { // Opaque slice to concrete slice. |
|
| 4698 | + | let mut a = testResolver(); |
|
| 4699 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[opaque]) -> *[i32] { return s as *[i32]; }"); |
|
| 4700 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4701 | + | } { // Integer slice to byte slice. |
|
| 4702 | + | let mut a = testResolver(); |
|
| 4703 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[i32]) -> *[u8] { return s as *[u8]; }"); |
|
| 4704 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4705 | + | } { // Byte slice to integer slice. |
|
| 4706 | + | let mut a = testResolver(); |
|
| 4707 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[u8]) -> *[i32] { return s as *[i32]; }"); |
|
| 4708 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4709 | + | } { // Record slice to byte slice. |
|
| 4710 | + | let mut a = testResolver(); |
|
| 4711 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) -> *[u8] { return s as *[u8]; }"); |
|
| 4712 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4713 | + | } { // Nested slice element representation. |
|
| 4714 | + | let mut a = testResolver(); |
|
| 4715 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[*unsafe u8]) -> *[*unsafe i32] { return s as *[*unsafe i32]; }"); |
|
| 4716 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4717 | + | } { // Record pointer representation. |
|
| 4718 | + | let mut a = testResolver(); |
|
| 4719 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) -> *i32 { return p as *i32; }"); |
|
| 4720 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4721 | + | } { // Record slice element representation. |
|
| 4722 | + | let mut a = testResolver(); |
|
| 4723 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) -> *[i32] { return s as *[i32]; }"); |
|
| 4724 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 4527 | 4725 | } |
|
| 4726 | + | } |
|
| 4528 | 4727 | ||
| 4529 | - | { // *[u8] to *[i32]. |
|
| 4728 | + | /// Unsafe blocks may reinterpret pointers and slices without changing their class. |
|
| 4729 | + | @test fn testResolveAsCastsInUnsafeBlock() throws (testing::TestError) { |
|
| 4730 | + | { // Pointer pointee representation. |
|
| 4530 | 4731 | let mut a = testResolver(); |
|
| 4531 | - | let result = try resolveBlockStr(&mut a, "let s: *[u8] = undefined; s as *[i32];"); |
|
| 4732 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *u8) -> *i32 { unsafe { return p as *i32; } }"); |
|
| 4532 | 4733 | try expectNoErrors(&result); |
|
| 4533 | - | } { // *[*u8] to *[*i32] |
|
| 4734 | + | } { // Nested pointer pointee representation. |
|
| 4534 | 4735 | let mut a = testResolver(); |
|
| 4535 | - | let result = try resolveBlockStr(&mut a, "let s: *[*u8] = undefined; s as *[*i32];"); |
|
| 4736 | + | let result = try resolveProgramStr(&mut a, "fn f(p: **u8) -> **i32 { unsafe { return p as **i32; } }"); |
|
| 4536 | 4737 | try expectNoErrors(&result); |
|
| 4537 | - | } |
|
| 4538 | - | ||
| 4539 | - | { // Identity cast: *mut [i32] to *mut [i32]. |
|
| 4738 | + | } { // Concrete pointer to opaque pointer. |
|
| 4540 | 4739 | let mut a = testResolver(); |
|
| 4541 | - | let result = try resolveBlockStr(&mut a, "let s: *mut [i32] = undefined; s as *mut [i32];"); |
|
| 4740 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *i32) -> *opaque { unsafe { return p as *opaque; } }"); |
|
| 4542 | 4741 | try expectNoErrors(&result); |
|
| 4543 | - | } { // Identity cast: *i32 to *i32. |
|
| 4742 | + | } { // Opaque pointer to concrete pointer. |
|
| 4544 | 4743 | let mut a = testResolver(); |
|
| 4545 | - | let result = try resolveBlockStr(&mut a, "let p: *i32 = undefined; p as *i32;"); |
|
| 4744 | + | let result = try resolveProgramStr(&mut a, "fn f(p: *opaque) -> *i32 { unsafe { return p as *i32; } }"); |
|
| 4546 | 4745 | try expectNoErrors(&result); |
|
| 4547 | - | } { // Identity cast: i32 to i32. |
|
| 4746 | + | } { // Concrete slice to opaque slice. |
|
| 4548 | 4747 | let mut a = testResolver(); |
|
| 4549 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;"); |
|
| 4748 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[i32]) -> *[opaque] { unsafe { return s as *[opaque]; } }"); |
|
| 4749 | + | try expectNoErrors(&result); |
|
| 4750 | + | } { // Opaque slice to concrete slice. |
|
| 4751 | + | let mut a = testResolver(); |
|
| 4752 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[opaque]) -> *[i32] { unsafe { return s as *[i32]; } }"); |
|
| 4753 | + | try expectNoErrors(&result); |
|
| 4754 | + | } { // Integer slice to byte slice. |
|
| 4755 | + | let mut a = testResolver(); |
|
| 4756 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[i32]) -> *[u8] { unsafe { return s as *[u8]; } }"); |
|
| 4757 | + | try expectNoErrors(&result); |
|
| 4758 | + | } { // Byte slice to integer slice. |
|
| 4759 | + | let mut a = testResolver(); |
|
| 4760 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[u8]) -> *[i32] { unsafe { return s as *[i32]; } }"); |
|
| 4761 | + | try expectNoErrors(&result); |
|
| 4762 | + | } { // Record slice to byte slice. |
|
| 4763 | + | let mut a = testResolver(); |
|
| 4764 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) -> *[u8] { unsafe { return s as *[u8]; } }"); |
|
| 4765 | + | try expectNoErrors(&result); |
|
| 4766 | + | } { // Nested slice element representation. |
|
| 4767 | + | let mut a = testResolver(); |
|
| 4768 | + | let result = try resolveProgramStr(&mut a, "fn f(s: *[*unsafe u8]) -> *[*unsafe i32] { unsafe { return s as *[*unsafe i32]; } }"); |
|
| 4769 | + | try expectNoErrors(&result); |
|
| 4770 | + | } { // Record pointer representation. |
|
| 4771 | + | let mut a = testResolver(); |
|
| 4772 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) -> *i32 { unsafe { return p as *i32; } }"); |
|
| 4773 | + | try expectNoErrors(&result); |
|
| 4774 | + | } { // Record slice element representation. |
|
| 4775 | + | let mut a = testResolver(); |
|
| 4776 | + | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) -> *[i32] { unsafe { return s as *[i32]; } }"); |
|
| 4550 | 4777 | try expectNoErrors(&result); |
|
| 4551 | 4778 | } |
|
| 4552 | 4779 | } |
|
| 4553 | 4780 | ||
| 4554 | - | /// Tests for invalid `as` casts that should be rejected. |
|
| 4781 | + | /// Tests for invalid `as` casts that should be rejected even in unsafe code. |
|
| 4555 | 4782 | @test fn testResolveAsCastsInvalid() throws (testing::TestError) { |
|
| 4556 | 4783 | { // Pointer to slice is invalid. |
|
| 4557 | 4784 | let mut a = testResolver(); |
|
| 4558 | - | let result = try resolveBlockStr(&mut a, "let p: *i32 = undefined; p as *[i32];"); |
|
| 4785 | + | let result = try resolveBlockStr(&mut a, "unsafe { let p: *i32 = undefined; p as *[i32]; }"); |
|
| 4559 | 4786 | let err = try expectError(&result); |
|
| 4560 | 4787 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4561 | 4788 | else throw testing::TestError::Failed; |
|
| 4562 | 4789 | } { // Slice to pointer is invalid. |
|
| 4563 | 4790 | let mut a = testResolver(); |
|
| 4564 | - | let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as *i32;"); |
|
| 4791 | + | let result = try resolveBlockStr(&mut a, "unsafe { let s: *[i32] = undefined; s as *i32; }"); |
|
| 4565 | 4792 | let err = try expectError(&result); |
|
| 4566 | 4793 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4567 | 4794 | else throw testing::TestError::Failed; |
|
| 4568 | - | } { // *T to *i32 is invalid. |
|
| 4795 | + | } { // Slice to numeric is invalid. |
|
| 4569 | 4796 | let mut a = testResolver(); |
|
| 4570 | - | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *i32; }"); |
|
| 4797 | + | let result = try resolveBlockStr(&mut a, "unsafe { let s: *[i32] = undefined; s as u32; }"); |
|
| 4571 | 4798 | let err = try expectError(&result); |
|
| 4572 | 4799 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4573 | 4800 | else throw testing::TestError::Failed; |
|
| 4574 | - | } { // *[T] to *[i32] is invalid. |
|
| 4801 | + | } { // Integer to pointer is invalid. |
|
| 4575 | 4802 | let mut a = testResolver(); |
|
| 4576 | - | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) { s as *[i32]; }"); |
|
| 4803 | + | let result = try resolveBlockStr(&mut a, "let address: u64 = 0; unsafe { address as *u8; }"); |
|
| 4577 | 4804 | let err = try expectError(&result); |
|
| 4578 | 4805 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4579 | 4806 | else throw testing::TestError::Failed; |
|
| 4580 | - | } { // Slice to numeric is invalid. |
|
| 4807 | + | } { // Pointer cast cannot add mutability. |
|
| 4581 | 4808 | let mut a = testResolver(); |
|
| 4582 | - | let result = try resolveBlockStr(&mut a, "let s: *[i32] = undefined; s as u32;"); |
|
| 4809 | + | let result = try resolveBlockStr(&mut a, "unsafe { let p: *i32 = undefined; p as *mut i32; }"); |
|
| 4583 | 4810 | let err = try expectError(&result); |
|
| 4584 | 4811 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4585 | 4812 | else throw testing::TestError::Failed; |
|
| 4586 | - | } { // *record to *u8 is invalid. |
|
| 4813 | + | } { // Slice cast cannot add mutability. |
|
| 4587 | 4814 | let mut a = testResolver(); |
|
| 4588 | - | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *u8; }"); |
|
| 4815 | + | let result = try resolveBlockStr(&mut a, "unsafe { let s: *[i32] = undefined; s as *mut [i32]; }"); |
|
| 4589 | 4816 | let err = try expectError(&result); |
|
| 4590 | 4817 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 4591 | 4818 | else throw testing::TestError::Failed; |
|
| 4592 | 4819 | } |
|
| 4593 | 4820 | } |
| 4815 | 5042 | else throw testing::TestError::Failed; |
|
| 4816 | 5043 | } |
|
| 4817 | 5044 | ||
| 4818 | 5045 | @test fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) { |
|
| 4819 | 5046 | let mut a = testResolver(); |
|
| 4820 | - | 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); }"; |
|
| 5047 | + | 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); }"; |
|
| 4821 | 5048 | let result = try resolveProgramStr(&mut a, program); |
|
| 4822 | 5049 | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
|
| 4823 | 5050 | } |
|
| 4824 | 5051 | ||
| 4825 | 5052 | /// Trait declares immutable receiver (*Trait) but instance uses mutable (*mut Type). |
| 4873 | 5100 | @test fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) { |
|
| 4874 | 5101 | let mut a = testResolver(); |
|
| 4875 | 5102 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4876 | 5103 | ||
| 4877 | 5104 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena); |
|
| 4878 | - | 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); |
|
| 4879 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { let mut c = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 5105 | + | 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); |
|
| 5106 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn dispatch(a: &mut opaque defs::Adder) -> i32 { return a.add(5); } fn test() -> i32 { let mut c = defs::Counter { value: 10 }; return dispatch(&mut c); }", &mut arena); |
|
| 4880 | 5107 | ||
| 4881 | 5108 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4882 | 5109 | try expectNoErrors(&result); |
|
| 4883 | 5110 | } |
|
| 4884 | 5111 |
| 4886 | 5113 | @test fn testResolveInstanceCrossModule() throws (testing::TestError) { |
|
| 4887 | 5114 | let mut a = testResolver(); |
|
| 4888 | 5115 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4889 | 5116 | ||
| 4890 | 5117 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena); |
|
| 4891 | - | 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); |
|
| 4892 | - | 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); |
|
| 4893 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { let mut c = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 5118 | + | 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); |
|
| 5119 | + | 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); |
|
| 5120 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn dispatch(a: &mut opaque defs::Adder) -> i32 { return a.add(5); } fn test() -> i32 { let mut c = defs::Counter { value: 10 }; return dispatch(&mut c); }", &mut arena); |
|
| 4894 | 5121 | ||
| 4895 | 5122 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4896 | 5123 | try expectNoErrors(&result); |
|
| 4897 | 5124 | } |
|
| 4898 | 5125 | ||
| 4899 | 5126 | /// Calling a mutable-receiver trait method on an immutable trait object |
|
| 4900 | 5127 | /// must be rejected. |
|
| 4901 | 5128 | @test fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) { |
|
| 4902 | 5129 | let mut a = testResolver(); |
|
| 4903 | - | 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); }"; |
|
| 5130 | + | 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); }"; |
|
| 4904 | 5131 | let result = try resolveProgramStr(&mut a, program); |
|
| 4905 | 5132 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4906 | 5133 | } |
|
| 4907 | 5134 | ||
| 4908 | 5135 | /// Immutable methods on an immutable trait object should be accepted. |
|
| 4909 | 5136 | @test fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) { |
|
| 4910 | 5137 | let mut a = testResolver(); |
|
| 4911 | - | 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(); }"; |
|
| 5138 | + | 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(); }"; |
|
| 4912 | 5139 | let result = try resolveProgramStr(&mut a, program); |
|
| 4913 | 5140 | try expectNoErrors(&result); |
|
| 4914 | 5141 | } |
|
| 4915 | 5142 | ||
| 4916 | 5143 | /// Both mutable and immutable methods on a mutable trait object should work. |
|
| 4917 | 5144 | @test fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) { |
|
| 4918 | 5145 | let mut a = testResolver(); |
|
| 4919 | - | 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(); }"; |
|
| 5146 | + | 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(); }"; |
|
| 4920 | 5147 | let result = try resolveProgramStr(&mut a, program); |
|
| 4921 | 5148 | try expectNoErrors(&result); |
|
| 4922 | 5149 | } |
|
| 4923 | 5150 | ||
| 4924 | 5151 | /// Instance method body type must match the trait return type. |
| 5002 | 5229 | } |
|
| 5003 | 5230 | ||
| 5004 | 5231 | /// Instance method correctly matches the trait's throws clause. |
|
| 5005 | 5232 | @test fn testResolveInstanceThrowsMatch() throws (testing::TestError) { |
|
| 5006 | 5233 | let mut a = testResolver(); |
|
| 5007 | - | 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; } }"; |
|
| 5234 | + | 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; } }"; |
|
| 5008 | 5235 | let result = try resolveProgramStr(&mut a, program); |
|
| 5009 | 5236 | try expectNoErrors(&result); |
|
| 5010 | 5237 | } |
|
| 5011 | 5238 | ||
| 5012 | 5239 | // Constant expression folding tests ////////////////////////////////////////// |
| 5089 | 5316 | /// Test cross-module constant expression used as array size. |
|
| 5090 | 5317 | @test fn testCrossModuleConstExprArraySize() throws (testing::TestError) { |
|
| 5091 | 5318 | let mut a = testResolver(); |
|
| 5092 | 5319 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5093 | 5320 | ||
| 5094 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 5321 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; unsafe mod app;", &mut arena); |
|
| 5095 | 5322 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena); |
|
| 5096 | 5323 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = undefined;", &mut arena); |
|
| 5097 | 5324 | ||
| 5098 | 5325 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5099 | 5326 | try expectNoErrors(&result); |
| 5170 | 5397 | let program = "record Marker: Linear {} fn bad(value: &u32) -> &u32 { return value; }"; |
|
| 5171 | 5398 | let result = try resolveProgramStr(&mut a, program); |
|
| 5172 | 5399 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 5173 | 5400 | } |
|
| 5174 | 5401 | ||
| 5402 | + | /// Field observation cannot read an owner after it was moved. |
|
| 5403 | + | @test fn testLinearFieldObserveAfterMoveRejected() throws (testing::TestError) { |
|
| 5404 | + | let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn run() { let token = Token { value: 1 }; consume(token); token.value; }"; |
|
| 5405 | + | try expectLinearUseAfterConsume(program, "token", 140); |
|
| 5406 | + | } |
|
| 5407 | + | ||
| 5408 | + | /// Dereferencing cannot read an owner after it was moved. |
|
| 5409 | + | @test fn testLinearDerefAfterMoveRejected() throws (testing::TestError) { |
|
| 5410 | + | let program = "fn consume(pointer: *u32) { consume(pointer); } fn run(pointer: *u32) { consume(pointer); *pointer; }"; |
|
| 5411 | + | try expectLinearUseAfterConsume(program, "pointer", 91); |
|
| 5412 | + | } |
|
| 5413 | + | ||
| 5414 | + | /// Immutable borrowing cannot borrow an owner after it was moved. |
|
| 5415 | + | @test fn testLinearBorrowAfterMoveRejected() throws (testing::TestError) { |
|
| 5416 | + | let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn inspect(token: &Token) {} fn run() { let token = Token { value: 1 }; consume(token); inspect(&token); }"; |
|
| 5417 | + | try expectLinearUseAfterConsume(program, "token", 178); |
|
| 5418 | + | } |
|
| 5419 | + | ||
| 5420 | + | /// Mutable borrowing cannot borrow an owner after it was moved. |
|
| 5421 | + | @test fn testLinearMutableBorrowAfterMoveRejected() throws (testing::TestError) { |
|
| 5422 | + | let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn inspect(token: &mut Token) {} fn run() { let mut token = Token { value: 1 }; consume(token); inspect(&mut token); }"; |
|
| 5423 | + | try expectLinearUseAfterConsume(program, "token", 190); |
|
| 5424 | + | } |
|
| 5425 | + | ||
| 5426 | + | /// A non-consuming receiver cannot borrow an owner after it was moved. |
|
| 5427 | + | @test fn testLinearRefReceiverAfterMoveRejected() throws (testing::TestError) { |
|
| 5428 | + | let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn (token: &Token) inspect() {} fn run() { let token = Token { value: 1 }; consume(token); token.inspect(); }"; |
|
| 5429 | + | try expectLinearUseAfterConsume(program, "token", 172); |
|
| 5430 | + | } |
|
| 5431 | + | ||
| 5432 | + | /// A branch-joined consumed owner remains unavailable afterward. |
|
| 5433 | + | @test fn testLinearObserveAfterBranchJoinedMoveRejected() throws (testing::TestError) { |
|
| 5434 | + | let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn run(flag: bool) { let token = Token { value: 1 }; if flag { consume(token); } else { consume(token); } token.value; }"; |
|
| 5435 | + | try expectLinearUseAfterConsume(program, "token", 187); |
|
| 5436 | + | } |
|
| 5437 | + | ||
| 5438 | + | /// Observation before exactly one move remains valid. |
|
| 5439 | + | @test fn testLinearObserveBeforeMoveAllowed() throws (testing::TestError) { |
|
| 5440 | + | let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn run() { let token = Token { value: 1 }; token.value; consume(token); }"; |
|
| 5441 | + | try expectAnalyzeOk(program); |
|
| 5442 | + | } |
|
| 5443 | + | ||
| 5444 | + | /// A short-circuit RHS cannot be the only path that consumes an owner. |
|
| 5445 | + | @test fn testLinearShortCircuitConsumptionRejected() throws (testing::TestError) { |
|
| 5446 | + | { |
|
| 5447 | + | let mut a = testResolver(); |
|
| 5448 | + | let program = "record Token: Linear {} fn consume(token: Token) { consume(token); } fn take(token: Token) -> bool { consume(token); return true; } fn run(flag: bool) { let token = Token {}; flag and take(token); }"; |
|
| 5449 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5450 | + | try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("token")); |
|
| 5451 | + | } { |
|
| 5452 | + | let mut a = testResolver(); |
|
| 5453 | + | let program = "record Token: Linear {} fn consume(token: Token) { consume(token); } fn take(token: Token) -> bool { consume(token); return true; } fn run(flag: bool) { let token = Token {}; flag or take(token); }"; |
|
| 5454 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5455 | + | try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("token")); |
|
| 5456 | + | } |
|
| 5457 | + | } |
|
| 5458 | + | ||
| 5459 | + | /// Short-circuit branches with identical owner states remain valid. |
|
| 5460 | + | @test fn testLinearShortCircuitIdenticalStatesAllowed() throws (testing::TestError) { |
|
| 5461 | + | try expectAnalyzeOk( |
|
| 5462 | + | "record Token: Linear {} fn consume(token: Token) { consume(token); } fn run(flag: bool) { let token = Token {}; flag and true; consume(token); }" |
|
| 5463 | + | ); |
|
| 5464 | + | try expectAnalyzeOk( |
|
| 5465 | + | "record Token: Linear {} fn consume(token: Token) { consume(token); } fn run(flag: bool) { let token = Token {}; flag or false; consume(token); }" |
|
| 5466 | + | ); |
|
| 5467 | + | } |
|
| 5468 | + | ||
| 5469 | + | /// A propagated error exit must not leave an owner available. |
|
| 5470 | + | @test fn testLinearTryPropagationChecksErrorExit() throws (testing::TestError) { |
|
| 5471 | + | let mut a = testResolver(); |
|
| 5472 | + | let program = "union Failure { Failed } record Token: Linear {} fn consume(token: Token) { consume(token); } fn fail() throws (Failure) { throw Failure::Failed; } fn run(token: Token) throws (Failure) { try fail(); consume(token); }"; |
|
| 5473 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5474 | + | try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token")); |
|
| 5475 | + | } |
|
| 5476 | + | ||
| 5477 | + | /// Propagation remains valid when the call consumes every live owner. |
|
| 5478 | + | @test fn testLinearTryPropagationAfterConsumptionAllowed() throws (testing::TestError) { |
|
| 5479 | + | let program = "union Failure { Failed } record Token: Linear {} fn consume(token: Token) { consume(token); } fn fail(token: Token) throws (Failure) { consume(token); throw Failure::Failed; } fn run(token: Token) throws (Failure) { try fail(token); }"; |
|
| 5480 | + | try expectAnalyzeOk(program); |
|
| 5481 | + | } |
|
| 5482 | + | ||
| 5175 | 5483 | /// Case-pattern fallbacks must terminate instead of synthesizing bindings. |
|
| 5176 | 5484 | @test fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) { |
|
| 5177 | 5485 | let mut a = testResolver(); |
|
| 5178 | 5486 | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }"; |
|
| 5179 | 5487 | let result = try resolveProgramStr(&mut a, program); |
| 5216 | 5524 | let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }"; |
|
| 5217 | 5525 | let result = try resolveProgramStr(&mut a, program); |
|
| 5218 | 5526 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 5219 | 5527 | } |
|
| 5220 | 5528 | ||
| 5529 | + | /// A safe function may dereference an unsafe pointer inside an unsafe block. |
|
| 5530 | + | @test fn testUnsafeBlockPointerOperationAllowed() throws (testing::TestError) { |
|
| 5531 | + | let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) { unsafe { *pointer; } } fn run(pointer: *unsafe u32) { load(pointer); }"; |
|
| 5532 | + | try expectAnalyzeOk(program); |
|
| 5533 | + | } |
|
| 5534 | + | ||
| 5535 | + | /// An unsafe block permits calls to unsafe functions. |
|
| 5536 | + | @test fn testUnsafeBlockCallAllowed() throws (testing::TestError) { |
|
| 5537 | + | let program = "unsafe fn dangerous() {} fn run() { unsafe { dangerous(); } }"; |
|
| 5538 | + | try expectAnalyzeOk(program); |
|
| 5539 | + | } |
|
| 5540 | + | ||
| 5541 | + | /// Unsafe context ends at the closing brace of an unsafe block. |
|
| 5542 | + | @test fn testUnsafeBlockContextDoesNotLeak() throws (testing::TestError) { |
|
| 5543 | + | let mut a = testResolver(); |
|
| 5544 | + | let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) { unsafe { *pointer; } *pointer; }"; |
|
| 5545 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5546 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5547 | + | } |
|
| 5548 | + | ||
| 5549 | + | /// Leaving a nested unsafe block preserves the enclosing unsafe context. |
|
| 5550 | + | @test fn testNestedUnsafeBlockPreservesContext() throws (testing::TestError) { |
|
| 5551 | + | let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) { unsafe { unsafe { *pointer; } *pointer; } }"; |
|
| 5552 | + | try expectAnalyzeOk(program); |
|
| 5553 | + | } |
|
| 5554 | + | ||
| 5555 | + | /// Linear consumption inside an unsafe block remains visible after the block. |
|
| 5556 | + | @test fn testUnsafeBlockPreservesLinearConsumption() throws (testing::TestError) { |
|
| 5557 | + | let mut a = testResolver(); |
|
| 5558 | + | let program = "union Token: Linear { Value(u32) } fn consume(token: Token) { match token { case Token::Value(value) => {} } } fn run() { let token = Token::Value(1); unsafe { consume(token); } consume(token); }"; |
|
| 5559 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5560 | + | try expectErrorKind( |
|
| 5561 | + | &result, |
|
| 5562 | + | super::ErrorKind::LinearUseAfterConsume("token"), |
|
| 5563 | + | ); |
|
| 5564 | + | } |
|
| 5565 | + | ||
| 5566 | + | /// Pointer arithmetic requires an unsafe context. |
|
| 5567 | + | @test fn testPointerArithmeticRequiresUnsafe() throws (testing::TestError) { |
|
| 5568 | + | let mut a = testResolver(); |
|
| 5569 | + | let program = "fn advance(pointer: *u32) -> *u32 { return pointer + 1; }"; |
|
| 5570 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5571 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5572 | + | } |
|
| 5573 | + | ||
| 5574 | + | /// An unsafe block permits pointer arithmetic. |
|
| 5575 | + | @test fn testPointerArithmeticAllowedInUnsafeBlock() throws (testing::TestError) { |
|
| 5576 | + | let program = "fn advance(pointer: *u32) -> *u32 { unsafe { return pointer + 1; } }"; |
|
| 5577 | + | try expectAnalyzeOk(program); |
|
| 5578 | + | } |
|
| 5579 | + | ||
| 5580 | + | /// Unchecked slice construction requires an unsafe context. |
|
| 5581 | + | @test fn testSliceOfRequiresUnsafe() throws (testing::TestError) { |
|
| 5582 | + | let mut a = testResolver(); |
|
| 5583 | + | let program = "fn make(pointer: *u32) -> *[u32] { return @sliceOf(pointer, 1); }"; |
|
| 5584 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5585 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5586 | + | } |
|
| 5587 | + | ||
| 5588 | + | /// An unsafe block permits unchecked slice construction. |
|
| 5589 | + | @test fn testSliceOfAllowedInUnsafeBlock() throws (testing::TestError) { |
|
| 5590 | + | let program = "fn make(pointer: *u32) -> *[u32] { unsafe { return @sliceOf(pointer, 1); } }"; |
|
| 5591 | + | try expectAnalyzeOk(program); |
|
| 5592 | + | } |
|
| 5593 | + | ||
| 5594 | + | /// Undefined source values require an unsafe context. |
|
| 5595 | + | @test fn testUndefinedRequiresUnsafe() throws (testing::TestError) { |
|
| 5596 | + | let mut a = testResolver(); |
|
| 5597 | + | let program = "fn value() -> u32 { return undefined; }"; |
|
| 5598 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5599 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5600 | + | } |
|
| 5601 | + | ||
| 5602 | + | /// An unsafe block permits an undefined source value. |
|
| 5603 | + | @test fn testUndefinedAllowedInUnsafeBlock() throws (testing::TestError) { |
|
| 5604 | + | let program = "fn value() -> u32 { unsafe { return undefined; } }"; |
|
| 5605 | + | try expectAnalyzeOk(program); |
|
| 5606 | + | } |
|
| 5607 | + | ||
| 5608 | + | /// A safe static initializer cannot use an undefined value. |
|
| 5609 | + | @test fn testUndefinedStaticRequiresUnsafe() throws (testing::TestError) { |
|
| 5610 | + | let mut a = testResolver(); |
|
| 5611 | + | let result = try resolveProgramStr(&mut a, "static VALUE: u32 = undefined;"); |
|
| 5612 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5613 | + | } |
|
| 5614 | + | ||
| 5615 | + | /// An unsafe static initializer may use an undefined value. |
|
| 5616 | + | @test fn testUndefinedAllowedInUnsafeStatic() throws (testing::TestError) { |
|
| 5617 | + | try expectAnalyzeOk("unsafe static VALUE: u32 = undefined;"); |
|
| 5618 | + | } |
|
| 5619 | + | ||
| 5221 | 5620 | /// References cannot be embedded in aggregate fields. |
|
| 5222 | 5621 | @test fn testRefFieldRejected() throws (testing::TestError) { |
|
| 5223 | 5622 | let mut a = testResolver(); |
|
| 5224 | 5623 | let program = "record Marker: Linear {} record Bad { value: &u32 }"; |
|
| 5225 | 5624 | let result = try resolveProgramStr(&mut a, program); |
| 5246 | 5645 | let program = "record Token: Linear + Linear { value: u32 }"; |
|
| 5247 | 5646 | let result = try resolveProgramStr(&mut a, program); |
|
| 5248 | 5647 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Linear")); |
|
| 5249 | 5648 | } |
|
| 5250 | 5649 | ||
| 5251 | - | /// A `Linear` marker does not change legacy pointer inference. |
|
| 5252 | - | @test fn testLinearMarkerKeepsLegacyPointerInference() throws (testing::TestError) { |
|
| 5253 | - | let program = "record Marker: Linear {} fn run() { let value: u32 = 0; let pointer: *u32 = &value; pointer; }"; |
|
| 5254 | - | try expectAnalyzeOk(program); |
|
| 5650 | + | /// Address-of inference creates a loan that cannot be stored in a local. |
|
| 5651 | + | @test fn testAddressOfInferenceCreatesLoan() throws (testing::TestError) { |
|
| 5652 | + | let mut a = testResolver(); |
|
| 5653 | + | let program = "fn run() { let value: u32 = 0; let pointer = &value; }"; |
|
| 5654 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5655 | + | try expectErrorKind(&result, super::ErrorKind::RefBinding); |
|
| 5656 | + | } |
|
| 5657 | + | ||
| 5658 | + | /// Taking a local address cannot create an owner that escapes the function. |
|
| 5659 | + | @test fn testAddressOfLocalCannotCreateOwner() throws (testing::TestError) { |
|
| 5660 | + | let mut a = testResolver(); |
|
| 5661 | + | let program = "fn bad() -> *u32 { let value: u32 = 0; return &value; }"; |
|
| 5662 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5663 | + | let err = try expectError(&result); |
|
| 5664 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5665 | + | else throw testing::TestError::Failed; |
|
| 5666 | + | } |
|
| 5667 | + | ||
| 5668 | + | /// A loan cannot be converted into an owning pointer. |
|
| 5669 | + | @test fn testReferenceCannotBecomeOwner() throws (testing::TestError) { |
|
| 5670 | + | let mut a = testResolver(); |
|
| 5671 | + | let program = "fn bad(value: &u32) -> *u32 { return value; }"; |
|
| 5672 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5673 | + | let err = try expectError(&result); |
|
| 5674 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5675 | + | else throw testing::TestError::Failed; |
|
| 5255 | 5676 | } |
|
| 5256 | 5677 | ||
| 5257 | 5678 | /// References are rejected from every nested or storable type position. |
|
| 5258 | 5679 | @test fn testNestedRefPositionsRejected() throws (testing::TestError) { |
|
| 5259 | 5680 | { |
| 5377 | 5798 | } |
|
| 5378 | 5799 | ||
| 5379 | 5800 | /// Recursive cast validation cannot hide a checked-to-unsafe transition. |
|
| 5380 | 5801 | @test fn testNestedUnsafePointerCastRejected() throws (testing::TestError) { |
|
| 5381 | 5802 | let mut a = testResolver(); |
|
| 5382 | - | let program = "record Marker: Linear {} fn run(value: **u32) { value as **unsafe u32; }"; |
|
| 5803 | + | let program = "record Marker: Linear {} fn run(value: &*u32) { unsafe { value as **unsafe u32; } }"; |
|
| 5383 | 5804 | let result = try resolveProgramStr(&mut a, program); |
|
| 5384 | 5805 | let err = try expectError(&result); |
|
| 5385 | 5806 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5386 | 5807 | else throw testing::TestError::Failed; |
|
| 5387 | 5808 | } |
| 5427 | 5848 | let mut a = testResolver(); |
|
| 5428 | 5849 | let program = "record Marker: Linear {} 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(); }"; |
|
| 5429 | 5850 | let result = try resolveProgramStr(&mut a, program); |
|
| 5430 | 5851 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 5431 | 5852 | } |
|
| 5853 | + | ||
| 5854 | + | /// Module-qualified statics retain one loan root across call arguments. |
|
| 5855 | + | @test fn testScopedStaticLoanConflicts() throws (testing::TestError) { |
|
| 5856 | + | { |
|
| 5857 | + | let mut a = testResolver(); |
|
| 5858 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5859 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "loan_mut_root", "export mod values; mod app;", &mut arena); |
|
| 5860 | + | let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export static X: u32 = 0;", &mut arena); |
|
| 5861 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use loan_mut_root::values; fn both(left: &mut u32, right: &mut u32) {} fn run() { both(&mut values::X, &mut values::X); }", &mut arena); |
|
| 5862 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5863 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("X")); |
|
| 5864 | + | } { |
|
| 5865 | + | let mut a = testResolver(); |
|
| 5866 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5867 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "loan_shared_root", "export mod values; mod app;", &mut arena); |
|
| 5868 | + | let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export static X: u32 = 0;", &mut arena); |
|
| 5869 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use loan_shared_root::values; fn both(left: &u32, right: &mut u32) {} fn run() { both(&values::X, &mut values::X); }", &mut arena); |
|
| 5870 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5871 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("X")); |
|
| 5872 | + | } |
|
| 5873 | + | } |
|
| 5874 | + | ||
| 5875 | + | /// Distinct module-qualified statics do not overlap. |
|
| 5876 | + | @test fn testDistinctScopedStaticLoansAllowed() throws (testing::TestError) { |
|
| 5877 | + | let mut a = testResolver(); |
|
| 5878 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5879 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "loan_distinct_root", "export mod values; mod app;", &mut arena); |
|
| 5880 | + | let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export static X: u32 = 0; export static Y: u32 = 0;", &mut arena); |
|
| 5881 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use loan_distinct_root::values; fn both(left: &mut u32, right: &mut u32) {} fn run() { both(&mut values::X, &mut values::Y); }", &mut arena); |
|
| 5882 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5883 | + | try expectNoErrors(&result); |
|
| 5884 | + | } |
|
| 5885 | + | ||
| 5886 | + | /// Earlier argument loans remain active inside later nested calls. |
|
| 5887 | + | @test fn testOuterArgumentLoanConflictsWithNestedCall() throws (testing::TestError) { |
|
| 5888 | + | { |
|
| 5889 | + | let mut a = testResolver(); |
|
| 5890 | + | let program = "fn observe(value: &u32) -> u32 { return *value; } fn pair(first: &mut u32, second: u32) {} fn run() { let mut value: u32 = 0; pair(&mut value, observe(&value)); }"; |
|
| 5891 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5892 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value")); |
|
| 5893 | + | } { |
|
| 5894 | + | let mut a = testResolver(); |
|
| 5895 | + | let program = "record Token: Linear { value: u32 } fn consume(token: Token) -> u32 { return consume(token); } fn pair(first: &Token, second: u32) {} fn run() { let token = Token { value: 0 }; pair(&token, consume(token)); }"; |
|
| 5896 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5897 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("token")); |
|
| 5898 | + | } { |
|
| 5899 | + | let mut a = testResolver(); |
|
| 5900 | + | let program = "fn update(value: &mut u32) -> u32 { set *value = 1; return *value; } fn pair(first: &u32, second: u32) {} fn run() { let mut value: u32 = 0; pair(&value, update(&mut value)); }"; |
|
| 5901 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5902 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value")); |
|
| 5903 | + | } |
|
| 5904 | + | } |
|
| 5905 | + | ||
| 5906 | + | /// Nested later arguments may use a different root. |
|
| 5907 | + | @test fn testNonOverlappingNestedCallArgumentsAllowed() throws (testing::TestError) { |
|
| 5908 | + | let program = "fn observe(value: &u32) -> u32 { return *value; } fn pair(first: &mut u32, second: u32) {} fn run() { let mut left: u32 = 0; let right: u32 = 1; pair(&mut left, observe(&right)); }"; |
|
| 5909 | + | try expectAnalyzeOk(program); |
|
| 5910 | + | } |
|
| 5911 | + | ||
| 5912 | + | /// Linear values cannot live in reusable global declarations. |
|
| 5913 | + | @test fn testLinearGlobalsRejected() throws (testing::TestError) { |
|
| 5914 | + | { |
|
| 5915 | + | let mut a = testResolver(); |
|
| 5916 | + | let program = "record Token: Linear { value: u32 } static TOKEN: Token = Token { value: 1 };"; |
|
| 5917 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5918 | + | try expectErrorKind(&result, super::ErrorKind::LinearDiscard); |
|
| 5919 | + | } { |
|
| 5920 | + | let mut a = testResolver(); |
|
| 5921 | + | let program = "record Token: Linear { value: u32 } constant TOKEN: Token = Token { value: 1 };"; |
|
| 5922 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5923 | + | try expectErrorKind(&result, super::ErrorKind::LinearDiscard); |
|
| 5924 | + | } |
|
| 5925 | + | { |
|
| 5926 | + | let mut a = testResolver(); |
|
| 5927 | + | let program = "constant TEXT: *[u8] = \"text\";"; |
|
| 5928 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5929 | + | try expectErrorKind(&result, super::ErrorKind::LinearDiscard); |
|
| 5930 | + | } |
|
| 5931 | + | } |
|
| 5932 | + | ||
| 5933 | + | /// Function-typed unsafe statics require unsafe access like other globals. |
|
| 5934 | + | @test fn testUnsafeFunctionStaticAccessRequiresUnsafe() throws (testing::TestError) { |
|
| 5935 | + | let mut a = testResolver(); |
|
| 5936 | + | let program = "fn callback() {} unsafe static CALLBACK: fn() = callback; fn run() { CALLBACK(); }"; |
|
| 5937 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5938 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5939 | + | } |
|
| 5940 | + | ||
| 5941 | + | /// Assignments cannot overlap an earlier shared call-scoped loan. |
|
| 5942 | + | @test fn testOuterArgumentLoanConflictsWithLaterAssignment() throws (testing::TestError) { |
|
| 5943 | + | let mut a = testResolver(); |
|
| 5944 | + | let program = "union Error { Fail } fn fail() -> i32 throws (Error) { return 0; } fn pair(first: &u32, second: i32) {} fn run() -> i32 { let mut value: u32 = 0; pair(&value, try fail() catch { set value = 1; return 0; }); return 0; }"; |
|
| 5945 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5946 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value")); |
|
| 5947 | + | } |
|
| 5948 | + | ||
| 5949 | + | /// Ordinary scalar globals remain reusable. |
|
| 5950 | + | @test fn testScalarGlobalsAllowed() throws (testing::TestError) { |
|
| 5951 | + | try expectAnalyzeOk("static COUNT: u32 = 0; constant LIMIT: u32 = 1;"); |
|
| 5952 | + | } |
|
| 5953 | + | ||
| 5954 | + | /// Explicit unsafe globals may hold linear values. |
|
| 5955 | + | @test fn testUnsafeLinearGlobalsAllowed() throws (testing::TestError) { |
|
| 5956 | + | let program = "record Token: Linear { value: u32 } unsafe static TOKEN: Token = Token { value: 1 }; unsafe constant TEXT: *[u8] = \"text\";"; |
|
| 5957 | + | try expectAnalyzeOk(program); |
|
| 5958 | + | } |
|
| 5959 | + | ||
| 5960 | + | /// Reading an unsafe global requires unsafe context. |
|
| 5961 | + | @test fn testUnsafeGlobalAccessRequiresUnsafe() throws (testing::TestError) { |
|
| 5962 | + | let mut a = testResolver(); |
|
| 5963 | + | let program = "unsafe static VALUE: u32 = 0; fn read() -> u32 { return VALUE; }"; |
|
| 5964 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5965 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5966 | + | } |
|
| 5967 | + | ||
| 5968 | + | /// Unsafe context permits reading an unsafe linear global. |
|
| 5969 | + | @test fn testUnsafeGlobalAccessAllowedInUnsafeBlock() throws (testing::TestError) { |
|
| 5970 | + | let program = "unsafe constant TEXT: *[u8] = \"text\"; fn consume(value: *[u8]) { consume(value); } fn run() { unsafe { consume(TEXT); } }"; |
|
| 5971 | + | try expectAnalyzeOk(program); |
|
| 5972 | + | } |
|
| 5973 | + | ||
| 5974 | + | /// Exported unsafe globals retain their access requirement across modules. |
|
| 5975 | + | @test fn testScopedUnsafeGlobalAccess() throws (testing::TestError) { |
|
| 5976 | + | { |
|
| 5977 | + | let mut a = testResolver(); |
|
| 5978 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5979 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "unsafe_global_root", "export mod values; mod app;", &mut arena); |
|
| 5980 | + | let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export unsafe static VALUE: u32 = 0;", &mut arena); |
|
| 5981 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use unsafe_global_root::values; fn read() -> u32 { return values::VALUE; }", &mut arena); |
|
| 5982 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5983 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5984 | + | } { |
|
| 5985 | + | let mut a = testResolver(); |
|
| 5986 | + | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5987 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "unsafe_global_ok_root", "export mod values; mod app;", &mut arena); |
|
| 5988 | + | let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export unsafe static VALUE: u32 = 0;", &mut arena); |
|
| 5989 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use unsafe_global_ok_root::values; fn read() -> u32 { unsafe { return values::VALUE; } }", &mut arena); |
|
| 5990 | + | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5991 | + | try expectNoErrors(&result); |
|
| 5992 | + | } |
|
| 5993 | + | } |
|
| 5994 | + | ||
| 5995 | + | /// Identity casts do not hide duplicate or conflicting loans. |
|
| 5996 | + | @test fn testCastWrappedLoanConflicts() throws (testing::TestError) { |
|
| 5997 | + | { |
|
| 5998 | + | let mut a = testResolver(); |
|
| 5999 | + | let program = "fn both(left: &mut u32, right: &mut u32) {} fn run() { let mut value: u32 = 0; both(&mut value as &mut u32, &mut value); }"; |
|
| 6000 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6001 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value")); |
|
| 6002 | + | } { |
|
| 6003 | + | let mut a = testResolver(); |
|
| 6004 | + | let program = "fn both(left: &u32, right: &mut u32) {} fn run() { let mut value: u32 = 0; both(&value as &u32, &mut value); }"; |
|
| 6005 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 6006 | + | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value")); |
|
| 6007 | + | } |
|
| 6008 | + | } |
lib/std/lang/scanner.rad
+1 -1
| 115 | 115 | /// Corresponding token. |
|
| 116 | 116 | tok: TokenKind, |
|
| 117 | 117 | } |
|
| 118 | 118 | ||
| 119 | 119 | /// Sorted keyword table for binary search. |
|
| 120 | - | constant KEYWORDS: [Keyword; 52] = [ |
|
| 120 | + | unsafe constant KEYWORDS: [Keyword; 52] = [ |
|
| 121 | 121 | { name: "align", tok: TokenKind::Align }, |
|
| 122 | 122 | { name: "and", tok: TokenKind::And }, |
|
| 123 | 123 | { name: "as", tok: TokenKind::As }, |
|
| 124 | 124 | { name: "assert", tok: TokenKind::Assert }, |
|
| 125 | 125 | { name: "bool", tok: TokenKind::Bool }, |
lib/std/lang/scanner/tests.rad
+1 -1
| 1 | 1 | use std::lang::strings; |
|
| 2 | 2 | use std::mem; |
|
| 3 | 3 | use std::testing; |
|
| 4 | 4 | ||
| 5 | 5 | /// String pool for testing. |
|
| 6 | - | static TEST_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 6 | + | unsafe static TEST_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 7 | 7 | ||
| 8 | 8 | fn testScanner(source: *[u8]) -> super::Scanner { |
|
| 9 | 9 | return super::scanner(super::SourceLoc::File("test.r"), source, &mut TEST_STRING_POOL); |
|
| 10 | 10 | } |
|
| 11 | 11 |
lib/std/sys/unix.rad
+46 -20
| 29 | 29 | export constant STDERR: i64 = 2; |
|
| 30 | 30 | ||
| 31 | 31 | /// Special value representing current working directory for `openat()`. |
|
| 32 | 32 | constant AT_FDCWD: i64 = -100; |
|
| 33 | 33 | ||
| 34 | - | /// Opens a file at the given path and returns a file descriptor. |
|
| 35 | - | /// Returns a negative value on error. |
|
| 36 | - | export fn open(path: *[u8], flags: OpenFlags) -> i64 { |
|
| 34 | + | /// Open a file at the given path and return a file descriptor. |
|
| 35 | + | /// Return a negative value on error. |
|
| 36 | + | /// |
|
| 37 | + | /// The caller must ensure `path.ptr` identifies at least `path.len + 1` |
|
| 38 | + | /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes |
|
| 39 | + | /// remain readable until the system call returns. |
|
| 40 | + | export unsafe fn open(path: *[u8], flags: OpenFlags) -> i64 { |
|
| 37 | 41 | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, 0); |
|
| 38 | 42 | } |
|
| 39 | 43 | ||
| 40 | - | /// Opens a file at the given path with mode, returns a file descriptor. |
|
| 41 | - | export fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 { |
|
| 44 | + | /// Open a file at the given path with mode and return a file descriptor. |
|
| 45 | + | /// |
|
| 46 | + | /// The caller must ensure `path.ptr` identifies at least `path.len + 1` |
|
| 47 | + | /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes |
|
| 48 | + | /// remain readable until the system call returns. |
|
| 49 | + | export unsafe fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 { |
|
| 42 | 50 | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, mode); |
|
| 43 | 51 | } |
|
| 44 | 52 | ||
| 45 | 53 | /// Reads from a file descriptor into the provided buffer. |
|
| 46 | 54 | /// Returns the number of bytes read, or a negative value on error. |
|
| 47 | 55 | export fn read(fd: i64, buf: *mut [u8]) -> i64 { |
|
| 48 | - | return intrinsics::ecall(63, fd, buf.ptr as i64, buf.len as i64, 0); |
|
| 56 | + | let mut result: i64 = 0; |
|
| 57 | + | unsafe { set result = intrinsics::ecall(63, fd, buf.ptr as i64, buf.len as i64, 0); } |
|
| 58 | + | return result; |
|
| 49 | 59 | } |
|
| 50 | 60 | ||
| 51 | 61 | /// Reads from a file descriptor until EOF or buffer is full. |
|
| 52 | 62 | /// Returns the total number of bytes read, or a negative value on error. |
|
| 53 | 63 | export fn readToEnd(fd: i64, buf: *mut [u8]) -> i64 { |
| 68 | 78 | } |
|
| 69 | 79 | ||
| 70 | 80 | /// Writes to a file descriptor from the provided buffer. |
|
| 71 | 81 | /// Returns the number of bytes written, or a negative value on error. |
|
| 72 | 82 | export fn write(fd: i64, buf: *[u8]) -> i64 { |
|
| 73 | - | return intrinsics::ecall(64, fd, buf.ptr as i64, buf.len as i64, 0); |
|
| 83 | + | let mut result: i64 = 0; |
|
| 84 | + | unsafe { set result = intrinsics::ecall(64, fd, buf.ptr as i64, buf.len as i64, 0); } |
|
| 85 | + | return result; |
|
| 74 | 86 | } |
|
| 75 | 87 | ||
| 76 | 88 | /// Writes the entire contents of a buffer to a file descriptor. |
|
| 77 | 89 | /// Returns `false` when the descriptor cannot accept the full buffer. |
|
| 78 | 90 | export fn writeAll(fd: i64, data: *[u8]) -> bool { |
| 88 | 100 | } |
|
| 89 | 101 | ||
| 90 | 102 | /// Closes a file descriptor. |
|
| 91 | 103 | /// Returns `0` on success, or a negative value on error. |
|
| 92 | 104 | export fn close(fd: i64) -> i64 { |
|
| 93 | - | return intrinsics::ecall(57, fd, 0, 0, 0); |
|
| 105 | + | let mut result: i64 = 0; |
|
| 106 | + | unsafe { set result = intrinsics::ecall(57, fd, 0, 0, 0); } |
|
| 107 | + | return result; |
|
| 94 | 108 | } |
|
| 95 | 109 | ||
| 96 | - | /// Reads the entire contents of a file at the given path into the provided buffer. |
|
| 97 | - | /// Returns a slice containing the data read, or `nil` on error. |
|
| 98 | - | export fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] { |
|
| 110 | + | /// Read the entire contents of a file at the given path into the buffer. |
|
| 111 | + | /// Return a slice containing the data read, or `nil` on error. |
|
| 112 | + | /// |
|
| 113 | + | /// The caller must ensure `path.ptr` identifies at least `path.len + 1` |
|
| 114 | + | /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes |
|
| 115 | + | /// remain readable until this function returns. |
|
| 116 | + | export unsafe fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] { |
|
| 99 | 117 | let fd = open(path, O_RDONLY); |
|
| 100 | 118 | if fd < 0 { |
|
| 101 | 119 | return nil; |
|
| 102 | 120 | } |
|
| 103 | 121 | let n = readToEnd(fd, buf); |
| 111 | 129 | return &buf[..n as u32]; |
|
| 112 | 130 | } |
|
| 113 | 131 | ||
| 114 | 132 | /// Exit the current process with the given status code. |
|
| 115 | 133 | export fn exit(status: i64) { |
|
| 116 | - | intrinsics::ecall(93, status, 0, 0, 0); |
|
| 134 | + | unsafe { intrinsics::ecall(93, status, 0, 0, 0); } |
|
| 117 | 135 | } |
|
| 118 | 136 | ||
| 119 | - | /// Writes the entire contents of a buffer to a file at the given path. |
|
| 120 | - | /// Creates the file if it doesn't exist, truncates if it does. |
|
| 121 | - | /// Returns `true` on success. |
|
| 122 | - | export fn writeFile(path: *[u8], data: *[u8]) -> bool { |
|
| 137 | + | /// Write the entire contents of a buffer to a file at the given path. |
|
| 138 | + | /// Create the file if it does not exist and truncate it if it does. |
|
| 139 | + | /// Return `true` on success. |
|
| 140 | + | /// |
|
| 141 | + | /// The caller must ensure `path.ptr` identifies at least `path.len + 1` |
|
| 142 | + | /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes |
|
| 143 | + | /// remain readable until this function returns. |
|
| 144 | + | export unsafe fn writeFile(path: *[u8], data: *[u8]) -> bool { |
|
| 123 | 145 | return writeFileParts(path, &[data]); |
|
| 124 | 146 | } |
|
| 125 | 147 | ||
| 126 | - | /// Writes each buffer to a file at the given path. |
|
| 127 | - | /// Creates the file if it doesn't exist, truncates if it does. |
|
| 128 | - | /// Returns `true` only when every part and the final close succeed. |
|
| 129 | - | export fn writeFileParts(path: *[u8], parts: *[*[u8]]) -> bool { |
|
| 148 | + | /// Write each buffer to a file at the given path. |
|
| 149 | + | /// Create the file if it does not exist and truncate it if it does. |
|
| 150 | + | /// Return `true` only when every part and the final close succeed. |
|
| 151 | + | /// |
|
| 152 | + | /// The caller must ensure `path.ptr` identifies at least `path.len + 1` |
|
| 153 | + | /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes |
|
| 154 | + | /// remain readable until this function returns. |
|
| 155 | + | export unsafe fn writeFileParts(path: *[u8], parts: *[*[u8]]) -> bool { |
|
| 130 | 156 | let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC); |
|
| 131 | 157 | let fd = openOpts(path, flags, 420); // 0644 in octal. |
|
| 132 | 158 | if fd < 0 { |
|
| 133 | 159 | return false; |
|
| 134 | 160 | } |
lib/std/testing.rad
+1 -1
| 25 | 25 | export fn test(module: *[u8], name: *[u8], func: fn() throws (TestError)) -> TestInfo { |
|
| 26 | 26 | return TestInfo { module, name, func }; |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// Run all tests and return `0` on success or `1` if any test failed. |
|
| 30 | - | export fn runAllTests(tests: *[TestInfo]) -> i32 { |
|
| 30 | + | export fn runAllTests(tests: &[TestInfo]) -> i32 { |
|
| 31 | 31 | let mut ctx: Ctx = Ctx { passed: 0, failed: 0 }; |
|
| 32 | 32 | ||
| 33 | 33 | io::print("Running "); |
|
| 34 | 34 | io::printU32(tests.len); |
|
| 35 | 35 | io::print(" test(s)...\n\n"); |
lib/std/tests.rad
+6 -2
| 246 | 246 | ||
| 247 | 247 | @test fn testWriteFileParts() throws (testing::TestError) { |
|
| 248 | 248 | let path = "/tmp/radiance-std-write-file-parts.test"; |
|
| 249 | 249 | let mut buffer: [u8; 16] = undefined; |
|
| 250 | 250 | ||
| 251 | - | if not unix::writeFileParts(path, &["hello", " ", "world"]) { |
|
| 251 | + | let mut wrote = false; |
|
| 252 | + | unsafe { set wrote = unix::writeFileParts(path, &["hello", " ", "world"]); } |
|
| 253 | + | if not wrote { |
|
| 252 | 254 | throw testing::TestError::Failed; |
|
| 253 | 255 | } |
|
| 254 | - | let data = unix::readFile(path, &mut buffer[..]) else { |
|
| 256 | + | let mut readResult: ?*[u8] = nil; |
|
| 257 | + | unsafe { set readResult = unix::readFile(path, &mut buffer[..]); } |
|
| 258 | + | let data = readResult else { |
|
| 255 | 259 | throw testing::TestError::Failed; |
|
| 256 | 260 | }; |
|
| 257 | 261 | try testing::expectBytesEq(data, "hello world"); |
|
| 258 | 262 | } |
|
| 259 | 263 |
lib/std/vec.rad
+30 -5
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Push an element onto the end of the vector. |
|
| 64 | 64 | /// |
|
| 65 | 65 | /// Returns false if the vector is at capacity. |
|
| 66 | - | export fn push(vec: *mut RawVec, elem: *opaque) -> bool { |
|
| 66 | + | /// |
|
| 67 | + | /// The caller must preserve the metadata invariants established by [`new`], |
|
| 68 | + | /// including `vec.stride > 0` and `vec.len <= capacity(vec)`. When the vector |
|
| 69 | + | /// has spare capacity, `elem` must point to at least `vec.stride` readable, |
|
| 70 | + | /// initialized bytes, and the destination range beginning at |
|
| 71 | + | /// `vec.len * vec.stride` in `vec.data` must be writable for `vec.stride` |
|
| 72 | + | /// bytes. The copy is byte-wise, so `elem` has no alignment requirement and |
|
| 73 | + | /// the destination need not already be initialized. |
|
| 74 | + | export unsafe fn push(vec: *mut RawVec, elem: *opaque) -> bool { |
|
| 67 | 75 | if vec.len >= capacity(vec) { |
|
| 68 | 76 | return false; |
|
| 69 | 77 | } |
|
| 70 | 78 | let off: u32 = vec.len * vec.stride; |
|
| 71 | 79 | let dst: *mut u8 = &mut vec.data[off]; |
| 79 | 87 | ||
| 80 | 88 | /// Pop an element from the end of the vector. |
|
| 81 | 89 | /// |
|
| 82 | 90 | /// Copies the element into the provided output pointer. |
|
| 83 | 91 | /// Returns false if the vector is empty. |
|
| 84 | - | export fn pop(vec: *mut RawVec, out: *mut opaque) -> bool { |
|
| 92 | + | /// |
|
| 93 | + | /// The caller must preserve the metadata invariants established by [`new`], |
|
| 94 | + | /// including `vec.stride > 0` and `vec.len <= capacity(vec)`. When the vector |
|
| 95 | + | /// is non-empty, `out` must point to at least `vec.stride` writable bytes, and |
|
| 96 | + | /// the source range for the last logical element in `vec.data` must contain |
|
| 97 | + | /// `vec.stride` initialized bytes. The copy is byte-wise, so `out` has no |
|
| 98 | + | /// alignment requirement and its previous contents need not be initialized. |
|
| 99 | + | export unsafe fn pop(vec: *mut RawVec, out: *mut opaque) -> bool { |
|
| 85 | 100 | if vec.len == 0 { |
|
| 86 | 101 | return false; |
|
| 87 | 102 | } |
|
| 88 | 103 | set vec.len -= 1; |
|
| 89 | 104 |
| 97 | 112 | } |
|
| 98 | 113 | ||
| 99 | 114 | /// Set the element at the given index. |
|
| 100 | 115 | /// |
|
| 101 | 116 | /// Returns false if index is out of bounds. |
|
| 102 | - | export fn put(vec: *mut RawVec, index: u32, elem: *opaque) -> bool { |
|
| 117 | + | /// |
|
| 118 | + | /// The caller must preserve the metadata invariants established by [`new`], |
|
| 119 | + | /// including `vec.stride > 0` and `vec.len <= capacity(vec)`. When `index` is |
|
| 120 | + | /// in bounds, `elem` must point to at least `vec.stride` readable, initialized |
|
| 121 | + | /// bytes, and the destination range beginning at `index * vec.stride` in |
|
| 122 | + | /// `vec.data` must be writable for `vec.stride` bytes. The copy is byte-wise, |
|
| 123 | + | /// so `elem` has no alignment requirement. |
|
| 124 | + | export unsafe fn put(vec: *mut RawVec, index: u32, elem: *opaque) -> bool { |
|
| 103 | 125 | if index >= vec.len { |
|
| 104 | 126 | return false; |
|
| 105 | 127 | } |
|
| 106 | 128 | let off: u32 = index * vec.stride; |
|
| 107 | 129 | let dst: *mut u8 = &mut vec.data[off]; |
| 110 | 132 | copyBytes(dst, src, vec.stride); |
|
| 111 | 133 | ||
| 112 | 134 | return true; |
|
| 113 | 135 | } |
|
| 114 | 136 | ||
| 115 | - | /// Copy bytes from source to destination. |
|
| 116 | - | fn copyBytes(dst: *mut u8, src: *u8, count: u32) { |
|
| 137 | + | /// Copy `count` initialized bytes from `src` into writable storage at `dst`. |
|
| 138 | + | /// |
|
| 139 | + | /// The caller must ensure both pointers are valid for `count` bytes. No |
|
| 140 | + | /// alignment is required because the copy operates one byte at a time. |
|
| 141 | + | unsafe fn copyBytes(dst: *mut u8, src: *u8, count: u32) { |
|
| 117 | 142 | for i in 0..count { |
|
| 118 | 143 | set *(dst + i) = *(src + i); |
|
| 119 | 144 | } |
|
| 120 | 145 | } |
seed/radiance.rv64
+0 -0
Binary file changed.
seed/radiance.rv64.git
+1 -1
| 1 | - | d024ed30c27cc671d9371988d0155b3e08ff004582cf59f1b7eac8ffdf3e3467 |
|
| 1 | + | 1e2fdea952350036a4172f567442e608555094b9e18fa69f1fb615453d5352b6 |
seed/update
+2 -1
| 39 | 39 | # --------------------------------------------------------------------------- |
|
| 40 | 40 | # Command line flags for Radiance compiler |
|
| 41 | 41 | # --------------------------------------------------------------------------- |
|
| 42 | 42 | ||
| 43 | 43 | STD_MODS="$(sed 's/^/-mod /' std.lib | tr '\n' ' ')" |
|
| 44 | - | OPTS="-pkg std ${STD_MODS} -pkg radiance -mod compiler/radiance.rad -entry radiance" |
|
| 44 | + | OPTS="-pkg std ${STD_MODS} -pkg radiance -mod compiler/radiance.rad" |
|
| 45 | + | OPTS="${OPTS} -mod compiler/radiance/codegenSink.rad -entry radiance" |
|
| 45 | 46 | ||
| 46 | 47 | # --------------------------------------------------------------------------- |
|
| 47 | 48 | # Emulator settings |
|
| 48 | 49 | # --------------------------------------------------------------------------- |
|
| 49 | 50 |
std.lib.test
+1 -0
| 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/il/tests.rad |
|
| 8 | 9 | lib/std/lang/parser/tests.rad |
|
| 9 | 10 | lib/std/lang/module/tests.rad |
|
| 10 | 11 | lib/std/lang/scanner/tests.rad |
|
| 11 | 12 | lib/std/lang/resolver/tests.rad |
|
| 12 | 13 | lib/std/lang/gen/bitset/tests.rad |
test/runner.rad
+38 -27
| 28 | 28 | /// Arena size for AST/IL allocations (512 KB). |
|
| 29 | 29 | constant ARENA_SIZE: u32 = 524288; |
|
| 30 | 30 | /// Maximum path length for expected IL file path. |
|
| 31 | 31 | constant MAX_PATH_LEN: u32 = 256; |
|
| 32 | 32 | /// Source file extension for binary tests. |
|
| 33 | - | constant SOURCE_EXT: *[u8] = ".rad"; |
|
| 33 | + | unsafe constant SOURCE_EXT: *[u8] = ".rad"; |
|
| 34 | 34 | /// IL snapshot file extension for binary tests. |
|
| 35 | - | constant SNAPSHOT_EXT: *[u8] = ".ril"; |
|
| 35 | + | unsafe constant SNAPSHOT_EXT: *[u8] = ".ril"; |
|
| 36 | 36 | ||
| 37 | 37 | /// String pool. |
|
| 38 | - | static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 38 | + | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 39 | 39 | ||
| 40 | 40 | /// Maximum number of AST nodes per test file. |
|
| 41 | 41 | constant MAX_NODE_DATA: u32 = 4096; |
|
| 42 | 42 | /// Maximum number of resolver errors per test file. |
|
| 43 | 43 | constant MAX_ERRORS: u32 = 16; |
| 46 | 46 | /// Maximum number of data bytes in a `.ras` test binary. |
|
| 47 | 47 | constant ASM_DATA_CAPACITY: u32 = 1024; |
|
| 48 | 48 | ||
| 49 | 49 | // Static storage for large buffers to avoid stack overflow. |
|
| 50 | 50 | // Tests run serially so sharing these is safe. |
|
| 51 | - | static SOURCE_BUF: [u8; SOURCE_BUF_SIZE] = undefined; |
|
| 52 | - | static EXPECTED_BUF: [u8; EXPECTED_BUF_SIZE] = undefined; |
|
| 53 | - | static OUTPUT_BUF: [u8; OUTPUT_BUF_SIZE] = undefined; |
|
| 54 | - | static AST_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 55 | - | static IL_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 56 | - | static PRINT_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 57 | - | static RESOLVER_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 58 | - | static NODE_DATA_STORAGE: [resolver::NodeData; MAX_NODE_DATA] = undefined; |
|
| 59 | - | static ERROR_STORAGE: [resolver::Error; MAX_ERRORS] = undefined; |
|
| 60 | - | static ASM_TEXT_STORAGE: [u32; ASM_TEXT_CAPACITY] = undefined; |
|
| 61 | - | static ASM_DATA_STORAGE: [u8; ASM_DATA_CAPACITY] = undefined; |
|
| 51 | + | unsafe static SOURCE_BUF: [u8; SOURCE_BUF_SIZE] = undefined; |
|
| 52 | + | unsafe static EXPECTED_BUF: [u8; EXPECTED_BUF_SIZE] = undefined; |
|
| 53 | + | unsafe static OUTPUT_BUF: [u8; OUTPUT_BUF_SIZE] = undefined; |
|
| 54 | + | unsafe static AST_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 55 | + | unsafe static IL_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 56 | + | unsafe static PRINT_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 57 | + | unsafe static RESOLVER_ARENA_STORAGE: [u8; ARENA_SIZE] = undefined; |
|
| 58 | + | unsafe static NODE_DATA_STORAGE: [resolver::NodeData; MAX_NODE_DATA] = undefined; |
|
| 59 | + | unsafe static ERROR_STORAGE: [resolver::Error; MAX_ERRORS] = undefined; |
|
| 60 | + | unsafe static ASM_TEXT_STORAGE: [u32; ASM_TEXT_CAPACITY] = undefined; |
|
| 61 | + | unsafe static ASM_DATA_STORAGE: [u8; ASM_DATA_CAPACITY] = undefined; |
|
| 62 | 62 | ||
| 63 | 63 | /// Strip a `//` comment from a line, preserving `//` inside quoted strings. |
|
| 64 | 64 | /// Returns the content before the comment, trimmed of trailing whitespace. |
|
| 65 | - | fn stripLine(line: *[u8]) -> *[u8] { |
|
| 65 | + | unsafe fn stripLine(line: *[u8]) -> *[u8] { |
|
| 66 | 66 | let mut end = line.len; |
|
| 67 | 67 | let mut i: u32 = 0; |
|
| 68 | 68 | let mut inString = false; |
|
| 69 | 69 | let mut escaped = false; |
|
| 70 | 70 |
| 93 | 93 | } |
|
| 94 | 94 | return &line[..end]; |
|
| 95 | 95 | } |
|
| 96 | 96 | ||
| 97 | 97 | /// Get next line from string at offset. Returns the line and updates offset past newline. |
|
| 98 | - | fn nextLine(s: *[u8], offset: *mut u32) -> *[u8] { |
|
| 98 | + | unsafe fn nextLine(s: *[u8], offset: *mut u32) -> *[u8] { |
|
| 99 | 99 | let start = *offset; |
|
| 100 | 100 | let mut i = start; |
|
| 101 | 101 | ||
| 102 | 102 | while i < s.len and s[i] <> '\n' { |
|
| 103 | 103 | set i += 1; |
| 110 | 110 | } |
|
| 111 | 111 | return line; |
|
| 112 | 112 | } |
|
| 113 | 113 | ||
| 114 | 114 | /// Compare two strings ignoring comments, line by line. |
|
| 115 | - | fn stringsEqual(a: *[u8], b: *[u8]) -> bool { |
|
| 115 | + | unsafe fn stringsEqual(a: *[u8], b: *[u8]) -> bool { |
|
| 116 | 116 | let mut ai: u32 = 0; |
|
| 117 | 117 | let mut bi: u32 = 0; |
|
| 118 | 118 | ||
| 119 | 119 | while ai < a.len or bi < b.len { |
|
| 120 | 120 | let mut aLine = ""; |
| 131 | 131 | } |
|
| 132 | 132 | return true; |
|
| 133 | 133 | } |
|
| 134 | 134 | ||
| 135 | 135 | /// Print diff between expected and actual output. |
|
| 136 | - | fn printDiff(expected: *[u8], actual: *[u8]) { |
|
| 136 | + | unsafe fn printDiff(expected: *[u8], actual: *[u8]) { |
|
| 137 | 137 | io::printLn("\n// Expected"); |
|
| 138 | 138 | io::print(expected); |
|
| 139 | 139 | io::print("\n"); |
|
| 140 | 140 | ||
| 141 | 141 | io::printLn("// Actual"); |
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | 146 | /// Derive the `.ril` path from a `.rad` source path. Returns nil if the path |
|
| 147 | 147 | /// does not end in `.rad` or the buffer is too small. The result is |
|
| 148 | 148 | /// null-terminated for use with syscalls. |
|
| 149 | - | fn deriveRilPath(sourcePath: *[u8], buf: *mut [u8]) -> ?*[u8] { |
|
| 149 | + | unsafe fn deriveRilPath(sourcePath: *[u8], buf: *mut [u8]) -> ?*[u8] { |
|
| 150 | 150 | let len = sourcePath.len; |
|
| 151 | 151 | if len < SOURCE_EXT.len { |
|
| 152 | 152 | return nil; |
|
| 153 | 153 | } |
|
| 154 | 154 | let extStart = len - SOURCE_EXT.len; |
| 170 | 170 | ||
| 171 | 171 | return &buf[..len]; |
|
| 172 | 172 | } |
|
| 173 | 173 | ||
| 174 | 174 | /// Write a self-contained RV64 image containing text and data sections. |
|
| 175 | - | fn writeImage(code: *[u32], roData: *[u8], rwData: *[u8], path: *[u8]) -> bool { |
|
| 175 | + | unsafe fn writeImage(code: *[u32], roData: *[u8], rwData: *[u8], path: *[u8]) -> bool { |
|
| 176 | 176 | let mut header = rv64::imageHeader(code.len * rv64::INSTR_SIZE as u32, roData.len, rwData.len); |
|
| 177 | 177 | let headerWords = &header[..]; |
|
| 178 | 178 | let headerBytes = @sliceOf(headerWords.ptr as *u8, headerWords.len * rv64::WORD_SIZE as u32); |
|
| 179 | 179 | let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32); |
|
| 180 | 180 | ||
| 181 | - | return unix::writeFileParts(path, &[headerBytes, codeBytes, roData, rwData]); |
|
| 181 | + | let mut success = false; |
|
| 182 | + | unsafe { |
|
| 183 | + | set success = unix::writeFileParts(path, &[headerBytes, codeBytes, roData, rwData]); |
|
| 184 | + | } |
|
| 185 | + | return success; |
|
| 182 | 186 | } |
|
| 183 | 187 | ||
| 184 | - | fn assembleBinary(sourcePath: *[u8], outputPath: *[u8]) -> bool { |
|
| 185 | - | let source = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else { |
|
| 188 | + | /// Assemble one source file and write its RV64 image. |
|
| 189 | + | unsafe fn assembleBinary(sourcePath: *[u8], outputPath: *[u8]) -> bool { |
|
| 190 | + | let mut sourceResult: ?*[u8] = nil; |
|
| 191 | + | unsafe { set sourceResult = unix::readFile(sourcePath, &mut SOURCE_BUF[..]); } |
|
| 192 | + | let source = sourceResult else { |
|
| 186 | 193 | io::printError("error: could not read source: "); |
|
| 187 | 194 | io::printError(sourcePath); |
|
| 188 | 195 | io::printError("\n"); |
|
| 189 | 196 | return false; |
|
| 190 | 197 | }; |
| 213 | 220 | } |
|
| 214 | 221 | return true; |
|
| 215 | 222 | } |
|
| 216 | 223 | ||
| 217 | 224 | /// Run a single IL snapshot test case. Returns `true` on success. |
|
| 218 | - | fn runTest(sourcePath: *[u8]) -> bool { |
|
| 225 | + | unsafe fn runTest(sourcePath: *[u8]) -> bool { |
|
| 219 | 226 | // Path buffer. |
|
| 220 | 227 | let mut rilPathBuf: [u8; MAX_PATH_LEN] = undefined; |
|
| 221 | 228 | let mut pkgScope: resolver::Scope = undefined; |
|
| 222 | 229 | ||
| 223 | 230 | // Derive .ril path from source path. |
| 226 | 233 | io::printLn(sourcePath); |
|
| 227 | 234 | return false; |
|
| 228 | 235 | }; |
|
| 229 | 236 | ||
| 230 | 237 | // Read expected IL. |
|
| 231 | - | let expected = unix::readFile(rilPath, &mut EXPECTED_BUF[..]) else { |
|
| 238 | + | let mut expectedResult: ?*[u8] = nil; |
|
| 239 | + | unsafe { set expectedResult = unix::readFile(rilPath, &mut EXPECTED_BUF[..]); } |
|
| 240 | + | let expected = expectedResult else { |
|
| 232 | 241 | io::print("error: could not read expected IL: "); |
|
| 233 | 242 | io::printLn(rilPath); |
|
| 234 | 243 | return false; |
|
| 235 | 244 | }; |
|
| 236 | 245 | ||
| 237 | 246 | // Read source file. |
|
| 238 | - | let source = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else { |
|
| 247 | + | let mut sourceResult: ?*[u8] = nil; |
|
| 248 | + | unsafe { set sourceResult = unix::readFile(sourcePath, &mut SOURCE_BUF[..]); } |
|
| 249 | + | let source = sourceResult else { |
|
| 239 | 250 | io::print("error: could not read source: "); |
|
| 240 | 251 | io::printLn(sourcePath); |
|
| 241 | 252 | return false; |
|
| 242 | 253 | }; |
|
| 243 | 254 |
| 289 | 300 | ||
| 290 | 301 | return true; |
|
| 291 | 302 | } |
|
| 292 | 303 | ||
| 293 | 304 | /// Run a single test specified as an argument. |
|
| 294 | - | @default fn main(env: *sys::Env) -> i32 { |
|
| 305 | + | @default unsafe fn main(env: *sys::Env) -> i32 { |
|
| 295 | 306 | let args = env.args; |
|
| 296 | 307 | ||
| 297 | 308 | if args.len == 4 and mem::eq(args[1], "assemble") { |
|
| 298 | 309 | if assembleBinary(args[2], args[3]) { |
|
| 299 | 310 | return 0; |
test/tests/array.aggregate.stride.rad
+2 -2
| 8 | 8 | fn arrayOfRecords(arr: [Point; 3], idx: u32) -> i32 { |
|
| 9 | 9 | return arr[idx].x; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// Take address of record in array. |
|
| 13 | - | fn addressOfRecord(arr: [Point; 3], idx: u32) -> *Point { |
|
| 14 | - | return &arr[idx]; |
|
| 13 | + | unsafe fn addressOfRecord(arr: [Point; 3], idx: u32) -> *Point { |
|
| 14 | + | return &arr[idx] as *Point; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Array of arrays (16 bytes per element). |
|
| 18 | 18 | fn arrayOfArrays(arr: [[i32; 4]; 3], idx: u32) -> i32 { |
|
| 19 | 19 | return arr[idx][0]; |
test/tests/array.slice.empty.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | - | fn length(slice: *[u8]) -> u32 { |
|
| 2 | + | fn length(slice: &[u8]) -> u32 { |
|
| 3 | 3 | return slice.len; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | @default fn main() -> u32 { |
|
| 7 | 7 | return length(&[]); |
test/tests/array.slice.full.rad
+2 -2
| 1 | 1 | /// Creates a slice from an array with full range. |
|
| 2 | - | fn sliceFull(a: [i32; 4]) -> *[i32] { |
|
| 3 | - | return &a[..]; |
|
| 2 | + | unsafe fn sliceFull(a: [i32; 4]) -> *[i32] { |
|
| 3 | + | return &a[..] as *[i32]; |
|
| 4 | 4 | } |
test/tests/array.slice.gen.end.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[..2]; |
|
| 4 | + | let mut slice: *[i32] = &arr[..2] as *[i32]; |
|
| 5 | 5 | ||
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/array.slice.gen.index.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[1..]; |
|
| 4 | + | let mut slice: *[i32] = &arr[1..] as *[i32]; |
|
| 5 | 5 | ||
| 6 | 6 | return (slice[1]) - 3; |
|
| 7 | 7 | } |
test/tests/array.slice.gen.open.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[..]; |
|
| 4 | + | let mut slice: *[i32] = &arr[..] as *[i32]; |
|
| 5 | 5 | ||
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/array.slice.gen.start.end.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[1..3]; |
|
| 4 | + | let mut slice: *[i32] = &arr[1..3] as *[i32]; |
|
| 5 | 5 | ||
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/array.slice.gen.start.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[2..]; |
|
| 4 | + | let mut slice: *[i32] = &arr[2..] as *[i32]; |
|
| 5 | 5 | ||
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/array.slice.openend.rad
+2 -2
| 1 | 1 | /// Creates a slice from an array with open end bound. |
|
| 2 | - | fn sliceOpenEnd(a: [i32; 4], start: u32) -> *[i32] { |
|
| 3 | - | return &a[start..]; |
|
| 2 | + | unsafe fn sliceOpenEnd(a: [i32; 4], start: u32) -> *[i32] { |
|
| 3 | + | return &a[start..] as *[i32]; |
|
| 4 | 4 | } |
test/tests/array.slice.openstart.rad
+2 -2
| 1 | 1 | /// Creates a slice from an array with open start bound. |
|
| 2 | - | fn sliceOpenStart(a: [i32; 4], end: u32) -> *[i32] { |
|
| 3 | - | return &a[..end]; |
|
| 2 | + | unsafe fn sliceOpenStart(a: [i32; 4], end: u32) -> *[i32] { |
|
| 3 | + | return &a[..end] as *[i32]; |
|
| 4 | 4 | } |
test/tests/array.slice.rad
+5 -5
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test array slicing with various ranges. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | let arr: [i32; 5] = [1, 2, 3, 4, 5]; |
|
| 6 | 6 | ||
| 7 | - | let slice1: *[i32] = &arr[..]; |
|
| 8 | - | let slice2: *[i32] = &arr[1..4]; |
|
| 9 | - | let slice3: *[i32] = &arr[..3]; |
|
| 10 | - | let slice4: *[i32] = &arr[2..]; |
|
| 7 | + | let slice1: *[i32] = &arr[..] as *[i32]; |
|
| 8 | + | let slice2: *[i32] = &arr[1..4] as *[i32]; |
|
| 9 | + | let slice3: *[i32] = &arr[..3] as *[i32]; |
|
| 10 | + | let slice4: *[i32] = &arr[2..] as *[i32]; |
|
| 11 | 11 | ||
| 12 | 12 | assert slice1.len == 5; |
|
| 13 | 13 | assert slice2.len == 3; |
|
| 14 | 14 | assert slice3.len == 3; |
|
| 15 | 15 | assert slice4.len == 3; |
test/tests/asm.rodata.prefix.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | 3 | fn asmDataValue() -> i32; |
|
| 4 | 4 | ||
| 5 | - | @default fn main() -> i32 { |
|
| 5 | + | @default unsafe fn main() -> i32 { |
|
| 6 | 6 | let s = "hello"; |
|
| 7 | 7 | ||
| 8 | 8 | assert s.len == 5; |
|
| 9 | 9 | assert s[0] == 'h' as u8; |
|
| 10 | 10 | assert s[4] == 'o' as u8; |
test/tests/average.rad
+1 -1
| 1 | 1 | /// Compute the sum of a list of numbers. |
|
| 2 | - | fn average(numbers: *[u32]) -> u32 { |
|
| 2 | + | fn average(numbers: &[u32]) -> u32 { |
|
| 3 | 3 | let mut sum: u32 = 0; |
|
| 4 | 4 | ||
| 5 | 5 | for i in numbers { |
|
| 6 | 6 | set sum += i; |
|
| 7 | 7 | } |
test/tests/bool.comparison.slice.rad
+46 -46
| 1 | 1 | //! returns: 0 |
|
| 2 | - | fn memEq(a: *[u8], b: *[u8]) -> bool { |
|
| 2 | + | unsafe fn memEq(a: *[u8], b: *[u8]) -> bool { |
|
| 3 | 3 | if a.len <> b.len { |
|
| 4 | 4 | return false; |
|
| 5 | 5 | } |
|
| 6 | 6 | for i in 0..a.len { |
|
| 7 | 7 | if a[i] <> b[i] { |
| 17 | 17 | ||
| 18 | 18 | fn sliceI32(input: *[i32]) -> *[i32] { |
|
| 19 | 19 | return input; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | fn sliceEqualU32(a: *[u32], b: *[u32]) -> bool { |
|
| 22 | + | unsafe fn sliceEqualU32(a: *[u32], b: *[u32]) -> bool { |
|
| 23 | 23 | if a.len <> b.len { |
|
| 24 | 24 | return false; |
|
| 25 | 25 | } |
|
| 26 | 26 | for i in 0..a.len { |
|
| 27 | 27 | if a[i] <> b[i] { |
| 29 | 29 | } |
|
| 30 | 30 | } |
|
| 31 | 31 | return true; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | - | fn sliceEqualI32(a: *[i32], b: *[i32]) -> bool { |
|
| 34 | + | unsafe fn sliceEqualI32(a: *[i32], b: *[i32]) -> bool { |
|
| 35 | 35 | if a.len <> b.len { |
|
| 36 | 36 | return false; |
|
| 37 | 37 | } |
|
| 38 | 38 | for i in 0..a.len { |
|
| 39 | 39 | if a[i] <> b[i] { |
| 41 | 41 | } |
|
| 42 | 42 | } |
|
| 43 | 43 | return true; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | - | fn sliceEqual1() -> bool { |
|
| 46 | + | unsafe fn sliceEqual1() -> bool { |
|
| 47 | 47 | let a1: [u32; 3] = [9, 42, 3]; |
|
| 48 | 48 | ||
| 49 | - | let s1: *[u32] = &a1[..]; |
|
| 50 | - | let s2: *[u32] = &a1[..]; |
|
| 49 | + | let s1: *[u32] = &a1[..] as *[u32]; |
|
| 50 | + | let s2: *[u32] = &a1[..] as *[u32]; |
|
| 51 | 51 | ||
| 52 | 52 | return s1 == s2 and sliceEqualU32(s1, s2); |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | - | fn sliceEqual2() -> bool { |
|
| 55 | + | unsafe fn sliceEqual2() -> bool { |
|
| 56 | 56 | let a1: [u32; 3] = [9, 42, 3]; |
|
| 57 | 57 | ||
| 58 | - | let s1: *[u32] = &a1[1..]; |
|
| 59 | - | let s2: *[u32] = &a1[1..]; |
|
| 58 | + | let s1: *[u32] = &a1[1..] as *[u32]; |
|
| 59 | + | let s2: *[u32] = &a1[1..] as *[u32]; |
|
| 60 | 60 | ||
| 61 | 61 | return s1 == s2 and sliceEqualU32(s1, s2); |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn sliceEqual3() -> bool { |
|
| 64 | + | unsafe fn sliceEqual3() -> bool { |
|
| 65 | 65 | let a1: [u32; 3] = [9, 42, 3]; |
|
| 66 | 66 | ||
| 67 | - | let s1: *[u32] = &a1[..1]; |
|
| 68 | - | let s2: *[u32] = &a1[..1]; |
|
| 67 | + | let s1: *[u32] = &a1[..1] as *[u32]; |
|
| 68 | + | let s2: *[u32] = &a1[..1] as *[u32]; |
|
| 69 | 69 | ||
| 70 | 70 | return s1 == s2 and sliceEqualU32(s1, s2); |
|
| 71 | 71 | } |
|
| 72 | 72 | ||
| 73 | - | fn sliceEqual4() -> bool { |
|
| 73 | + | unsafe fn sliceEqual4() -> bool { |
|
| 74 | 74 | let a1: [u32; 3] = [9, 42, 3]; |
|
| 75 | 75 | ||
| 76 | - | let s1: *[u32] = &a1[2..]; |
|
| 77 | - | let s2: *[u32] = &a1[2..]; |
|
| 76 | + | let s1: *[u32] = &a1[2..] as *[u32]; |
|
| 77 | + | let s2: *[u32] = &a1[2..] as *[u32]; |
|
| 78 | 78 | ||
| 79 | 79 | return s1 == s2 and sliceEqualU32(s1, s2); |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | - | fn sliceNotEqualSameArray1() -> bool { |
|
| 82 | + | unsafe fn sliceNotEqualSameArray1() -> bool { |
|
| 83 | 83 | let a1: [u32; 3] = [42, 8, 3]; |
|
| 84 | 84 | ||
| 85 | - | let s1: *[u32] = &a1[..2]; |
|
| 86 | - | let s2: *[u32] = &a1[1..3]; |
|
| 85 | + | let s1: *[u32] = &a1[..2] as *[u32]; |
|
| 86 | + | let s2: *[u32] = &a1[1..3] as *[u32]; |
|
| 87 | 87 | ||
| 88 | 88 | return s1 <> s2 and not sliceEqualU32(s1, s2); |
|
| 89 | 89 | } |
|
| 90 | 90 | ||
| 91 | - | fn sliceNotEqualSameArray2() -> bool { |
|
| 91 | + | unsafe fn sliceNotEqualSameArray2() -> bool { |
|
| 92 | 92 | let a1: [u32; 3] = [42, 8, 3]; |
|
| 93 | 93 | ||
| 94 | - | let s1: *[u32] = &a1[..2]; |
|
| 95 | - | let s2: *[u32] = &a1[..3]; |
|
| 94 | + | let s1: *[u32] = &a1[..2] as *[u32]; |
|
| 95 | + | let s2: *[u32] = &a1[..3] as *[u32]; |
|
| 96 | 96 | ||
| 97 | 97 | return s1 <> s2 and not sliceEqualU32(s1, s2); |
|
| 98 | 98 | } |
|
| 99 | 99 | ||
| 100 | - | fn sliceEqualDifferentArray() -> bool { |
|
| 100 | + | unsafe fn sliceEqualDifferentArray() -> bool { |
|
| 101 | 101 | let a1: [u32; 3] = [42, 8, 3]; |
|
| 102 | 102 | let a2: [u32; 3] = [42, 8, 3]; |
|
| 103 | 103 | ||
| 104 | - | let s1: *[u32] = &a1[..]; |
|
| 105 | - | let s2: *[u32] = &a2[..]; |
|
| 104 | + | let s1: *[u32] = &a1[..] as *[u32]; |
|
| 105 | + | let s2: *[u32] = &a2[..] as *[u32]; |
|
| 106 | 106 | ||
| 107 | 107 | return sliceEqualU32(s1, s2) and s1 <> s2; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | - | fn sliceEqualString1() -> bool { |
|
| 110 | + | unsafe fn sliceEqualString1() -> bool { |
|
| 111 | 111 | let s1: *[u8] = "ABC"; |
|
| 112 | 112 | let s2: *[u8] = "ABC"; |
|
| 113 | 113 | ||
| 114 | 114 | return memEq(s1, s2); |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | - | fn sliceEqualString2() -> bool { |
|
| 117 | + | unsafe fn sliceEqualString2() -> bool { |
|
| 118 | 118 | let s1: *[u8] = "ABC"; |
|
| 119 | 119 | let s2: *[u8] = "DEF"; |
|
| 120 | 120 | ||
| 121 | 121 | return not memEq(s1, s2); |
|
| 122 | 122 | } |
|
| 123 | 123 | ||
| 124 | - | fn sliceEqualString3() -> bool { |
|
| 124 | + | unsafe fn sliceEqualString3() -> bool { |
|
| 125 | 125 | let a1: [u8; 3] = ['A', 'B', 'C']; |
|
| 126 | - | let s1: *[u8] = &a1[..]; |
|
| 126 | + | let s1: *[u8] = &a1[..] as *[u8]; |
|
| 127 | 127 | let s2: *[u8] = "ABC"; |
|
| 128 | 128 | ||
| 129 | 129 | return memEq(s1, s2) |
|
| 130 | 130 | and memEq(s1, "ABC") |
|
| 131 | - | and memEq(&a1[..], "ABC"); |
|
| 131 | + | and memEq(&a1[..] as *[u8], "ABC"); |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | - | fn sliceEqualString4() -> bool { |
|
| 134 | + | unsafe fn sliceEqualString4() -> bool { |
|
| 135 | 135 | let s1: *[u8] = "ABC"; |
|
| 136 | - | let s2: *[u8] = &['A', 'B', 'C']; |
|
| 136 | + | let s2: *[u8] = &['A', 'B', 'C'] as *[u8]; |
|
| 137 | 137 | ||
| 138 | 138 | return memEq(s1, s2); |
|
| 139 | 139 | } |
|
| 140 | 140 | ||
| 141 | - | fn sliceEqualString5() -> bool { |
|
| 141 | + | unsafe fn sliceEqualString5() -> bool { |
|
| 142 | 142 | let s1: *[u8] = "ABC"; |
|
| 143 | 143 | ||
| 144 | 144 | // Sub-slicing a slice currently trips a separate compiler bug. |
|
| 145 | 145 | // Keep this check focused on slice equality behavior itself. |
|
| 146 | 146 | return memEq(s1, "ABC"); |
|
| 147 | 147 | } |
|
| 148 | 148 | ||
| 149 | - | fn sliceNotEqualU8() -> bool { |
|
| 149 | + | unsafe fn sliceNotEqualU8() -> bool { |
|
| 150 | 150 | let a: [u8; 3] = [1, 2, 3]; |
|
| 151 | 151 | let b: [u8; 3] = [1, 2, 4]; |
|
| 152 | - | let s: *[u8] = &[1, 2, 3]; |
|
| 152 | + | let s: *[u8] = &[1, 2, 3] as *[u8]; |
|
| 153 | 153 | ||
| 154 | - | return not memEq(s, &[1, 2, 4]) |
|
| 155 | - | and not memEq(s, &[1, 0, 3]) |
|
| 154 | + | return not memEq(s, &[1, 2, 4] as *[u8]) |
|
| 155 | + | and not memEq(s, &[1, 0, 3] as *[u8]) |
|
| 156 | 156 | and not memEq("ABC", "ABCD") |
|
| 157 | 157 | and not memEq("ABC", "ABD") |
|
| 158 | - | and not memEq(&a[..], &a[1..]) |
|
| 159 | - | and not memEq(&a[..], &b[..]) |
|
| 160 | - | and not memEq(&a[..], &[1, 3, 3]); |
|
| 158 | + | and not memEq(&a[..] as *[u8], &a[1..] as *[u8]) |
|
| 159 | + | and not memEq(&a[..] as *[u8], &b[..] as *[u8]) |
|
| 160 | + | and not memEq(&a[..] as *[u8], &[1, 3, 3] as *[u8]); |
|
| 161 | 161 | } |
|
| 162 | 162 | ||
| 163 | - | fn sliceNotEqualU16() -> bool { |
|
| 163 | + | unsafe fn sliceNotEqualU16() -> bool { |
|
| 164 | 164 | let a: [u16; 3] = [1, 2, 3]; |
|
| 165 | 165 | let b: [u16; 3] = [1, 2, 4]; |
|
| 166 | - | let s: *[u16] = &[1, 2, 3]; |
|
| 166 | + | let s: *[u16] = &[1, 2, 3] as *[u16]; |
|
| 167 | 167 | ||
| 168 | - | return s <> &[1, 2, 4] |
|
| 169 | - | and s <> &[1, 0, 3] |
|
| 168 | + | return s <> &[1, 2, 4] as *[u16] |
|
| 169 | + | and s <> &[1, 0, 3] as *[u16] |
|
| 170 | 170 | and &a[..] <> &a[1..] |
|
| 171 | 171 | and &a[..] <> &b[..] |
|
| 172 | 172 | and &a[..] <> &[1, 3, 3]; |
|
| 173 | 173 | } |
|
| 174 | 174 | ||
| 175 | - | fn sliceReturnEqual() -> bool { |
|
| 175 | + | unsafe fn sliceReturnEqual() -> bool { |
|
| 176 | 176 | return memEq(sliceU8("ABC"), "ABC") |
|
| 177 | - | and sliceEqualI32(sliceI32(&[1, 2, 3]), &[1, 2, 3]); |
|
| 177 | + | and sliceEqualI32(sliceI32(&[1, 2, 3] as *[i32]), &[1, 2, 3] as *[i32]); |
|
| 178 | 178 | } |
|
| 179 | 179 | ||
| 180 | - | @default fn main() -> i32 { |
|
| 180 | + | @default unsafe fn main() -> i32 { |
|
| 181 | 181 | assert sliceEqual1(); |
|
| 182 | 182 | assert sliceEqual2(); |
|
| 183 | 183 | assert sliceEqual3(); |
|
| 184 | 184 | assert sliceEqual4(); |
|
| 185 | 185 | assert sliceEqualString1(); |
test/tests/bool.comparison.slice.record.gen.rad
+15 -15
| 7 | 7 | ||
| 8 | 8 | record Line { |
|
| 9 | 9 | points: *[Point], |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | fn pointsEqual(a: *[Point], b: *[Point]) -> bool { |
|
| 12 | + | unsafe fn pointsEqual(a: *[Point], b: *[Point]) -> bool { |
|
| 13 | 13 | if a.len <> b.len { |
|
| 14 | 14 | return false; |
|
| 15 | 15 | } |
|
| 16 | 16 | for i in 0..a.len { |
|
| 17 | 17 | if a[i].x <> b[i].x or a[i].y <> b[i].y { |
| 19 | 19 | } |
|
| 20 | 20 | } |
|
| 21 | 21 | return true; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | fn lineEqual(a: Line, b: Line) -> bool { |
|
| 24 | + | unsafe fn lineEqual(a: Line, b: Line) -> bool { |
|
| 25 | 25 | return pointsEqual(a.points, b.points); |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn testSliceStructEqual() -> bool { |
|
| 28 | + | unsafe fn testSliceStructEqual() -> bool { |
|
| 29 | 29 | let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 30 | 30 | let b: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 31 | 31 | ||
| 32 | - | return pointsEqual(&a[..], &b[..]); |
|
| 32 | + | return pointsEqual(&a[..] as *[Point], &b[..] as *[Point]); |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | - | fn testSliceStructNotEqualContent() -> bool { |
|
| 35 | + | unsafe fn testSliceStructNotEqualContent() -> bool { |
|
| 36 | 36 | let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 37 | 37 | let b: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }]; |
|
| 38 | 38 | ||
| 39 | - | return not pointsEqual(&a[..], &b[..]); |
|
| 39 | + | return not pointsEqual(&a[..] as *[Point], &b[..] as *[Point]); |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | - | fn testSliceStructNotEqualLength() -> bool { |
|
| 42 | + | unsafe fn testSliceStructNotEqualLength() -> bool { |
|
| 43 | 43 | let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 44 | 44 | let b: [Point; 1] = [Point { x: 1, y: 2 }]; |
|
| 45 | 45 | ||
| 46 | - | return not pointsEqual(&a[..], &b[..]); |
|
| 46 | + | return not pointsEqual(&a[..] as *[Point], &b[..] as *[Point]); |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | - | fn testStructWithSliceFieldEqual() -> bool { |
|
| 49 | + | unsafe fn testStructWithSliceFieldEqual() -> bool { |
|
| 50 | 50 | let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 51 | 51 | let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 52 | 52 | ||
| 53 | - | let line1: Line = Line { points: &pts1[..] }; |
|
| 54 | - | let line2: Line = Line { points: &pts2[..] }; |
|
| 53 | + | let line1: Line = Line { points: &pts1[..] as *[Point] }; |
|
| 54 | + | let line2: Line = Line { points: &pts2[..] as *[Point] }; |
|
| 55 | 55 | ||
| 56 | 56 | return lineEqual(line1, line2); |
|
| 57 | 57 | } |
|
| 58 | 58 | ||
| 59 | - | fn testStructWithSliceFieldNotEqual() -> bool { |
|
| 59 | + | unsafe fn testStructWithSliceFieldNotEqual() -> bool { |
|
| 60 | 60 | let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 61 | 61 | let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }]; |
|
| 62 | 62 | ||
| 63 | - | let line1: Line = Line { points: &pts1[..] }; |
|
| 64 | - | let line2: Line = Line { points: &pts2[..] }; |
|
| 63 | + | let line1: Line = Line { points: &pts1[..] as *[Point] }; |
|
| 64 | + | let line2: Line = Line { points: &pts2[..] as *[Point] }; |
|
| 65 | 65 | ||
| 66 | 66 | return not lineEqual(line1, line2); |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | - | @default fn main() -> i32 { |
|
| 69 | + | @default unsafe fn main() -> i32 { |
|
| 70 | 70 | assert testSliceStructEqual(); |
|
| 71 | 71 | assert testSliceStructNotEqualContent(); |
|
| 72 | 72 | assert testSliceStructNotEqualLength(); |
|
| 73 | 73 | assert testStructWithSliceFieldEqual(); |
|
| 74 | 74 | assert testStructWithSliceFieldNotEqual(); |
test/tests/bool.comparison.slice.union.gen.rad
+16 -16
| 13 | 13 | union Shape { |
|
| 14 | 14 | points(*[Point]), |
|
| 15 | 15 | sizes(*[Size]), |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | fn pointsEqual(a: *[Point], b: *[Point]) -> bool { |
|
| 18 | + | unsafe fn pointsEqual(a: *[Point], b: *[Point]) -> bool { |
|
| 19 | 19 | if a.len <> b.len { |
|
| 20 | 20 | return false; |
|
| 21 | 21 | } |
|
| 22 | 22 | for i in 0..a.len { |
|
| 23 | 23 | if a[i].x <> b[i].x or a[i].y <> b[i].y { |
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | return true; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | - | fn sizesEqual(a: *[Size], b: *[Size]) -> bool { |
|
| 30 | + | unsafe fn sizesEqual(a: *[Size], b: *[Size]) -> bool { |
|
| 31 | 31 | if a.len <> b.len { |
|
| 32 | 32 | return false; |
|
| 33 | 33 | } |
|
| 34 | 34 | for i in 0..a.len { |
|
| 35 | 35 | if a[i].width <> b[i].width or a[i].height <> b[i].height { |
| 37 | 37 | } |
|
| 38 | 38 | } |
|
| 39 | 39 | return true; |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | - | fn shapeEqual(a: Shape, b: Shape) -> bool { |
|
| 42 | + | unsafe fn shapeEqual(a: Shape, b: Shape) -> bool { |
|
| 43 | 43 | if let case Shape::points(pointsA) = a { |
|
| 44 | 44 | if let case Shape::points(pointsB) = b { |
|
| 45 | 45 | return pointsEqual(pointsA, pointsB); |
|
| 46 | 46 | } |
|
| 47 | 47 | return false; |
| 53 | 53 | return false; |
|
| 54 | 54 | } |
|
| 55 | 55 | return false; |
|
| 56 | 56 | } |
|
| 57 | 57 | ||
| 58 | - | fn testEnumSliceEqual() -> bool { |
|
| 58 | + | unsafe fn testEnumSliceEqual() -> bool { |
|
| 59 | 59 | let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 60 | 60 | let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 61 | 61 | ||
| 62 | - | let s1: Shape = Shape::points(&pts1[..]); |
|
| 63 | - | let s2: Shape = Shape::points(&pts2[..]); |
|
| 62 | + | let s1: Shape = Shape::points(&pts1[..] as *[Point]); |
|
| 63 | + | let s2: Shape = Shape::points(&pts2[..] as *[Point]); |
|
| 64 | 64 | ||
| 65 | 65 | return shapeEqual(s1, s2); |
|
| 66 | 66 | } |
|
| 67 | 67 | ||
| 68 | - | fn testEnumSliceNotEqual() -> bool { |
|
| 68 | + | unsafe fn testEnumSliceNotEqual() -> bool { |
|
| 69 | 69 | let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 70 | 70 | let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }]; |
|
| 71 | 71 | ||
| 72 | - | let s1: Shape = Shape::points(&pts1[..]); |
|
| 73 | - | let s2: Shape = Shape::points(&pts2[..]); |
|
| 72 | + | let s1: Shape = Shape::points(&pts1[..] as *[Point]); |
|
| 73 | + | let s2: Shape = Shape::points(&pts2[..] as *[Point]); |
|
| 74 | 74 | ||
| 75 | 75 | return not shapeEqual(s1, s2); |
|
| 76 | 76 | } |
|
| 77 | 77 | ||
| 78 | - | fn testEnumSizesSliceEqual() -> bool { |
|
| 78 | + | unsafe fn testEnumSizesSliceEqual() -> bool { |
|
| 79 | 79 | let sizes1: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }]; |
|
| 80 | 80 | let sizes2: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }]; |
|
| 81 | 81 | ||
| 82 | - | let s1: Shape = Shape::sizes(&sizes1[..]); |
|
| 83 | - | let s2: Shape = Shape::sizes(&sizes2[..]); |
|
| 82 | + | let s1: Shape = Shape::sizes(&sizes1[..] as *[Size]); |
|
| 83 | + | let s2: Shape = Shape::sizes(&sizes2[..] as *[Size]); |
|
| 84 | 84 | ||
| 85 | 85 | return shapeEqual(s1, s2); |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn testEnumDifferentVariants() -> bool { |
|
| 88 | + | unsafe fn testEnumDifferentVariants() -> bool { |
|
| 89 | 89 | let pts: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 90 | 90 | let sizes: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }]; |
|
| 91 | 91 | ||
| 92 | - | let s1: Shape = Shape::points(&pts[..]); |
|
| 93 | - | let s2: Shape = Shape::sizes(&sizes[..]); |
|
| 92 | + | let s1: Shape = Shape::points(&pts[..] as *[Point]); |
|
| 93 | + | let s2: Shape = Shape::sizes(&sizes[..] as *[Size]); |
|
| 94 | 94 | ||
| 95 | 95 | return not shapeEqual(s1, s2); |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | - | @default fn main() -> i32 { |
|
| 98 | + | @default unsafe fn main() -> i32 { |
|
| 99 | 99 | assert testEnumSliceEqual(); |
|
| 100 | 100 | assert testEnumSliceNotEqual(); |
|
| 101 | 101 | assert testEnumSizesSliceEqual(); |
|
| 102 | 102 | assert testEnumDifferentVariants(); |
|
| 103 | 103 | return 0; |
test/tests/bool.short.circuit.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test short-circuiting behavior of 'and' and 'or' operators. |
|
| 3 | - | fn modify(counter: *mut i32, ret: bool) -> bool { |
|
| 3 | + | fn modify(counter: &mut i32, ret: bool) -> bool { |
|
| 4 | 4 | set *counter += 1; |
|
| 5 | 5 | return ret; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | @default fn main() -> i32 { |
test/tests/builtin.sliceof.invalid.cap.rad
+2 -3
| 1 | 1 | //! returns: 133 |
|
| 2 | 2 | //! Test @sliceOf runtime validation for len > cap. |
|
| 3 | - | ||
| 4 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 4 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 6 | - | let ptr: *i32 = &arr[0]; |
|
| 5 | + | let ptr: *i32 = &arr[0] as *i32; |
|
| 7 | 6 | let slice: *[i32] = @sliceOf(ptr, 4, 3); |
|
| 8 | 7 | ||
| 9 | 8 | return slice.len as i32; |
|
| 10 | 9 | } |
test/tests/builtin.sliceof.mut.rad
+2 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | // Test the @sliceOf builtin with mutable slice. |
|
| 3 | - | ||
| 4 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 4 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 6 | 5 | ||
| 7 | 6 | // Get a mutable pointer to the first element |
|
| 8 | - | let ptr: *mut i32 = &mut arr[0]; |
|
| 7 | + | let ptr: *mut i32 = &mut arr[0] as *mut i32; |
|
| 9 | 8 | ||
| 10 | 9 | // Create a mutable slice from the pointer and length |
|
| 11 | 10 | let slice: *mut [i32] = @sliceOf(ptr, 4); |
|
| 12 | 11 | ||
| 13 | 12 | // Double each element via the slice |
test/tests/builtin.sliceof.rad
+2 -3
| 1 | 1 | //! returns: 100 |
|
| 2 | 2 | // Test @sliceOf builtin: create a slice from a pointer and length. |
|
| 3 | - | ||
| 4 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 4 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 6 | 5 | ||
| 7 | 6 | // Get a pointer to the first element |
|
| 8 | - | let ptr: *i32 = &arr[0]; |
|
| 7 | + | let ptr: *i32 = &arr[0] as *i32; |
|
| 9 | 8 | ||
| 10 | 9 | // Create a slice from the pointer and length |
|
| 11 | 10 | let slice: *[i32] = @sliceOf(ptr, 4); |
|
| 12 | 11 | ||
| 13 | 12 | // Access elements via the slice and sum them |
test/tests/byte.load.store.rad
+4 -4
| 1 | 1 | // Test byte-level load and store operations (W8) |
|
| 2 | 2 | ||
| 3 | - | fn loadByte(ptr: *u8) -> u8 { |
|
| 3 | + | unsafe fn loadByte(ptr: *u8) -> u8 { |
|
| 4 | 4 | return *ptr; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn storeByte(ptr: *mut u8, val: u8) { |
|
| 7 | + | unsafe fn storeByte(ptr: *mut u8, val: u8) { |
|
| 8 | 8 | set *ptr = val; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | fn loadStoreByte(src: *u8, dst: *mut u8) { |
|
| 11 | + | unsafe fn loadStoreByte(src: *u8, dst: *mut u8) { |
|
| 12 | 12 | set *dst = *src; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | fn byteArrayAccess(arr: [u8; 4], idx: u32) -> u8 { |
|
| 16 | 16 | return arr[idx]; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | fn byteSliceAccess(slice: *[u8], idx: u32) -> u8 { |
|
| 19 | + | unsafe fn byteSliceAccess(slice: *[u8], idx: u32) -> u8 { |
|
| 20 | 20 | return slice[idx]; |
|
| 21 | 21 | } |
test/tests/call.aggregate.arg.snapshot.rad
+1 -1
| 5 | 5 | record Pair { |
|
| 6 | 6 | x: i32, |
|
| 7 | 7 | y: i32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | fn mutate(pair: *mut Pair) -> i32 { |
|
| 10 | + | fn mutate(pair: &mut Pair) -> i32 { |
|
| 11 | 11 | set pair.x = 99; |
|
| 12 | 12 | return 0; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | fn first(pair: Pair, ignored: i32) -> i32 { |
test/tests/call.clobber.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that values live across function calls are not clobbered. |
|
| 3 | - | fn modify(counter: *mut i32, ret: bool) -> bool { |
|
| 3 | + | unsafe fn modify(counter: *mut i32, ret: bool) -> bool { |
|
| 4 | 4 | set *counter += 1; |
|
| 5 | 5 | return ret; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let mut x: i32 = 0; |
|
| 10 | - | let r: bool = modify(&mut x, false); |
|
| 10 | + | let r: bool = modify(&mut x as *mut i32, false); |
|
| 11 | 11 | assert x == 1; |
|
| 12 | 12 | assert not r; |
|
| 13 | 13 | ||
| 14 | 14 | // Pointer live across call. |
|
| 15 | 15 | let mut y: i32 = 42; |
|
| 16 | - | let p: *mut i32 = &mut y; |
|
| 16 | + | let p: *mut i32 = &mut y as *mut i32; |
|
| 17 | 17 | let r2: bool = modify(p, true); |
|
| 18 | 18 | assert *p == 43; |
|
| 19 | 19 | assert r2; |
|
| 20 | 20 | return 0; |
|
| 21 | 21 | } |
test/tests/cast.basic.rad
+1 -1
| 37 | 37 | fn i32ToI16(x: i32) -> i16 { |
|
| 38 | 38 | return x as i16; |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Cast pointer types (same size, no instruction). |
|
| 42 | - | fn ptrCast(x: *u8) -> *i32 { |
|
| 42 | + | unsafe fn ptrCast(x: *u8) -> *i32 { |
|
| 43 | 43 | return x as *i32; |
|
| 44 | 44 | } |
test/tests/compound.assign.index.once.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Compound assignment evaluates a side-effecting lvalue exactly once. |
|
| 3 | 3 | ||
| 4 | - | fn nextIndex(calls: *mut u32) -> u32 { |
|
| 4 | + | fn nextIndex(calls: &mut u32) -> u32 { |
|
| 5 | 5 | let index = *calls; |
|
| 6 | 6 | set *calls += 1; |
|
| 7 | 7 | return index; |
|
| 8 | 8 | } |
|
| 9 | 9 |
test/tests/compound.assign.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | /// Test compound assignment operators (+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=). |
|
| 3 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 4 | let mut a: i32 = 10; |
|
| 5 | 5 | ||
| 6 | 6 | // Test += |
|
| 7 | 7 | set a += 5; |
|
| 8 | 8 | assert a == 15; |
| 50 | 50 | set arr[1] += 5; |
|
| 51 | 51 | assert arr[1] == 25; |
|
| 52 | 52 | ||
| 53 | 53 | // Test compound assignment with pointer dereference. |
|
| 54 | 54 | let mut val: i32 = 100; |
|
| 55 | - | let p: *mut i32 = &mut val; |
|
| 55 | + | let p: *mut i32 = &mut val as *mut i32; |
|
| 56 | 56 | set *p += 50; |
|
| 57 | 57 | assert val == 150; |
|
| 58 | 58 | ||
| 59 | 59 | // Test chained compound assignments. |
|
| 60 | 60 | let mut x: i32 = 1; |
test/tests/cond.iflet.optional.rad
+1 -1
| 1 | 1 | // Returns 1 when the optional pointer is present, otherwise returns 0. |
|
| 2 | - | fn ifLetOptional(p: ?*i32) -> i32 { |
|
| 2 | + | unsafe fn ifLetOptional(p: ?*i32) -> i32 { |
|
| 3 | 3 | if let x = p { |
|
| 4 | 4 | return 1; |
|
| 5 | 5 | } else { |
|
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/cond.letelse.optional.rad
+1 -1
| 1 | 1 | // Returns 1 when the optional pointer is present, otherwise returns 0. |
|
| 2 | - | fn letElseOptional(p: ?*i32) -> i32 { |
|
| 2 | + | unsafe fn letElseOptional(p: ?*i32) -> i32 { |
|
| 3 | 3 | let x = p else { return 0; }; |
|
| 4 | 4 | return 1; |
|
| 5 | 5 | } |
test/tests/cond.match.guard.regalloc.rad
+6 -6
| 18 | 18 | span1: u32, |
|
| 19 | 19 | span2: u32, |
|
| 20 | 20 | kind: Kind, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | fn getPath(node: *Node, buf: *mut [*[u8]]) -> *[*[u8]] { |
|
| 24 | - | let mut out: *[*[u8]] = &[]; |
|
| 23 | + | unsafe fn getPath(node: *Node, buf: *mut [*[u8]]) -> *[*[u8]] { |
|
| 24 | + | let mut out: *[*[u8]] = &[] as *[*[u8]]; |
|
| 25 | 25 | ||
| 26 | 26 | match node.kind { |
|
| 27 | 27 | case Kind::Name(name) if name.len > 0 => { |
|
| 28 | 28 | set buf[0] = name; |
|
| 29 | - | set out = &buf[..1]; |
|
| 29 | + | set out = &buf[..1] as *[*[u8]]; |
|
| 30 | 30 | } |
|
| 31 | 31 | case Kind::Pair { a, b } => { |
|
| 32 | - | set out = &buf[..1]; |
|
| 32 | + | set out = &buf[..1] as *[*[u8]]; |
|
| 33 | 33 | } |
|
| 34 | 34 | else => {} |
|
| 35 | 35 | } |
|
| 36 | 36 | return out; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | - | @default fn main() -> i32 { |
|
| 39 | + | @default unsafe fn main() -> i32 { |
|
| 40 | 40 | let n = Node { |
|
| 41 | 41 | id: 0, span1: 0, span2: 5, |
|
| 42 | 42 | kind: Kind::Name("Hello"), |
|
| 43 | 43 | }; |
|
| 44 | 44 | let mut buffer: [*[u8]; 4] = undefined; |
|
| 45 | - | let path = getPath(&n, &mut buffer[..]); |
|
| 45 | + | let path = getPath(&n as *Node, &mut buffer[..] as *mut [*[u8]]); |
|
| 46 | 46 | ||
| 47 | 47 | let pathLen = path.len as i32; |
|
| 48 | 48 | assert pathLen <= 1; |
|
| 49 | 49 | assert pathLen > 0; |
|
| 50 | 50 |
test/tests/const-expr-array-size.rad
+2 -2
| 5 | 5 | ||
| 6 | 6 | constant ROWS: u32 = 3; |
|
| 7 | 7 | constant COLS: u32 = 4; |
|
| 8 | 8 | constant TOTAL: u32 = ROWS * COLS; |
|
| 9 | 9 | ||
| 10 | - | static DATA: [i32; TOTAL] = undefined; |
|
| 10 | + | unsafe static DATA: [i32; TOTAL] = undefined; |
|
| 11 | 11 | ||
| 12 | - | @default fn main() -> i32 { |
|
| 12 | + | @default unsafe fn main() -> i32 { |
|
| 13 | 13 | // Verify the array has the expected length. |
|
| 14 | 14 | if DATA.len <> 12 { return 1; } |
|
| 15 | 15 | return 0; |
|
| 16 | 16 | } |
test/tests/const-expr-cast.rad
+2 -2
| 10 | 10 | constant SHIFTED: u64 = 4 as u64; |
|
| 11 | 11 | ||
| 12 | 12 | // Chained casts. |
|
| 13 | 13 | constant LEN: u32 = 8; |
|
| 14 | 14 | constant LEN2: u32 = (LEN as u64) as u32; |
|
| 15 | - | static BUF: [u8; LEN2] = undefined; |
|
| 15 | + | unsafe static BUF: [u8; LEN2] = undefined; |
|
| 16 | 16 | ||
| 17 | 17 | // Cast of unsuffixed literal arithmetic. |
|
| 18 | 18 | constant E: u32 = (3 + 4) as u32; |
|
| 19 | 19 | // Nested casts of literal arithmetic. |
|
| 20 | 20 | constant F: u32 = ((3 + 4) as u64) as u32; |
| 30 | 30 | // Typed constant * typed constant through cast. |
|
| 31 | 31 | constant X: u8 = 3; |
|
| 32 | 32 | constant Y: u8 = 4; |
|
| 33 | 33 | constant Z: i32 = (X as i32) * (Y as i32); |
|
| 34 | 34 | ||
| 35 | - | @default fn main() -> i32 { |
|
| 35 | + | @default unsafe fn main() -> i32 { |
|
| 36 | 36 | assert BUF.len == 8; |
|
| 37 | 37 | assert WIDE == 255; |
|
| 38 | 38 | assert Z == 12; |
|
| 39 | 39 | assert E == 7; |
|
| 40 | 40 | assert F == 7; |
test/tests/const-expr-literal.rad
+3 -3
| 13 | 13 | constant E: u32 = 2 * 3 + 4; |
|
| 14 | 14 | // Unary negation with literal. |
|
| 15 | 15 | constant F: i32 = -5; |
|
| 16 | 16 | constant G: i32 = F * 2; |
|
| 17 | 17 | ||
| 18 | - | static BUF: [u8; A] = undefined; |
|
| 19 | - | static BUF2: [u8; C] = undefined; |
|
| 18 | + | unsafe static BUF: [u8; A] = undefined; |
|
| 19 | + | unsafe static BUF2: [u8; C] = undefined; |
|
| 20 | 20 | ||
| 21 | - | @default fn main() -> i32 { |
|
| 21 | + | @default unsafe fn main() -> i32 { |
|
| 22 | 22 | assert A == 16; |
|
| 23 | 23 | assert BUF.len == 16; |
|
| 24 | 24 | assert C == 20; |
|
| 25 | 25 | assert BUF2.len == 20; |
|
| 26 | 26 | assert D == 10; |
test/tests/const.array.repeat.string.slice.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | /// Repeated string slices in constant arrays must emit complete slice headers. |
|
| 3 | - | constant WORDS: [*[u8]; 2] = ["abc"; 2]; |
|
| 3 | + | unsafe constant WORDS: [*[u8]; 2] = ["abc"; 2]; |
|
| 4 | 4 | ||
| 5 | - | @default fn main() -> i32 { |
|
| 5 | + | @default unsafe fn main() -> i32 { |
|
| 6 | 6 | assert WORDS[0].len == 3; |
|
| 7 | 7 | assert WORDS[1].len == 3; |
|
| 8 | 8 | assert WORDS[0][0] == 'a'; |
|
| 9 | 9 | assert WORDS[1][2] == 'c'; |
|
| 10 | 10 | return 0; |
test/tests/const.array.strings.slice.rad
+2 -2
| 1 | 1 | /// Const array of string slices lowers to slice headers (sym+len+padding) |
|
| 2 | 2 | /// and reuses deduplicated string storage. |
|
| 3 | - | constant WORDS: [*[u8]; 3] = [ |
|
| 3 | + | unsafe constant WORDS: [*[u8]; 3] = [ |
|
| 4 | 4 | "apple", |
|
| 5 | 5 | "pencil", |
|
| 6 | 6 | "apple", |
|
| 7 | 7 | ]; |
|
| 8 | 8 | ||
| 9 | - | fn totalLen() -> u32 { |
|
| 9 | + | unsafe fn totalLen() -> u32 { |
|
| 10 | 10 | return WORDS[0].len + WORDS[1].len + WORDS[2].len; |
|
| 11 | 11 | } |
test/tests/const.record.union.rad
+2 -2
| 9 | 9 | record Keyword { |
|
| 10 | 10 | name: *[u8], |
|
| 11 | 11 | tok: TokenKind, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | constant KEYWORDS: [Keyword; 3] = [ |
|
| 14 | + | unsafe constant KEYWORDS: [Keyword; 3] = [ |
|
| 15 | 15 | Keyword { name: "fn", tok: TokenKind::Fn }, |
|
| 16 | 16 | Keyword { name: "let", tok: TokenKind::Let }, |
|
| 17 | 17 | Keyword { name: "if", tok: TokenKind::If }, |
|
| 18 | 18 | ]; |
|
| 19 | 19 | ||
| 20 | - | fn getFirstTag() -> i32 { |
|
| 20 | + | unsafe fn getFirstTag() -> i32 { |
|
| 21 | 21 | match KEYWORDS[0].tok { |
|
| 22 | 22 | case TokenKind::Fn => return 0, |
|
| 23 | 23 | case TokenKind::Let => return 1, |
|
| 24 | 24 | case TokenKind::If => return 2, |
|
| 25 | 25 | } |
test/tests/const.slice.of.slices.rad
+2 -2
| 1 | 1 | /// Const nested slices should lower through generic constant `&[...]` support. |
|
| 2 | - | constant GROUPS: [*[*[u8]]; 2] = [ |
|
| 2 | + | unsafe constant GROUPS: [*[*[u8]]; 2] = [ |
|
| 3 | 3 | &["ab", "cd"], |
|
| 4 | 4 | &["efg"], |
|
| 5 | 5 | ]; |
|
| 6 | 6 | ||
| 7 | - | fn totalLen() -> u32 { |
|
| 7 | + | unsafe fn totalLen() -> u32 { |
|
| 8 | 8 | return GROUPS[0].len + GROUPS[1].len + GROUPS[0][1].len; |
|
| 9 | 9 | } |
test/tests/const.slice.param.rad
+4 -5
| 1 | 1 | //! returns: 36 |
|
| 2 | 2 | ||
| 3 | 3 | constant DATA: [i32; 4] = [3, 5, 7, 9]; |
|
| 4 | 4 | ||
| 5 | - | fn sumPair(slice: *[i32]) -> i32 { |
|
| 5 | + | fn sumPair(slice: &[i32]) -> i32 { |
|
| 6 | 6 | return slice[0] + slice[1]; |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | @default fn main() -> i32 { |
|
| 10 | - | let all: *[i32] = &DATA[..]; |
|
| 11 | - | let head: i32 = sumPair(all); |
|
| 12 | - | let tail: i32 = sumPair(&all[2..]); |
|
| 13 | - | let mid: i32 = sumPair(&all[1..3]); |
|
| 10 | + | let head: i32 = sumPair(&DATA[..]); |
|
| 11 | + | let tail: i32 = sumPair(&DATA[2..]); |
|
| 12 | + | let mid: i32 = sumPair(&DATA[1..3]); |
|
| 14 | 13 | ||
| 15 | 14 | return head + tail + mid; |
|
| 16 | 15 | } |
test/tests/const.string.rad
+2 -2
| 1 | 1 | /// Test string constant lowering. |
|
| 2 | - | constant HELLO: *[u8] = "hello"; |
|
| 2 | + | unsafe constant HELLO: *[u8] = "hello"; |
|
| 3 | 3 | ||
| 4 | - | fn getLen() -> u32 { |
|
| 4 | + | unsafe fn getLen() -> u32 { |
|
| 5 | 5 | return HELLO.len; |
|
| 6 | 6 | } |
test/tests/const.string.scoped.names.rad
+4 -4
| 1 | 1 | /// Backing string symbols should be declaration-scoped. |
|
| 2 | - | constant HELLO1: *[u8] = "hello"; |
|
| 3 | - | constant HELLO2: *[u8] = "hello"; |
|
| 4 | - | constant MSGS: [*[u8]; 2] = ["hello", "world"]; |
|
| 2 | + | unsafe constant HELLO1: *[u8] = "hello"; |
|
| 3 | + | unsafe constant HELLO2: *[u8] = "hello"; |
|
| 4 | + | unsafe constant MSGS: [*[u8]; 2] = ["hello", "world"]; |
|
| 5 | 5 | ||
| 6 | - | fn totalLen() -> u32 { |
|
| 6 | + | unsafe fn totalLen() -> u32 { |
|
| 7 | 7 | return HELLO1.len + HELLO2.len + MSGS[0].len + MSGS[1].len; |
|
| 8 | 8 | } |
test/tests/const.union.payload.record.array.rad
+2 -2
| 11 | 11 | record Entry { |
|
| 12 | 12 | name: *[u8], |
|
| 13 | 13 | spec: InstrSpec, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | - | constant ENTRIES: [Entry; 4] = [ |
|
| 16 | + | unsafe constant ENTRIES: [Entry; 4] = [ |
|
| 17 | 17 | Entry { name: "empty", spec: InstrSpec::Empty }, |
|
| 18 | 18 | Entry { name: "small", spec: InstrSpec::Small { flag: true } }, |
|
| 19 | 19 | Entry { name: "addi", spec: InstrSpec::Imm { opcode: 0x13, funct3: 0, imm: -7 } }, |
|
| 20 | 20 | Entry { name: "add", spec: InstrSpec::Reg { opcode: 0x33, funct3: 0, funct7: 0 } }, |
|
| 21 | 21 | ]; |
|
| 22 | 22 | ||
| 23 | - | @default fn main() -> i32 { |
|
| 23 | + | @default unsafe fn main() -> i32 { |
|
| 24 | 24 | match ENTRIES[0].spec { |
|
| 25 | 25 | case InstrSpec::Empty => {} |
|
| 26 | 26 | else => return 1, |
|
| 27 | 27 | } |
|
| 28 | 28 | assert ENTRIES[0].name[0] == 'e'; |
test/tests/data.simple.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Simple test for data symbol access. |
|
| 3 | 3 | constant DATA: [u8; 4] = [10, 20, 30, 40]; |
|
| 4 | 4 | ||
| 5 | - | @default fn main() -> i32 { |
|
| 5 | + | @default unsafe fn main() -> i32 { |
|
| 6 | 6 | if DATA[0] == 10 and |
|
| 7 | 7 | DATA[1] == 20 and |
|
| 8 | 8 | DATA[2] == 30 and |
|
| 9 | 9 | DATA[3] == 40 and DATA.len == 4 { |
|
| 10 | - | let ptr = &DATA[..]; |
|
| 10 | + | let ptr = &DATA[..] as *[u8]; |
|
| 11 | 11 | ||
| 12 | 12 | if ptr[0] == 10 and |
|
| 13 | 13 | ptr[1] == 20 and |
|
| 14 | 14 | ptr[2] == 30 and |
|
| 15 | 15 | ptr[3] == 40 and ptr.len == 4 { |
test/tests/ecall.i64.rad
+8 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | @intrinsic fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64; |
|
| 3 | + | /// Issue a raw environment call for this intrinsic ABI test. |
|
| 4 | + | /// |
|
| 5 | + | /// The caller must satisfy the selected system call's ABI, including every |
|
| 6 | + | /// memory extent represented by an integer argument. |
|
| 7 | + | @intrinsic unsafe fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64; |
|
| 4 | 8 | ||
| 5 | 9 | /// Test that ecall can pass and return i64 values. |
|
| 6 | - | @default fn main() -> i32 { |
|
| 10 | + | /// |
|
| 11 | + | /// The execution environment must provide writable standard output. |
|
| 12 | + | @default unsafe fn main() -> i32 { |
|
| 7 | 13 | // ecall(64, fd, buf, len, 0) = write(fd, buf, len). |
|
| 8 | 14 | let msg: *[u8] = "ok\n"; |
|
| 9 | 15 | ||
| 10 | 16 | // Write to stdout. The pointer is passed as i64. |
|
| 11 | 17 | let n = ecall(64, 1, msg.ptr as i64, msg.len as i64, 0); |
test/tests/edge.cases.2.rad
+2 -2
| 3 | 3 | ||
| 4 | 4 | export record Scanner { |
|
| 5 | 5 | source: *[u8], |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn peek(s: *Scanner) -> ?u8 { |
|
| 8 | + | fn peek(s: &Scanner) -> ?u8 { |
|
| 9 | 9 | if 0 + 0 >= s.source.len { |
|
| 10 | 10 | return nil; |
|
| 11 | 11 | } |
|
| 12 | 12 | return 'a'; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | @default fn main() -> i32 { |
|
| 15 | + | @default unsafe fn main() -> i32 { |
|
| 16 | 16 | let s: Scanner = Scanner { source: "xyz" }; |
|
| 17 | 17 | ||
| 18 | 18 | if let z = peek(&s) { |
|
| 19 | 19 | return 0; |
|
| 20 | 20 | } |
test/tests/edge.cases.3.rad
+4 -4
| 4 | 4 | export record Scanner { |
|
| 5 | 5 | source: *[u8], |
|
| 6 | 6 | cursor: u32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn isEof(s: *Scanner) -> bool { |
|
| 9 | + | fn isEof(s: &Scanner) -> bool { |
|
| 10 | 10 | return s.cursor >= s.source.len; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | export fn current(s: *Scanner) -> ?u8 { |
|
| 13 | + | export fn current(s: &Scanner) -> ?u8 { |
|
| 14 | 14 | if isEof(s) { |
|
| 15 | 15 | return nil; |
|
| 16 | 16 | } |
|
| 17 | 17 | return s.source[s.cursor]; |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | fn peek(s: *Scanner) -> ?u8 { |
|
| 20 | + | fn peek(s: &Scanner) -> ?u8 { |
|
| 21 | 21 | if s.cursor + 1 >= s.source.len { |
|
| 22 | 22 | return nil; |
|
| 23 | 23 | } |
|
| 24 | 24 | return s.source[s.cursor + 1]; |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | - | @default fn main() -> u8 { |
|
| 27 | + | @default unsafe fn main() -> u8 { |
|
| 28 | 28 | let s: Scanner = Scanner { source: "abc", cursor: 0 }; |
|
| 29 | 29 | if let c = current(&s) { |
|
| 30 | 30 | return c; |
|
| 31 | 31 | } |
|
| 32 | 32 | return 1; |
test/tests/edge.cases.4.rad
+4 -4
| 18 | 18 | ||
| 19 | 19 | record Node { |
|
| 20 | 20 | value: NodeValue, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | fn node(nodes: *mut Node, count: *mut u32, value: NodeValue) -> *Node { |
|
| 23 | + | unsafe fn node(nodes: *mut Node, count: &mut u32, value: NodeValue) -> *Node { |
|
| 24 | 24 | let index = *count; |
|
| 25 | 25 | let slot = nodes + index; |
|
| 26 | 26 | set *slot = Node { value }; |
|
| 27 | 27 | set *count = index + 1; |
|
| 28 | 28 | return slot; |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | - | fn nodeTypeInt(nodes: *mut Node, count: *mut u32, width: u8, sign: Signedness) -> *Node { |
|
| 31 | + | unsafe fn nodeTypeInt(nodes: *mut Node, count: &mut u32, width: u8, sign: Signedness) -> *Node { |
|
| 32 | 32 | return node(nodes, count, NodeValue::TypeSig( |
|
| 33 | 33 | TypeSig::Integer { width, sign } |
|
| 34 | 34 | )); |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | - | @default fn main() -> i32 { |
|
| 37 | + | @default unsafe fn main() -> i32 { |
|
| 38 | 38 | let direct = TypeSig::Integer { |
|
| 39 | 39 | width: 4, |
|
| 40 | 40 | sign: Signedness::Signed, |
|
| 41 | 41 | }; |
|
| 42 | 42 | let case TypeSig::Integer { width: w1, .. } = direct |
|
| 43 | 43 | else return 50; |
|
| 44 | 44 | ||
| 45 | 45 | assert w1 == 4; |
|
| 46 | 46 | let mut nodes: [Node; 2] = undefined; |
|
| 47 | - | let nodesPtr: *mut Node = &mut nodes[0]; |
|
| 47 | + | let nodesPtr: *mut Node = &mut nodes[0] as *mut Node; |
|
| 48 | 48 | let mut count: u32 = 0; |
|
| 49 | 49 | let typeNode = nodeTypeInt(nodesPtr, &mut count, 4, Signedness::Signed); |
|
| 50 | 50 | let case NodeValue::TypeSig(sig) = typeNode.value |
|
| 51 | 51 | else return 40; |
|
| 52 | 52 |
test/tests/edge.cases.6.rad
+8 -8
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | record Analyzer { |
|
| 28 | 28 | pad0: u32, |
|
| 29 | 29 | pad1: u32, |
|
| 30 | - | entries: *mut [Entry], |
|
| 30 | + | entries: *unsafe mut [Entry], |
|
| 31 | 31 | len: u32, |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | static STORAGE: [Entry; 2] = [ |
|
| 35 | 35 | Entry { a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, h: 0, i: 0, j: 0, k: 0, l: 0, m: 0, n: 0, o: 0, p: 0, q: 0, r: 0, s: 0, t: 0 }, |
| 55 | 55 | q: 0x1234560C, |
|
| 56 | 56 | r: 0x1234560D, |
|
| 57 | 57 | s: 0x1234560E, |
|
| 58 | 58 | t: 0x1234560F, |
|
| 59 | 59 | }; |
|
| 60 | - | static ANALYZER: Analyzer = undefined; |
|
| 60 | + | unsafe static ANALYZER: Analyzer = undefined; |
|
| 61 | 61 | ||
| 62 | - | fn fillEntry(out: *mut Entry) { |
|
| 62 | + | unsafe fn fillEntry(out: *mut Entry) { |
|
| 63 | 63 | set *out = DEFAULT_ENTRY; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | fn mkEntry() -> Entry { |
|
| 67 | 67 | return DEFAULT_ENTRY; |
| 72 | 72 | return nil; |
|
| 73 | 73 | } |
|
| 74 | 74 | return mkEntry(); |
|
| 75 | 75 | } |
|
| 76 | 76 | ||
| 77 | - | fn init(entries: *mut [Entry]) { |
|
| 77 | + | unsafe fn init(entries: *unsafe mut [Entry]) { |
|
| 78 | 78 | set ANALYZER = Analyzer { |
|
| 79 | 79 | pad0: 0xDEADAAA0, |
|
| 80 | 80 | pad1: 0xDEADAAA1, |
|
| 81 | 81 | entries, |
|
| 82 | 82 | len: 0, |
|
| 83 | 83 | }; |
|
| 84 | 84 | } |
|
| 85 | 85 | ||
| 86 | - | fn add() { |
|
| 86 | + | unsafe fn add() { |
|
| 87 | 87 | let idx = ANALYZER.len; |
|
| 88 | 88 | let entry = mkOptEntry(true) else { |
|
| 89 | 89 | return; |
|
| 90 | 90 | }; |
|
| 91 | 91 | set ANALYZER.entries[idx] = entry; |
|
| 92 | 92 | set ANALYZER.len = idx + 1; |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | - | fn checkHeader(expected: *[Entry]) -> i32 { |
|
| 95 | + | unsafe fn checkHeader(expected: *unsafe [Entry]) -> i32 { |
|
| 96 | 96 | if ANALYZER.entries.ptr <> expected.ptr or ANALYZER.entries.len <> expected.len { |
|
| 97 | 97 | // Slice header got clobbered instead of the backing storage. |
|
| 98 | 98 | return 1; |
|
| 99 | 99 | } |
|
| 100 | 100 | if ANALYZER.len <> 1 { |
| 109 | 109 | return 3; |
|
| 110 | 110 | } |
|
| 111 | 111 | return 0; |
|
| 112 | 112 | } |
|
| 113 | 113 | ||
| 114 | - | @default fn main() -> i32 { |
|
| 115 | - | let target = &mut STORAGE[..]; |
|
| 114 | + | @default unsafe fn main() -> i32 { |
|
| 115 | + | let target = &mut STORAGE[..] as *unsafe mut [Entry]; |
|
| 116 | 116 | init(target); |
|
| 117 | 117 | add(); |
|
| 118 | 118 | return checkHeader(target); |
|
| 119 | 119 | } |
test/tests/edge.cases.7.addr.bug.rad
+4 -4
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | 3 | record PtrHolder { |
|
| 4 | - | ptr: *mut i32, |
|
| 4 | + | ptr: *unsafe mut i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | static target: i32 = 10; |
|
| 8 | - | static holder: PtrHolder = undefined; |
|
| 8 | + | unsafe static holder: PtrHolder = undefined; |
|
| 9 | 9 | ||
| 10 | - | @default fn main() -> i32 { |
|
| 11 | - | set holder.ptr = &mut target; |
|
| 10 | + | @default unsafe fn main() -> i32 { |
|
| 11 | + | set holder.ptr = &mut target as *unsafe mut i32; |
|
| 12 | 12 | set *holder.ptr = 42; |
|
| 13 | 13 | ||
| 14 | 14 | return target; |
|
| 15 | 15 | } |
test/tests/edge.cases.8.bug.rad
+2 -2
| 10 | 10 | record Outer { |
|
| 11 | 11 | padding: i32, |
|
| 12 | 12 | inner: Inner, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | static global: Outer = undefined; |
|
| 15 | + | unsafe static global: Outer = undefined; |
|
| 16 | 16 | ||
| 17 | 17 | fn readInner(i: Inner) -> i32 { |
|
| 18 | 18 | return i.value; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | @default fn main() -> i32 { |
|
| 21 | + | @default unsafe fn main() -> i32 { |
|
| 22 | 22 | set global = Outer { padding: 0, inner: Inner { value: 42 } }; |
|
| 23 | 23 | ||
| 24 | 24 | assert readInner(global.inner) == 42; |
|
| 25 | 25 | return 0; |
|
| 26 | 26 | } |
test/tests/edge.cases.rad
+2 -2
| 2 | 2 | ||
| 3 | 3 | export record Scanner { |
|
| 4 | 4 | source: *[u8], |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn peek(s: *Scanner) -> i32 { |
|
| 7 | + | fn peek(s: &Scanner) -> i32 { |
|
| 8 | 8 | assert 0 + 0 <= s.source.len; |
|
| 9 | 9 | return 0; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | @default fn main() -> i32 { |
|
| 12 | + | @default unsafe fn main() -> i32 { |
|
| 13 | 13 | let s: Scanner = Scanner { source: "1" }; |
|
| 14 | 14 | return peek(&s); |
|
| 15 | 15 | } |
test/tests/error.catch.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | 3 | union TestError { Fail } |
|
| 4 | 4 | static PTR_VALUE: i32 = 7; |
|
| 5 | 5 | ||
| 6 | - | @default fn main() -> u32 { |
|
| 6 | + | @default unsafe fn main() -> u32 { |
|
| 7 | 7 | // Catch block with early return |
|
| 8 | 8 | let val1: u32 = catchWithReturn(true); |
|
| 9 | 9 | assert val1 == 42; |
|
| 10 | 10 | ||
| 11 | 11 | // Catch block with early return (success case) |
| 71 | 71 | ||
| 72 | 72 | fn returnsErr() -> u32 throws (TestError) { |
|
| 73 | 73 | throw TestError::Fail; |
|
| 74 | 74 | } |
|
| 75 | 75 | ||
| 76 | - | fn returnsPtrOk() -> *i32 throws (TestError) { |
|
| 77 | - | return &PTR_VALUE; |
|
| 76 | + | unsafe fn returnsPtrOk() -> *i32 throws (TestError) { |
|
| 77 | + | return &PTR_VALUE as *i32; |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | - | fn returnsPtrErr() -> *i32 throws (TestError) { |
|
| 80 | + | unsafe fn returnsPtrErr() -> *i32 throws (TestError) { |
|
| 81 | 81 | throw TestError::Fail; |
|
| 82 | 82 | } |
test/tests/error.slice.bounds.rad
+1 -2
| 1 | 1 | //! returns: 133 |
|
| 2 | 2 | //! Test slice bounds checking with runtime EBREAK. |
|
| 3 | 3 | ||
| 4 | 4 | @default fn main() -> i32 { |
|
| 5 | 5 | let arr: [i32; 3] = [1, 2, 3]; |
|
| 6 | - | let slice: *[i32] = &arr[..]; |
|
| 7 | - | let value: i32 = slice[3]; |
|
| 6 | + | let value: i32 = (&arr[..])[3]; |
|
| 8 | 7 | ||
| 9 | 8 | return value; |
|
| 10 | 9 | } |
test/tests/error.try.rad
+3 -3
| 70 | 70 | throw TestError::Bust; |
|
| 71 | 71 | } |
|
| 72 | 72 | return 77; |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | - | fn catchReturn(flag: *mut u32, fail: bool) -> bool { |
|
| 75 | + | fn catchReturn(flag: &mut u32, fail: bool) -> bool { |
|
| 76 | 76 | let value: u32 = try maybeReturn(fail) catch { |
|
| 77 | 77 | return true; |
|
| 78 | 78 | }; |
|
| 79 | 79 | set *flag = value; |
|
| 80 | 80 | return false; |
| 96 | 96 | set idx += 1; |
|
| 97 | 97 | } |
|
| 98 | 98 | return total; |
|
| 99 | 99 | } |
|
| 100 | 100 | ||
| 101 | - | fn structSuccess(state: *mut ResultSink) -> u32 throws (TestError) { |
|
| 101 | + | fn structSuccess(state: &mut ResultSink) -> u32 throws (TestError) { |
|
| 102 | 102 | let value: u32 = try returnsOk(); |
|
| 103 | 103 | set state.last = value; |
|
| 104 | 104 | set state.count += 1; |
|
| 105 | 105 | return value; |
|
| 106 | 106 | } |
|
| 107 | 107 | ||
| 108 | - | fn structFailure(state: *mut ResultSink) -> u32 throws (TestError) { |
|
| 108 | + | fn structFailure(state: &mut ResultSink) -> u32 throws (TestError) { |
|
| 109 | 109 | try returnsErr(1); |
|
| 110 | 110 | set state.last = 999; |
|
| 111 | 111 | return 999; |
|
| 112 | 112 | } |
|
| 113 | 113 |
test/tests/field.aggregate.rad
+2 -2
| 19 | 19 | let r = HasUnion { maybe: MaybeInt::Some(42), z: 99 }; |
|
| 20 | 20 | return r.maybe; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// Access a slice field and get its length. |
|
| 24 | - | fn accessSliceField() -> u32 { |
|
| 24 | + | unsafe fn accessSliceField() -> u32 { |
|
| 25 | 25 | let arr: [i32; 3] = [1, 2, 3]; |
|
| 26 | - | let r = HasSlice { data: &arr[..], z: 77 }; |
|
| 26 | + | let r = HasSlice { data: &arr[..] as *[i32], z: 77 }; |
|
| 27 | 27 | return r.data.len; |
|
| 28 | 28 | } |
test/tests/fn.callback.nested.rad
+5 -6
| 15 | 15 | return n + 1; |
|
| 16 | 16 | } |
|
| 17 | 17 | return current; |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | fn maxRegCallback(reg: Reg, ctx: *mut opaque) { |
|
| 21 | - | let max = ctx as *mut u32; |
|
| 22 | - | set *max = maxRegNum(reg.n, *max); |
|
| 20 | + | fn maxRegCallback(reg: Reg, ctx: &mut opaque) { |
|
| 21 | + | unsafe { set *(ctx as &mut u32) = maxRegNum(reg.n, *(ctx as &mut u32)); } |
|
| 23 | 22 | } |
|
| 24 | 23 | ||
| 25 | - | fn withReg(val: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 24 | + | fn withReg(val: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 26 | 25 | if let case Val::Reg(r) = val { |
|
| 27 | 26 | callback(r, ctx); |
|
| 28 | 27 | } |
|
| 29 | 28 | } |
|
| 30 | 29 | ||
| 31 | - | fn forEachVal(a: Val, b: Val, c: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 30 | + | fn forEachVal(a: Val, b: Val, c: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 32 | 31 | withReg(a, callback, ctx); |
|
| 33 | 32 | withReg(b, callback, ctx); |
|
| 34 | 33 | withReg(c, callback, ctx); |
|
| 35 | 34 | } |
|
| 36 | 35 |
| 40 | 39 | forEachVal( |
|
| 41 | 40 | Val::Reg(Reg { n: 0 }), |
|
| 42 | 41 | Val::Reg(Reg { n: 5 }), |
|
| 43 | 42 | Val::Reg(Reg { n: 2 }), |
|
| 44 | 43 | maxRegCallback, |
|
| 45 | - | &mut maxReg as *mut opaque |
|
| 44 | + | &mut maxReg |
|
| 46 | 45 | ); |
|
| 47 | 46 | ||
| 48 | 47 | // maxReg should be 6 (max register 5 + 1) |
|
| 49 | 48 | if maxReg == 6 { |
|
| 50 | 49 | return 0; |
test/tests/for.else.continue.rad
+5 -5
| 4 | 4 | record Field { |
|
| 5 | 5 | name: ?*[u8], |
|
| 6 | 6 | value: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn findField(fields: *[Field], target: *[u8]) -> ?i32 { |
|
| 9 | + | unsafe fn findField(fields: *[Field], target: *[u8]) -> ?i32 { |
|
| 10 | 10 | for i in 0..fields.len { |
|
| 11 | 11 | let name = fields[i].name |
|
| 12 | 12 | else continue; |
|
| 13 | 13 | if name.len == target.len { |
|
| 14 | 14 | let mut eq = true; |
| 23 | 23 | } |
|
| 24 | 24 | } |
|
| 25 | 25 | return nil; |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | @default fn main() -> i32 { |
|
| 28 | + | @default unsafe fn main() -> i32 { |
|
| 29 | 29 | let mut fields: [Field; 4] = undefined; |
|
| 30 | 30 | set fields[0] = Field { name: nil, value: 10 }; |
|
| 31 | 31 | set fields[1] = Field { name: "foo", value: 20 }; |
|
| 32 | 32 | set fields[2] = Field { name: nil, value: 30 }; |
|
| 33 | 33 | set fields[3] = Field { name: "bar", value: 40 }; |
|
| 34 | 34 | ||
| 35 | - | let v1 = findField(&fields[..], "foo") else { |
|
| 35 | + | let v1 = findField(&fields[..] as *[Field], "foo") else { |
|
| 36 | 36 | return 1; |
|
| 37 | 37 | }; |
|
| 38 | 38 | assert v1 == 20; |
|
| 39 | 39 | ||
| 40 | - | let v2 = findField(&fields[..], "bar") else { |
|
| 40 | + | let v2 = findField(&fields[..] as *[Field], "bar") else { |
|
| 41 | 41 | return 3; |
|
| 42 | 42 | }; |
|
| 43 | 43 | assert v2 == 40; |
|
| 44 | 44 | ||
| 45 | - | if let _ = findField(&fields[..], "baz") { |
|
| 45 | + | if let _ = findField(&fields[..] as *[Field], "baz") { |
|
| 46 | 46 | return 5; |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | return 0; |
|
| 50 | 50 | } |
test/tests/frame.large.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test large stack frames and addressing near top of frame. |
|
| 3 | - | fn bigFrame1() -> i32 { |
|
| 3 | + | unsafe fn bigFrame1() -> i32 { |
|
| 4 | 4 | let pad: [u8; 2024] = undefined; |
|
| 5 | 5 | let n: i32 = 491823; |
|
| 6 | 6 | ||
| 7 | 7 | return n + 1; |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | fn bigFrame2() -> i32 { |
|
| 10 | + | unsafe fn bigFrame2() -> i32 { |
|
| 11 | 11 | let pad: [u8; 2028] = undefined; |
|
| 12 | 12 | let arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 13 | 13 | ||
| 14 | 14 | return arr[0] + arr[1] + arr[3]; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | fn bigFrame3() -> i32 { |
|
| 17 | + | unsafe fn bigFrame3() -> i32 { |
|
| 18 | 18 | let mut ary: [u8; 4096] = undefined; |
|
| 19 | 19 | set ary[4091] = 192; |
|
| 20 | 20 | ||
| 21 | 21 | return ary[4091] as i32; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | @default fn main() -> i32 { |
|
| 24 | + | @default unsafe fn main() -> i32 { |
|
| 25 | 25 | assert bigFrame1() == 491824; |
|
| 26 | 26 | assert bigFrame2() == 7; |
|
| 27 | 27 | assert bigFrame3() == 192; |
|
| 28 | 28 | return 0; |
|
| 29 | 29 | } |
test/tests/index.eval.order.rad
+2 -2
| 2 | 2 | //! The container expression of a subscript must run before its index expression. |
|
| 3 | 3 | ||
| 4 | 4 | static VALUES: [i32; 2] = [1, 42]; |
|
| 5 | 5 | static NEXT_INDEX: u32 = 0; |
|
| 6 | 6 | ||
| 7 | - | fn selectValues() -> *[i32] { |
|
| 7 | + | unsafe fn selectValues() -> *[i32] { |
|
| 8 | 8 | set NEXT_INDEX = 1; |
|
| 9 | 9 | return &VALUES[..]; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | fn selectIndex() -> u32 { |
|
| 13 | 13 | return NEXT_INDEX; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | - | @default fn main() -> i32 { |
|
| 16 | + | @default unsafe fn main() -> i32 { |
|
| 17 | 17 | return selectValues()[selectIndex()]; |
|
| 18 | 18 | } |
test/tests/index.u8.rad
+1 -2
| 21 | 21 | // Unsuffixed integer literal index. |
|
| 22 | 22 | if arr[1] <> 20 { |
|
| 23 | 23 | return 4; |
|
| 24 | 24 | } |
|
| 25 | 25 | // u8 index into slice. |
|
| 26 | - | let s = &arr[..]; |
|
| 27 | 26 | let si: u8 = 1; |
|
| 28 | - | if s[si] <> 20 { |
|
| 27 | + | if (&arr[..])[si] <> 20 { |
|
| 29 | 28 | return 5; |
|
| 30 | 29 | } |
|
| 31 | 30 | return 0; |
|
| 32 | 31 | } |
test/tests/intrinsic.ecall.rad
+3 -3
| 1 | 1 | //! Test intrinsic ecall lowering. |
|
| 2 | 2 | ||
| 3 | - | @intrinsic fn ecall(num: u32, a0: i32, a1: i32, a2: i32, a3: i32) -> i32; |
|
| 3 | + | @intrinsic unsafe fn ecall(num: u32, a0: i64, a1: i64, a2: i64, a3: i64) -> i64; |
|
| 4 | 4 | ||
| 5 | - | fn test() -> i32 { |
|
| 6 | - | return ecall(64, 1, 100, 5, 0); |
|
| 5 | + | fn test() -> i64 { |
|
| 6 | + | unsafe { return ecall(64, 1, 100, 5, 0); } |
|
| 7 | 7 | } |
test/tests/intrinsic.ecall.ril
+2 -2
| 1 | - | extern fn w32 $ecall(w32 %0, w32 %1, w32 %2, w32 %3, w32 %4); |
|
| 1 | + | extern fn w64 $ecall(w32 %0, w64 %1, w64 %2, w64 %3, w64 %4); |
|
| 2 | 2 | ||
| 3 | - | fn w32 $test() { |
|
| 3 | + | fn w64 $test() { |
|
| 4 | 4 | @entry0 |
|
| 5 | 5 | ecall %0 64 1 100 5 0; |
|
| 6 | 6 | ret %0; |
|
| 7 | 7 | } |
test/tests/large.blit.store.rad
+4 -4
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | /// Store at offset 2200 (> MAX_IMM) into a record field. |
|
| 22 | 22 | /// Generates `store w32 <imm> %0 2200` in IL, which triggers the |
|
| 23 | 23 | /// SCRATCH1 aliasing bug when adjustOffset clobbers the value. |
|
| 24 | - | fn storeLargeOffset() -> i32 { |
|
| 24 | + | unsafe fn storeLargeOffset() -> i32 { |
|
| 25 | 25 | let mut b: Big = undefined; |
|
| 26 | 26 | set b.tag = 42; |
|
| 27 | 27 | assert b.tag == 42; |
|
| 28 | 28 | set b.tag = 99; |
|
| 29 | 29 | assert b.tag == 99; |
|
| 30 | 30 | return 0; |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | 33 | /// Copy a >2047 byte struct (triggers blit offset overflow). |
|
| 34 | - | fn copyBig() -> i32 { |
|
| 34 | + | unsafe fn copyBig() -> i32 { |
|
| 35 | 35 | let mut src: Big = undefined; |
|
| 36 | 36 | set src.a[0] = 10; |
|
| 37 | 37 | set src.a[1000] = 20; |
|
| 38 | 38 | set src.a[2199] = 30; |
|
| 39 | 39 | set src.tag = 77; |
| 46 | 46 | assert dst.tag == 77; |
|
| 47 | 47 | return 0; |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | /// Mutate a copy to ensure the blit produced an independent copy. |
|
| 51 | - | fn copyIndependence() -> i32 { |
|
| 51 | + | unsafe fn copyIndependence() -> i32 { |
|
| 52 | 52 | let mut a: Big = undefined; |
|
| 53 | 53 | set a.a[0] = 1; |
|
| 54 | 54 | set a.a[2199] = 2; |
|
| 55 | 55 | set a.tag = 100; |
|
| 56 | 56 |
| 64 | 64 | assert b.a[2199] == 2; |
|
| 65 | 65 | assert b.tag == 200; |
|
| 66 | 66 | return 0; |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | - | @default fn main() -> i32 { |
|
| 69 | + | @default unsafe fn main() -> i32 { |
|
| 70 | 70 | let r1 = storeLargeOffset(); |
|
| 71 | 71 | if r1 <> 0 { |
|
| 72 | 72 | return 10 + r1; |
|
| 73 | 73 | } |
|
| 74 | 74 |
test/tests/let.guard.rad
+6 -6
| 27 | 27 | let value = opt else return true; |
|
| 28 | 28 | ||
| 29 | 29 | return false; |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | - | fn caseGuardSuccess() -> bool { |
|
| 32 | + | unsafe fn caseGuardSuccess() -> bool { |
|
| 33 | 33 | let resp: Response = Response::success(9); |
|
| 34 | 34 | let case Response::success(inner) = resp else { |
|
| 35 | 35 | return false; |
|
| 36 | 36 | }; |
|
| 37 | 37 | return inner == 9; |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | - | fn caseGuardElseBlk() -> bool { |
|
| 40 | + | unsafe fn caseGuardElseBlk() -> bool { |
|
| 41 | 41 | let resp: Response = Response::failure; |
|
| 42 | 42 | let case Response::success(inner) = resp else { |
|
| 43 | 43 | return true; |
|
| 44 | 44 | }; |
|
| 45 | 45 | return false; |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | - | fn caseGuardElseStmt() -> bool { |
|
| 48 | + | unsafe fn caseGuardElseStmt() -> bool { |
|
| 49 | 49 | let resp: Response = Response::failure; |
|
| 50 | 50 | let case Response::success(inner) = resp else return true; |
|
| 51 | 51 | ||
| 52 | 52 | return false; |
|
| 53 | 53 | } |
| 62 | 62 | let b = second else return false; |
|
| 63 | 63 | ||
| 64 | 64 | return a == 1 and b == 2; |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | - | fn caseGuardNoPayload() -> bool { |
|
| 67 | + | unsafe fn caseGuardNoPayload() -> bool { |
|
| 68 | 68 | let resp: Response = Response::failure; |
|
| 69 | 69 | let case Response::failure = resp else { |
|
| 70 | 70 | return false; |
|
| 71 | 71 | }; |
|
| 72 | 72 | return true; |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | - | fn caseGuardStruct() -> bool { |
|
| 75 | + | unsafe fn caseGuardStruct() -> bool { |
|
| 76 | 76 | let resp: Response = Response::other { message: "ouch" }; |
|
| 77 | 77 | let case Response::other { message } = resp else { |
|
| 78 | 78 | return false; |
|
| 79 | 79 | }; |
|
| 80 | 80 | return message.len == 4; |
|
| 81 | 81 | } |
|
| 82 | 82 | ||
| 83 | - | @default fn main() -> bool { |
|
| 83 | + | @default unsafe fn main() -> bool { |
|
| 84 | 84 | return |
|
| 85 | 85 | optionGuardSuccess() and |
|
| 86 | 86 | optionGuardElseBlk() and |
|
| 87 | 87 | optionGuardElseStmt() and |
|
| 88 | 88 | caseGuardSuccess() and |
test/tests/let.placeholder.rad
+1 -1
| 3 | 3 | fn placeholder() -> i32 { |
|
| 4 | 4 | let _: i32 = 42; |
|
| 5 | 5 | return 1; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn placeholderWithSideEffect(ptr: *mut i32) -> i32 { |
|
| 8 | + | fn placeholderWithSideEffect(ptr: &i32) -> i32 { |
|
| 9 | 9 | let _ = *ptr; |
|
| 10 | 10 | return 0; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | fn placeholderIgnoreResult() -> i32 { |
test/tests/literal.slice.bytes.rad
+1 -1
| 1 | 1 | /// Test byte slice literal with alignment 1. |
|
| 2 | - | fn byteSlice() -> u32 { |
|
| 2 | + | unsafe fn byteSlice() -> u32 { |
|
| 3 | 3 | let s: *[u8] = &[65, 66, 67]; |
|
| 4 | 4 | return s.len; |
|
| 5 | 5 | } |
test/tests/literal.slice.dedup.rad
+1 -1
| 1 | 1 | /// Test that duplicate slice literals are deduplicated into a single data entry. |
|
| 2 | - | fn dedupSlice() -> u32 { |
|
| 2 | + | unsafe fn dedupSlice() -> u32 { |
|
| 3 | 3 | let a: *[i32] = &[1, 2, 3]; |
|
| 4 | 4 | let b: *[i32] = &[1, 2, 3]; |
|
| 5 | 5 | return a.len + b.len; |
|
| 6 | 6 | } |
test/tests/literal.slice.empty.rad
+1 -1
| 1 | 1 | /// Test empty slice literal. |
|
| 2 | - | fn emptySlice() -> u32 { |
|
| 2 | + | unsafe fn emptySlice() -> u32 { |
|
| 3 | 3 | let s: *[i32] = &[]; |
|
| 4 | 4 | return s.len; |
|
| 5 | 5 | } |
test/tests/literal.slice.multi.rad
+1 -1
| 1 | 1 | /// Test multiple slice literals get unique data names. |
|
| 2 | - | fn multiSlice() -> u32 { |
|
| 2 | + | unsafe fn multiSlice() -> u32 { |
|
| 3 | 3 | let a: *[i32] = &[1, 2]; |
|
| 4 | 4 | let b: *[i32] = &[3, 4, 5]; |
|
| 5 | 5 | return a.len + b.len; |
|
| 6 | 6 | } |
test/tests/literal.slice.rad
+1 -1
| 1 | 1 | /// Test slice literal creates static data. |
|
| 2 | - | fn sliceLiteral() -> u32 { |
|
| 2 | + | unsafe fn sliceLiteral() -> u32 { |
|
| 3 | 3 | let s: *[i32] = &[1, 2, 3]; |
|
| 4 | 4 | return s.len; |
|
| 5 | 5 | } |
test/tests/literal.slice.record.rad
+1 -1
| 1 | 1 | /// Test slice literal with record elements. |
|
| 2 | 2 | record Point { x: i32, y: i32 } |
|
| 3 | 3 | ||
| 4 | - | fn localSliceOfRecords() -> i32 { |
|
| 4 | + | unsafe fn localSliceOfRecords() -> i32 { |
|
| 5 | 5 | let s: *[Point] = &[Point { x: 5, y: 6 }, Point { x: 7, y: 8 }]; |
|
| 6 | 6 | return s[0].x + s[1].y; |
|
| 7 | 7 | } |
test/tests/literal.string.dedup.rad
+1 -1
| 1 | 1 | /// Test that duplicate string literals are deduplicated into a single data entry. |
|
| 2 | - | fn dedupString() -> u32 { |
|
| 2 | + | unsafe fn dedupString() -> u32 { |
|
| 3 | 3 | let a = "hello"; |
|
| 4 | 4 | let b = "hello"; |
|
| 5 | 5 | return a.len + b.len; |
|
| 6 | 6 | } |
test/tests/literal.string.empty.rad
+1 -1
| 1 | 1 | /// Test empty string literal. |
|
| 2 | - | fn emptyString() -> u32 { |
|
| 2 | + | unsafe fn emptyString() -> u32 { |
|
| 3 | 3 | let s = ""; |
|
| 4 | 4 | return s.len; |
|
| 5 | 5 | } |
test/tests/literal.string.fns.rad
+2 -2
| 1 | 1 | /// Test string data names are prefixed with function name. |
|
| 2 | - | fn first() -> u32 { |
|
| 2 | + | unsafe fn first() -> u32 { |
|
| 3 | 3 | let s = "foo"; |
|
| 4 | 4 | return s.len; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn second() -> u32 { |
|
| 7 | + | unsafe fn second() -> u32 { |
|
| 8 | 8 | let s = "bar"; |
|
| 9 | 9 | return s.len; |
|
| 10 | 10 | } |
test/tests/literal.string.multi.rad
+1 -1
| 1 | 1 | /// Test multiple string literals get unique data names. |
|
| 2 | - | fn multiString() -> u32 { |
|
| 2 | + | unsafe fn multiString() -> u32 { |
|
| 3 | 3 | let a = "hello"; |
|
| 4 | 4 | let b = "world"; |
|
| 5 | 5 | return a.len + b.len; |
|
| 6 | 6 | } |
test/tests/literal.string.rad
+1 -1
| 1 | 1 | /// Return the length of a string literal. |
|
| 2 | - | fn stringLen() -> u32 { |
|
| 2 | + | unsafe fn stringLen() -> u32 { |
|
| 3 | 3 | let s = "hello"; |
|
| 4 | 4 | return s.len; |
|
| 5 | 5 | } |
test/tests/loc.addr.offset.bug.rad
+2 -2
| 9 | 9 | record Outer { |
|
| 10 | 10 | pad: i32, // offset 0, size 4 |
|
| 11 | 11 | inner: Inner, // offset 4, size 4 |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | static outer: Outer = undefined; |
|
| 14 | + | unsafe static outer: Outer = undefined; |
|
| 15 | 15 | ||
| 16 | - | @default fn main() -> i32 { |
|
| 16 | + | @default unsafe fn main() -> i32 { |
|
| 17 | 17 | set outer.inner.value = 42; |
|
| 18 | 18 | ||
| 19 | 19 | let ptr: *Inner = &outer.inner; |
|
| 20 | 20 | ||
| 21 | 21 | assert (*ptr).value == 42; |
test/tests/loc.addr.opt.to.opt.rad
+2 -2
| 5 | 5 | record Container { |
|
| 6 | 6 | pad: i32, // offset 0 |
|
| 7 | 7 | opt: ?i32, // offset 4 |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | static container: Container = undefined; |
|
| 10 | + | unsafe static container: Container = undefined; |
|
| 11 | 11 | ||
| 12 | - | @default fn main() -> i32 { |
|
| 12 | + | @default unsafe fn main() -> i32 { |
|
| 13 | 13 | let sourceOpt: ?i32 = 42; |
|
| 14 | 14 | ||
| 15 | 15 | set container.opt = sourceOpt; |
|
| 16 | 16 | ||
| 17 | 17 | if let val = container.opt { |
test/tests/loc.addr.optional.assign.rad
+2 -2
| 4 | 4 | record Container { |
|
| 5 | 5 | pad: i32, // offset 0, size 4 |
|
| 6 | 6 | opt: ?i32, // offset 4, size 8 (tag + value) |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | static container: Container = undefined; |
|
| 9 | + | unsafe static container: Container = undefined; |
|
| 10 | 10 | ||
| 11 | - | @default fn main() -> i32 { |
|
| 11 | + | @default unsafe fn main() -> i32 { |
|
| 12 | 12 | set container.opt = 42; |
|
| 13 | 13 | ||
| 14 | 14 | if let val = container.opt { |
|
| 15 | 15 | assert val == 42; |
|
| 16 | 16 | return 0; |
test/tests/loc.addr.record.assign.rad
+3 -3
| 9 | 9 | record Outer { |
|
| 10 | 10 | pad: i32, // offset 0, size 4 |
|
| 11 | 11 | inner: Inner, // offset 4, size 4 |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | static outer: Outer = undefined; |
|
| 15 | - | static source: Inner = undefined; |
|
| 14 | + | unsafe static outer: Outer = undefined; |
|
| 15 | + | unsafe static source: Inner = undefined; |
|
| 16 | 16 | ||
| 17 | - | @default fn main() -> i32 { |
|
| 17 | + | @default unsafe fn main() -> i32 { |
|
| 18 | 18 | set source.value = 42; |
|
| 19 | 19 | ||
| 20 | 20 | set outer.inner = source; |
|
| 21 | 21 | ||
| 22 | 22 | assert outer.inner.value == 42; |
test/tests/loop.for.slice.rad
+1 -1
| 1 | 1 | /// Iterates over a slice with index binding and sums elements. |
|
| 2 | - | fn forSliceIndexed(s: *[i32]) -> i32 { |
|
| 2 | + | fn forSliceIndexed(s: &[i32]) -> i32 { |
|
| 3 | 3 | let mut sum: i32 = 0; |
|
| 4 | 4 | for elem, idx in s { |
|
| 5 | 5 | set sum += elem + idx as i32; |
|
| 6 | 6 | } |
|
| 7 | 7 | return sum; |
test/tests/loop.mutable.rad
+1 -1
| 5 | 5 | } |
|
| 6 | 6 | return b; |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | /// Computes the maximum alignment from a slice of alignment values. |
|
| 10 | - | fn computeMaxAlign(items: *[u32]) -> u32 { |
|
| 10 | + | fn computeMaxAlign(items: &[u32]) -> u32 { |
|
| 11 | 11 | let mut maxAlign: u32 = 1; |
|
| 12 | 12 | for i in 0..items.len { |
|
| 13 | 13 | set maxAlign = max(maxAlign, items[i]); |
|
| 14 | 14 | } |
|
| 15 | 15 | return maxAlign; |
test/tests/loop.whilelet.optional.rad
+1 -1
| 1 | 1 | // Returns the iteration count for a single guarded loop. |
|
| 2 | - | fn whileLetOptional(p: ?*i32) -> i32 { |
|
| 2 | + | unsafe fn whileLetOptional(p: ?*i32) -> i32 { |
|
| 3 | 3 | let mut steps: i32 = 0; |
|
| 4 | 4 | while let _ = p { |
|
| 5 | 5 | set steps += 1; |
|
| 6 | 6 | break; |
|
| 7 | 7 | } |
test/tests/lower.const.record.ident.rad
+2 -2
| 6 | 6 | record Entry { |
|
| 7 | 7 | name: *[u8], |
|
| 8 | 8 | reg: Reg, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | constant ENTRIES: [Entry; 2] = [ |
|
| 11 | + | unsafe constant ENTRIES: [Entry; 2] = [ |
|
| 12 | 12 | { name: "a0", reg: A0 }, |
|
| 13 | 13 | { name: "a1", reg: A1 }, |
|
| 14 | 14 | ]; |
|
| 15 | 15 | ||
| 16 | - | @default fn main() -> i32 { |
|
| 16 | + | @default unsafe fn main() -> i32 { |
|
| 17 | 17 | return ENTRIES.len as i32; |
|
| 18 | 18 | } |
test/tests/lower.private.union.const.rad
+2 -2
| 6 | 6 | record Entry { |
|
| 7 | 7 | name: *[u8], |
|
| 8 | 8 | kind: Kind, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | constant ENTRIES: [Entry; 2] = [ |
|
| 11 | + | unsafe constant ENTRIES: [Entry; 2] = [ |
|
| 12 | 12 | { name: "a", kind: Kind::A }, |
|
| 13 | 13 | { name: "b", kind: Kind::B }, |
|
| 14 | 14 | ]; |
|
| 15 | 15 | ||
| 16 | - | @default fn main() -> i32 { |
|
| 16 | + | @default unsafe fn main() -> i32 { |
|
| 17 | 17 | return ENTRIES.len as i32; |
|
| 18 | 18 | } |
test/tests/lower.record.scalar.record.const.rad
+2 -2
| 3 | 3 | record Entry { |
|
| 4 | 4 | name: *[u8], |
|
| 5 | 5 | reg: Reg, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | constant ENTRIES: [Entry; 2] = [ |
|
| 8 | + | unsafe constant ENTRIES: [Entry; 2] = [ |
|
| 9 | 9 | { name: "a0", reg: Reg(10) }, |
|
| 10 | 10 | { name: "a1", reg: Reg(11) }, |
|
| 11 | 11 | ]; |
|
| 12 | 12 | ||
| 13 | - | @default fn main() -> i32 { |
|
| 13 | + | @default unsafe fn main() -> i32 { |
|
| 14 | 14 | return ENTRIES.len as i32; |
|
| 15 | 15 | } |
test/tests/match.mutref.push.rad
+3 -3
| 9 | 9 | union Sealed { |
|
| 10 | 10 | No { items: U32List }, |
|
| 11 | 11 | Yes, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn pushItem(list: *mut U32List, value: u32) { |
|
| 14 | + | unsafe fn pushItem(list: &mut U32List, value: u32) { |
|
| 15 | 15 | set list.data[list.len] = value; |
|
| 16 | 16 | set list.len += 1; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | fn addToUnsealedBlock(state: *mut Sealed, value: u32) -> bool { |
|
| 19 | + | unsafe fn addToUnsealedBlock(state: *mut Sealed, value: u32) -> bool { |
|
| 20 | 20 | match state { |
|
| 21 | 21 | case Sealed::No { items } => { |
|
| 22 | 22 | pushItem(items, value); |
|
| 23 | 23 | return true; |
|
| 24 | 24 | }, |
| 26 | 26 | return false; |
|
| 27 | 27 | }, |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | - | @default fn main() -> i32 { |
|
| 31 | + | @default unsafe fn main() -> i32 { |
|
| 32 | 32 | let mut buf: [u32; 8] = undefined; |
|
| 33 | 33 | set buf[0] = 0; |
|
| 34 | 34 | let mut state = Sealed::No { items: U32List { data: &mut buf[0..8], len: 0 } }; |
|
| 35 | 35 | ||
| 36 | 36 | assert addToUnsealedBlock(&mut state, 42); |
test/tests/match.mutref.union.rad
+1 -1
| 10 | 10 | record Data { |
|
| 11 | 11 | state: State, |
|
| 12 | 12 | value: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn process(d: *mut Data) -> u32 { |
|
| 15 | + | fn process(d: &mut Data) -> u32 { |
|
| 16 | 16 | match &mut d.state { |
|
| 17 | 17 | case State::A { count } => { |
|
| 18 | 18 | let c = *count; |
|
| 19 | 19 | set *count = c + 1; |
|
| 20 | 20 | return c; |
test/tests/match.nested.deref.rad
+9 -9
| 10 | 10 | Some(*Inner), |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Match nested union variant through pointer dereference in match/case. |
|
| 15 | - | fn matchDeref(o: Outer) -> i32 { |
|
| 15 | + | unsafe fn matchDeref(o: Outer) -> i32 { |
|
| 16 | 16 | match o { |
|
| 17 | 17 | case Outer::Some(Inner::A(x)) => { |
|
| 18 | 18 | return x; |
|
| 19 | 19 | } |
|
| 20 | 20 | case Outer::Some(Inner::B) => { |
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// If-let-case with auto-deref nested pattern. |
|
| 30 | - | fn ifLetDeref(o: Outer) -> i32 { |
|
| 30 | + | unsafe fn ifLetDeref(o: Outer) -> i32 { |
|
| 31 | 31 | if let case Outer::Some(Inner::A(x)) = o { |
|
| 32 | 32 | return x; |
|
| 33 | 33 | } |
|
| 34 | 34 | return 0; |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Let-else with auto-deref nested pattern. |
|
| 38 | - | fn letElseDeref(o: Outer) -> i32 { |
|
| 38 | + | unsafe fn letElseDeref(o: Outer) -> i32 { |
|
| 39 | 39 | let case Outer::Some(Inner::A(x)) = o |
|
| 40 | 40 | else { return -1; }; |
|
| 41 | 41 | return x; |
|
| 42 | 42 | } |
|
| 43 | 43 |
| 51 | 51 | Some { c: Container }, |
|
| 52 | 52 | None, |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | 55 | /// Nested record with auto-deref on a pointer field. |
|
| 56 | - | fn matchRecordDeref(b: Boxed) -> i32 { |
|
| 56 | + | unsafe fn matchRecordDeref(b: Boxed) -> i32 { |
|
| 57 | 57 | match b { |
|
| 58 | 58 | case Boxed::Some { c: Container { inner: Inner::A(val), tag } } => { |
|
| 59 | 59 | return val + tag; |
|
| 60 | 60 | } |
|
| 61 | 61 | else => { |
| 63 | 63 | } |
|
| 64 | 64 | } |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | /// Auto-deref through pointer in if-let-case with record. |
|
| 68 | - | fn ifLetRecordDeref(b: Boxed) -> i32 { |
|
| 68 | + | unsafe fn ifLetRecordDeref(b: Boxed) -> i32 { |
|
| 69 | 69 | if let case Boxed::Some { c: Container { inner: Inner::A(val), tag } } = b { |
|
| 70 | 70 | return val + tag; |
|
| 71 | 71 | } |
|
| 72 | 72 | return 0; |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | /// Void variant through pointer deref. |
|
| 76 | - | fn matchDerefVoid(o: Outer) -> i32 { |
|
| 76 | + | unsafe fn matchDerefVoid(o: Outer) -> i32 { |
|
| 77 | 77 | if let case Outer::Some(Inner::B) = o { |
|
| 78 | 78 | return 1; |
|
| 79 | 79 | } |
|
| 80 | 80 | return 0; |
|
| 81 | 81 | } |
|
| 82 | 82 | ||
| 83 | 83 | /// Auto-deref with placeholder in nested pattern. |
|
| 84 | - | fn matchDerefPlaceholder(o: Outer) -> i32 { |
|
| 84 | + | unsafe fn matchDerefPlaceholder(o: Outer) -> i32 { |
|
| 85 | 85 | if let case Outer::Some(Inner::A(_)) = o { |
|
| 86 | 86 | return 1; |
|
| 87 | 87 | } |
|
| 88 | 88 | return 0; |
|
| 89 | 89 | } |
| 97 | 97 | union Holder { |
|
| 98 | 98 | Ptr { p: *Point, z: i32 }, |
|
| 99 | 99 | Empty, |
|
| 100 | 100 | } |
|
| 101 | 101 | ||
| 102 | - | fn derefRecordField(h: Holder) -> i32 { |
|
| 102 | + | unsafe fn derefRecordField(h: Holder) -> i32 { |
|
| 103 | 103 | if let case Holder::Ptr { p: Point { x, y }, z } = h { |
|
| 104 | 104 | return x + y + z; |
|
| 105 | 105 | } |
|
| 106 | 106 | return 0; |
|
| 107 | 107 | } |
|
| 108 | 108 | ||
| 109 | - | @default fn main() -> i32 { |
|
| 109 | + | @default unsafe fn main() -> i32 { |
|
| 110 | 110 | let innerA = Inner::A(42); |
|
| 111 | 111 | let innerB = Inner::B; |
|
| 112 | 112 | ||
| 113 | 113 | // matchDeref |
|
| 114 | 114 | assert matchDeref(Outer::Some(&innerA)) == 42; |
test/tests/match.nested.whilelet.rad
+15 -15
| 9 | 9 | union Opt { |
|
| 10 | 10 | Some { pair: Pair }, |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn get(items: *[Opt], idx: u32) -> Opt { |
|
| 14 | + | fn get(items: &[Opt], idx: u32) -> Opt { |
|
| 15 | 15 | if idx < items.len { |
|
| 16 | 16 | return items[idx]; |
|
| 17 | 17 | } |
|
| 18 | 18 | return Opt::None; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | /// While-let with nested record destructuring. |
|
| 22 | - | fn sumPairs(items: *[Opt]) -> i32 { |
|
| 22 | + | fn sumPairs(items: &[Opt]) -> i32 { |
|
| 23 | 23 | let mut sum: i32 = 0; |
|
| 24 | 24 | let mut i: u32 = 0; |
|
| 25 | 25 | while let case Opt::Some { pair: Pair { a, b } } = get(items, i) { |
|
| 26 | 26 | set sum = sum + a + b; |
|
| 27 | 27 | set i = i + 1; |
|
| 28 | 28 | } |
|
| 29 | 29 | return sum; |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | /// While-let with nested record and guard. |
|
| 33 | - | fn sumPositive(items: *[Opt]) -> i32 { |
|
| 33 | + | fn sumPositive(items: &[Opt]) -> i32 { |
|
| 34 | 34 | let mut sum: i32 = 0; |
|
| 35 | 35 | let mut i: u32 = 0; |
|
| 36 | 36 | while let case Opt::Some { pair: Pair { a, b } } = get(items, i); a > 0 { |
|
| 37 | 37 | set sum = sum + a + b; |
|
| 38 | 38 | set i = i + 1; |
| 45 | 45 | union Command { |
|
| 46 | 46 | Move { dir: Dir, speed: i32 }, |
|
| 47 | 47 | Stop, |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | - | fn getCmd(items: *[Command], idx: u32) -> Command { |
|
| 50 | + | fn getCmd(items: &[Command], idx: u32) -> Command { |
|
| 51 | 51 | if idx < items.len { |
|
| 52 | 52 | return items[idx]; |
|
| 53 | 53 | } |
|
| 54 | 54 | return Command::Stop; |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | 57 | /// While-let with nested union variant pattern. |
|
| 58 | - | fn sumNorthSpeeds(items: *[Command]) -> i32 { |
|
| 58 | + | fn sumNorthSpeeds(items: &[Command]) -> i32 { |
|
| 59 | 59 | let mut sum: i32 = 0; |
|
| 60 | 60 | let mut i: u32 = 0; |
|
| 61 | 61 | while let case Command::Move { dir: Dir::North, speed } = getCmd(items, i) { |
|
| 62 | 62 | set sum = sum + speed; |
|
| 63 | 63 | set i = i + 1; |
| 65 | 65 | return sum; |
|
| 66 | 66 | } |
|
| 67 | 67 | ||
| 68 | 68 | @default fn main() -> i32 { |
|
| 69 | 69 | // sumPairs |
|
| 70 | - | let items: *[Opt] = &[ |
|
| 70 | + | let items: [Opt; 2] = [ |
|
| 71 | 71 | Opt::Some { pair: Pair { a: 1, b: 2 } }, |
|
| 72 | 72 | Opt::Some { pair: Pair { a: 3, b: 4 } }, |
|
| 73 | 73 | ]; |
|
| 74 | - | assert sumPairs(items) == 10; |
|
| 74 | + | assert sumPairs(&items[..]) == 10; |
|
| 75 | 75 | ||
| 76 | 76 | // sumPairs: empty |
|
| 77 | - | let empty: *[Opt] = &[]; |
|
| 78 | - | assert sumPairs(empty) == 0; |
|
| 77 | + | let empty: [Opt; 0] = []; |
|
| 78 | + | assert sumPairs(&empty[..]) == 0; |
|
| 79 | 79 | ||
| 80 | 80 | // sumPositive: stops at first non-positive a. |
|
| 81 | - | let mixed: *[Opt] = &[ |
|
| 81 | + | let mixed: [Opt; 3] = [ |
|
| 82 | 82 | Opt::Some { pair: Pair { a: 3, b: 4 } }, |
|
| 83 | 83 | Opt::Some { pair: Pair { a: -1, b: 5 } }, |
|
| 84 | 84 | Opt::Some { pair: Pair { a: 2, b: 6 } }, |
|
| 85 | 85 | ]; |
|
| 86 | - | assert sumPositive(mixed) == 7; |
|
| 86 | + | assert sumPositive(&mixed[..]) == 7; |
|
| 87 | 87 | ||
| 88 | 88 | // sumNorthSpeeds |
|
| 89 | - | let cmds: *[Command] = &[ |
|
| 89 | + | let cmds: [Command; 2] = [ |
|
| 90 | 90 | Command::Move { dir: Dir::North, speed: 5 }, |
|
| 91 | 91 | Command::Move { dir: Dir::North, speed: 10 }, |
|
| 92 | 92 | ]; |
|
| 93 | - | assert sumNorthSpeeds(cmds) == 15; |
|
| 93 | + | assert sumNorthSpeeds(&cmds[..]) == 15; |
|
| 94 | 94 | ||
| 95 | 95 | // sumNorthSpeeds: stops at South. |
|
| 96 | - | let cmds2: *[Command] = &[ |
|
| 96 | + | let cmds2: [Command; 3] = [ |
|
| 97 | 97 | Command::Move { dir: Dir::North, speed: 3 }, |
|
| 98 | 98 | Command::Move { dir: Dir::South, speed: 7 }, |
|
| 99 | 99 | Command::Move { dir: Dir::North, speed: 9 }, |
|
| 100 | 100 | ]; |
|
| 101 | - | assert sumNorthSpeeds(cmds2) == 3; |
|
| 101 | + | assert sumNorthSpeeds(&cmds2[..]) == 3; |
|
| 102 | 102 | ||
| 103 | 103 | return 0; |
|
| 104 | 104 | } |
test/tests/match.optional.rad
+1 -1
| 1 | 1 | // Returns 1 when the optional pointer is present, otherwise returns 0. |
|
| 2 | - | fn matchOptional(p: ?*i32) -> i32 { |
|
| 2 | + | unsafe fn matchOptional(p: ?*i32) -> i32 { |
|
| 3 | 3 | match p { |
|
| 4 | 4 | case nil => return 0, |
|
| 5 | 5 | x => return 1, |
|
| 6 | 6 | } |
|
| 7 | 7 | } |
test/tests/match.optional.ref.rad
+1 -1
| 1 | 1 | /// Match an optional aggregate through a reference. The value binding is a pointer. |
|
| 2 | - | fn matchOptionalRef(ptr: *?u32) -> u32 { |
|
| 2 | + | fn matchOptionalRef(ptr: &?u32) -> u32 { |
|
| 3 | 3 | match ptr { |
|
| 4 | 4 | value => return *value, |
|
| 5 | 5 | case nil => return 0, |
|
| 6 | 6 | } |
|
| 7 | 7 | } |
test/tests/match.string.rad
+5 -5
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test string literal patterns in match statements. |
|
| 3 | 3 | ||
| 4 | 4 | /// Top-level string match. |
|
| 5 | - | fn classify(s: *[u8]) -> i32 { |
|
| 5 | + | unsafe fn classify(s: *[u8]) -> i32 { |
|
| 6 | 6 | match s { |
|
| 7 | 7 | case "red" => { |
|
| 8 | 8 | return 1; |
|
| 9 | 9 | } |
|
| 10 | 10 | case "green" => { |
| 23 | 23 | union Cmd { |
|
| 24 | 24 | Say { msg: *[u8], count: i32 }, |
|
| 25 | 25 | Quit, |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn dispatch(c: Cmd) -> i32 { |
|
| 28 | + | unsafe fn dispatch(c: Cmd) -> i32 { |
|
| 29 | 29 | match c { |
|
| 30 | 30 | case Cmd::Say { msg: "hello", count } => { |
|
| 31 | 31 | return count; |
|
| 32 | 32 | } |
|
| 33 | 33 | case Cmd::Say { msg: "bye", count } => { |
| 38 | 38 | } |
|
| 39 | 39 | } |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// String pattern in if-let-case. |
|
| 43 | - | fn isHello(c: Cmd) -> bool { |
|
| 43 | + | unsafe fn isHello(c: Cmd) -> bool { |
|
| 44 | 44 | if let case Cmd::Say { msg: "hello", .. } = c { |
|
| 45 | 45 | return true; |
|
| 46 | 46 | } |
|
| 47 | 47 | return false; |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | /// String pattern in let-else. |
|
| 51 | - | fn extractBye(c: Cmd) -> i32 { |
|
| 51 | + | unsafe fn extractBye(c: Cmd) -> i32 { |
|
| 52 | 52 | let case Cmd::Say { msg: "bye", count } = c |
|
| 53 | 53 | else { return -1; }; |
|
| 54 | 54 | return count; |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | - | @default fn main() -> i32 { |
|
| 57 | + | @default unsafe fn main() -> i32 { |
|
| 58 | 58 | // classify |
|
| 59 | 59 | assert classify("red") == 1; |
|
| 60 | 60 | assert classify("green") == 2; |
|
| 61 | 61 | assert classify("blue") == 3; |
|
| 62 | 62 | assert classify("other") == 0; |
test/tests/memzero.union.bug.rad
+1 -1
| 10 | 10 | guard1: u16, |
|
| 11 | 11 | val: Payload, |
|
| 12 | 12 | guard2: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | @default fn main() -> i32 { |
|
| 15 | + | @default unsafe fn main() -> i32 { |
|
| 16 | 16 | let mut f: Frame = Frame { guard1: 0xDEAD, val: undefined, guard2: 0xDEADBEEF }; |
|
| 17 | 17 | ||
| 18 | 18 | set f.val = Payload::Small(7); |
|
| 19 | 19 | set f.val = Payload::Big([1, 2, 3]); |
|
| 20 | 20 |
test/tests/method.basic.rad
+4 -4
| 4 | 4 | record Point { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn (p: *Point) sum() -> i32 { |
|
| 9 | + | unsafe fn (p: *Point) sum() -> i32 { |
|
| 10 | 10 | return p.x + p.y; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn (p: *mut Point) translate(dx: i32, dy: i32) { |
|
| 13 | + | unsafe fn (p: *mut Point) translate(dx: i32, dy: i32) { |
|
| 14 | 14 | set p.x = p.x + dx; |
|
| 15 | 15 | set p.y = p.y + dy; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | @default fn main() -> i32 { |
|
| 18 | + | @default unsafe fn main() -> i32 { |
|
| 19 | 19 | let mut pt = Point { x: 3, y: 4 }; |
|
| 20 | 20 | ||
| 21 | 21 | // Call immutable method. |
|
| 22 | 22 | assert pt.sum() == 7; |
|
| 23 | 23 |
| 25 | 25 | pt.translate(10, 20); |
|
| 26 | 26 | assert pt.x == 13; |
|
| 27 | 27 | assert pt.y == 24; |
|
| 28 | 28 | ||
| 29 | 29 | // Call via pointer. |
|
| 30 | - | let ptr = &pt; |
|
| 30 | + | let ptr: *Point = &pt; |
|
| 31 | 31 | assert ptr.sum() == 37; |
|
| 32 | 32 | ||
| 33 | 33 | return 0; |
|
| 34 | 34 | } |
test/tests/method.chain.rad
+4 -4
| 4 | 4 | record Builder { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn (b: *mut Builder) setX(x: i32) -> *mut Builder { |
|
| 9 | + | unsafe fn (b: *mut Builder) setX(x: i32) -> *mut Builder { |
|
| 10 | 10 | set b.x = x; |
|
| 11 | 11 | return b; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn (b: *mut Builder) setY(y: i32) -> *mut Builder { |
|
| 14 | + | unsafe fn (b: *mut Builder) setY(y: i32) -> *mut Builder { |
|
| 15 | 15 | set b.y = y; |
|
| 16 | 16 | return b; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | fn (b: *Builder) sum() -> i32 { |
|
| 19 | + | unsafe fn (b: *Builder) sum() -> i32 { |
|
| 20 | 20 | return b.x + b.y; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | @default fn main() -> i32 { |
|
| 23 | + | @default unsafe fn main() -> i32 { |
|
| 24 | 24 | let mut b = Builder { x: 0, y: 0 }; |
|
| 25 | 25 | ||
| 26 | 26 | // Chain method calls. |
|
| 27 | 27 | let p = b.setX(10).setY(20); |
|
| 28 | 28 | assert p.sum() == 30; |
test/tests/method.multiple.rad
+4 -4
| 4 | 4 | record Vec2 { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn (v: *Vec2) magnitudeSq() -> i32 { |
|
| 9 | + | fn (v: &Vec2) magnitudeSq() -> i32 { |
|
| 10 | 10 | return v.x * v.x + v.y * v.y; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn (v: *Vec2) dot(other: *Vec2) -> i32 { |
|
| 13 | + | fn (v: &Vec2) dot(other: &Vec2) -> i32 { |
|
| 14 | 14 | return v.x * other.x + v.y * other.y; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | fn (v: *mut Vec2) add(other: *Vec2) { |
|
| 17 | + | fn (v: &mut Vec2) add(other: &Vec2) { |
|
| 18 | 18 | set v.x = v.x + other.x; |
|
| 19 | 19 | set v.y = v.y + other.y; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | fn (v: *mut Vec2) scale(factor: i32) { |
|
| 22 | + | fn (v: &mut Vec2) scale(factor: i32) { |
|
| 23 | 23 | set v.x = v.x * factor; |
|
| 24 | 24 | set v.y = v.y * factor; |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | @default fn main() -> i32 { |
test/tests/method.ptr.rad
+5 -5
| 3 | 3 | ||
| 4 | 4 | record Counter { |
|
| 5 | 5 | value: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn (c: *Counter) get() -> i32 { |
|
| 8 | + | unsafe fn (c: *Counter) get() -> i32 { |
|
| 9 | 9 | return c.value; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | fn (c: *mut Counter) inc() { |
|
| 12 | + | unsafe fn (c: *mut Counter) inc() { |
|
| 13 | 13 | set c.value = c.value + 1; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | - | @default fn main() -> i32 { |
|
| 16 | + | @default unsafe fn main() -> i32 { |
|
| 17 | 17 | let mut c = Counter { value: 0 }; |
|
| 18 | 18 | ||
| 19 | 19 | // Direct call on value. |
|
| 20 | 20 | assert c.get() == 0; |
|
| 21 | 21 | ||
| 22 | 22 | // Mutable method on value. |
|
| 23 | 23 | c.inc(); |
|
| 24 | 24 | assert c.get() == 1; |
|
| 25 | 25 | ||
| 26 | 26 | // Call via immutable pointer. |
|
| 27 | - | let p = &c; |
|
| 27 | + | let p: *Counter = &c; |
|
| 28 | 28 | assert p.get() == 1; |
|
| 29 | 29 | ||
| 30 | 30 | // Call via mutable pointer. |
|
| 31 | - | let mp = &mut c; |
|
| 31 | + | let mp: *mut Counter = &mut c; |
|
| 32 | 32 | mp.inc(); |
|
| 33 | 33 | assert mp.get() == 2; |
|
| 34 | 34 | ||
| 35 | 35 | // Original value also updated. |
|
| 36 | 36 | assert c.get() == 2; |
test/tests/method.pub.rad
+1 -1
| 3 | 3 | ||
| 4 | 4 | record Foo { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | export fn (f: *Foo) getX() -> i32 { |
|
| 8 | + | export fn (f: &Foo) getX() -> i32 { |
|
| 9 | 9 | return f.x; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | @default fn main() -> i32 { |
|
| 13 | 13 | let f = Foo { x: 42 }; |
test/tests/method.return.rad
+4 -4
| 4 | 4 | record Pair { |
|
| 5 | 5 | a: i32, |
|
| 6 | 6 | b: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn (p: *Pair) sum() -> i32 { |
|
| 9 | + | fn (p: &Pair) sum() -> i32 { |
|
| 10 | 10 | return p.a + p.b; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn (p: *Pair) swapped() -> Pair { |
|
| 13 | + | fn (p: &Pair) swapped() -> Pair { |
|
| 14 | 14 | return Pair { a: p.b, b: p.a }; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | fn (p: *Pair) firstPtr() -> *i32 { |
|
| 17 | + | unsafe fn (p: *Pair) firstPtr() -> *i32 { |
|
| 18 | 18 | return &p.a; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | @default fn main() -> i32 { |
|
| 21 | + | @default unsafe fn main() -> i32 { |
|
| 22 | 22 | let pair = Pair { a: 10, b: 20 }; |
|
| 23 | 23 | ||
| 24 | 24 | // Scalar return. |
|
| 25 | 25 | assert pair.sum() == 30; |
|
| 26 | 26 |
test/tests/method.throws.rad
+2 -2
| 8 | 8 | record Parser { |
|
| 9 | 9 | pos: i32, |
|
| 10 | 10 | len: i32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn (p: *mut Parser) advance() throws (ParseError) { |
|
| 13 | + | fn (p: &mut Parser) advance() throws (ParseError) { |
|
| 14 | 14 | if p.pos >= p.len { |
|
| 15 | 15 | throw ParseError::Invalid; |
|
| 16 | 16 | } |
|
| 17 | 17 | set p.pos = p.pos + 1; |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | fn (p: *Parser) remaining() -> i32 { |
|
| 20 | + | fn (p: &Parser) remaining() -> i32 { |
|
| 21 | 21 | return p.len - p.pos; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | @default fn main() -> i32 { |
|
| 25 | 25 | let mut parser = Parser { pos: 0, len: 2 }; |
test/tests/method.union.rad
+1 -1
| 4 | 4 | union Shape { |
|
| 5 | 5 | Circle(i32), |
|
| 6 | 6 | Rect { w: i32, h: i32 }, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn (s: *Shape) isCircle() -> bool { |
|
| 9 | + | fn (s: &Shape) isCircle() -> bool { |
|
| 10 | 10 | match *s { |
|
| 11 | 11 | case Shape::Circle(_) => return true, |
|
| 12 | 12 | else => return false, |
|
| 13 | 13 | } |
|
| 14 | 14 | } |
test/tests/method.with.trait.rad
+4 -4
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | // Standalone method. |
|
| 10 | - | fn (w: *Widget) area() -> i32 { |
|
| 10 | + | unsafe fn (w: *Widget) area() -> i32 { |
|
| 11 | 11 | return w.x * w.y; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | // Trait with its own method. |
|
| 15 | 15 | trait Printable { |
|
| 16 | - | fn (*Printable) code() -> i32; |
|
| 16 | + | unsafe fn (*Printable) code() -> i32; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | instance Printable for Widget { |
|
| 20 | - | fn (w: *Widget) code() -> i32 { |
|
| 20 | + | unsafe fn (w: *Widget) code() -> i32 { |
|
| 21 | 21 | return w.x + w.y; |
|
| 22 | 22 | } |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | - | @default fn main() -> i32 { |
|
| 25 | + | @default unsafe fn main() -> i32 { |
|
| 26 | 26 | let w = Widget { x: 3, y: 5 }; |
|
| 27 | 27 | ||
| 28 | 28 | // Standalone method call. |
|
| 29 | 29 | assert w.area() == 15; |
|
| 30 | 30 |
test/tests/mutref.call.result.rad
+2 -2
| 3 | 3 | ||
| 4 | 4 | record Box { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn idBox(b: *mut Box) -> *mut Box { |
|
| 8 | + | unsafe fn idBox(b: *mut Box) -> *mut Box { |
|
| 9 | 9 | return b; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | @default fn main() -> i32 { |
|
| 12 | + | @default unsafe fn main() -> i32 { |
|
| 13 | 13 | let mut b = Box { x: 1 }; |
|
| 14 | 14 | ||
| 15 | 15 | let px: *mut i32 = &mut idBox(&mut b).x; |
|
| 16 | 16 | set *px = 9; |
|
| 17 | 17 |
test/tests/mutref.loop.bug.rad
+5 -5
| 8 | 8 | //! |
|
| 9 | 9 | //! The critical case is when the loop executes zero iterations: the merge |
|
| 10 | 10 | //! block tries to `load` through the initial integer value (not a valid |
|
| 11 | 11 | //! pointer), crashing the program. |
|
| 12 | 12 | ||
| 13 | - | fn store(ptr: *mut u32, val: u32) { |
|
| 13 | + | unsafe fn store(ptr: *mut u32, val: u32) { |
|
| 14 | 14 | set *ptr = val; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Zero-iteration loop with &mut inside the body. |
|
| 18 | 18 | /// Without the fix, `val` starts as integer 42 in SSA, but the post-loop |
|
| 19 | 19 | /// merge tries to `load` through it as if it were a pointer. |
|
| 20 | - | fn testZeroIter(n: u32) -> u32 { |
|
| 20 | + | unsafe fn testZeroIter(n: u32) -> u32 { |
|
| 21 | 21 | let mut val: u32 = 42; |
|
| 22 | 22 | let mut i: u32 = 0; |
|
| 23 | 23 | while i < n { |
|
| 24 | 24 | store(&mut val, val + 1); |
|
| 25 | 25 | set i += 1; |
|
| 26 | 26 | } |
|
| 27 | 27 | return val; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Multiple iterations: accumulate via &mut pointer in a loop. |
|
| 31 | - | fn testMultiIter() -> u32 { |
|
| 31 | + | unsafe fn testMultiIter() -> u32 { |
|
| 32 | 32 | let mut acc: u32 = 0; |
|
| 33 | 33 | let mut i: u32 = 0; |
|
| 34 | 34 | while i < 5 { |
|
| 35 | 35 | store(&mut acc, acc + i); |
|
| 36 | 36 | set i += 1; |
|
| 37 | 37 | } |
|
| 38 | 38 | return acc; |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Multiple address-taken variables in the same loop. |
|
| 42 | - | fn testMultipleVars() -> u32 { |
|
| 42 | + | unsafe fn testMultipleVars() -> u32 { |
|
| 43 | 43 | let mut a: u32 = 0; |
|
| 44 | 44 | let mut b: u32 = 100; |
|
| 45 | 45 | let mut i: u32 = 0; |
|
| 46 | 46 | while i < 3 { |
|
| 47 | 47 | store(&mut a, a + 1); |
| 49 | 49 | set i += 1; |
|
| 50 | 50 | } |
|
| 51 | 51 | return a + b; |
|
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | - | @default fn main() -> i32 { |
|
| 54 | + | @default unsafe fn main() -> i32 { |
|
| 55 | 55 | // Zero iterations: the critical regression case. |
|
| 56 | 56 | assert testZeroIter(0) == 42; |
|
| 57 | 57 | // Non-zero iterations still work. |
|
| 58 | 58 | assert testZeroIter(3) == 45; |
|
| 59 | 59 | // Accumulation: 0+0+1+2+3+4 = 10 |
test/tests/mutref.loop.rad
+3 -4
| 1 | - | fn callback(val: u32, ctx: *mut opaque) { |
|
| 2 | - | let max = ctx as *mut u32; |
|
| 3 | - | set *max = val; |
|
| 1 | + | fn callback(val: u32, ctx: &mut opaque) { |
|
| 2 | + | unsafe { set *(ctx as &mut u32) = val; } |
|
| 4 | 3 | } |
|
| 5 | 4 | ||
| 6 | 5 | fn test() -> i32 { |
|
| 7 | 6 | let mut maxReg: u32 = 0; |
|
| 8 | 7 | let mut i: u32 = 0; |
|
| 9 | 8 | while i < 3 { |
|
| 10 | - | callback(i, &mut maxReg as *mut opaque); |
|
| 9 | + | callback(i, &mut maxReg); |
|
| 11 | 10 | set i += 1; |
|
| 12 | 11 | } |
|
| 13 | 12 | return maxReg as i32; |
|
| 14 | 13 | } |
test/tests/mutref.scalar.rad
+1 -1
| 1 | - | fn modify(counter: *mut i32, ret: bool) -> bool { |
|
| 1 | + | fn modify(counter: &mut i32, ret: bool) -> bool { |
|
| 2 | 2 | set *counter += 1; |
|
| 3 | 3 | return ret; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | fn test() -> i32 { |
test/tests/nil.cmp.rad
+2 -2
| 9 | 9 | fn nilNeNil() -> bool { |
|
| 10 | 10 | return nil <> nil; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | // nil == opt (reversed order, pointer optional) |
|
| 14 | - | fn nilEqOptPtr(a: ?*i32) -> bool { |
|
| 14 | + | unsafe fn nilEqOptPtr(a: ?*i32) -> bool { |
|
| 15 | 15 | return nil == a; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | // nil <> opt (reversed order, pointer optional) |
|
| 19 | - | fn nilNeOptPtr(a: ?*i32) -> bool { |
|
| 19 | + | unsafe fn nilNeOptPtr(a: ?*i32) -> bool { |
|
| 20 | 20 | return nil <> a; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | // nil == opt (reversed order, aggregate optional) |
|
| 24 | 24 | fn nilEqOptAgg(a: ?i32) -> bool { |
test/tests/opt.nil.check.rad
+4 -4
| 2 | 2 | //! Test nil check for optional pointers and slices. |
|
| 3 | 3 | //! Ensure that nil checks compare the full pointer width, not just the low byte. |
|
| 4 | 4 | ||
| 5 | 5 | /// Return a pointer whose low byte is zero but is non-null. |
|
| 6 | 6 | /// This tests that nil checks use W64, not W8. |
|
| 7 | - | fn makeAlignedPtr() -> *u8 { |
|
| 7 | + | unsafe fn makeAlignedPtr() -> *u8 { |
|
| 8 | 8 | let arr: [u8; 512] = undefined; |
|
| 9 | 9 | // Find an address within arr whose low byte is 0x00. |
|
| 10 | 10 | let base: u64 = &arr[0] as u64; |
|
| 11 | 11 | let offset: u64 = 256 - (base % 256); |
|
| 12 | 12 | return &arr[offset as u32] as *u8; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Test: optional pointer nil check must use full 64-bit comparison. |
|
| 16 | - | fn testOptionalPtrNilCheck() -> i32 { |
|
| 16 | + | unsafe fn testOptionalPtrNilCheck() -> i32 { |
|
| 17 | 17 | let p = makeAlignedPtr(); |
|
| 18 | 18 | let opt: ?*u8 = p; |
|
| 19 | 19 | ||
| 20 | 20 | // The pointer is not nil, but its low byte is 0x00. |
|
| 21 | 21 | // A W8 comparison would wrongly say it's nil. |
| 26 | 26 | } |
|
| 27 | 27 | return 2; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Test: optional slice nil check must use full 64-bit comparison. |
|
| 31 | - | fn testOptionalSliceNilCheck() -> i32 { |
|
| 31 | + | unsafe fn testOptionalSliceNilCheck() -> i32 { |
|
| 32 | 32 | let arr: [u8; 512] = undefined; |
|
| 33 | 33 | let base: u64 = &arr[0] as u64; |
|
| 34 | 34 | let offset: u64 = 256 - (base % 256); |
|
| 35 | 35 | // Create a slice starting at an address whose low byte is 0. |
|
| 36 | 36 | let s: *[u8] = &arr[offset as u32 ..]; |
| 41 | 41 | return 0; |
|
| 42 | 42 | } |
|
| 43 | 43 | return 2; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | - | @default fn main() -> i32 { |
|
| 46 | + | @default unsafe fn main() -> i32 { |
|
| 47 | 47 | let r1 = testOptionalPtrNilCheck(); |
|
| 48 | 48 | if r1 <> 0 { |
|
| 49 | 49 | return r1; |
|
| 50 | 50 | } |
|
| 51 | 51 | let r2 = testOptionalSliceNilCheck(); |
test/tests/opt.ptr.return.nil.rad
+1 -1
| 1 | 1 | // Returns a nil optional pointer. |
|
| 2 | - | fn optPtrReturnNil() -> ?*i32 { |
|
| 2 | + | unsafe fn optPtrReturnNil() -> ?*i32 { |
|
| 3 | 3 | return nil; |
|
| 4 | 4 | } |
test/tests/opt.slice.npo.rad
+11 -11
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test null pointer optimization for optional slices (?*[T]). |
|
| 3 | 3 | //! Optional slices should have the same size as slices (16 bytes), |
|
| 4 | 4 | //! using a null data pointer to represent `nil`. |
|
| 5 | 5 | ||
| 6 | - | fn checkSizes() -> u8 { |
|
| 6 | + | unsafe fn checkSizes() -> u8 { |
|
| 7 | 7 | // ?*[T] should be the same size as *[T] (16 bytes, not 24). |
|
| 8 | 8 | assert @sizeOf(?*[u8]) == 16; |
|
| 9 | 9 | assert @alignOf(?*[u8]) == 8; |
|
| 10 | 10 | assert @sizeOf(?*[u16]) == 16; |
|
| 11 | 11 | assert @sizeOf(?*mut [u8]) == 16; |
|
| 12 | 12 | return 0; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn checkNil() -> u8 { |
|
| 15 | + | unsafe fn checkNil() -> u8 { |
|
| 16 | 16 | let x: ?*[u8] = nil; |
|
| 17 | 17 | assert x == nil; |
|
| 18 | 18 | return 0; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | fn checkWrap() -> u8 { |
|
| 21 | + | unsafe fn checkWrap() -> u8 { |
|
| 22 | 22 | let arr: [u8; 3] = [1, 2, 3]; |
|
| 23 | 23 | let s = &arr[..]; |
|
| 24 | 24 | let opt: ?*[u8] = s; |
|
| 25 | 25 | ||
| 26 | 26 | assert opt <> nil; |
|
| 27 | 27 | return 0; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | - | fn checkIfLet() -> u8 { |
|
| 30 | + | unsafe fn checkIfLet() -> u8 { |
|
| 31 | 31 | let arr: [u8; 3] = [10, 20, 30]; |
|
| 32 | 32 | let s = &arr[..]; |
|
| 33 | 33 | let opt: ?*[u8] = s; |
|
| 34 | 34 | ||
| 35 | 35 | if let val = opt { |
| 45 | 45 | return 35; |
|
| 46 | 46 | } |
|
| 47 | 47 | return 0; |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | - | fn checkLetElse() -> u8 { |
|
| 50 | + | unsafe fn checkLetElse() -> u8 { |
|
| 51 | 51 | let arr: [u8; 3] = [10, 20, 30]; |
|
| 52 | 52 | let s = &arr[..]; |
|
| 53 | 53 | let opt: ?*[u8] = s; |
|
| 54 | 54 | ||
| 55 | 55 | let val = opt else { |
| 57 | 57 | }; |
|
| 58 | 58 | assert val.len == 3; |
|
| 59 | 59 | return 0; |
|
| 60 | 60 | } |
|
| 61 | 61 | ||
| 62 | - | fn returnNil() -> ?*[u8] { |
|
| 62 | + | unsafe fn returnNil() -> ?*[u8] { |
|
| 63 | 63 | return nil; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | - | fn returnSome() -> ?*[u8] { |
|
| 66 | + | unsafe fn returnSome() -> ?*[u8] { |
|
| 67 | 67 | let arr: [u8; 2] = [42, 99]; |
|
| 68 | 68 | return &arr[..]; |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | - | fn checkReturn() -> u8 { |
|
| 71 | + | unsafe fn checkReturn() -> u8 { |
|
| 72 | 72 | let a = returnNil(); |
|
| 73 | 73 | assert a == nil; |
|
| 74 | 74 | let b = returnSome(); |
|
| 75 | 75 | assert b <> nil; |
|
| 76 | 76 | if let val = b { |
| 79 | 79 | return 53; |
|
| 80 | 80 | } |
|
| 81 | 81 | return 0; |
|
| 82 | 82 | } |
|
| 83 | 83 | ||
| 84 | - | fn checkMatch() -> u8 { |
|
| 84 | + | unsafe fn checkMatch() -> u8 { |
|
| 85 | 85 | let arr: [u8; 2] = [5, 6]; |
|
| 86 | 86 | let s = &arr[..]; |
|
| 87 | 87 | let opt: ?*[u8] = s; |
|
| 88 | 88 | ||
| 89 | 89 | match opt { |
| 101 | 101 | } |
|
| 102 | 102 | } |
|
| 103 | 103 | return 0; |
|
| 104 | 104 | } |
|
| 105 | 105 | ||
| 106 | - | fn checkEq() -> u8 { |
|
| 106 | + | unsafe fn checkEq() -> u8 { |
|
| 107 | 107 | let a: ?*[u8] = nil; |
|
| 108 | 108 | let b: ?*[u8] = nil; |
|
| 109 | 109 | ||
| 110 | 110 | // nil == nil |
|
| 111 | 111 | assert a == b; |
| 122 | 122 | assert c == d; |
|
| 123 | 123 | ||
| 124 | 124 | return 0; |
|
| 125 | 125 | } |
|
| 126 | 126 | ||
| 127 | - | @default fn main() -> u8 { |
|
| 127 | + | @default unsafe fn main() -> u8 { |
|
| 128 | 128 | let r1 = checkSizes(); |
|
| 129 | 129 | if r1 <> 0 { |
|
| 130 | 130 | return r1; |
|
| 131 | 131 | } |
|
| 132 | 132 | let r2 = checkNil(); |
test/tests/optional.ptr.eq.rad
+4 -4
| 1 | 1 | // Test optional pointer equality (fast path in lowerOptionalEq) |
|
| 2 | 2 | ||
| 3 | - | fn optPtrEq(a: ?*i32, b: ?*i32) -> bool { |
|
| 3 | + | unsafe fn optPtrEq(a: ?*i32, b: ?*i32) -> bool { |
|
| 4 | 4 | return a == b; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn optPtrNeq(a: ?*i32, b: ?*i32) -> bool { |
|
| 7 | + | unsafe fn optPtrNeq(a: ?*i32, b: ?*i32) -> bool { |
|
| 8 | 8 | return a <> b; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | fn optPtrEqNil(a: ?*i32) -> bool { |
|
| 11 | + | unsafe fn optPtrEqNil(a: ?*i32) -> bool { |
|
| 12 | 12 | return a == nil; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn optPtrNeqNil(a: ?*i32) -> bool { |
|
| 15 | + | unsafe fn optPtrNeqNil(a: ?*i32) -> bool { |
|
| 16 | 16 | return a <> nil; |
|
| 17 | 17 | } |
test/tests/pointer.copy.edge.case.rad
+3 -3
| 17 | 17 | record Parser { |
|
| 18 | 18 | nodes: [Node; 1], |
|
| 19 | 19 | count: u32, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | fn makeNode(p: *mut Parser, kind: NodeKind) -> *mut Node { |
|
| 22 | + | unsafe fn makeNode(p: *mut Parser, kind: NodeKind) -> *mut Node { |
|
| 23 | 23 | let idx: u32 = p.count; |
|
| 24 | 24 | set p.nodes[idx] = Node { |
|
| 25 | 25 | span: Span { length: 0 }, |
|
| 26 | 26 | kind, |
|
| 27 | 27 | }; |
|
| 28 | 28 | set p.count = idx + 1; |
|
| 29 | 29 | return &mut p.nodes[idx]; |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | - | fn setLen(n: *mut Node, len: u32) { |
|
| 32 | + | unsafe fn setLen(n: *mut Node, len: u32) { |
|
| 33 | 33 | set n.span.length = len; |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | - | @default fn main() -> i32 { |
|
| 36 | + | @default unsafe fn main() -> i32 { |
|
| 37 | 37 | let mut parser: Parser = Parser { |
|
| 38 | 38 | nodes: [Node { |
|
| 39 | 39 | span: Span { length: 0 }, |
|
| 40 | 40 | kind: NodeKind::Placeholder, |
|
| 41 | 41 | }; 1], |
test/tests/pointer.slice.index.rad
+1 -1
| 4 | 4 | ptr: *[i32], |
|
| 5 | 5 | zero: u32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | // Test that pointer-to-slice indexing correctly dereferences the slice header. |
|
| 9 | - | @default fn main() -> i32 { |
|
| 9 | + | @default unsafe fn main() -> i32 { |
|
| 10 | 10 | let arr: [i32; 2] = [5, 7]; |
|
| 11 | 11 | let h = Holder { ptr: &arr[..], zero: 0 }; |
|
| 12 | 12 | ||
| 13 | 13 | return (h.ptr[1]) - 7; |
|
| 14 | 14 | } |
test/tests/pointer.slice.store.rad
+4 -4
| 16 | 16 | ||
| 17 | 17 | record PtrBox { |
|
| 18 | 18 | ptr: *mut *mut [Entry], |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | static STORAGE: [Entry; 2] = undefined; |
|
| 22 | - | static TABLE: Table = undefined; |
|
| 23 | - | static HOLDER: PtrBox = undefined; |
|
| 21 | + | unsafe static STORAGE: [Entry; 2] = undefined; |
|
| 22 | + | unsafe static TABLE: Table = undefined; |
|
| 23 | + | unsafe static HOLDER: PtrBox = undefined; |
|
| 24 | 24 | ||
| 25 | - | @default fn main() -> i32 { |
|
| 25 | + | @default unsafe fn main() -> i32 { |
|
| 26 | 26 | set TABLE.entries = &mut STORAGE[..]; |
|
| 27 | 27 | set TABLE.len = 0; |
|
| 28 | 28 | ||
| 29 | 29 | set HOLDER.ptr = &mut TABLE.entries; |
|
| 30 | 30 |
test/tests/pointerfn.rad
+1 -1
| 1 | - | fn takePointer(p: *i32) -> *i32 { |
|
| 1 | + | unsafe fn takePointer(p: *i32) -> *i32 { |
|
| 2 | 2 | return p; |
|
| 3 | 3 | } |
test/tests/prog.bignum.rad
+18 -18
| 6 | 6 | ||
| 7 | 7 | /// Number of limbs per big number (128 bits = 4 x 32-bit words). |
|
| 8 | 8 | constant LIMBS: u32 = 4; |
|
| 9 | 9 | ||
| 10 | 10 | /// Set a big number to a u32 value. |
|
| 11 | - | fn bnFromU32(dst: *mut [u32], val: u32) { |
|
| 11 | + | unsafe fn bnFromU32(dst: *mut [u32], val: u32) { |
|
| 12 | 12 | set dst[0] = val; |
|
| 13 | 13 | let mut i: u32 = 1; |
|
| 14 | 14 | while i < LIMBS { |
|
| 15 | 15 | set dst[i] = 0; |
|
| 16 | 16 | set i += 1; |
|
| 17 | 17 | } |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | /// Set a big number to zero. |
|
| 21 | - | fn bnZero(dst: *mut [u32]) { |
|
| 21 | + | unsafe fn bnZero(dst: *mut [u32]) { |
|
| 22 | 22 | let mut i: u32 = 0; |
|
| 23 | 23 | while i < LIMBS { |
|
| 24 | 24 | set dst[i] = 0; |
|
| 25 | 25 | set i += 1; |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// Copy src to dst. |
|
| 30 | - | fn bnCopy(dst: *mut [u32], src: *[u32]) { |
|
| 30 | + | unsafe fn bnCopy(dst: *mut [u32], src: *[u32]) { |
|
| 31 | 31 | let mut i: u32 = 0; |
|
| 32 | 32 | while i < LIMBS { |
|
| 33 | 33 | set dst[i] = src[i]; |
|
| 34 | 34 | set i += 1; |
|
| 35 | 35 | } |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Compare two big numbers. Returns 0 if equal, 1 if a > b, -1 if a < b. |
|
| 39 | - | fn bnCmp(a: *[u32], b: *[u32]) -> i32 { |
|
| 39 | + | unsafe fn bnCmp(a: *[u32], b: *[u32]) -> i32 { |
|
| 40 | 40 | let mut i: i32 = LIMBS as i32 - 1; |
|
| 41 | 41 | while i >= 0 { |
|
| 42 | 42 | if a[i as u32] > b[i as u32] { |
|
| 43 | 43 | return 1; |
|
| 44 | 44 | } |
| 52 | 52 | } |
|
| 53 | 53 | return 0; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Add two big numbers: dst = a + b. Returns carry (0 or 1). |
|
| 57 | - | fn bnAdd(dst: *mut [u32], a: *[u32], b: *[u32]) -> u32 { |
|
| 57 | + | unsafe fn bnAdd(dst: *mut [u32], a: *[u32], b: *[u32]) -> u32 { |
|
| 58 | 58 | let mut carry: u32 = 0; |
|
| 59 | 59 | let mut i: u32 = 0; |
|
| 60 | 60 | while i < LIMBS { |
|
| 61 | 61 | let sumLo: u32 = a[i] + b[i]; |
|
| 62 | 62 | let mut carry1: u32 = 0; |
| 74 | 74 | } |
|
| 75 | 75 | return carry; |
|
| 76 | 76 | } |
|
| 77 | 77 | ||
| 78 | 78 | /// Subtract two big numbers: dst = a - b. Returns borrow (0 or 1). |
|
| 79 | - | fn bnSub(dst: *mut [u32], a: *[u32], b: *[u32]) -> u32 { |
|
| 79 | + | unsafe fn bnSub(dst: *mut [u32], a: *[u32], b: *[u32]) -> u32 { |
|
| 80 | 80 | let mut borrow: u32 = 0; |
|
| 81 | 81 | let mut i: u32 = 0; |
|
| 82 | 82 | while i < LIMBS { |
|
| 83 | 83 | let diff: u32 = a[i] - b[i]; |
|
| 84 | 84 | let mut borrow1: u32 = 0; |
| 96 | 96 | } |
|
| 97 | 97 | return borrow; |
|
| 98 | 98 | } |
|
| 99 | 99 | ||
| 100 | 100 | /// Multiply two LIMBS-word numbers, producing a 2*LIMBS-word result in wide. |
|
| 101 | - | fn bnMul(wide: *mut [u32], a: *[u32], b: *[u32]) { |
|
| 101 | + | unsafe fn bnMul(wide: *mut [u32], a: *[u32], b: *[u32]) { |
|
| 102 | 102 | let mut i: u32 = 0; |
|
| 103 | 103 | while i < LIMBS * 2 { |
|
| 104 | 104 | set wide[i] = 0; |
|
| 105 | 105 | set i += 1; |
|
| 106 | 106 | } |
| 152 | 152 | set i += 1; |
|
| 153 | 153 | } |
|
| 154 | 154 | } |
|
| 155 | 155 | ||
| 156 | 156 | /// Left shift a big number by 1 bit. |
|
| 157 | - | fn bnShl1(dst: *mut [u32], src: *[u32]) { |
|
| 157 | + | unsafe fn bnShl1(dst: *mut [u32], src: *[u32]) { |
|
| 158 | 158 | let mut carry: u32 = 0; |
|
| 159 | 159 | let mut i: u32 = 0; |
|
| 160 | 160 | while i < LIMBS { |
|
| 161 | 161 | let newCarry: u32 = src[i] >> 31; |
|
| 162 | 162 | set dst[i] = (src[i] << 1) | carry; |
| 164 | 164 | set i += 1; |
|
| 165 | 165 | } |
|
| 166 | 166 | } |
|
| 167 | 167 | ||
| 168 | 168 | /// Right shift a big number by 1 bit. |
|
| 169 | - | fn bnShr1(dst: *mut [u32], src: *[u32]) { |
|
| 169 | + | unsafe fn bnShr1(dst: *mut [u32], src: *[u32]) { |
|
| 170 | 170 | let mut carry: u32 = 0; |
|
| 171 | 171 | let mut i: u32 = LIMBS; |
|
| 172 | 172 | while i > 0 { |
|
| 173 | 173 | set i -= 1; |
|
| 174 | 174 | let newCarry: u32 = src[i] & 1; |
| 176 | 176 | set carry = newCarry; |
|
| 177 | 177 | } |
|
| 178 | 178 | } |
|
| 179 | 179 | ||
| 180 | 180 | /// Test basic addition. |
|
| 181 | - | fn testAdd() -> i32 { |
|
| 181 | + | unsafe fn testAdd() -> i32 { |
|
| 182 | 182 | let mut a: [u32; 4] = [0; 4]; |
|
| 183 | 183 | let mut b: [u32; 4] = [0; 4]; |
|
| 184 | 184 | let mut c: [u32; 4] = [0; 4]; |
|
| 185 | 185 | let mut d: [u32; 4] = [0; 4]; |
|
| 186 | 186 |
| 200 | 200 | ||
| 201 | 201 | return 0; |
|
| 202 | 202 | } |
|
| 203 | 203 | ||
| 204 | 204 | /// Test subtraction. |
|
| 205 | - | fn testSub() -> i32 { |
|
| 205 | + | unsafe fn testSub() -> i32 { |
|
| 206 | 206 | let mut a: [u32; 4] = [0; 4]; |
|
| 207 | 207 | let mut b: [u32; 4] = [0; 4]; |
|
| 208 | 208 | let mut c: [u32; 4] = [0; 4]; |
|
| 209 | 209 | ||
| 210 | 210 | bnZero(&mut a[..]); |
| 218 | 218 | ||
| 219 | 219 | return 0; |
|
| 220 | 220 | } |
|
| 221 | 221 | ||
| 222 | 222 | /// Test multiplication. |
|
| 223 | - | fn testMul() -> i32 { |
|
| 223 | + | unsafe fn testMul() -> i32 { |
|
| 224 | 224 | let mut a: [u32; 4] = [0; 4]; |
|
| 225 | 225 | let mut b: [u32; 4] = [0; 4]; |
|
| 226 | 226 | let mut wide: [u32; 8] = [0; 8]; |
|
| 227 | 227 | ||
| 228 | 228 | bnFromU32(&mut a[..], 12345); |
| 241 | 241 | ||
| 242 | 242 | return 0; |
|
| 243 | 243 | } |
|
| 244 | 244 | ||
| 245 | 245 | /// Test the identity a + b - b = a. |
|
| 246 | - | fn testAddSubIdentity() -> i32 { |
|
| 246 | + | unsafe fn testAddSubIdentity() -> i32 { |
|
| 247 | 247 | let mut a: [u32; 4] = [0; 4]; |
|
| 248 | 248 | let mut b: [u32; 4] = [0; 4]; |
|
| 249 | 249 | let mut c: [u32; 4] = [0; 4]; |
|
| 250 | 250 | let mut d: [u32; 4] = [0; 4]; |
|
| 251 | 251 |
| 269 | 269 | ||
| 270 | 270 | return 0; |
|
| 271 | 271 | } |
|
| 272 | 272 | ||
| 273 | 273 | /// Test shift operations. |
|
| 274 | - | fn testShift() -> i32 { |
|
| 274 | + | unsafe fn testShift() -> i32 { |
|
| 275 | 275 | let mut a: [u32; 4] = [0; 4]; |
|
| 276 | 276 | let mut b: [u32; 4] = [0; 4]; |
|
| 277 | 277 | let mut c: [u32; 4] = [0; 4]; |
|
| 278 | 278 | ||
| 279 | 279 | bnFromU32(&mut a[..], 1); |
| 291 | 291 | ||
| 292 | 292 | return 0; |
|
| 293 | 293 | } |
|
| 294 | 294 | ||
| 295 | 295 | /// Test large multiply. |
|
| 296 | - | fn testLargeMultiply() -> i32 { |
|
| 296 | + | unsafe fn testLargeMultiply() -> i32 { |
|
| 297 | 297 | let mut a: [u32; 4] = [0; 4]; |
|
| 298 | 298 | let mut b: [u32; 4] = [0; 4]; |
|
| 299 | 299 | let mut wide: [u32; 8] = [0; 8]; |
|
| 300 | 300 | ||
| 301 | 301 | set a[0] = 0xFFFFFFFF; |
| 321 | 321 | ||
| 322 | 322 | return 0; |
|
| 323 | 323 | } |
|
| 324 | 324 | ||
| 325 | 325 | /// Test Fibonacci sequence with big numbers. |
|
| 326 | - | fn testFibonacci() -> i32 { |
|
| 326 | + | unsafe fn testFibonacci() -> i32 { |
|
| 327 | 327 | let mut fibA: [u32; 4] = [0; 4]; |
|
| 328 | 328 | let mut fibB: [u32; 4] = [0; 4]; |
|
| 329 | 329 | let mut fibC: [u32; 4] = [0; 4]; |
|
| 330 | 330 | ||
| 331 | 331 | set fibA[0] = 0; |
| 359 | 359 | ||
| 360 | 360 | return 0; |
|
| 361 | 361 | } |
|
| 362 | 362 | ||
| 363 | 363 | /// Test compare function. |
|
| 364 | - | fn testCompare() -> i32 { |
|
| 364 | + | unsafe fn testCompare() -> i32 { |
|
| 365 | 365 | let mut a: [u32; 4] = [0; 4]; |
|
| 366 | 366 | let mut b: [u32; 4] = [0; 4]; |
|
| 367 | 367 | ||
| 368 | 368 | bnFromU32(&mut a[..], 100); |
|
| 369 | 369 | bnFromU32(&mut b[..], 200); |
| 381 | 381 | assert bnCmp(&a[..], &b[..]) == 1; |
|
| 382 | 382 | ||
| 383 | 383 | return 0; |
|
| 384 | 384 | } |
|
| 385 | 385 | ||
| 386 | - | @default fn main() -> i32 { |
|
| 386 | + | @default unsafe fn main() -> i32 { |
|
| 387 | 387 | let r1: i32 = testAdd(); |
|
| 388 | 388 | if r1 <> 0 { |
|
| 389 | 389 | return 10 + r1; |
|
| 390 | 390 | } |
|
| 391 | 391 |
test/tests/prog.binsearch.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Binary search. |
|
| 3 | 3 | //! Search a pre-sorted array for present and absent values. |
|
| 4 | 4 | ||
| 5 | 5 | /// Binary search for `target` in `data`. Returns the index if found, or nil. |
|
| 6 | - | fn binarySearch(data: *[i32], target: i32) -> ?u32 { |
|
| 6 | + | fn binarySearch(data: &[i32], target: i32) -> ?u32 { |
|
| 7 | 7 | let mut lo: i32 = 0; |
|
| 8 | 8 | let mut hi: i32 = data.len as i32 - 1; |
|
| 9 | 9 | ||
| 10 | 10 | while lo <= hi { |
|
| 11 | 11 | let mid: i32 = lo + (hi - lo) / 2; |
| 21 | 21 | } |
|
| 22 | 22 | return nil; |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | /// Test searching for elements that exist. |
|
| 26 | - | fn testPresent(data: *[i32]) -> i32 { |
|
| 26 | + | fn testPresent(data: &[i32]) -> i32 { |
|
| 27 | 27 | // First element. |
|
| 28 | 28 | let idx0 = binarySearch(data, 3) else { |
|
| 29 | 29 | return 1; |
|
| 30 | 30 | }; |
|
| 31 | 31 | assert idx0 == 0; |
| 56 | 56 | assert idx16 == 16; |
|
| 57 | 57 | return 0; |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | /// Test searching for elements that do not exist. |
|
| 61 | - | fn testAbsent(data: *[i32]) -> i32 { |
|
| 61 | + | fn testAbsent(data: &[i32]) -> i32 { |
|
| 62 | 62 | // Below range. |
|
| 63 | 63 | assert binarySearch(data, 1) == nil; |
|
| 64 | 64 | // Above range. |
|
| 65 | 65 | assert binarySearch(data, 200) == nil; |
|
| 66 | 66 | // Between existing elements. |
| 71 | 71 | assert binarySearch(data, 99) == nil; |
|
| 72 | 72 | return 0; |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | /// Test searching for every element in the array. |
|
| 76 | - | fn testAllPresent(data: *[i32]) -> i32 { |
|
| 76 | + | fn testAllPresent(data: &[i32]) -> i32 { |
|
| 77 | 77 | for value, index in data { |
|
| 78 | 78 | let found = binarySearch(data, value) else { |
|
| 79 | 79 | return index as i32 + 1; |
|
| 80 | 80 | }; |
|
| 81 | 81 | if found <> index { |
test/tests/prog.bubblesort.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Bubble sort. |
|
| 3 | 3 | //! Sort an array of integers and verify the result. |
|
| 4 | 4 | ||
| 5 | 5 | /// Bubble sort the array in ascending order. |
|
| 6 | - | fn bubbleSort(data: *mut [i32]) { |
|
| 6 | + | fn bubbleSort(data: &mut [i32]) { |
|
| 7 | 7 | let mut n: u32 = data.len; |
|
| 8 | 8 | while n > 1 { |
|
| 9 | 9 | let mut swapped: bool = false; |
|
| 10 | 10 | let mut i: u32 = 0; |
|
| 11 | 11 | while i < n - 1 { |
| 24 | 24 | set n -= 1; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | /// Verify the array is sorted in ascending order. |
|
| 29 | - | fn isSorted(data: *[i32]) -> bool { |
|
| 29 | + | fn isSorted(data: &[i32]) -> bool { |
|
| 30 | 30 | let mut prev: ?i32 = nil; |
|
| 31 | 31 | for val in data { |
|
| 32 | 32 | if let p = prev { |
|
| 33 | 33 | if p > val { |
|
| 34 | 34 | return false; |
| 38 | 38 | } |
|
| 39 | 39 | return true; |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// Compute the sum of all elements. |
|
| 43 | - | fn sum(data: *[i32]) -> i32 { |
|
| 43 | + | fn sum(data: &[i32]) -> i32 { |
|
| 44 | 44 | let mut total: i32 = 0; |
|
| 45 | 45 | for val in data { |
|
| 46 | 46 | set total += val; |
|
| 47 | 47 | } |
|
| 48 | 48 | return total; |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Verify specific positions in the sorted output. |
|
| 52 | - | fn verifyPositions(data: *[i32]) -> i32 { |
|
| 52 | + | fn verifyPositions(data: &[i32]) -> i32 { |
|
| 53 | 53 | // Sorted: 3 5 7 12 17 19 28 31 42 50 55 66 71 80 88 93 |
|
| 54 | 54 | assert data[0] == 3; |
|
| 55 | 55 | assert data[1] == 5; |
|
| 56 | 56 | assert data[2] == 7; |
|
| 57 | 57 | assert data[7] == 31; |
test/tests/prog.cordic.rad
+8 -8
| 27 | 27 | sin: i32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// CORDIC rotation mode: compute cos(angle) and sin(angle). |
|
| 31 | 31 | /// Input angle in Q16.16 radians, must be in [-pi/2, pi/2]. |
|
| 32 | - | fn cordicRotate(angle: i32, atanTable: *[i32]) -> CosSin { |
|
| 32 | + | fn cordicRotate(angle: i32, atanTable: &[i32]) -> CosSin { |
|
| 33 | 33 | let mut x: i32 = CORDIC_GAIN; |
|
| 34 | 34 | let mut y: i32 = 0; |
|
| 35 | 35 | let mut z: i32 = angle; |
|
| 36 | 36 | ||
| 37 | 37 | let mut i: u32 = 0; |
| 53 | 53 | ||
| 54 | 54 | return CosSin { cos: x, sin: y }; |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | 57 | /// Compute cos and sin for any angle by reducing to [-pi/2, pi/2]. |
|
| 58 | - | fn cosSin(angle: i32, atanTable: *[i32]) -> CosSin { |
|
| 58 | + | fn cosSin(angle: i32, atanTable: &[i32]) -> CosSin { |
|
| 59 | 59 | let mut a: i32 = angle; |
|
| 60 | 60 | ||
| 61 | 61 | // Reduce to [-pi, pi]. |
|
| 62 | 62 | while a > PI { |
|
| 63 | 63 | set a -= 2 * PI; |
| 120 | 120 | } |
|
| 121 | 121 | return result as i32; |
|
| 122 | 122 | } |
|
| 123 | 123 | ||
| 124 | 124 | /// Test cos(0) = 1, sin(0) = 0. |
|
| 125 | - | fn testZero(atanTable: *[i32]) -> i32 { |
|
| 125 | + | fn testZero(atanTable: &[i32]) -> i32 { |
|
| 126 | 126 | let r: CosSin = cosSin(0, atanTable); |
|
| 127 | 127 | // cos(0) should be close to 65536 (1.0 in Q16.16). |
|
| 128 | 128 | let cosErr: i32 = abs(r.cos - SCALE); |
|
| 129 | 129 | let sinErr: i32 = abs(r.sin); |
|
| 130 | 130 |
| 133 | 133 | assert sinErr <= 655; |
|
| 134 | 134 | return 0; |
|
| 135 | 135 | } |
|
| 136 | 136 | ||
| 137 | 137 | /// Test cos(pi/2) = 0, sin(pi/2) = 1. |
|
| 138 | - | fn testHalfPi(atanTable: *[i32]) -> i32 { |
|
| 138 | + | fn testHalfPi(atanTable: &[i32]) -> i32 { |
|
| 139 | 139 | let r: CosSin = cosSin(HALF_PI, atanTable); |
|
| 140 | 140 | let cosErr: i32 = abs(r.cos); |
|
| 141 | 141 | let sinErr: i32 = abs(r.sin - SCALE); |
|
| 142 | 142 | ||
| 143 | 143 | assert cosErr <= 655; |
|
| 144 | 144 | assert sinErr <= 655; |
|
| 145 | 145 | return 0; |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | 148 | /// Test cos(pi) = -1, sin(pi) = 0. |
|
| 149 | - | fn testPi(atanTable: *[i32]) -> i32 { |
|
| 149 | + | fn testPi(atanTable: &[i32]) -> i32 { |
|
| 150 | 150 | let r: CosSin = cosSin(PI, atanTable); |
|
| 151 | 151 | let cosErr: i32 = abs(r.cos + SCALE); |
|
| 152 | 152 | let sinErr: i32 = abs(r.sin); |
|
| 153 | 153 | ||
| 154 | 154 | assert cosErr <= 655; |
|
| 155 | 155 | assert sinErr <= 655; |
|
| 156 | 156 | return 0; |
|
| 157 | 157 | } |
|
| 158 | 158 | ||
| 159 | 159 | /// Test Pythagorean identity: sin^2 + cos^2 = 1 for several angles. |
|
| 160 | - | fn testPythagorean(atanTable: *[i32]) -> i32 { |
|
| 160 | + | fn testPythagorean(atanTable: &[i32]) -> i32 { |
|
| 161 | 161 | // Test at 16 evenly spaced angles from 0 to 2*pi. |
|
| 162 | 162 | let step: i32 = PI / 8; |
|
| 163 | 163 | let mut i: i32 = 0 - PI; |
|
| 164 | 164 | ||
| 165 | 165 | while i <= PI { |
| 176 | 176 | } |
|
| 177 | 177 | return 0; |
|
| 178 | 178 | } |
|
| 179 | 179 | ||
| 180 | 180 | /// Test symmetry: sin(-x) = -sin(x), cos(-x) = cos(x). |
|
| 181 | - | fn testSymmetry(atanTable: *[i32]) -> i32 { |
|
| 181 | + | fn testSymmetry(atanTable: &[i32]) -> i32 { |
|
| 182 | 182 | let angles: [i32; 5] = [16384, 32768, 51472, 65536, 81920]; |
|
| 183 | 183 | ||
| 184 | 184 | let mut i: u32 = 0; |
|
| 185 | 185 | while i < 5 { |
|
| 186 | 186 | let a: i32 = angles[i]; |
| 196 | 196 | } |
|
| 197 | 197 | return 0; |
|
| 198 | 198 | } |
|
| 199 | 199 | ||
| 200 | 200 | /// Test specific known value: cos(pi/3) = 0.5, sin(pi/3) = 0.866. |
|
| 201 | - | fn testPiThird(atanTable: *[i32]) -> i32 { |
|
| 201 | + | fn testPiThird(atanTable: &[i32]) -> i32 { |
|
| 202 | 202 | // pi/3 in Q16.16 = 68629. |
|
| 203 | 203 | let piThird: i32 = 68629; |
|
| 204 | 204 | let r: CosSin = cosSin(piThird, atanTable); |
|
| 205 | 205 | ||
| 206 | 206 | // cos(pi/3) = 0.5 = 32768 in Q16.16. |
test/tests/prog.crc32.rad
+5 -5
| 2 | 2 | //! CRC-32. |
|
| 3 | 3 | //! Compute CRC-32 of a byte buffer using a 256-entry lookup table. |
|
| 4 | 4 | //! The table is built at startup. Verify against known checksums. |
|
| 5 | 5 | ||
| 6 | 6 | /// Build the CRC-32 lookup table using the standard polynomial 0xEDB88320. |
|
| 7 | - | fn buildTable(table: *mut [u32]) { |
|
| 7 | + | fn buildTable(table: &mut [u32]) { |
|
| 8 | 8 | let mut i: u32 = 0; |
|
| 9 | 9 | while i < 256 { |
|
| 10 | 10 | let mut crc: u32 = i; |
|
| 11 | 11 | let mut j: u32 = 0; |
|
| 12 | 12 | while j < 8 { |
| 21 | 21 | set i += 1; |
|
| 22 | 22 | } |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | /// Compute CRC-32 of a byte slice. |
|
| 26 | - | fn crc32(table: *[u32], data: *[u8]) -> u32 { |
|
| 26 | + | fn crc32(table: &[u32], data: &[u8]) -> u32 { |
|
| 27 | 27 | let mut crc: u32 = 0xFFFFFFFF; |
|
| 28 | 28 | let mut i: u32 = 0; |
|
| 29 | 29 | while i < data.len { |
|
| 30 | 30 | let byte: u8 = data[i]; |
|
| 31 | 31 | let index: u32 = (crc ^ byte as u32) & 0xFF; |
| 34 | 34 | } |
|
| 35 | 35 | return crc ^ 0xFFFFFFFF; |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Test the lookup table has been built correctly. |
|
| 39 | - | fn testTable(table: *[u32]) -> i32 { |
|
| 39 | + | fn testTable(table: &[u32]) -> i32 { |
|
| 40 | 40 | // TABLE[0] should be 0 (zero input, all shifts produce zero). |
|
| 41 | 41 | assert table[0] == 0; |
|
| 42 | 42 | // Known value: TABLE[1] = 0x77073096 |
|
| 43 | 43 | assert table[1] == 0x77073096; |
|
| 44 | 44 | // TABLE[255] is a known value: 0x2D02EF8D |
|
| 45 | 45 | assert table[255] == 0x2D02EF8D; |
|
| 46 | 46 | return 0; |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Test CRC-32 of known strings. |
|
| 50 | - | fn testKnownCRC(table: *[u32]) -> i32 { |
|
| 50 | + | fn testKnownCRC(table: &[u32]) -> i32 { |
|
| 51 | 51 | // CRC-32 of empty data should be 0x00000000. |
|
| 52 | 52 | assert crc32(table, &[]) == 0x00000000; |
|
| 53 | 53 | ||
| 54 | 54 | // CRC-32 of "123456789" = 0xCBF43926 (the standard check value). |
|
| 55 | 55 | let check: [u8; 9] = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]; |
| 60 | 60 | assert crc32(table, &single[..]) == 0xD3D99E8B; |
|
| 61 | 61 | return 0; |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | 64 | /// Test CRC-32 of incrementally built data. |
|
| 65 | - | fn testIncremental(table: *[u32]) -> i32 { |
|
| 65 | + | fn testIncremental(table: &[u32]) -> i32 { |
|
| 66 | 66 | // Build a 32-byte buffer with values 0..31. |
|
| 67 | 67 | let mut buf: [u8; 32] = [0; 32]; |
|
| 68 | 68 | let mut i: u32 = 0; |
|
| 69 | 69 | while i < 32 { |
|
| 70 | 70 | set buf[i] = i as u8; |
test/tests/prog.dijkstra.rad
+18 -18
| 19 | 19 | numNodes: u32, |
|
| 20 | 20 | heap: *mut [HeapEntry], |
|
| 21 | 21 | heapSize: u32, |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | fn heapSwap(g: *mut Graph, i: u32, j: u32) { |
|
| 24 | + | unsafe fn heapSwap(g: *mut Graph, i: u32, j: u32) { |
|
| 25 | 25 | let tmp: HeapEntry = g.heap[i]; |
|
| 26 | 26 | set g.heap[i] = g.heap[j]; |
|
| 27 | 27 | set g.heap[j] = tmp; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | - | fn siftUp(g: *mut Graph, pos: u32) { |
|
| 30 | + | unsafe fn siftUp(g: *mut Graph, pos: u32) { |
|
| 31 | 31 | let mut i: u32 = pos; |
|
| 32 | 32 | while i > 0 { |
|
| 33 | 33 | let parent: u32 = (i - 1) / 2; |
|
| 34 | 34 | if g.heap[i].dist < g.heap[parent].dist { |
|
| 35 | 35 | heapSwap(g, i, parent); |
| 38 | 38 | return; |
|
| 39 | 39 | } |
|
| 40 | 40 | } |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | - | fn siftDown(g: *mut Graph, pos: u32) { |
|
| 43 | + | unsafe fn siftDown(g: *mut Graph, pos: u32) { |
|
| 44 | 44 | let mut i: u32 = pos; |
|
| 45 | 45 | while true { |
|
| 46 | 46 | let left: u32 = 2 * i + 1; |
|
| 47 | 47 | let right: u32 = 2 * i + 2; |
|
| 48 | 48 | let mut smallest: u32 = i; |
| 59 | 59 | heapSwap(g, i, smallest); |
|
| 60 | 60 | set i = smallest; |
|
| 61 | 61 | } |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn heapPush(g: *mut Graph, dist: u32, node: u32) { |
|
| 64 | + | unsafe fn heapPush(g: *mut Graph, dist: u32, node: u32) { |
|
| 65 | 65 | set g.heap[g.heapSize] = HeapEntry { dist, node }; |
|
| 66 | 66 | set g.heapSize += 1; |
|
| 67 | 67 | siftUp(g, g.heapSize - 1); |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | /// Pop from the heap. Returns nil if the heap is empty. |
|
| 71 | - | fn heapPop(g: *mut Graph) -> ?HeapEntry { |
|
| 71 | + | unsafe fn heapPop(g: *mut Graph) -> ?HeapEntry { |
|
| 72 | 72 | if g.heapSize == 0 { |
|
| 73 | 73 | return nil; |
|
| 74 | 74 | } |
|
| 75 | 75 | let result: HeapEntry = g.heap[0]; |
|
| 76 | 76 | set g.heapSize -= 1; |
| 79 | 79 | siftDown(g, 0); |
|
| 80 | 80 | } |
|
| 81 | 81 | return result; |
|
| 82 | 82 | } |
|
| 83 | 83 | ||
| 84 | - | fn addEdge(g: *mut Graph, from: u32, to: u32, weight: u32) { |
|
| 84 | + | unsafe fn addEdge(g: *mut Graph, from: u32, to: u32, weight: u32) { |
|
| 85 | 85 | set g.adj[from][to] = weight; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn addBidiEdge(g: *mut Graph, from: u32, to: u32, weight: u32) { |
|
| 88 | + | unsafe fn addBidiEdge(g: *mut Graph, from: u32, to: u32, weight: u32) { |
|
| 89 | 89 | set g.adj[from][to] = weight; |
|
| 90 | 90 | set g.adj[to][from] = weight; |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | - | fn resetGraph(g: *mut Graph, n: u32) { |
|
| 93 | + | unsafe fn resetGraph(g: *mut Graph, n: u32) { |
|
| 94 | 94 | set g.numNodes = n; |
|
| 95 | 95 | let mut i: u32 = 0; |
|
| 96 | 96 | while i < MAX_NODES { |
|
| 97 | 97 | let mut j: u32 = 0; |
|
| 98 | 98 | while j < MAX_NODES { |
| 106 | 106 | } |
|
| 107 | 107 | set g.heapSize = 0; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | 110 | /// Get the predecessor as an optional; -1 means no predecessor. |
|
| 111 | - | fn getPrev(g: *Graph, node: u32) -> ?u32 { |
|
| 111 | + | unsafe fn getPrev(g: *Graph, node: u32) -> ?u32 { |
|
| 112 | 112 | let p = g.prev[node]; |
|
| 113 | 113 | if p < 0 { |
|
| 114 | 114 | return nil; |
|
| 115 | 115 | } |
|
| 116 | 116 | return p as u32; |
|
| 117 | 117 | } |
|
| 118 | 118 | ||
| 119 | - | fn dijkstra(g: *mut Graph, source: u32) { |
|
| 119 | + | unsafe fn dijkstra(g: *mut Graph, source: u32) { |
|
| 120 | 120 | set g.dist[source] = 0; |
|
| 121 | 121 | heapPush(g, 0, source); |
|
| 122 | 122 | ||
| 123 | 123 | while let entry = heapPop(g) { |
|
| 124 | 124 | let u: u32 = entry.node; |
| 142 | 142 | } |
|
| 143 | 143 | } |
|
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | 146 | /// Reconstruct the shortest path from source to target. |
|
| 147 | - | fn reconstructPath(g: *Graph, target: u32, path: *mut [u32]) -> u32 { |
|
| 147 | + | unsafe fn reconstructPath(g: *Graph, target: u32, path: *mut [u32]) -> u32 { |
|
| 148 | 148 | let mut len: u32 = 0; |
|
| 149 | 149 | ||
| 150 | 150 | set path[len] = target; |
|
| 151 | 151 | set len += 1; |
|
| 152 | 152 |
| 168 | 168 | set b -= 1; |
|
| 169 | 169 | } |
|
| 170 | 170 | return len; |
|
| 171 | 171 | } |
|
| 172 | 172 | ||
| 173 | - | fn testLinear(g: *mut Graph) -> i32 { |
|
| 173 | + | unsafe fn testLinear(g: *mut Graph) -> i32 { |
|
| 174 | 174 | resetGraph(g, 5); |
|
| 175 | 175 | addEdge(g, 0, 1, 10); |
|
| 176 | 176 | addEdge(g, 1, 2, 10); |
|
| 177 | 177 | addEdge(g, 2, 3, 10); |
|
| 178 | 178 | addEdge(g, 3, 4, 10); |
| 193 | 193 | assert path[4] == 4; |
|
| 194 | 194 | ||
| 195 | 195 | return 0; |
|
| 196 | 196 | } |
|
| 197 | 197 | ||
| 198 | - | fn testShortcut(g: *mut Graph) -> i32 { |
|
| 198 | + | unsafe fn testShortcut(g: *mut Graph) -> i32 { |
|
| 199 | 199 | resetGraph(g, 4); |
|
| 200 | 200 | addEdge(g, 0, 1, 10); |
|
| 201 | 201 | addEdge(g, 1, 2, 10); |
|
| 202 | 202 | addEdge(g, 0, 3, 5); |
|
| 203 | 203 | addEdge(g, 3, 2, 3); |
| 212 | 212 | assert prev2 == 3; |
|
| 213 | 213 | ||
| 214 | 214 | return 0; |
|
| 215 | 215 | } |
|
| 216 | 216 | ||
| 217 | - | fn testBidirectional(g: *mut Graph) -> i32 { |
|
| 217 | + | unsafe fn testBidirectional(g: *mut Graph) -> i32 { |
|
| 218 | 218 | resetGraph(g, 5); |
|
| 219 | 219 | addBidiEdge(g, 0, 1, 1); |
|
| 220 | 220 | addBidiEdge(g, 1, 2, 2); |
|
| 221 | 221 | addBidiEdge(g, 2, 3, 3); |
|
| 222 | 222 | addBidiEdge(g, 3, 0, 4); |
| 234 | 234 | } |
|
| 235 | 235 | ||
| 236 | 236 | return 0; |
|
| 237 | 237 | } |
|
| 238 | 238 | ||
| 239 | - | fn testComplete(g: *mut Graph) -> i32 { |
|
| 239 | + | unsafe fn testComplete(g: *mut Graph) -> i32 { |
|
| 240 | 240 | resetGraph(g, 8); |
|
| 241 | 241 | ||
| 242 | 242 | let mut i: u32 = 0; |
|
| 243 | 243 | while i < 8 { |
|
| 244 | 244 | let mut j: u32 = 0; |
| 267 | 267 | } |
|
| 268 | 268 | ||
| 269 | 269 | return 0; |
|
| 270 | 270 | } |
|
| 271 | 271 | ||
| 272 | - | fn testDisconnected(g: *mut Graph) -> i32 { |
|
| 272 | + | unsafe fn testDisconnected(g: *mut Graph) -> i32 { |
|
| 273 | 273 | resetGraph(g, 6); |
|
| 274 | 274 | addEdge(g, 0, 1, 5); |
|
| 275 | 275 | addEdge(g, 1, 2, 3); |
|
| 276 | 276 | addEdge(g, 3, 4, 2); |
|
| 277 | 277 | addEdge(g, 4, 5, 1); |
| 290 | 290 | assert getPrev(g, 3) == nil; |
|
| 291 | 291 | ||
| 292 | 292 | return 0; |
|
| 293 | 293 | } |
|
| 294 | 294 | ||
| 295 | - | fn testDiamond(g: *mut Graph) -> i32 { |
|
| 295 | + | unsafe fn testDiamond(g: *mut Graph) -> i32 { |
|
| 296 | 296 | resetGraph(g, 5); |
|
| 297 | 297 | addEdge(g, 0, 1, 5); |
|
| 298 | 298 | addEdge(g, 0, 2, 5); |
|
| 299 | 299 | addEdge(g, 1, 3, 5); |
|
| 300 | 300 | addEdge(g, 2, 3, 5); |
| 311 | 311 | assert prev3 == 1 or prev3 == 2; |
|
| 312 | 312 | ||
| 313 | 313 | return 0; |
|
| 314 | 314 | } |
|
| 315 | 315 | ||
| 316 | - | @default fn main() -> i32 { |
|
| 316 | + | @default unsafe fn main() -> i32 { |
|
| 317 | 317 | let mut adj: [[u32; 16]; 16] = [[0xFFFFFFFF; 16]; 16]; |
|
| 318 | 318 | let mut dist: [u32; 16] = [0xFFFFFFFF; 16]; |
|
| 319 | 319 | let mut prev: [i32; 16] = [-1; 16]; |
|
| 320 | 320 | let mut visited: [bool; 16] = [false; 16]; |
|
| 321 | 321 | let mut heap: [HeapEntry; 256] = [HeapEntry { dist: 0, node: 0 }; 256]; |
test/tests/prog.eval.rad
+50 -30
| 31 | 31 | union EvalError { |
|
| 32 | 32 | DivByZero |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Allocate a new node, returning its index. |
|
| 36 | - | fn newNode(pool: *mut Pool, expr: Expr) -> u32 { |
|
| 36 | + | fn newNode(pool: &mut Pool, expr: Expr) -> u32 { |
|
| 37 | 37 | let idx = pool.count; |
|
| 38 | 38 | set pool.nodes[idx] = expr; |
|
| 39 | 39 | set pool.count += 1; |
|
| 40 | 40 | return idx; |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | 43 | /// Convenience constructors. |
|
| 44 | - | fn num(pool: *mut Pool, n: i32) -> u32 { |
|
| 44 | + | fn num(pool: &mut Pool, n: i32) -> u32 { |
|
| 45 | 45 | return newNode(pool, Expr::Num(n)); |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | - | fn add(pool: *mut Pool, left: u32, right: u32) -> u32 { |
|
| 48 | + | fn add(pool: &mut Pool, left: u32, right: u32) -> u32 { |
|
| 49 | 49 | return newNode(pool, Expr::Add(BinOp { left, right })); |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | - | fn sub(pool: *mut Pool, left: u32, right: u32) -> u32 { |
|
| 52 | + | fn sub(pool: &mut Pool, left: u32, right: u32) -> u32 { |
|
| 53 | 53 | return newNode(pool, Expr::Sub(BinOp { left, right })); |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | - | fn mul(pool: *mut Pool, left: u32, right: u32) -> u32 { |
|
| 56 | + | fn mul(pool: &mut Pool, left: u32, right: u32) -> u32 { |
|
| 57 | 57 | return newNode(pool, Expr::Mul(BinOp { left, right })); |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | - | fn div(pool: *mut Pool, left: u32, right: u32) -> u32 { |
|
| 60 | + | fn div(pool: &mut Pool, left: u32, right: u32) -> u32 { |
|
| 61 | 61 | return newNode(pool, Expr::Div(BinOp { left, right })); |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn neg(pool: *mut Pool, child: u32) -> u32 { |
|
| 64 | + | fn neg(pool: &mut Pool, child: u32) -> u32 { |
|
| 65 | 65 | return newNode(pool, Expr::Neg(child)); |
|
| 66 | 66 | } |
|
| 67 | 67 | ||
| 68 | 68 | /// Recursively evaluate the expression tree rooted at nodes[idx]. |
|
| 69 | - | fn eval(nodes: *[Expr], idx: u32) -> i32 throws (EvalError) { |
|
| 69 | + | fn eval(nodes: &[Expr], idx: u32) -> i32 throws (EvalError) { |
|
| 70 | 70 | let node = nodes[idx]; |
|
| 71 | 71 | match node { |
|
| 72 | 72 | case Expr::Num(n) => { |
|
| 73 | 73 | return n; |
|
| 74 | 74 | } |
| 94 | 94 | } |
|
| 95 | 95 | } |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | 98 | /// Count the total number of nodes in the tree rooted at idx. |
|
| 99 | - | fn countNodes(nodes: *[Expr], idx: u32) -> u32 { |
|
| 99 | + | fn countNodes(nodes: &[Expr], idx: u32) -> u32 { |
|
| 100 | 100 | let node = nodes[idx]; |
|
| 101 | 101 | match node { |
|
| 102 | 102 | case Expr::Num(_) => { |
|
| 103 | 103 | return 1; |
|
| 104 | 104 | } |
| 119 | 119 | } |
|
| 120 | 120 | } |
|
| 121 | 121 | } |
|
| 122 | 122 | ||
| 123 | 123 | /// Reset the node pool. |
|
| 124 | - | fn reset(pool: *mut Pool) { |
|
| 124 | + | fn reset(pool: &mut Pool) { |
|
| 125 | 125 | set pool.count = 0; |
|
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | /// Test 1: Simple addition: 3 + 4 = 7 |
|
| 129 | - | fn testSimpleAdd(pool: *mut Pool) -> i32 { |
|
| 129 | + | fn testSimpleAdd(pool: &mut Pool) -> i32 { |
|
| 130 | 130 | reset(pool); |
|
| 131 | - | let root = add(pool, num(pool, 3), num(pool, 4)); |
|
| 131 | + | let three = num(pool, 3); |
|
| 132 | + | let four = num(pool, 4); |
|
| 133 | + | let root = add(pool, three, four); |
|
| 132 | 134 | assert try! eval(&pool.nodes[..], root) == 7; |
|
| 133 | 135 | return 0; |
|
| 134 | 136 | } |
|
| 135 | 137 | ||
| 136 | 138 | /// Test 2: Nested expression: (2 + 3) * (4 - 1) = 5 * 3 = 15 |
|
| 137 | - | fn testNested(pool: *mut Pool) -> i32 { |
|
| 139 | + | fn testNested(pool: &mut Pool) -> i32 { |
|
| 138 | 140 | reset(pool); |
|
| 139 | - | let left = add(pool, num(pool, 2), num(pool, 3)); |
|
| 140 | - | let right = sub(pool, num(pool, 4), num(pool, 1)); |
|
| 141 | + | let two = num(pool, 2); |
|
| 142 | + | let three = num(pool, 3); |
|
| 143 | + | let left = add(pool, two, three); |
|
| 144 | + | let four = num(pool, 4); |
|
| 145 | + | let one = num(pool, 1); |
|
| 146 | + | let right = sub(pool, four, one); |
|
| 141 | 147 | let root = mul(pool, left, right); |
|
| 142 | 148 | assert try! eval(&pool.nodes[..], root) == 15; |
|
| 143 | 149 | assert countNodes(&pool.nodes[..], root) == 7; |
|
| 144 | 150 | return 0; |
|
| 145 | 151 | } |
|
| 146 | 152 | ||
| 147 | 153 | /// Test 3: Complex expression: ((10 + 5) * 2 - 6) / 4 = (30 - 6) / 4 = 24 / 4 = 6 |
|
| 148 | - | fn testComplex(pool: *mut Pool) -> i32 { |
|
| 154 | + | fn testComplex(pool: &mut Pool) -> i32 { |
|
| 149 | 155 | reset(pool); |
|
| 150 | - | let a = add(pool, num(pool, 10), num(pool, 5)); |
|
| 151 | - | let b = mul(pool, a, num(pool, 2)); |
|
| 152 | - | let c = sub(pool, b, num(pool, 6)); |
|
| 153 | - | let root = div(pool, c, num(pool, 4)); |
|
| 156 | + | let ten = num(pool, 10); |
|
| 157 | + | let five = num(pool, 5); |
|
| 158 | + | let a = add(pool, ten, five); |
|
| 159 | + | let two = num(pool, 2); |
|
| 160 | + | let b = mul(pool, a, two); |
|
| 161 | + | let six = num(pool, 6); |
|
| 162 | + | let c = sub(pool, b, six); |
|
| 163 | + | let four = num(pool, 4); |
|
| 164 | + | let root = div(pool, c, four); |
|
| 154 | 165 | assert try! eval(&pool.nodes[..], root) == 6; |
|
| 155 | 166 | return 0; |
|
| 156 | 167 | } |
|
| 157 | 168 | ||
| 158 | 169 | /// Test 4: Negation: -(3 + 4) = -7 |
|
| 159 | - | fn testNeg(pool: *mut Pool) -> i32 { |
|
| 170 | + | fn testNeg(pool: &mut Pool) -> i32 { |
|
| 160 | 171 | reset(pool); |
|
| 161 | - | let root = neg(pool, add(pool, num(pool, 3), num(pool, 4))); |
|
| 172 | + | let three = num(pool, 3); |
|
| 173 | + | let four = num(pool, 4); |
|
| 174 | + | let sum = add(pool, three, four); |
|
| 175 | + | let root = neg(pool, sum); |
|
| 162 | 176 | assert try! eval(&pool.nodes[..], root) == -7; |
|
| 163 | 177 | return 0; |
|
| 164 | 178 | } |
|
| 165 | 179 | ||
| 166 | 180 | /// Test 5: Deep tree: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36 |
|
| 167 | - | fn testDeep(pool: *mut Pool) -> i32 { |
|
| 181 | + | fn testDeep(pool: &mut Pool) -> i32 { |
|
| 168 | 182 | reset(pool); |
|
| 169 | 183 | let mut root = num(pool, 1); |
|
| 170 | 184 | let values: [i32; 7] = [2, 3, 4, 5, 6, 7, 8]; |
|
| 171 | 185 | for v in values { |
|
| 172 | - | set root = add(pool, root, num(pool, v)); |
|
| 186 | + | let node = num(pool, v); |
|
| 187 | + | set root = add(pool, root, node); |
|
| 173 | 188 | } |
|
| 174 | 189 | assert try! eval(&pool.nodes[..], root) == 36; |
|
| 175 | 190 | assert countNodes(&pool.nodes[..], root) == 15; |
|
| 176 | 191 | return 0; |
|
| 177 | 192 | } |
|
| 178 | 193 | ||
| 179 | 194 | /// Test 6: Mixed operations: (100 - 3 * (2 + 8)) / 7 = (100 - 30) / 7 = 70 / 7 = 10 |
|
| 180 | - | fn testMixed(pool: *mut Pool) -> i32 { |
|
| 195 | + | fn testMixed(pool: &mut Pool) -> i32 { |
|
| 181 | 196 | reset(pool); |
|
| 182 | - | let inner = add(pool, num(pool, 2), num(pool, 8)); |
|
| 183 | - | let product = mul(pool, num(pool, 3), inner); |
|
| 184 | - | let diff = sub(pool, num(pool, 100), product); |
|
| 185 | - | let root = div(pool, diff, num(pool, 7)); |
|
| 197 | + | let two = num(pool, 2); |
|
| 198 | + | let eight = num(pool, 8); |
|
| 199 | + | let inner = add(pool, two, eight); |
|
| 200 | + | let three = num(pool, 3); |
|
| 201 | + | let product = mul(pool, three, inner); |
|
| 202 | + | let hundred = num(pool, 100); |
|
| 203 | + | let diff = sub(pool, hundred, product); |
|
| 204 | + | let seven = num(pool, 7); |
|
| 205 | + | let root = div(pool, diff, seven); |
|
| 186 | 206 | assert try! eval(&pool.nodes[..], root) == 10; |
|
| 187 | 207 | return 0; |
|
| 188 | 208 | } |
|
| 189 | 209 | ||
| 190 | 210 | /// Test 7: Single number. |
|
| 191 | - | fn testSingleNum(pool: *mut Pool) -> i32 { |
|
| 211 | + | fn testSingleNum(pool: &mut Pool) -> i32 { |
|
| 192 | 212 | reset(pool); |
|
| 193 | 213 | let root = num(pool, 42); |
|
| 194 | 214 | assert try! eval(&pool.nodes[..], root) == 42; |
|
| 195 | 215 | assert countNodes(&pool.nodes[..], root) == 1; |
|
| 196 | 216 | return 0; |
test/tests/prog.hanoi.rad
+7 -7
| 19 | 19 | moves: [Move; 63], |
|
| 20 | 20 | count: u32, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// Record a move. |
|
| 24 | - | fn recordMove(ml: *mut MoveLog, disk: u32, from: u32, to: u32) { |
|
| 24 | + | fn recordMove(ml: &mut MoveLog, disk: u32, from: u32, to: u32) { |
|
| 25 | 25 | if ml.count < MAX_MOVES { |
|
| 26 | 26 | set ml.moves[ml.count] = Move { disk, from, to }; |
|
| 27 | 27 | set ml.count += 1; |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | /// Solve Tower of Hanoi recursively. |
|
| 32 | 32 | /// Move `n` disks from peg `from` to peg `to` using `aux` as auxiliary. |
|
| 33 | - | fn hanoi(ml: *mut MoveLog, n: u32, from: u32, to: u32, aux: u32) { |
|
| 33 | + | fn hanoi(ml: &mut MoveLog, n: u32, from: u32, to: u32, aux: u32) { |
|
| 34 | 34 | if n == 0 { |
|
| 35 | 35 | return; |
|
| 36 | 36 | } |
|
| 37 | 37 | hanoi(ml, n - 1, from, aux, to); |
|
| 38 | 38 | recordMove(ml, n, from, to); |
|
| 39 | 39 | hanoi(ml, n - 1, aux, to, from); |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// Verify total move count. |
|
| 43 | - | fn testMoveCount(ml: *MoveLog) -> i32 { |
|
| 43 | + | fn testMoveCount(ml: &MoveLog) -> i32 { |
|
| 44 | 44 | // For N disks, there are 2^N - 1 moves. |
|
| 45 | 45 | assert ml.count == MAX_MOVES; |
|
| 46 | 46 | return 0; |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Verify specific moves. |
|
| 50 | - | fn testSpecificMoves(ml: *MoveLog) -> i32 { |
|
| 50 | + | fn testSpecificMoves(ml: &MoveLog) -> i32 { |
|
| 51 | 51 | // First move: smallest disk (1) from peg 0 to peg 2. |
|
| 52 | 52 | assert ml.moves[0].disk == 1; |
|
| 53 | 53 | assert ml.moves[0].from == 0; |
|
| 54 | 54 | assert ml.moves[0].to == 2; |
|
| 55 | 55 |
| 70 | 70 | record Pegs { |
|
| 71 | 71 | stacks: [[u32; 6]; 3], |
|
| 72 | 72 | top: [u32; 3], |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | - | fn pegPush(pegs: *mut Pegs, peg: u32, disk: u32) -> bool { |
|
| 75 | + | fn pegPush(pegs: &mut Pegs, peg: u32, disk: u32) -> bool { |
|
| 76 | 76 | let t = pegs.top[peg]; |
|
| 77 | 77 | if t > 0 { |
|
| 78 | 78 | // Check that the top disk is larger than the one being placed. |
|
| 79 | 79 | if pegs.stacks[peg][t - 1] < disk { |
|
| 80 | 80 | return false; |
| 83 | 83 | set pegs.stacks[peg][t] = disk; |
|
| 84 | 84 | set pegs.top[peg] = t + 1; |
|
| 85 | 85 | return true; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn pegPop(pegs: *mut Pegs, peg: u32) -> u32 { |
|
| 88 | + | fn pegPop(pegs: &mut Pegs, peg: u32) -> u32 { |
|
| 89 | 89 | let t = pegs.top[peg]; |
|
| 90 | 90 | if t == 0 { |
|
| 91 | 91 | // Should not happen in a valid solution. |
|
| 92 | 92 | return 0; |
|
| 93 | 93 | } |
| 97 | 97 | ||
| 98 | 98 | /// Simulate the peg state to verify correctness. |
|
| 99 | 99 | /// Replay all moves and check: |
|
| 100 | 100 | /// 1. No larger disk is placed on a smaller disk. |
|
| 101 | 101 | /// 2. All disks end up on peg 1. |
|
| 102 | - | fn testSimulate(ml: *MoveLog) -> i32 { |
|
| 102 | + | fn testSimulate(ml: &MoveLog) -> i32 { |
|
| 103 | 103 | let mut pegs = Pegs { |
|
| 104 | 104 | stacks: [[0; 6]; 3], |
|
| 105 | 105 | top: [0, 0, 0], |
|
| 106 | 106 | }; |
|
| 107 | 107 |
test/tests/prog.huffman.rad
+21 -21
| 33 | 33 | codeLen: *mut [u32], |
|
| 34 | 34 | bitstream: *mut [u8], |
|
| 35 | 35 | bitCount: u32, |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | - | fn newLeaf(s: *mut HuffState, freq: u32, symbol: u32) -> u32 { |
|
| 38 | + | unsafe fn newLeaf(s: *mut HuffState, freq: u32, symbol: u32) -> u32 { |
|
| 39 | 39 | let idx: u32 = s.nodeCount; |
|
| 40 | 40 | set s.nodes[idx] = HNode { freq, kind: HNodeKind::Leaf(symbol), left: NIL, right: NIL }; |
|
| 41 | 41 | set s.nodeCount += 1; |
|
| 42 | 42 | return idx; |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | - | fn newInterior(s: *mut HuffState, freq: u32, left: u32, right: u32) -> u32 { |
|
| 45 | + | unsafe fn newInterior(s: *mut HuffState, freq: u32, left: u32, right: u32) -> u32 { |
|
| 46 | 46 | let idx: u32 = s.nodeCount; |
|
| 47 | 47 | set s.nodes[idx] = HNode { freq, kind: HNodeKind::Interior, left, right }; |
|
| 48 | 48 | set s.nodeCount += 1; |
|
| 49 | 49 | return idx; |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | 52 | /// Get the symbol from a node, or nil if it's an interior node. |
|
| 53 | - | fn nodeSymbol(node: *HNode) -> ?u32 { |
|
| 53 | + | unsafe fn nodeSymbol(node: *HNode) -> ?u32 { |
|
| 54 | 54 | match node.kind { |
|
| 55 | 55 | case HNodeKind::Leaf(sym) => { |
|
| 56 | 56 | return sym; |
|
| 57 | 57 | } |
|
| 58 | 58 | case HNodeKind::Interior => { |
|
| 59 | 59 | return nil; |
|
| 60 | 60 | } |
|
| 61 | 61 | } |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn heapSwap(s: *mut HuffState, i: u32, j: u32) { |
|
| 64 | + | unsafe fn heapSwap(s: *mut HuffState, i: u32, j: u32) { |
|
| 65 | 65 | let tmp: u32 = s.heap[i]; |
|
| 66 | 66 | set s.heap[i] = s.heap[j]; |
|
| 67 | 67 | set s.heap[j] = tmp; |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | - | fn heapFreq(s: *HuffState, i: u32) -> u32 { |
|
| 70 | + | unsafe fn heapFreq(s: *HuffState, i: u32) -> u32 { |
|
| 71 | 71 | return s.nodes[s.heap[i]].freq; |
|
| 72 | 72 | } |
|
| 73 | 73 | ||
| 74 | - | fn siftUp(s: *mut HuffState, pos: u32) { |
|
| 74 | + | unsafe fn siftUp(s: *mut HuffState, pos: u32) { |
|
| 75 | 75 | let mut i: u32 = pos; |
|
| 76 | 76 | while i > 0 { |
|
| 77 | 77 | let parent: u32 = (i - 1) / 2; |
|
| 78 | 78 | if heapFreq(s, i) < heapFreq(s, parent) { |
|
| 79 | 79 | heapSwap(s, i, parent); |
| 82 | 82 | return; |
|
| 83 | 83 | } |
|
| 84 | 84 | } |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | - | fn siftDown(s: *mut HuffState, pos: u32) { |
|
| 87 | + | unsafe fn siftDown(s: *mut HuffState, pos: u32) { |
|
| 88 | 88 | let mut i: u32 = pos; |
|
| 89 | 89 | while true { |
|
| 90 | 90 | let left: u32 = 2 * i + 1; |
|
| 91 | 91 | let right: u32 = 2 * i + 2; |
|
| 92 | 92 | let mut smallest: u32 = i; |
| 103 | 103 | heapSwap(s, i, smallest); |
|
| 104 | 104 | set i = smallest; |
|
| 105 | 105 | } |
|
| 106 | 106 | } |
|
| 107 | 107 | ||
| 108 | - | fn heapPush(s: *mut HuffState, nodeIdx: u32) { |
|
| 108 | + | unsafe fn heapPush(s: *mut HuffState, nodeIdx: u32) { |
|
| 109 | 109 | set s.heap[s.heapSize] = nodeIdx; |
|
| 110 | 110 | set s.heapSize += 1; |
|
| 111 | 111 | siftUp(s, s.heapSize - 1); |
|
| 112 | 112 | } |
|
| 113 | 113 | ||
| 114 | - | fn heapPop(s: *mut HuffState) -> u32 { |
|
| 114 | + | unsafe fn heapPop(s: *mut HuffState) -> u32 { |
|
| 115 | 115 | let result: u32 = s.heap[0]; |
|
| 116 | 116 | set s.heapSize -= 1; |
|
| 117 | 117 | set s.heap[0] = s.heap[s.heapSize]; |
|
| 118 | 118 | if s.heapSize > 0 { |
|
| 119 | 119 | siftDown(s, 0); |
|
| 120 | 120 | } |
|
| 121 | 121 | return result; |
|
| 122 | 122 | } |
|
| 123 | 123 | ||
| 124 | - | fn buildTree(s: *mut HuffState, freqs: *[u32]) -> u32 { |
|
| 124 | + | unsafe fn buildTree(s: *mut HuffState, freqs: *[u32]) -> u32 { |
|
| 125 | 125 | set s.nodeCount = 0; |
|
| 126 | 126 | set s.heapSize = 0; |
|
| 127 | 127 | ||
| 128 | 128 | for freq, sym in freqs { |
|
| 129 | 129 | if freq > 0 { |
| 141 | 141 | } |
|
| 142 | 142 | ||
| 143 | 143 | return heapPop(s); |
|
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | - | fn generateCodes(s: *mut HuffState, nodeIdx: u32, code: u32, depth: u32) { |
|
| 146 | + | unsafe fn generateCodes(s: *mut HuffState, nodeIdx: u32, code: u32, depth: u32) { |
|
| 147 | 147 | match s.nodes[nodeIdx].kind { |
|
| 148 | 148 | case HNodeKind::Leaf(sym) => { |
|
| 149 | 149 | set s.codeBits[sym] = code; |
|
| 150 | 150 | set s.codeLen[sym] = depth; |
|
| 151 | 151 | } |
| 158 | 158 | } |
|
| 159 | 159 | } |
|
| 160 | 160 | } |
|
| 161 | 161 | } |
|
| 162 | 162 | ||
| 163 | - | fn writeBit(s: *mut HuffState, bit: u32) { |
|
| 163 | + | unsafe fn writeBit(s: *mut HuffState, bit: u32) { |
|
| 164 | 164 | let byteIdx: u32 = s.bitCount / 8; |
|
| 165 | 165 | let bitIdx: u32 = 7 - (s.bitCount % 8); |
|
| 166 | 166 | if bit == 1 { |
|
| 167 | 167 | set s.bitstream[byteIdx] |= (1 as u8 << bitIdx as u8); |
|
| 168 | 168 | } |
|
| 169 | 169 | set s.bitCount += 1; |
|
| 170 | 170 | } |
|
| 171 | 171 | ||
| 172 | - | fn readBit(s: *HuffState, pos: u32) -> u32 { |
|
| 172 | + | unsafe fn readBit(s: *HuffState, pos: u32) -> u32 { |
|
| 173 | 173 | let byteIdx: u32 = pos / 8; |
|
| 174 | 174 | let bitIdx: u32 = 7 - (pos % 8); |
|
| 175 | 175 | return (s.bitstream[byteIdx] >> bitIdx as u8) as u32 & 1; |
|
| 176 | 176 | } |
|
| 177 | 177 | ||
| 178 | - | fn encode(s: *mut HuffState, msg: *[u32]) { |
|
| 178 | + | unsafe fn encode(s: *mut HuffState, msg: *[u32]) { |
|
| 179 | 179 | set s.bitCount = 0; |
|
| 180 | 180 | let mut i: u32 = 0; |
|
| 181 | 181 | while i < MAX_BITS { |
|
| 182 | 182 | set s.bitstream[i] = 0; |
|
| 183 | 183 | set i += 1; |
| 193 | 193 | set b += 1; |
|
| 194 | 194 | } |
|
| 195 | 195 | } |
|
| 196 | 196 | } |
|
| 197 | 197 | ||
| 198 | - | fn decode(s: *HuffState, root: u32, numSymbols: u32, out: *mut [u32]) -> u32 { |
|
| 198 | + | unsafe fn decode(s: *HuffState, root: u32, numSymbols: u32, out: *mut [u32]) -> u32 { |
|
| 199 | 199 | let mut bitPos: u32 = 0; |
|
| 200 | 200 | let mut decoded: u32 = 0; |
|
| 201 | 201 | ||
| 202 | 202 | while decoded < numSymbols { |
|
| 203 | 203 | let mut cur: u32 = root; |
| 218 | 218 | set decoded += 1; |
|
| 219 | 219 | } |
|
| 220 | 220 | return decoded; |
|
| 221 | 221 | } |
|
| 222 | 222 | ||
| 223 | - | fn resetCodes(s: *mut HuffState) { |
|
| 223 | + | unsafe fn resetCodes(s: *mut HuffState) { |
|
| 224 | 224 | let mut i: u32 = 0; |
|
| 225 | 225 | while i < MAX_SYMBOLS { |
|
| 226 | 226 | set s.codeBits[i] = 0; |
|
| 227 | 227 | set s.codeLen[i] = 0; |
|
| 228 | 228 | set i += 1; |
|
| 229 | 229 | } |
|
| 230 | 230 | } |
|
| 231 | 231 | ||
| 232 | - | fn testBasic(s: *mut HuffState) -> i32 { |
|
| 232 | + | unsafe fn testBasic(s: *mut HuffState) -> i32 { |
|
| 233 | 233 | let freqs: [u32; 5] = [5, 9, 12, 13, 16]; |
|
| 234 | 234 | let root: u32 = buildTree(s, &freqs[..]); |
|
| 235 | 235 | ||
| 236 | 236 | assert s.nodes[root].freq == 55; |
|
| 237 | 237 |
| 259 | 259 | } |
|
| 260 | 260 | ||
| 261 | 261 | return 0; |
|
| 262 | 262 | } |
|
| 263 | 263 | ||
| 264 | - | fn testRoundTrip(s: *mut HuffState) -> i32 { |
|
| 264 | + | unsafe fn testRoundTrip(s: *mut HuffState) -> i32 { |
|
| 265 | 265 | let freqs: [u32; 5] = [5, 9, 12, 13, 16]; |
|
| 266 | 266 | let root: u32 = buildTree(s, &freqs[..]); |
|
| 267 | 267 | ||
| 268 | 268 | resetCodes(s); |
|
| 269 | 269 | generateCodes(s, root, 0, 0); |
| 284 | 284 | } |
|
| 285 | 285 | ||
| 286 | 286 | return 0; |
|
| 287 | 287 | } |
|
| 288 | 288 | ||
| 289 | - | fn testSkewed(s: *mut HuffState) -> i32 { |
|
| 289 | + | unsafe fn testSkewed(s: *mut HuffState) -> i32 { |
|
| 290 | 290 | let freqs: [u32; 6] = [100, 1, 1, 1, 1, 1]; |
|
| 291 | 291 | let root: u32 = buildTree(s, &freqs[..]); |
|
| 292 | 292 | ||
| 293 | 293 | resetCodes(s); |
|
| 294 | 294 | generateCodes(s, root, 0, 0); |
| 318 | 318 | } |
|
| 319 | 319 | ||
| 320 | 320 | return 0; |
|
| 321 | 321 | } |
|
| 322 | 322 | ||
| 323 | - | fn testUniform(s: *mut HuffState) -> i32 { |
|
| 323 | + | unsafe fn testUniform(s: *mut HuffState) -> i32 { |
|
| 324 | 324 | let freqs: [u32; 8] = [10, 10, 10, 10, 10, 10, 10, 10]; |
|
| 325 | 325 | let root: u32 = buildTree(s, &freqs[..]); |
|
| 326 | 326 | ||
| 327 | 327 | assert s.nodes[root].freq == 80; |
|
| 328 | 328 |
| 349 | 349 | } |
|
| 350 | 350 | ||
| 351 | 351 | return 0; |
|
| 352 | 352 | } |
|
| 353 | 353 | ||
| 354 | - | @default fn main() -> i32 { |
|
| 354 | + | @default unsafe fn main() -> i32 { |
|
| 355 | 355 | let mut nodes: [HNode; 63] = [HNode { freq: 0, kind: HNodeKind::Interior, left: 0xFFFFFFFF, right: 0xFFFFFFFF }; 63]; |
|
| 356 | 356 | let mut heap: [u32; 63] = [0; 63]; |
|
| 357 | 357 | let mut codeBits: [u32; 32] = [0; 32]; |
|
| 358 | 358 | let mut codeLen: [u32; 32] = [0; 32]; |
|
| 359 | 359 | let mut bitstream: [u8; 512] = [0; 512]; |
test/tests/prog.hybridsort.rad
+7 -7
| 2 | 2 | //! Insertion sort and selection sort. |
|
| 3 | 3 | //! Sort two copies of the same data with each algorithm, then verify |
|
| 4 | 4 | //! both produce identical sorted output. |
|
| 5 | 5 | ||
| 6 | 6 | /// Copy `src` into `dst`. |
|
| 7 | - | fn copy(dst: *mut [i32], src: *[i32]) { |
|
| 7 | + | fn copy(dst: &mut [i32], src: &[i32]) { |
|
| 8 | 8 | for val, i in src { |
|
| 9 | 9 | set dst[i] = val; |
|
| 10 | 10 | } |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | /// Insertion sort on the given array. |
|
| 14 | - | fn insertionSort(data: *mut [i32]) { |
|
| 14 | + | fn insertionSort(data: &mut [i32]) { |
|
| 15 | 15 | let mut i: u32 = 1; |
|
| 16 | 16 | while i < data.len { |
|
| 17 | 17 | let key = data[i]; |
|
| 18 | 18 | let mut j: i32 = i as i32 - 1; |
|
| 19 | 19 | while j >= 0 and data[j as u32] > key { |
| 24 | 24 | set i += 1; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | /// Selection sort on the given array. |
|
| 29 | - | fn selectionSort(data: *mut [i32]) { |
|
| 29 | + | fn selectionSort(data: &mut [i32]) { |
|
| 30 | 30 | let mut i: u32 = 0; |
|
| 31 | 31 | while i < data.len - 1 { |
|
| 32 | 32 | let mut minIdx: u32 = i; |
|
| 33 | 33 | let mut j: u32 = i + 1; |
|
| 34 | 34 | while j < data.len { |
| 45 | 45 | set i += 1; |
|
| 46 | 46 | } |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Check that an array is sorted in ascending order. |
|
| 50 | - | fn isSorted(data: *[i32]) -> bool { |
|
| 50 | + | fn isSorted(data: &[i32]) -> bool { |
|
| 51 | 51 | let mut prev: ?i32 = nil; |
|
| 52 | 52 | for val in data { |
|
| 53 | 53 | if let p = prev { |
|
| 54 | 54 | if p > val { |
|
| 55 | 55 | return false; |
| 59 | 59 | } |
|
| 60 | 60 | return true; |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Compute sum of all elements. |
|
| 64 | - | fn sum(data: *[i32]) -> i32 { |
|
| 64 | + | fn sum(data: &[i32]) -> i32 { |
|
| 65 | 65 | let mut total: i32 = 0; |
|
| 66 | 66 | for val in data { |
|
| 67 | 67 | set total += val; |
|
| 68 | 68 | } |
|
| 69 | 69 | return total; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Compare two arrays element by element. |
|
| 73 | - | fn arraysEqual(a: *[i32], b: *[i32]) -> i32 { |
|
| 73 | + | fn arraysEqual(a: &[i32], b: &[i32]) -> i32 { |
|
| 74 | 74 | for val, i in a { |
|
| 75 | 75 | if val <> b[i] { |
|
| 76 | 76 | return i as i32 + 1; |
|
| 77 | 77 | } |
|
| 78 | 78 | } |
|
| 79 | 79 | return 0; |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | 82 | /// Verify specific positions in the sorted output. |
|
| 83 | - | fn verifyPositions(data: *[i32]) -> i32 { |
|
| 83 | + | fn verifyPositions(data: &[i32]) -> i32 { |
|
| 84 | 84 | // Sorted: 2 3 6 8 10 14 19 25 27 33 39 41 45 48 53 56 62 67 72 74 81 88 91 97 |
|
| 85 | 85 | let expected: [i32; 6] = [2, 3, 6, 41, 91, 97]; |
|
| 86 | 86 | let indices: [u32; 6] = [0, 1, 2, 11, 22, 23]; |
|
| 87 | 87 | ||
| 88 | 88 | for exp, i in expected { |
test/tests/prog.linkedlist.rad
+14 -14
| 15 | 15 | free: u32, |
|
| 16 | 16 | head: ?u32, |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | /// Allocate a node from the pool. Returns its index. |
|
| 20 | - | fn alloc(list: *mut List, value: i32) -> u32 { |
|
| 20 | + | fn alloc(list: &mut List, value: i32) -> u32 { |
|
| 21 | 21 | let idx = list.free; |
|
| 22 | 22 | set list.free += 1; |
|
| 23 | 23 | set list.pool[idx] = Node { value, next: nil }; |
|
| 24 | 24 | return idx; |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Push a value onto the front of the list. |
|
| 28 | - | fn push(list: *mut List, value: i32) { |
|
| 28 | + | fn push(list: &mut List, value: i32) { |
|
| 29 | 29 | let idx = alloc(list, value); |
|
| 30 | 30 | set list.pool[idx].next = list.head; |
|
| 31 | 31 | set list.head = idx; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | /// Pop a value from the front of the list. Returns nil if empty. |
|
| 35 | - | fn pop(list: *mut List) -> ?i32 { |
|
| 35 | + | fn pop(list: &mut List) -> ?i32 { |
|
| 36 | 36 | let idx = list.head else { |
|
| 37 | 37 | return nil; |
|
| 38 | 38 | }; |
|
| 39 | 39 | let value = list.pool[idx].value; |
|
| 40 | 40 | set list.head = list.pool[idx].next; |
|
| 41 | 41 | return value; |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Get the length of the list. |
|
| 45 | - | fn length(list: *List) -> u32 { |
|
| 45 | + | fn length(list: &List) -> u32 { |
|
| 46 | 46 | let mut count: u32 = 0; |
|
| 47 | 47 | let mut cur = list.head; |
|
| 48 | 48 | while let idx = cur { |
|
| 49 | 49 | set count += 1; |
|
| 50 | 50 | set cur = list.pool[idx].next; |
|
| 51 | 51 | } |
|
| 52 | 52 | return count; |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | 55 | /// Find a value in the list. Returns the node index if found, or nil. |
|
| 56 | - | fn find(list: *List, value: i32) -> ?u32 { |
|
| 56 | + | fn find(list: &List, value: i32) -> ?u32 { |
|
| 57 | 57 | let mut cur = list.head; |
|
| 58 | 58 | while let idx = cur { |
|
| 59 | 59 | if list.pool[idx].value == value { |
|
| 60 | 60 | return idx; |
|
| 61 | 61 | } |
| 63 | 63 | } |
|
| 64 | 64 | return nil; |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | /// Get the value at a given index (0-based from head). |
|
| 68 | - | fn valueAt(list: *List, index: u32) -> ?i32 { |
|
| 68 | + | fn valueAt(list: &List, index: u32) -> ?i32 { |
|
| 69 | 69 | let mut cur = list.head; |
|
| 70 | 70 | let mut i: u32 = 0; |
|
| 71 | 71 | while let idx = cur { |
|
| 72 | 72 | if i == index { |
|
| 73 | 73 | return list.pool[idx].value; |
| 77 | 77 | } |
|
| 78 | 78 | return nil; |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Reverse the linked list in-place. |
|
| 82 | - | fn reverse(list: *mut List) { |
|
| 82 | + | fn reverse(list: &mut List) { |
|
| 83 | 83 | let mut prev: ?u32 = nil; |
|
| 84 | 84 | let mut cur = list.head; |
|
| 85 | 85 | while let idx = cur { |
|
| 86 | 86 | let next = list.pool[idx].next; |
|
| 87 | 87 | set list.pool[idx].next = prev; |
| 90 | 90 | } |
|
| 91 | 91 | set list.head = prev; |
|
| 92 | 92 | } |
|
| 93 | 93 | ||
| 94 | 94 | /// Compute the sum of all values in the list. |
|
| 95 | - | fn sum(list: *List) -> i32 { |
|
| 95 | + | fn sum(list: &List) -> i32 { |
|
| 96 | 96 | let mut total: i32 = 0; |
|
| 97 | 97 | let mut cur = list.head; |
|
| 98 | 98 | while let idx = cur { |
|
| 99 | 99 | set total += list.pool[idx].value; |
|
| 100 | 100 | set cur = list.pool[idx].next; |
|
| 101 | 101 | } |
|
| 102 | 102 | return total; |
|
| 103 | 103 | } |
|
| 104 | 104 | ||
| 105 | 105 | /// Reset the list to empty. |
|
| 106 | - | fn reset(list: *mut List) { |
|
| 106 | + | fn reset(list: &mut List) { |
|
| 107 | 107 | set list.head = nil; |
|
| 108 | 108 | set list.free = 0; |
|
| 109 | 109 | } |
|
| 110 | 110 | ||
| 111 | 111 | /// Test basic push and length. |
|
| 112 | - | fn testPushLength(list: *mut List) -> i32 { |
|
| 112 | + | fn testPushLength(list: &mut List) -> i32 { |
|
| 113 | 113 | push(list, 10); |
|
| 114 | 114 | push(list, 20); |
|
| 115 | 115 | push(list, 30); |
|
| 116 | 116 | ||
| 117 | 117 | assert length(list) == 3; |
| 130 | 130 | assert v2 == 10; |
|
| 131 | 131 | return 0; |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | 134 | /// Test pop. |
|
| 135 | - | fn testPop(list: *mut List) -> i32 { |
|
| 135 | + | fn testPop(list: &mut List) -> i32 { |
|
| 136 | 136 | let v = pop(list) else { |
|
| 137 | 137 | return 1; |
|
| 138 | 138 | }; |
|
| 139 | 139 | assert v == 30; |
|
| 140 | 140 | assert length(list) == 2; |
| 144 | 144 | assert v0 == 20; |
|
| 145 | 145 | return 0; |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | 148 | /// Test find. |
|
| 149 | - | fn testFind(list: *mut List) -> i32 { |
|
| 149 | + | fn testFind(list: &mut List) -> i32 { |
|
| 150 | 150 | // 20 should be found. |
|
| 151 | 151 | assert find(list, 20) <> nil; |
|
| 152 | 152 | // 10 should be found. |
|
| 153 | 153 | assert find(list, 10) <> nil; |
|
| 154 | 154 | // 30 was popped, should not be found. |
| 157 | 157 | assert find(list, 99) == nil; |
|
| 158 | 158 | return 0; |
|
| 159 | 159 | } |
|
| 160 | 160 | ||
| 161 | 161 | /// Test reverse. |
|
| 162 | - | fn testReverse(list: *mut List) -> i32 { |
|
| 162 | + | fn testReverse(list: &mut List) -> i32 { |
|
| 163 | 163 | // Current list: 20 -> 10 |
|
| 164 | 164 | // Add more elements. |
|
| 165 | 165 | push(list, 40); |
|
| 166 | 166 | push(list, 50); |
|
| 167 | 167 | // List: 50 -> 40 -> 20 -> 10 |
| 193 | 193 | assert sum(list) == sumBefore; |
|
| 194 | 194 | return 0; |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// Test building a larger list. |
|
| 198 | - | fn testLargerList(list: *mut List) -> i32 { |
|
| 198 | + | fn testLargerList(list: &mut List) -> i32 { |
|
| 199 | 199 | reset(list); |
|
| 200 | 200 | ||
| 201 | 201 | // Push 32 elements. |
|
| 202 | 202 | let mut i: u32 = 0; |
|
| 203 | 203 | while i < 32 { |
test/tests/prog.lzw.rad
+18 -18
| 23 | 23 | decoded: *mut [u8], |
|
| 24 | 24 | decLen: u32, |
|
| 25 | 25 | temp: *mut [u8], |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn initEncDict(s: *mut LzwState) { |
|
| 28 | + | unsafe fn initEncDict(s: *mut LzwState) { |
|
| 29 | 29 | set s.encDictSize = INIT_DICT; |
|
| 30 | 30 | let mut i: u32 = 0; |
|
| 31 | 31 | while i < 256 { |
|
| 32 | 32 | set s.encDict[i] = DictEntry { prefix: 0xFFFF, suffix: i as u8 }; |
|
| 33 | 33 | set i += 1; |
|
| 34 | 34 | } |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | - | fn initDecDict(s: *mut LzwState) { |
|
| 37 | + | unsafe fn initDecDict(s: *mut LzwState) { |
|
| 38 | 38 | set s.decDictSize = INIT_DICT; |
|
| 39 | 39 | let mut i: u32 = 0; |
|
| 40 | 40 | while i < 256 { |
|
| 41 | 41 | set s.decDict[i] = DictEntry { prefix: 0xFFFF, suffix: i as u8 }; |
|
| 42 | 42 | set i += 1; |
|
| 43 | 43 | } |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | - | fn dictLookup(s: *LzwState, prefix: u32, suffix: u8) -> u32 { |
|
| 46 | + | unsafe fn dictLookup(s: *LzwState, prefix: u32, suffix: u8) -> u32 { |
|
| 47 | 47 | let mut i: u32 = 0; |
|
| 48 | 48 | while i < s.encDictSize { |
|
| 49 | 49 | if s.encDict[i].prefix == prefix and s.encDict[i].suffix == suffix { |
|
| 50 | 50 | return i; |
|
| 51 | 51 | } |
|
| 52 | 52 | set i += 1; |
|
| 53 | 53 | } |
|
| 54 | 54 | return 0xFFFFFFFF; |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | - | fn dictAdd(s: *mut LzwState, prefix: u32, suffix: u8) { |
|
| 57 | + | unsafe fn dictAdd(s: *mut LzwState, prefix: u32, suffix: u8) { |
|
| 58 | 58 | if s.encDictSize < MAX_DICT { |
|
| 59 | 59 | set s.encDict[s.encDictSize] = DictEntry { prefix, suffix }; |
|
| 60 | 60 | set s.encDictSize += 1; |
|
| 61 | 61 | } |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn emitCode(s: *mut LzwState, code: u32) { |
|
| 64 | + | unsafe fn emitCode(s: *mut LzwState, code: u32) { |
|
| 65 | 65 | set s.encoded[s.encLen] = code; |
|
| 66 | 66 | set s.encLen += 1; |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | - | fn encode(s: *mut LzwState, data: *[u8]) { |
|
| 69 | + | unsafe fn encode(s: *mut LzwState, data: *[u8]) { |
|
| 70 | 70 | initEncDict(s); |
|
| 71 | 71 | set s.encLen = 0; |
|
| 72 | 72 | ||
| 73 | 73 | if data.len == 0 { |
|
| 74 | 74 | emitCode(s, EOI_CODE); |
| 92 | 92 | } |
|
| 93 | 93 | emitCode(s, w); |
|
| 94 | 94 | emitCode(s, EOI_CODE); |
|
| 95 | 95 | } |
|
| 96 | 96 | ||
| 97 | - | fn decodeString(s: *mut LzwState, code: u32) -> u32 { |
|
| 97 | + | unsafe fn decodeString(s: *mut LzwState, code: u32) -> u32 { |
|
| 98 | 98 | let mut len: u32 = 0; |
|
| 99 | 99 | let mut c: u32 = code; |
|
| 100 | 100 | while c <> 0xFFFF and c < s.decDictSize { |
|
| 101 | 101 | set s.temp[len] = s.decDict[c].suffix; |
|
| 102 | 102 | set len += 1; |
| 112 | 112 | set b -= 1; |
|
| 113 | 113 | } |
|
| 114 | 114 | return len; |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | - | fn firstByte(s: *LzwState, code: u32) -> u8 { |
|
| 117 | + | unsafe fn firstByte(s: *LzwState, code: u32) -> u8 { |
|
| 118 | 118 | let mut c: u32 = code; |
|
| 119 | 119 | while s.decDict[c].prefix <> 0xFFFF and s.decDict[c].prefix < s.decDictSize { |
|
| 120 | 120 | set c = s.decDict[c].prefix; |
|
| 121 | 121 | } |
|
| 122 | 122 | return s.decDict[c].suffix; |
|
| 123 | 123 | } |
|
| 124 | 124 | ||
| 125 | - | fn decDictAdd(s: *mut LzwState, prefix: u32, suffix: u8) { |
|
| 125 | + | unsafe fn decDictAdd(s: *mut LzwState, prefix: u32, suffix: u8) { |
|
| 126 | 126 | if s.decDictSize < MAX_DICT { |
|
| 127 | 127 | set s.decDict[s.decDictSize] = DictEntry { prefix, suffix }; |
|
| 128 | 128 | set s.decDictSize += 1; |
|
| 129 | 129 | } |
|
| 130 | 130 | } |
|
| 131 | 131 | ||
| 132 | - | fn outputByte(s: *mut LzwState, b: u8) { |
|
| 132 | + | unsafe fn outputByte(s: *mut LzwState, b: u8) { |
|
| 133 | 133 | set s.decoded[s.decLen] = b; |
|
| 134 | 134 | set s.decLen += 1; |
|
| 135 | 135 | } |
|
| 136 | 136 | ||
| 137 | - | fn decode(s: *mut LzwState) { |
|
| 137 | + | unsafe fn decode(s: *mut LzwState) { |
|
| 138 | 138 | initDecDict(s); |
|
| 139 | 139 | set s.decLen = 0; |
|
| 140 | 140 | ||
| 141 | 141 | if s.encLen == 0 { |
|
| 142 | 142 | return; |
| 182 | 182 | } |
|
| 183 | 183 | set prevCode = code; |
|
| 184 | 184 | } |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | - | fn testSimple(s: *mut LzwState) -> i32 { |
|
| 187 | + | unsafe fn testSimple(s: *mut LzwState) -> i32 { |
|
| 188 | 188 | let data: *[u8] = "ABABABABAB"; |
|
| 189 | 189 | encode(s, data); |
|
| 190 | 190 | ||
| 191 | 191 | assert s.encoded[s.encLen - 1] == EOI_CODE; |
|
| 192 | 192 |
| 198 | 198 | set i += 1; |
|
| 199 | 199 | } |
|
| 200 | 200 | return 0; |
|
| 201 | 201 | } |
|
| 202 | 202 | ||
| 203 | - | fn testDistinct(s: *mut LzwState) -> i32 { |
|
| 203 | + | unsafe fn testDistinct(s: *mut LzwState) -> i32 { |
|
| 204 | 204 | let data: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
|
| 205 | 205 | encode(s, &data[..]); |
|
| 206 | 206 | ||
| 207 | 207 | decode(s); |
|
| 208 | 208 | assert s.decLen == 16; |
| 212 | 212 | set i += 1; |
|
| 213 | 213 | } |
|
| 214 | 214 | return 0; |
|
| 215 | 215 | } |
|
| 216 | 216 | ||
| 217 | - | fn testRepetitive(s: *mut LzwState) -> i32 { |
|
| 217 | + | unsafe fn testRepetitive(s: *mut LzwState) -> i32 { |
|
| 218 | 218 | let mut data: [u8; 64] = [65; 64]; |
|
| 219 | 219 | encode(s, &data[..]); |
|
| 220 | 220 | ||
| 221 | 221 | assert s.encLen < 32; |
|
| 222 | 222 |
| 228 | 228 | set i += 1; |
|
| 229 | 229 | } |
|
| 230 | 230 | return 0; |
|
| 231 | 231 | } |
|
| 232 | 232 | ||
| 233 | - | fn testMixed(s: *mut LzwState) -> i32 { |
|
| 233 | + | unsafe fn testMixed(s: *mut LzwState) -> i32 { |
|
| 234 | 234 | let data: *[u8] = "TOBEORNOTTOBEORTOBEORNOT"; |
|
| 235 | 235 | encode(s, data); |
|
| 236 | 236 | ||
| 237 | 237 | decode(s); |
|
| 238 | 238 | assert s.decLen == 24; |
| 245 | 245 | assert s.encLen - 1 < 24; |
|
| 246 | 246 | ||
| 247 | 247 | return 0; |
|
| 248 | 248 | } |
|
| 249 | 249 | ||
| 250 | - | fn testEmpty(s: *mut LzwState) -> i32 { |
|
| 250 | + | unsafe fn testEmpty(s: *mut LzwState) -> i32 { |
|
| 251 | 251 | encode(s, &[]); |
|
| 252 | 252 | assert s.encLen == 1; |
|
| 253 | 253 | assert s.encoded[0] == EOI_CODE; |
|
| 254 | 254 | ||
| 255 | 255 | decode(s); |
|
| 256 | 256 | assert s.decLen == 0; |
|
| 257 | 257 | return 0; |
|
| 258 | 258 | } |
|
| 259 | 259 | ||
| 260 | - | fn testSingle(s: *mut LzwState) -> i32 { |
|
| 260 | + | unsafe fn testSingle(s: *mut LzwState) -> i32 { |
|
| 261 | 261 | let data: *[u8] = "*"; |
|
| 262 | 262 | encode(s, data); |
|
| 263 | 263 | ||
| 264 | 264 | decode(s); |
|
| 265 | 265 | assert s.decLen == 1; |
|
| 266 | 266 | assert s.decoded[0] == 42; |
|
| 267 | 267 | return 0; |
|
| 268 | 268 | } |
|
| 269 | 269 | ||
| 270 | - | @default fn main() -> i32 { |
|
| 270 | + | @default unsafe fn main() -> i32 { |
|
| 271 | 271 | let mut encDict: [DictEntry; 512] = [DictEntry { prefix: 0xFFFF, suffix: 0 }; 512]; |
|
| 272 | 272 | let mut decDict: [DictEntry; 512] = [DictEntry { prefix: 0xFFFF, suffix: 0 }; 512]; |
|
| 273 | 273 | let mut encoded: [u32; 512] = [0; 512]; |
|
| 274 | 274 | let mut decoded: [u8; 256] = [0; 256]; |
|
| 275 | 275 | let mut temp: [u8; 256] = [0; 256]; |
test/tests/prog.matmul.rad
+5 -5
| 9 | 9 | record Mat4 { |
|
| 10 | 10 | rows: [[i32; 4]; 4], |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | /// Perform matrix multiplication: c = a * b. |
|
| 14 | - | fn matmul(c: *mut Mat4, a: *Mat4, b: *Mat4) { |
|
| 14 | + | fn matmul(c: &mut Mat4, a: &Mat4, b: &Mat4) { |
|
| 15 | 15 | let mut i: u32 = 0; |
|
| 16 | 16 | while i < N { |
|
| 17 | 17 | let mut j: u32 = 0; |
|
| 18 | 18 | while j < N { |
|
| 19 | 19 | let mut sum: i32 = 0; |
| 28 | 28 | set i += 1; |
|
| 29 | 29 | } |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | /// Verify c matches expected. |
|
| 33 | - | fn verifyResult(c: *Mat4, expected: *Mat4) -> i32 { |
|
| 33 | + | fn verifyResult(c: &Mat4, expected: &Mat4) -> i32 { |
|
| 34 | 34 | let mut i: u32 = 0; |
|
| 35 | 35 | while i < N { |
|
| 36 | 36 | let mut j: u32 = 0; |
|
| 37 | 37 | while j < N { |
|
| 38 | 38 | if c.rows[i][j] <> expected.rows[i][j] { |
| 44 | 44 | } |
|
| 45 | 45 | return 0; |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | 48 | /// Compute the trace (sum of diagonal) of a matrix. |
|
| 49 | - | fn trace(m: *Mat4) -> i32 { |
|
| 49 | + | fn trace(m: &Mat4) -> i32 { |
|
| 50 | 50 | let mut sum: i32 = 0; |
|
| 51 | 51 | let mut i: u32 = 0; |
|
| 52 | 52 | while i < N { |
|
| 53 | 53 | set sum += m.rows[i][i]; |
|
| 54 | 54 | set i += 1; |
|
| 55 | 55 | } |
|
| 56 | 56 | return sum; |
|
| 57 | 57 | } |
|
| 58 | 58 | ||
| 59 | 59 | /// Zero out a matrix. |
|
| 60 | - | fn zero(m: *mut Mat4) { |
|
| 60 | + | fn zero(m: &mut Mat4) { |
|
| 61 | 61 | let mut i: u32 = 0; |
|
| 62 | 62 | while i < N { |
|
| 63 | 63 | let mut j: u32 = 0; |
|
| 64 | 64 | while j < N { |
|
| 65 | 65 | set m.rows[i][j] = 0; |
| 68 | 68 | set i += 1; |
|
| 69 | 69 | } |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Multiply matrices multiple times to test repeated computation. |
|
| 73 | - | fn testRepeatedMultiply(c: *mut Mat4, a: *Mat4, b: *Mat4) -> i32 { |
|
| 73 | + | fn testRepeatedMultiply(c: &mut Mat4, a: &Mat4, b: &Mat4) -> i32 { |
|
| 74 | 74 | // First pass already done, trace = 13+43+43+85 = 184. |
|
| 75 | 75 | assert trace(c) == 184; |
|
| 76 | 76 | ||
| 77 | 77 | // Zero out c and re-multiply to ensure idempotent. |
|
| 78 | 78 | zero(c); |
test/tests/prog.mersenne.rad
+10 -10
| 10 | 10 | record MtState { |
|
| 11 | 11 | mt: *mut [u32], |
|
| 12 | 12 | mti: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn mtInit(s: *mut MtState, seed: u32) { |
|
| 15 | + | unsafe fn mtInit(s: *mut MtState, seed: u32) { |
|
| 16 | 16 | set s.mt[0] = seed; |
|
| 17 | 17 | let mut i: u32 = 1; |
|
| 18 | 18 | while i < N { |
|
| 19 | 19 | let prev: u32 = s.mt[i - 1]; |
|
| 20 | 20 | let xored: u32 = prev ^ (prev >> 30); |
| 34 | 34 | set i += 1; |
|
| 35 | 35 | } |
|
| 36 | 36 | set s.mti = N; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | - | fn generateNumbers(s: *mut MtState) { |
|
| 39 | + | unsafe fn generateNumbers(s: *mut MtState) { |
|
| 40 | 40 | let mut i: u32 = 0; |
|
| 41 | 41 | ||
| 42 | 42 | while i < N - M { |
|
| 43 | 43 | let y: u32 = (s.mt[i] & UPPER_MASK) | (s.mt[i + 1] & LOWER_MASK); |
|
| 44 | 44 | let mut mag: u32 = 0; |
| 67 | 67 | set s.mt[N - 1] = s.mt[M - 1] ^ (y >> 1) ^ mag; |
|
| 68 | 68 | ||
| 69 | 69 | set s.mti = 0; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | - | fn mtNext(s: *mut MtState) -> u32 { |
|
| 72 | + | unsafe fn mtNext(s: *mut MtState) -> u32 { |
|
| 73 | 73 | if s.mti >= N { |
|
| 74 | 74 | generateNumbers(s); |
|
| 75 | 75 | } |
|
| 76 | 76 | ||
| 77 | 77 | let mut y: u32 = s.mt[s.mti]; |
| 83 | 83 | set y ^= (y >> 18); |
|
| 84 | 84 | ||
| 85 | 85 | return y; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn testKnownSequence(s: *mut MtState) -> i32 { |
|
| 88 | + | unsafe fn testKnownSequence(s: *mut MtState) -> i32 { |
|
| 89 | 89 | mtInit(s, 1); |
|
| 90 | 90 | ||
| 91 | 91 | let v0: u32 = mtNext(s); |
|
| 92 | 92 | assert v0 == 1791095845; |
|
| 93 | 93 |
| 104 | 104 | assert v4 == 491263; |
|
| 105 | 105 | ||
| 106 | 106 | return 0; |
|
| 107 | 107 | } |
|
| 108 | 108 | ||
| 109 | - | fn testDeterminism(s: *mut MtState) -> i32 { |
|
| 109 | + | unsafe fn testDeterminism(s: *mut MtState) -> i32 { |
|
| 110 | 110 | mtInit(s, 42); |
|
| 111 | 111 | ||
| 112 | 112 | let mut first: [u32; 10] = [0; 10]; |
|
| 113 | 113 | let mut i: u32 = 0; |
|
| 114 | 114 | while i < 10 { |
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | return 0; |
|
| 129 | 129 | } |
|
| 130 | 130 | ||
| 131 | - | fn testDifferentSeeds(s: *mut MtState) -> i32 { |
|
| 131 | + | unsafe fn testDifferentSeeds(s: *mut MtState) -> i32 { |
|
| 132 | 132 | mtInit(s, 1); |
|
| 133 | 133 | let a: u32 = mtNext(s); |
|
| 134 | 134 | ||
| 135 | 135 | mtInit(s, 2); |
|
| 136 | 136 | let b: u32 = mtNext(s); |
| 143 | 143 | assert a <> c; |
|
| 144 | 144 | ||
| 145 | 145 | return 0; |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | - | fn testChiSquared(s: *mut MtState) -> i32 { |
|
| 148 | + | unsafe fn testChiSquared(s: *mut MtState) -> i32 { |
|
| 149 | 149 | mtInit(s, 12345); |
|
| 150 | 150 | ||
| 151 | 151 | constant NUM_BINS: u32 = 16; |
|
| 152 | 152 | constant NUM_SAMPLES: u32 = 1600; |
|
| 153 | 153 | constant EXPECTED: u32 = 100; |
| 187 | 187 | } |
|
| 188 | 188 | ||
| 189 | 189 | return 0; |
|
| 190 | 190 | } |
|
| 191 | 191 | ||
| 192 | - | fn testRegeneration(s: *mut MtState) -> i32 { |
|
| 192 | + | unsafe fn testRegeneration(s: *mut MtState) -> i32 { |
|
| 193 | 193 | mtInit(s, 7); |
|
| 194 | 194 | ||
| 195 | 195 | let mut last: u32 = 0; |
|
| 196 | 196 | let mut i: u32 = 0; |
|
| 197 | 197 | while i < 700 { |
| 218 | 218 | assert more <> 0; |
|
| 219 | 219 | ||
| 220 | 220 | return 0; |
|
| 221 | 221 | } |
|
| 222 | 222 | ||
| 223 | - | fn testBitCoverage(s: *mut MtState) -> i32 { |
|
| 223 | + | unsafe fn testBitCoverage(s: *mut MtState) -> i32 { |
|
| 224 | 224 | mtInit(s, 999); |
|
| 225 | 225 | ||
| 226 | 226 | let mut orAll: u32 = 0; |
|
| 227 | 227 | let mut andAll: u32 = 0xFFFFFFFF; |
|
| 228 | 228 |
| 239 | 239 | assert andAll == 0; |
|
| 240 | 240 | ||
| 241 | 241 | return 0; |
|
| 242 | 242 | } |
|
| 243 | 243 | ||
| 244 | - | @default fn main() -> i32 { |
|
| 244 | + | @default unsafe fn main() -> i32 { |
|
| 245 | 245 | let mut mt: [u32; 624] = [0; 624]; |
|
| 246 | 246 | let mut s: MtState = MtState { |
|
| 247 | 247 | mt: &mut mt[..], |
|
| 248 | 248 | mti: 625, |
|
| 249 | 249 | }; |
test/tests/prog.nqueens.rad
+7 -7
| 9 | 9 | queens: *mut [i32], |
|
| 10 | 10 | solutionCount: u32, |
|
| 11 | 11 | boardSize: u32, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn isSafe(b: *Board, row: u32, col: u32) -> bool { |
|
| 14 | + | unsafe fn isSafe(b: *Board, row: u32, col: u32) -> bool { |
|
| 15 | 15 | let mut i: u32 = 0; |
|
| 16 | 16 | while i < row { |
|
| 17 | 17 | let qcol: i32 = b.queens[i]; |
|
| 18 | 18 | if qcol == col as i32 { |
|
| 19 | 19 | return false; |
| 26 | 26 | set i += 1; |
|
| 27 | 27 | } |
|
| 28 | 28 | return true; |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | - | fn solve(b: *mut Board, row: u32) { |
|
| 31 | + | unsafe fn solve(b: *mut Board, row: u32) { |
|
| 32 | 32 | if row == b.boardSize { |
|
| 33 | 33 | set b.solutionCount += 1; |
|
| 34 | 34 | return; |
|
| 35 | 35 | } |
|
| 36 | 36 | let mut col: u32 = 0; |
| 42 | 42 | } |
|
| 43 | 43 | set col += 1; |
|
| 44 | 44 | } |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | - | fn resetBoard(b: *mut Board) { |
|
| 47 | + | unsafe fn resetBoard(b: *mut Board) { |
|
| 48 | 48 | let mut i: u32 = 0; |
|
| 49 | 49 | while i < MAX_N { |
|
| 50 | 50 | set b.queens[i] = -1; |
|
| 51 | 51 | set i += 1; |
|
| 52 | 52 | } |
|
| 53 | 53 | set b.solutionCount = 0; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | - | fn solveNQueens(b: *mut Board, n: u32) -> u32 { |
|
| 56 | + | unsafe fn solveNQueens(b: *mut Board, n: u32) -> u32 { |
|
| 57 | 57 | resetBoard(b); |
|
| 58 | 58 | set b.boardSize = n; |
|
| 59 | 59 | solve(b, 0); |
|
| 60 | 60 | return b.solutionCount; |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | - | fn testAllSizes(b: *mut Board) -> i32 { |
|
| 63 | + | unsafe fn testAllSizes(b: *mut Board) -> i32 { |
|
| 64 | 64 | let expected: [u32; 9] = [0, 1, 0, 0, 2, 10, 4, 40, 92]; |
|
| 65 | 65 | let mut n: u32 = 1; |
|
| 66 | 66 | while n <= 8 { |
|
| 67 | 67 | let count: u32 = solveNQueens(b, n); |
|
| 68 | 68 | if count <> expected[n] { |
| 101 | 101 | } |
|
| 102 | 102 | ||
| 103 | 103 | return 0; |
|
| 104 | 104 | } |
|
| 105 | 105 | ||
| 106 | - | fn testDeterminism(b: *mut Board) -> i32 { |
|
| 106 | + | unsafe fn testDeterminism(b: *mut Board) -> i32 { |
|
| 107 | 107 | let count1: u32 = solveNQueens(b, 8); |
|
| 108 | 108 | let count2: u32 = solveNQueens(b, 8); |
|
| 109 | 109 | let count3: u32 = solveNQueens(b, 8); |
|
| 110 | 110 | ||
| 111 | 111 | assert count1 == 92; |
| 129 | 129 | assert expected[8] > expected[7]; |
|
| 130 | 130 | ||
| 131 | 131 | return 0; |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | - | @default fn main() -> i32 { |
|
| 134 | + | @default unsafe fn main() -> i32 { |
|
| 135 | 135 | let mut queens: [i32; 8] = [-1; 8]; |
|
| 136 | 136 | let mut b: Board = Board { |
|
| 137 | 137 | queens: &mut queens[..], |
|
| 138 | 138 | solutionCount: 0, |
|
| 139 | 139 | boardSize: 0, |
test/tests/prog.rbtree.rad
+16 -16
| 22 | 22 | root: u32, |
|
| 23 | 23 | inorder: *mut [i32], |
|
| 24 | 24 | inorderCount: u32, |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | - | fn allocNode(t: *mut RBTree, key: i32) -> u32 { |
|
| 27 | + | unsafe fn allocNode(t: *mut RBTree, key: i32) -> u32 { |
|
| 28 | 28 | let idx: u32 = t.poolNext; |
|
| 29 | 29 | set t.poolNext += 1; |
|
| 30 | 30 | set t.pool[idx] = RBNode { key, color: RED, left: NIL, right: NIL, parent: NIL }; |
|
| 31 | 31 | return idx; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | - | fn rotateLeft(t: *mut RBTree, x: u32) { |
|
| 34 | + | unsafe fn rotateLeft(t: *mut RBTree, x: u32) { |
|
| 35 | 35 | let y: u32 = t.pool[x].right; |
|
| 36 | 36 | set t.pool[x].right = t.pool[y].left; |
|
| 37 | 37 | if t.pool[y].left <> NIL { |
|
| 38 | 38 | set t.pool[t.pool[y].left].parent = x; |
|
| 39 | 39 | } |
| 47 | 47 | } |
|
| 48 | 48 | set t.pool[y].left = x; |
|
| 49 | 49 | set t.pool[x].parent = y; |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | - | fn rotateRight(t: *mut RBTree, x: u32) { |
|
| 52 | + | unsafe fn rotateRight(t: *mut RBTree, x: u32) { |
|
| 53 | 53 | let y: u32 = t.pool[x].left; |
|
| 54 | 54 | set t.pool[x].left = t.pool[y].right; |
|
| 55 | 55 | if t.pool[y].right <> NIL { |
|
| 56 | 56 | set t.pool[t.pool[y].right].parent = x; |
|
| 57 | 57 | } |
| 65 | 65 | } |
|
| 66 | 66 | set t.pool[y].right = x; |
|
| 67 | 67 | set t.pool[x].parent = y; |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | - | fn insertFixup(t: *mut RBTree, zArg: u32) { |
|
| 70 | + | unsafe fn insertFixup(t: *mut RBTree, zArg: u32) { |
|
| 71 | 71 | let mut z: u32 = zArg; |
|
| 72 | 72 | while t.pool[t.pool[z].parent].color == RED { |
|
| 73 | 73 | if t.pool[z].parent == t.pool[t.pool[t.pool[z].parent].parent].left { |
|
| 74 | 74 | let y: u32 = t.pool[t.pool[t.pool[z].parent].parent].right; |
|
| 75 | 75 | if t.pool[y].color == RED { |
| 105 | 105 | } |
|
| 106 | 106 | } |
|
| 107 | 107 | set t.pool[t.root].color = BLACK; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | - | fn insert(t: *mut RBTree, key: i32) { |
|
| 110 | + | unsafe fn insert(t: *mut RBTree, key: i32) { |
|
| 111 | 111 | let z: u32 = allocNode(t, key); |
|
| 112 | 112 | let mut y: u32 = NIL; |
|
| 113 | 113 | let mut x: u32 = t.root; |
|
| 114 | 114 | ||
| 115 | 115 | while x <> NIL { |
| 131 | 131 | } |
|
| 132 | 132 | ||
| 133 | 133 | insertFixup(t, z); |
|
| 134 | 134 | } |
|
| 135 | 135 | ||
| 136 | - | fn search(t: *RBTree, key: i32) -> bool { |
|
| 136 | + | unsafe fn search(t: *RBTree, key: i32) -> bool { |
|
| 137 | 137 | let mut x: u32 = t.root; |
|
| 138 | 138 | while x <> NIL { |
|
| 139 | 139 | if key == t.pool[x].key { |
|
| 140 | 140 | return true; |
|
| 141 | 141 | } else if key < t.pool[x].key { |
| 145 | 145 | } |
|
| 146 | 146 | } |
|
| 147 | 147 | return false; |
|
| 148 | 148 | } |
|
| 149 | 149 | ||
| 150 | - | fn inorderWalk(t: *mut RBTree, x: u32) { |
|
| 150 | + | unsafe fn inorderWalk(t: *mut RBTree, x: u32) { |
|
| 151 | 151 | if x == NIL { |
|
| 152 | 152 | return; |
|
| 153 | 153 | } |
|
| 154 | 154 | inorderWalk(t, t.pool[x].left); |
|
| 155 | 155 | set t.inorder[t.inorderCount] = t.pool[x].key; |
|
| 156 | 156 | set t.inorderCount += 1; |
|
| 157 | 157 | inorderWalk(t, t.pool[x].right); |
|
| 158 | 158 | } |
|
| 159 | 159 | ||
| 160 | - | fn countNodes(t: *RBTree, x: u32) -> u32 { |
|
| 160 | + | unsafe fn countNodes(t: *RBTree, x: u32) -> u32 { |
|
| 161 | 161 | if x == NIL { |
|
| 162 | 162 | return 0; |
|
| 163 | 163 | } |
|
| 164 | 164 | return 1 + countNodes(t, t.pool[x].left) + countNodes(t, t.pool[x].right); |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | - | fn blackHeight(t: *RBTree, x: u32) -> i32 { |
|
| 167 | + | unsafe fn blackHeight(t: *RBTree, x: u32) -> i32 { |
|
| 168 | 168 | if x == NIL { |
|
| 169 | 169 | return 1; |
|
| 170 | 170 | } |
|
| 171 | 171 | let leftBH: i32 = blackHeight(t, t.pool[x].left); |
|
| 172 | 172 | let rightBH: i32 = blackHeight(t, t.pool[x].right); |
| 182 | 182 | return leftBH + 1; |
|
| 183 | 183 | } |
|
| 184 | 184 | return leftBH; |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | - | fn noRedRed(t: *RBTree, x: u32) -> bool { |
|
| 187 | + | unsafe fn noRedRed(t: *RBTree, x: u32) -> bool { |
|
| 188 | 188 | if x == NIL { |
|
| 189 | 189 | return true; |
|
| 190 | 190 | } |
|
| 191 | 191 | if t.pool[x].color == RED { |
|
| 192 | 192 | if t.pool[t.pool[x].left].color == RED { |
| 200 | 200 | return false; |
|
| 201 | 201 | } |
|
| 202 | 202 | return noRedRed(t, t.pool[x].right); |
|
| 203 | 203 | } |
|
| 204 | 204 | ||
| 205 | - | fn resetTree(t: *mut RBTree) { |
|
| 205 | + | unsafe fn resetTree(t: *mut RBTree) { |
|
| 206 | 206 | let mut i: u32 = 0; |
|
| 207 | 207 | while i < POOL_SIZE { |
|
| 208 | 208 | set t.pool[i] = RBNode { key: 0, color: BLACK, left: NIL, right: NIL, parent: NIL }; |
|
| 209 | 209 | set i += 1; |
|
| 210 | 210 | } |
|
| 211 | 211 | set t.poolNext = 1; |
|
| 212 | 212 | set t.root = NIL; |
|
| 213 | 213 | set t.inorderCount = 0; |
|
| 214 | 214 | } |
|
| 215 | 215 | ||
| 216 | - | fn testAscending(t: *mut RBTree) -> i32 { |
|
| 216 | + | unsafe fn testAscending(t: *mut RBTree) -> i32 { |
|
| 217 | 217 | resetTree(t); |
|
| 218 | 218 | ||
| 219 | 219 | let mut i: i32 = 0; |
|
| 220 | 220 | while i < 32 { |
|
| 221 | 221 | insert(t, i); |
| 240 | 240 | } |
|
| 241 | 241 | ||
| 242 | 242 | return 0; |
|
| 243 | 243 | } |
|
| 244 | 244 | ||
| 245 | - | fn testDescending(t: *mut RBTree) -> i32 { |
|
| 245 | + | unsafe fn testDescending(t: *mut RBTree) -> i32 { |
|
| 246 | 246 | resetTree(t); |
|
| 247 | 247 | ||
| 248 | 248 | let mut i: i32 = 31; |
|
| 249 | 249 | while i >= 0 { |
|
| 250 | 250 | insert(t, i); |
| 267 | 267 | } |
|
| 268 | 268 | ||
| 269 | 269 | return 0; |
|
| 270 | 270 | } |
|
| 271 | 271 | ||
| 272 | - | fn testRandom(t: *mut RBTree) -> i32 { |
|
| 272 | + | unsafe fn testRandom(t: *mut RBTree) -> i32 { |
|
| 273 | 273 | resetTree(t); |
|
| 274 | 274 | ||
| 275 | 275 | let mut inserted: [bool; 48] = [false; 48]; |
|
| 276 | 276 | let mut seed: u32 = 42; |
|
| 277 | 277 | let mut count: u32 = 0; |
| 310 | 310 | } |
|
| 311 | 311 | ||
| 312 | 312 | return 0; |
|
| 313 | 313 | } |
|
| 314 | 314 | ||
| 315 | - | fn testHeight(t: *mut RBTree) -> i32 { |
|
| 315 | + | unsafe fn testHeight(t: *mut RBTree) -> i32 { |
|
| 316 | 316 | resetTree(t); |
|
| 317 | 317 | ||
| 318 | 318 | let mut i: i32 = 0; |
|
| 319 | 319 | while i < 63 { |
|
| 320 | 320 | insert(t, i); |
| 326 | 326 | assert bh <= 7; |
|
| 327 | 327 | ||
| 328 | 328 | return 0; |
|
| 329 | 329 | } |
|
| 330 | 330 | ||
| 331 | - | @default fn main() -> i32 { |
|
| 331 | + | @default unsafe fn main() -> i32 { |
|
| 332 | 332 | let mut pool: [RBNode; 128] = [RBNode { key: 0, color: 1, left: 0, right: 0, parent: 0 }; 128]; |
|
| 333 | 333 | let mut inorder: [i32; 128] = [0; 128]; |
|
| 334 | 334 | ||
| 335 | 335 | let mut t: RBTree = RBTree { |
|
| 336 | 336 | pool: &mut pool[..], |
test/tests/prog.regex.rad
+21 -21
| 35 | 35 | closure: *mut [u32], |
|
| 36 | 36 | fragStack: *mut [Frag], |
|
| 37 | 37 | fragTop: u32, |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | - | fn newState(nfa: *mut NfaState) -> u32 { |
|
| 40 | + | unsafe fn newState(nfa: *mut NfaState) -> u32 { |
|
| 41 | 41 | let s: u32 = nfa.stateCount; |
|
| 42 | 42 | set nfa.stateFirst[s] = NIL; |
|
| 43 | 43 | set nfa.stateCount += 1; |
|
| 44 | 44 | return s; |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | - | fn addTrans(nfa: *mut NfaState, from: u32, kind: u32, ch: u8, to: u32) { |
|
| 47 | + | unsafe fn addTrans(nfa: *mut NfaState, from: u32, kind: u32, ch: u8, to: u32) { |
|
| 48 | 48 | let idx: u32 = nfa.transCount; |
|
| 49 | 49 | set nfa.trans[idx] = Trans { kind, ch, to }; |
|
| 50 | 50 | set nfa.transNext[idx] = nfa.stateFirst[from]; |
|
| 51 | 51 | set nfa.stateFirst[from] = idx; |
|
| 52 | 52 | set nfa.transCount += 1; |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | - | fn pushFrag(nfa: *mut NfaState, f: Frag) { |
|
| 55 | + | unsafe fn pushFrag(nfa: *mut NfaState, f: Frag) { |
|
| 56 | 56 | set nfa.fragStack[nfa.fragTop] = f; |
|
| 57 | 57 | set nfa.fragTop += 1; |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | - | fn popFrag(nfa: *mut NfaState) -> Frag { |
|
| 60 | + | unsafe fn popFrag(nfa: *mut NfaState) -> Frag { |
|
| 61 | 61 | set nfa.fragTop -= 1; |
|
| 62 | 62 | return nfa.fragStack[nfa.fragTop]; |
|
| 63 | 63 | } |
|
| 64 | 64 | ||
| 65 | - | fn setEmpty(s: *mut [u32]) { |
|
| 65 | + | unsafe fn setEmpty(s: *mut [u32]) { |
|
| 66 | 66 | set s[0] = 0; |
|
| 67 | 67 | set s[1] = 0; |
|
| 68 | 68 | set s[2] = 0; |
|
| 69 | 69 | set s[3] = 0; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | - | fn setAdd(s: *mut [u32], bit: u32) { |
|
| 72 | + | unsafe fn setAdd(s: *mut [u32], bit: u32) { |
|
| 73 | 73 | let word: u32 = bit / 32; |
|
| 74 | 74 | let pos: u32 = bit % 32; |
|
| 75 | 75 | set s[word] |= (1 << pos); |
|
| 76 | 76 | } |
|
| 77 | 77 | ||
| 78 | - | fn setHas(s: *[u32], bit: u32) -> bool { |
|
| 78 | + | unsafe fn setHas(s: *[u32], bit: u32) -> bool { |
|
| 79 | 79 | let word: u32 = bit / 32; |
|
| 80 | 80 | let pos: u32 = bit % 32; |
|
| 81 | 81 | return (s[word] >> pos) & 1 == 1; |
|
| 82 | 82 | } |
|
| 83 | 83 | ||
| 84 | - | fn setIsEmpty(s: *[u32]) -> bool { |
|
| 84 | + | unsafe fn setIsEmpty(s: *[u32]) -> bool { |
|
| 85 | 85 | return s[0] == 0 and s[1] == 0 and s[2] == 0 and s[3] == 0; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn setCopy(dst: *mut [u32], src: *[u32]) { |
|
| 88 | + | unsafe fn setCopy(dst: *mut [u32], src: *[u32]) { |
|
| 89 | 89 | set dst[0] = src[0]; |
|
| 90 | 90 | set dst[1] = src[1]; |
|
| 91 | 91 | set dst[2] = src[2]; |
|
| 92 | 92 | set dst[3] = src[3]; |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | - | fn epsilonClosure(nfa: *mut NfaState, states: *mut [u32]) { |
|
| 95 | + | unsafe fn epsilonClosure(nfa: *mut NfaState, states: *mut [u32]) { |
|
| 96 | 96 | setCopy(nfa.closure, states); |
|
| 97 | 97 | ||
| 98 | 98 | let mut changed: bool = true; |
|
| 99 | 99 | while changed { |
|
| 100 | 100 | set changed = false; |
| 117 | 117 | } |
|
| 118 | 118 | ||
| 119 | 119 | setCopy(states, nfa.closure); |
|
| 120 | 120 | } |
|
| 121 | 121 | ||
| 122 | - | fn resetNFA(nfa: *mut NfaState) { |
|
| 122 | + | unsafe fn resetNFA(nfa: *mut NfaState) { |
|
| 123 | 123 | set nfa.stateCount = 0; |
|
| 124 | 124 | set nfa.transCount = 0; |
|
| 125 | 125 | set nfa.fragTop = 0; |
|
| 126 | 126 | let mut i: u32 = 0; |
|
| 127 | 127 | while i < MAX_STATES { |
| 133 | 133 | set nfa.transNext[i] = NIL; |
|
| 134 | 134 | set i += 1; |
|
| 135 | 135 | } |
|
| 136 | 136 | } |
|
| 137 | 137 | ||
| 138 | - | fn compile(nfa: *mut NfaState, pattern: *[u8]) -> u32 { |
|
| 138 | + | unsafe fn compile(nfa: *mut NfaState, pattern: *[u8]) -> u32 { |
|
| 139 | 139 | resetNFA(nfa); |
|
| 140 | 140 | ||
| 141 | 141 | let mut i: u32 = 0; |
|
| 142 | 142 | while i < pattern.len { |
|
| 143 | 143 | let ch: u8 = pattern[i]; |
| 233 | 233 | ||
| 234 | 234 | set nfa.acceptState = frags[numFrags - 1].endState; |
|
| 235 | 235 | return frags[0].start; |
|
| 236 | 236 | } |
|
| 237 | 237 | ||
| 238 | - | fn nfaMatches(nfa: *mut NfaState, start: u32, input: *[u8]) -> bool { |
|
| 238 | + | unsafe fn nfaMatches(nfa: *mut NfaState, start: u32, input: *[u8]) -> bool { |
|
| 239 | 239 | setEmpty(nfa.current); |
|
| 240 | 240 | setAdd(nfa.current, start); |
|
| 241 | 241 | epsilonClosure(nfa, nfa.current); |
|
| 242 | 242 | ||
| 243 | 243 | let mut i: u32 = 0; |
| 271 | 271 | } |
|
| 272 | 272 | ||
| 273 | 273 | return setHas(nfa.current, nfa.acceptState); |
|
| 274 | 274 | } |
|
| 275 | 275 | ||
| 276 | - | fn testLiteral(nfa: *mut NfaState) -> i32 { |
|
| 276 | + | unsafe fn testLiteral(nfa: *mut NfaState) -> i32 { |
|
| 277 | 277 | let start: u32 = compile(nfa, "abc"); |
|
| 278 | 278 | ||
| 279 | 279 | assert nfaMatches(nfa, start, "abc"); |
|
| 280 | 280 | if nfaMatches(nfa, start, "ab") { return 2; } |
|
| 281 | 281 | if nfaMatches(nfa, start, "abcd") { return 3; } |
|
| 282 | 282 | if nfaMatches(nfa, start, "abd") { return 4; } |
|
| 283 | 283 | ||
| 284 | 284 | return 0; |
|
| 285 | 285 | } |
|
| 286 | 286 | ||
| 287 | - | fn testStar(nfa: *mut NfaState) -> i32 { |
|
| 287 | + | unsafe fn testStar(nfa: *mut NfaState) -> i32 { |
|
| 288 | 288 | let start: u32 = compile(nfa, "a*"); |
|
| 289 | 289 | ||
| 290 | 290 | assert nfaMatches(nfa, start, ""); |
|
| 291 | 291 | assert nfaMatches(nfa, start, "a"); |
|
| 292 | 292 | assert nfaMatches(nfa, start, "aaa"); |
|
| 293 | 293 | if nfaMatches(nfa, start, "b") { return 4; } |
|
| 294 | 294 | ||
| 295 | 295 | return 0; |
|
| 296 | 296 | } |
|
| 297 | 297 | ||
| 298 | - | fn testPlus(nfa: *mut NfaState) -> i32 { |
|
| 298 | + | unsafe fn testPlus(nfa: *mut NfaState) -> i32 { |
|
| 299 | 299 | let start: u32 = compile(nfa, "a+"); |
|
| 300 | 300 | ||
| 301 | 301 | if nfaMatches(nfa, start, "") { return 1; } |
|
| 302 | 302 | assert nfaMatches(nfa, start, "a"); |
|
| 303 | 303 | assert nfaMatches(nfa, start, "aaaaa"); |
|
| 304 | 304 | ||
| 305 | 305 | return 0; |
|
| 306 | 306 | } |
|
| 307 | 307 | ||
| 308 | - | fn testQuestion(nfa: *mut NfaState) -> i32 { |
|
| 308 | + | unsafe fn testQuestion(nfa: *mut NfaState) -> i32 { |
|
| 309 | 309 | let start: u32 = compile(nfa, "a?"); |
|
| 310 | 310 | ||
| 311 | 311 | assert nfaMatches(nfa, start, ""); |
|
| 312 | 312 | assert nfaMatches(nfa, start, "a"); |
|
| 313 | 313 | if nfaMatches(nfa, start, "aa") { return 3; } |
|
| 314 | 314 | ||
| 315 | 315 | return 0; |
|
| 316 | 316 | } |
|
| 317 | 317 | ||
| 318 | - | fn testDot(nfa: *mut NfaState) -> i32 { |
|
| 318 | + | unsafe fn testDot(nfa: *mut NfaState) -> i32 { |
|
| 319 | 319 | let start: u32 = compile(nfa, ".."); |
|
| 320 | 320 | ||
| 321 | 321 | assert nfaMatches(nfa, start, "ab"); |
|
| 322 | 322 | assert nfaMatches(nfa, start, "zz"); |
|
| 323 | 323 | if nfaMatches(nfa, start, "a") { return 3; } |
|
| 324 | 324 | if nfaMatches(nfa, start, "abc") { return 4; } |
|
| 325 | 325 | ||
| 326 | 326 | return 0; |
|
| 327 | 327 | } |
|
| 328 | 328 | ||
| 329 | - | fn testComplex(nfa: *mut NfaState) -> i32 { |
|
| 329 | + | unsafe fn testComplex(nfa: *mut NfaState) -> i32 { |
|
| 330 | 330 | let start: u32 = compile(nfa, "ab*c"); |
|
| 331 | 331 | ||
| 332 | 332 | assert nfaMatches(nfa, start, "ac"); |
|
| 333 | 333 | assert nfaMatches(nfa, start, "abc"); |
|
| 334 | 334 | assert nfaMatches(nfa, start, "abbc"); |
| 337 | 337 | if nfaMatches(nfa, start, "adc") { return 6; } |
|
| 338 | 338 | ||
| 339 | 339 | return 0; |
|
| 340 | 340 | } |
|
| 341 | 341 | ||
| 342 | - | fn testDotStar(nfa: *mut NfaState) -> i32 { |
|
| 342 | + | unsafe fn testDotStar(nfa: *mut NfaState) -> i32 { |
|
| 343 | 343 | let start: u32 = compile(nfa, ".*"); |
|
| 344 | 344 | ||
| 345 | 345 | assert nfaMatches(nfa, start, ""); |
|
| 346 | 346 | assert nfaMatches(nfa, start, "hello"); |
|
| 347 | 347 | assert nfaMatches(nfa, start, "x"); |
|
| 348 | 348 | ||
| 349 | 349 | return 0; |
|
| 350 | 350 | } |
|
| 351 | 351 | ||
| 352 | - | @default fn main() -> i32 { |
|
| 352 | + | @default unsafe fn main() -> i32 { |
|
| 353 | 353 | let mut trans: [Trans; 256] = [Trans { kind: 0, ch: 0, to: 0 }; 256]; |
|
| 354 | 354 | let mut stateFirst: [u32; 128] = [0xFFFFFFFF; 128]; |
|
| 355 | 355 | let mut transNext: [u32; 256] = [0xFFFFFFFF; 256]; |
|
| 356 | 356 | let mut current: [u32; 4] = [0; 4]; |
|
| 357 | 357 | let mut nextSet: [u32; 4] = [0; 4]; |
test/tests/prog.sha256.rad
+7 -7
| 50 | 50 | fn ssig1(x: u32) -> u32 { |
|
| 51 | 51 | return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10); |
|
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | 54 | /// Prepare the message schedule from a 16-word (512-bit) block. |
|
| 55 | - | fn prepareSchedule(s: *mut Sha256, block: *[u32]) { |
|
| 55 | + | unsafe fn prepareSchedule(s: *mut Sha256, block: *[u32]) { |
|
| 56 | 56 | // Copy the first 16 words directly. |
|
| 57 | 57 | let mut i: u32 = 0; |
|
| 58 | 58 | while i < 16 { |
|
| 59 | 59 | set s.w[i] = block[i]; |
|
| 60 | 60 | set i += 1; |
| 65 | 65 | set i += 1; |
|
| 66 | 66 | } |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | 69 | /// Run the 64-round compression function. |
|
| 70 | - | fn compress(s: *mut Sha256, k: *[u32]) { |
|
| 70 | + | unsafe fn compress(s: *mut Sha256, k: *[u32]) { |
|
| 71 | 71 | let mut a: u32 = s.h[0]; |
|
| 72 | 72 | let mut b: u32 = s.h[1]; |
|
| 73 | 73 | let mut c: u32 = s.h[2]; |
|
| 74 | 74 | let mut d: u32 = s.h[3]; |
|
| 75 | 75 | let mut e: u32 = s.h[4]; |
| 101 | 101 | set s.h[6] += g; |
|
| 102 | 102 | set s.h[7] += hh; |
|
| 103 | 103 | } |
|
| 104 | 104 | ||
| 105 | 105 | /// Reset hash state to initial values. |
|
| 106 | - | fn resetHash(s: *mut Sha256) { |
|
| 106 | + | unsafe fn resetHash(s: *mut Sha256) { |
|
| 107 | 107 | let mut i: u32 = 0; |
|
| 108 | 108 | while i < 8 { |
|
| 109 | 109 | set s.h[i] = INIT_H[i]; |
|
| 110 | 110 | set i += 1; |
|
| 111 | 111 | } |
|
| 112 | 112 | } |
|
| 113 | 113 | ||
| 114 | 114 | /// Pad and hash a short message (up to 55 bytes, fits in one 512-bit block). |
|
| 115 | - | fn hashMessage(s: *mut Sha256, k: *[u32], msg: *[u8]) -> i32 { |
|
| 115 | + | unsafe fn hashMessage(s: *mut Sha256, k: *[u32], msg: *[u8]) -> i32 { |
|
| 116 | 116 | // The message must fit in a single block (max 55 bytes for 1-block padding). |
|
| 117 | 117 | if msg.len > 55 { |
|
| 118 | 118 | return -1; |
|
| 119 | 119 | } |
|
| 120 | 120 |
| 157 | 157 | return 0; |
|
| 158 | 158 | } |
|
| 159 | 159 | ||
| 160 | 160 | /// Test SHA-256 of the empty string "". |
|
| 161 | 161 | /// Expected: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
|
| 162 | - | fn testEmpty(s: *mut Sha256, k: *[u32]) -> i32 { |
|
| 162 | + | unsafe fn testEmpty(s: *mut Sha256, k: *[u32]) -> i32 { |
|
| 163 | 163 | assert hashMessage(s, k, &[]) == 0; |
|
| 164 | 164 | ||
| 165 | 165 | assert s.h[0] == 0xE3B0C442; |
|
| 166 | 166 | assert s.h[1] == 0x98FC1C14; |
|
| 167 | 167 | assert s.h[2] == 0x9AFBF4C8; |
| 173 | 173 | return 0; |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Test SHA-256 of "abc". |
|
| 177 | 177 | /// Expected: ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad |
|
| 178 | - | fn testAbc(s: *mut Sha256, k: *[u32]) -> i32 { |
|
| 178 | + | unsafe fn testAbc(s: *mut Sha256, k: *[u32]) -> i32 { |
|
| 179 | 179 | let msg: [u8; 3] = [0x61, 0x62, 0x63]; |
|
| 180 | 180 | assert hashMessage(s, k, &msg[..]) == 0; |
|
| 181 | 181 | ||
| 182 | 182 | assert s.h[0] == 0xBA7816BF; |
|
| 183 | 183 | assert s.h[1] == 0x8F01CFEA; |
| 208 | 208 | // result = 0x0F ^ 0xF0 ^ 0x00 = 0xFF |
|
| 209 | 209 | assert maj(0xFF, 0x0F, 0xF0) == 0xFF; |
|
| 210 | 210 | return 0; |
|
| 211 | 211 | } |
|
| 212 | 212 | ||
| 213 | - | @default fn main() -> i32 { |
|
| 213 | + | @default unsafe fn main() -> i32 { |
|
| 214 | 214 | // SHA-256 round constants (first 32 bits of fractional parts of cube roots |
|
| 215 | 215 | // of the first 64 primes). |
|
| 216 | 216 | let k: [u32; 64] = [ |
|
| 217 | 217 | 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, |
|
| 218 | 218 | 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, |
test/tests/prog.sieve.rad
+7 -7
| 2 | 2 | //! Sieve of Eratosthenes. |
|
| 3 | 3 | //! Find all primes up to 256 using a boolean array. |
|
| 4 | 4 | //! Verify the count of primes matches the known value (54 primes <= 256). |
|
| 5 | 5 | ||
| 6 | 6 | /// Mark all multiples of p as composite. |
|
| 7 | - | fn markMultiples(sieve: *mut [bool], p: u32) { |
|
| 7 | + | unsafe fn markMultiples(sieve: *mut [bool], p: u32) { |
|
| 8 | 8 | let mut i: u32 = p * p; |
|
| 9 | 9 | while i < sieve.len { |
|
| 10 | 10 | set sieve[i] = true; |
|
| 11 | 11 | set i += p; |
|
| 12 | 12 | } |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Run the sieve algorithm. |
|
| 16 | - | fn runSieve(sieve: *mut [bool]) { |
|
| 16 | + | unsafe fn runSieve(sieve: *mut [bool]) { |
|
| 17 | 17 | // 0 and 1 are not prime. |
|
| 18 | 18 | set sieve[0] = true; |
|
| 19 | 19 | set sieve[1] = true; |
|
| 20 | 20 | ||
| 21 | 21 | let mut p: u32 = 2; |
| 26 | 26 | set p += 1; |
|
| 27 | 27 | } |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Count the number of primes found. |
|
| 31 | - | fn countPrimes(sieve: *[bool]) -> u32 { |
|
| 31 | + | unsafe fn countPrimes(sieve: *[bool]) -> u32 { |
|
| 32 | 32 | let mut count: u32 = 0; |
|
| 33 | 33 | for composite in sieve { |
|
| 34 | 34 | if not composite { |
|
| 35 | 35 | set count += 1; |
|
| 36 | 36 | } |
|
| 37 | 37 | } |
|
| 38 | 38 | return count; |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Collect primes into a slice, return count. |
|
| 42 | - | fn collectPrimes(sieve: *[bool], primes: *mut [u32]) -> u32 { |
|
| 42 | + | unsafe fn collectPrimes(sieve: *[bool], primes: *mut [u32]) -> u32 { |
|
| 43 | 43 | let mut count: u32 = 0; |
|
| 44 | 44 | for composite, idx in sieve { |
|
| 45 | 45 | if not composite { |
|
| 46 | 46 | if count < primes.len { |
|
| 47 | 47 | set primes[count] = idx; |
| 51 | 51 | } |
|
| 52 | 52 | return count; |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | 55 | /// Check that specific known primes are marked correctly. |
|
| 56 | - | fn verifyKnownPrimes(sieve: *[bool]) -> i32 { |
|
| 56 | + | unsafe fn verifyKnownPrimes(sieve: *[bool]) -> i32 { |
|
| 57 | 57 | // Known small primes. |
|
| 58 | 58 | let primes: [u32; 6] = [2, 3, 5, 7, 11, 13]; |
|
| 59 | 59 | for p in primes { |
|
| 60 | 60 | if sieve[p] { |
|
| 61 | 61 | return 1; |
| 78 | 78 | assert sieve[250]; |
|
| 79 | 79 | return 0; |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | 82 | /// Verify that collected primes are in ascending order and all valid. |
|
| 83 | - | fn verifyCollected(sieve: *[bool]) -> i32 { |
|
| 83 | + | unsafe fn verifyCollected(sieve: *[bool]) -> i32 { |
|
| 84 | 84 | let mut primesBuf: [u32; 64] = [0; 64]; |
|
| 85 | 85 | let count = collectPrimes(sieve, &mut primesBuf[..]); |
|
| 86 | 86 | ||
| 87 | 87 | assert count == 54; |
|
| 88 | 88 |
| 101 | 101 | set prev = p; |
|
| 102 | 102 | } |
|
| 103 | 103 | return 0; |
|
| 104 | 104 | } |
|
| 105 | 105 | ||
| 106 | - | @default fn main() -> i32 { |
|
| 106 | + | @default unsafe fn main() -> i32 { |
|
| 107 | 107 | let mut sieve: [bool; 256] = [false; 256]; |
|
| 108 | 108 | runSieve(&mut sieve[..]); |
|
| 109 | 109 | ||
| 110 | 110 | let r1 = verifyKnownPrimes(&sieve[..]); |
|
| 111 | 111 | if r1 <> 0 { |
test/tests/prog.symtab.rad
+15 -15
| 38 | 38 | scopeDepth: u32, |
|
| 39 | 39 | buckets: *mut [u32], |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// Simple string hash function. |
|
| 43 | - | fn hashName(name: *[u8]) -> u32 { |
|
| 43 | + | unsafe fn hashName(name: *[u8]) -> u32 { |
|
| 44 | 44 | let mut h: u32 = 5381; |
|
| 45 | 45 | for ch in name { |
|
| 46 | 46 | set h = ((h << 5) + h) + ch as u32; |
|
| 47 | 47 | } |
|
| 48 | 48 | return h; |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Initialize the symbol table. |
|
| 52 | - | fn init(tab: *mut SymTab) { |
|
| 52 | + | unsafe fn init(tab: *mut SymTab) { |
|
| 53 | 53 | set tab.symbolCount = 0; |
|
| 54 | 54 | set tab.scopeDepth = 0; |
|
| 55 | 55 | ||
| 56 | 56 | for i in 0..HASH_SIZE { |
|
| 57 | 57 | set tab.buckets[i] = NIL; |
|
| 58 | 58 | } |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Push a new scope. |
|
| 62 | - | fn pushScope(tab: *mut SymTab) { |
|
| 62 | + | unsafe fn pushScope(tab: *mut SymTab) { |
|
| 63 | 63 | set tab.scopes[tab.scopeDepth] = ScopeMarker { symbolCount: tab.symbolCount }; |
|
| 64 | 64 | set tab.scopeDepth += 1; |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | /// Pop the current scope, removing all symbols defined in it. |
|
| 68 | - | fn popScope(tab: *mut SymTab) { |
|
| 68 | + | unsafe fn popScope(tab: *mut SymTab) { |
|
| 69 | 69 | if tab.scopeDepth == 0 { |
|
| 70 | 70 | return; |
|
| 71 | 71 | } |
|
| 72 | 72 | set tab.scopeDepth -= 1; |
|
| 73 | 73 | let marker = tab.scopes[tab.scopeDepth]; |
| 91 | 91 | } |
|
| 92 | 92 | } |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | 95 | /// Define a symbol in the current scope. |
|
| 96 | - | fn define(tab: *mut SymTab, name: *[u8], value: i32) -> u32 { |
|
| 96 | + | unsafe fn define(tab: *mut SymTab, name: *[u8], value: i32) -> u32 { |
|
| 97 | 97 | let h = hashName(name); |
|
| 98 | 98 | let bucket = h % HASH_SIZE; |
|
| 99 | 99 | ||
| 100 | 100 | // Check for shadowed symbol with same name. |
|
| 101 | 101 | let mut shadowIdx: u32 = NIL; |
| 126 | 126 | set tab.symbolCount += 1; |
|
| 127 | 127 | return idx; |
|
| 128 | 128 | } |
|
| 129 | 129 | ||
| 130 | 130 | /// Look up a symbol by name. Returns the value if found. |
|
| 131 | - | fn lookup(tab: *SymTab, name: *[u8]) -> ?i32 { |
|
| 131 | + | unsafe fn lookup(tab: *SymTab, name: *[u8]) -> ?i32 { |
|
| 132 | 132 | let h = hashName(name); |
|
| 133 | 133 | let bucket = h % HASH_SIZE; |
|
| 134 | 134 | let mut cur = tab.buckets[bucket]; |
|
| 135 | 135 | ||
| 136 | 136 | while cur <> NIL { |
| 141 | 141 | } |
|
| 142 | 142 | return nil; |
|
| 143 | 143 | } |
|
| 144 | 144 | ||
| 145 | 145 | /// Update a symbol's value. Returns true if the symbol was found. |
|
| 146 | - | fn update(tab: *mut SymTab, name: *[u8], newValue: i32) -> bool { |
|
| 146 | + | unsafe fn update(tab: *mut SymTab, name: *[u8], newValue: i32) -> bool { |
|
| 147 | 147 | let h = hashName(name); |
|
| 148 | 148 | let bucket = h % HASH_SIZE; |
|
| 149 | 149 | let mut cur = tab.buckets[bucket]; |
|
| 150 | 150 | ||
| 151 | 151 | while cur <> NIL { |
| 157 | 157 | } |
|
| 158 | 158 | return false; |
|
| 159 | 159 | } |
|
| 160 | 160 | ||
| 161 | 161 | /// Test basic define and lookup. |
|
| 162 | - | fn testBasic(tab: *mut SymTab) -> i32 { |
|
| 162 | + | unsafe fn testBasic(tab: *mut SymTab) -> i32 { |
|
| 163 | 163 | init(tab); |
|
| 164 | 164 | pushScope(tab); |
|
| 165 | 165 | ||
| 166 | 166 | define(tab, "x", 10); |
|
| 167 | 167 | define(tab, "y", 20); |
| 190 | 190 | popScope(tab); |
|
| 191 | 191 | return 0; |
|
| 192 | 192 | } |
|
| 193 | 193 | ||
| 194 | 194 | /// Test scope shadowing. |
|
| 195 | - | fn testShadowing(tab: *mut SymTab) -> i32 { |
|
| 195 | + | unsafe fn testShadowing(tab: *mut SymTab) -> i32 { |
|
| 196 | 196 | init(tab); |
|
| 197 | 197 | pushScope(tab); |
|
| 198 | 198 | define(tab, "x", 1); |
|
| 199 | 199 | ||
| 200 | 200 | // Verify outer x. |
| 223 | 223 | popScope(tab); |
|
| 224 | 224 | return 0; |
|
| 225 | 225 | } |
|
| 226 | 226 | ||
| 227 | 227 | /// Test deep nesting with shadowing. |
|
| 228 | - | fn testDeepNesting(tab: *mut SymTab) -> i32 { |
|
| 228 | + | unsafe fn testDeepNesting(tab: *mut SymTab) -> i32 { |
|
| 229 | 229 | init(tab); |
|
| 230 | 230 | ||
| 231 | 231 | // Define x at each of 8 scope levels. |
|
| 232 | 232 | let mut i: u32 = 0; |
|
| 233 | 233 | while i < 8 { |
| 257 | 257 | popScope(tab); |
|
| 258 | 258 | return 0; |
|
| 259 | 259 | } |
|
| 260 | 260 | ||
| 261 | 261 | /// Test multiple symbols per scope. |
|
| 262 | - | fn testMultipleSymbols(tab: *mut SymTab) -> i32 { |
|
| 262 | + | unsafe fn testMultipleSymbols(tab: *mut SymTab) -> i32 { |
|
| 263 | 263 | init(tab); |
|
| 264 | 264 | pushScope(tab); |
|
| 265 | 265 | ||
| 266 | 266 | // Define a bunch of symbols. |
|
| 267 | 267 | let names: [*[u8]; 8] = ["a", "bb", "ccc", "dddd", "eeeee", "ff", "ggg", "h"]; |
| 287 | 287 | popScope(tab); |
|
| 288 | 288 | return 0; |
|
| 289 | 289 | } |
|
| 290 | 290 | ||
| 291 | 291 | /// Test update functionality. |
|
| 292 | - | fn testUpdate(tab: *mut SymTab) -> i32 { |
|
| 292 | + | unsafe fn testUpdate(tab: *mut SymTab) -> i32 { |
|
| 293 | 293 | init(tab); |
|
| 294 | 294 | pushScope(tab); |
|
| 295 | 295 | ||
| 296 | 296 | define(tab, "counter", 0); |
|
| 297 | 297 |
| 318 | 318 | popScope(tab); |
|
| 319 | 319 | return 0; |
|
| 320 | 320 | } |
|
| 321 | 321 | ||
| 322 | 322 | /// Test scope isolation: symbols in popped scopes are gone. |
|
| 323 | - | fn testScopeIsolation(tab: *mut SymTab) -> i32 { |
|
| 323 | + | unsafe fn testScopeIsolation(tab: *mut SymTab) -> i32 { |
|
| 324 | 324 | init(tab); |
|
| 325 | 325 | ||
| 326 | 326 | pushScope(tab); |
|
| 327 | 327 | define(tab, "outer", 1); |
|
| 328 | 328 |
| 356 | 356 | popScope(tab); |
|
| 357 | 357 | return 0; |
|
| 358 | 358 | } |
|
| 359 | 359 | ||
| 360 | 360 | /// Test interleaved defines and lookups across scopes using while-let. |
|
| 361 | - | fn testInterleaved(tab: *mut SymTab) -> i32 { |
|
| 361 | + | unsafe fn testInterleaved(tab: *mut SymTab) -> i32 { |
|
| 362 | 362 | init(tab); |
|
| 363 | 363 | pushScope(tab); |
|
| 364 | 364 | ||
| 365 | 365 | define(tab, "a", 100); |
|
| 366 | 366 | define(tab, "b", 200); |
| 400 | 400 | popScope(tab); |
|
| 401 | 401 | popScope(tab); |
|
| 402 | 402 | return 0; |
|
| 403 | 403 | } |
|
| 404 | 404 | ||
| 405 | - | @default fn main() -> i32 { |
|
| 405 | + | @default unsafe fn main() -> i32 { |
|
| 406 | 406 | let mut symbols: [Symbol; 256] = [Symbol { nameHash: 0, value: 0, depth: 0, next: NIL, shadow: NIL }; 256]; |
|
| 407 | 407 | let mut scopes: [ScopeMarker; 16] = [ScopeMarker { symbolCount: 0 }; 16]; |
|
| 408 | 408 | let mut buckets: [u32; 64] = [NIL; 64]; |
|
| 409 | 409 | ||
| 410 | 410 | let mut tab = SymTab { |
test/tests/prog.tokenizer.rad
+28 -28
| 76 | 76 | fn isSpace(c: u8) -> bool { |
|
| 77 | 77 | return c == 32 or c == 9 or c == 10 or c == 13; |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// Peek at the current character, returning nil at end. |
|
| 81 | - | fn peek(lex: *Lexer) -> ?u8 { |
|
| 81 | + | unsafe fn peek(lex: *Lexer) -> ?u8 { |
|
| 82 | 82 | if lex.pos < lex.source.len { |
|
| 83 | 83 | return lex.source[lex.pos]; |
|
| 84 | 84 | } |
|
| 85 | 85 | return nil; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | 88 | /// Advance the lexer by one character. |
|
| 89 | - | fn advance(lex: *mut Lexer) { |
|
| 89 | + | unsafe fn advance(lex: *mut Lexer) { |
|
| 90 | 90 | if lex.pos < lex.source.len { |
|
| 91 | 91 | set lex.pos += 1; |
|
| 92 | 92 | } |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | 95 | /// Skip whitespace characters. |
|
| 96 | - | fn skipWhitespace(lex: *mut Lexer) { |
|
| 96 | + | unsafe fn skipWhitespace(lex: *mut Lexer) { |
|
| 97 | 97 | while let ch = peek(lex); isSpace(ch) { |
|
| 98 | 98 | advance(lex); |
|
| 99 | 99 | } |
|
| 100 | 100 | } |
|
| 101 | 101 | ||
| 102 | 102 | /// Scan a number literal. |
|
| 103 | - | fn scanNumber(lex: *mut Lexer) -> i32 { |
|
| 103 | + | unsafe fn scanNumber(lex: *mut Lexer) -> i32 { |
|
| 104 | 104 | let mut value: i32 = 0; |
|
| 105 | 105 | while let ch = peek(lex); isDigit(ch) { |
|
| 106 | 106 | set value = value * 10 + (ch - 48) as i32; |
|
| 107 | 107 | advance(lex); |
|
| 108 | 108 | } |
|
| 109 | 109 | return value; |
|
| 110 | 110 | } |
|
| 111 | 111 | ||
| 112 | 112 | /// Get the next token from the lexer. |
|
| 113 | - | fn nextToken(lex: *mut Lexer) -> Token { |
|
| 113 | + | unsafe fn nextToken(lex: *mut Lexer) -> Token { |
|
| 114 | 114 | skipWhitespace(lex); |
|
| 115 | 115 | ||
| 116 | 116 | if let ch = peek(lex) { |
|
| 117 | 117 | if isDigit(ch) { |
|
| 118 | 118 | return Token::Number(scanNumber(lex)); |
| 131 | 131 | } |
|
| 132 | 132 | return Token::Eof; |
|
| 133 | 133 | } |
|
| 134 | 134 | ||
| 135 | 135 | /// Tokenize the entire source into a token list. |
|
| 136 | - | fn tokenize(lex: *mut Lexer, list: *mut TokenList) -> bool { |
|
| 136 | + | unsafe fn tokenize(lex: *mut Lexer, list: *mut TokenList) -> bool { |
|
| 137 | 137 | let mut done: bool = false; |
|
| 138 | 138 | while not done { |
|
| 139 | 139 | let tok = nextToken(lex); |
|
| 140 | 140 | match tok { |
|
| 141 | 141 | case Token::Invalid(_) => { |
| 157 | 157 | } |
|
| 158 | 158 | return true; |
|
| 159 | 159 | } |
|
| 160 | 160 | ||
| 161 | 161 | /// Allocate a new expression node. |
|
| 162 | - | fn newExpr(pool: *mut ExprPool, expr: Expr) -> u32 { |
|
| 162 | + | unsafe fn newExpr(pool: *mut ExprPool, expr: Expr) -> u32 { |
|
| 163 | 163 | let idx = pool.count; |
|
| 164 | 164 | set pool.nodes[idx] = expr; |
|
| 165 | 165 | set pool.count += 1; |
|
| 166 | 166 | return idx; |
|
| 167 | 167 | } |
|
| 168 | 168 | ||
| 169 | 169 | /// Get the current token in the parser. |
|
| 170 | - | fn currentToken(p: *Parser) -> Token { |
|
| 170 | + | unsafe fn currentToken(p: *Parser) -> Token { |
|
| 171 | 171 | if p.pos < p.tokenCount { |
|
| 172 | 172 | return p.tokens[p.pos]; |
|
| 173 | 173 | } |
|
| 174 | 174 | return Token::Eof; |
|
| 175 | 175 | } |
|
| 176 | 176 | ||
| 177 | 177 | /// Advance the parser to the next token. |
|
| 178 | - | fn advanceParser(p: *mut Parser) { |
|
| 178 | + | unsafe fn advanceParser(p: *mut Parser) { |
|
| 179 | 179 | if p.pos < p.tokenCount { |
|
| 180 | 180 | set p.pos += 1; |
|
| 181 | 181 | } |
|
| 182 | 182 | } |
|
| 183 | 183 | ||
| 184 | 184 | /// Parse a primary expression (number, parenthesized expression, or unary minus). |
|
| 185 | - | fn parsePrimary(p: *mut Parser) -> ?u32 { |
|
| 185 | + | unsafe fn parsePrimary(p: *mut Parser) -> ?u32 { |
|
| 186 | 186 | let tok = currentToken(p); |
|
| 187 | 187 | ||
| 188 | 188 | match tok { |
|
| 189 | 189 | case Token::Number(n) => { |
|
| 190 | 190 | advanceParser(p); |
| 220 | 220 | } |
|
| 221 | 221 | } |
|
| 222 | 222 | } |
|
| 223 | 223 | ||
| 224 | 224 | /// Parse multiplication and division (higher precedence). |
|
| 225 | - | fn parseMulDiv(p: *mut Parser, left: u32) -> u32 { |
|
| 225 | + | unsafe fn parseMulDiv(p: *mut Parser, left: u32) -> u32 { |
|
| 226 | 226 | let mut result: u32 = left; |
|
| 227 | 227 | let mut cont: bool = true; |
|
| 228 | 228 | ||
| 229 | 229 | while cont { |
|
| 230 | 230 | let tok = currentToken(p); |
| 252 | 252 | } |
|
| 253 | 253 | return result; |
|
| 254 | 254 | } |
|
| 255 | 255 | ||
| 256 | 256 | /// Parse addition and subtraction (lower precedence). |
|
| 257 | - | fn parseAddSub(p: *mut Parser, left: u32) -> u32 { |
|
| 257 | + | unsafe fn parseAddSub(p: *mut Parser, left: u32) -> u32 { |
|
| 258 | 258 | let mut result: u32 = parseMulDiv(p, left); |
|
| 259 | 259 | let mut cont: bool = true; |
|
| 260 | 260 | ||
| 261 | 261 | while cont { |
|
| 262 | 262 | let tok = currentToken(p); |
| 286 | 286 | } |
|
| 287 | 287 | return result; |
|
| 288 | 288 | } |
|
| 289 | 289 | ||
| 290 | 290 | /// Parse a full expression. |
|
| 291 | - | fn parseExpr(p: *mut Parser) -> ?u32 { |
|
| 291 | + | unsafe fn parseExpr(p: *mut Parser) -> ?u32 { |
|
| 292 | 292 | let left = parsePrimary(p) else { |
|
| 293 | 293 | return nil; |
|
| 294 | 294 | }; |
|
| 295 | 295 | return parseAddSub(p, left); |
|
| 296 | 296 | } |
|
| 297 | 297 | ||
| 298 | 298 | /// Evaluate an expression tree. |
|
| 299 | - | fn eval(nodes: *[Expr], idx: u32) -> i32 { |
|
| 299 | + | unsafe fn eval(nodes: *[Expr], idx: u32) -> i32 { |
|
| 300 | 300 | let node = nodes[idx]; |
|
| 301 | 301 | match node { |
|
| 302 | 302 | case Expr::Num(n) => { |
|
| 303 | 303 | return n; |
|
| 304 | 304 | } |
| 324 | 324 | } |
|
| 325 | 325 | } |
|
| 326 | 326 | } |
|
| 327 | 327 | ||
| 328 | 328 | /// Count nodes in an expression tree. |
|
| 329 | - | fn countNodes(nodes: *[Expr], idx: u32) -> u32 { |
|
| 329 | + | unsafe fn countNodes(nodes: *[Expr], idx: u32) -> u32 { |
|
| 330 | 330 | let node = nodes[idx]; |
|
| 331 | 331 | match node { |
|
| 332 | 332 | case Expr::Num(_) => { return 1; } |
|
| 333 | 333 | case Expr::BinOp(data) => { |
|
| 334 | 334 | return 1 + countNodes(nodes, data.left) + countNodes(nodes, data.right); |
| 338 | 338 | } |
|
| 339 | 339 | } |
|
| 340 | 340 | } |
|
| 341 | 341 | ||
| 342 | 342 | /// Helper: tokenize, parse, and evaluate a string expression. |
|
| 343 | - | fn evaluate( |
|
| 343 | + | unsafe fn evaluate( |
|
| 344 | 344 | source: *[u8], |
|
| 345 | 345 | tokenBuf: *mut [Token], |
|
| 346 | 346 | exprBuf: *mut [Expr] |
|
| 347 | 347 | ) -> ?i32 { |
|
| 348 | 348 | let mut lex = Lexer { source, pos: 0 }; |
| 365 | 365 | }; |
|
| 366 | 366 | return eval(exprBuf, root); |
|
| 367 | 367 | } |
|
| 368 | 368 | ||
| 369 | 369 | /// Test simple number. |
|
| 370 | - | fn testNumber(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 370 | + | unsafe fn testNumber(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 371 | 371 | let result = evaluate("42", tokenBuf, exprBuf) else { |
|
| 372 | 372 | return 1; |
|
| 373 | 373 | }; |
|
| 374 | 374 | assert result == 42; |
|
| 375 | 375 | return 0; |
|
| 376 | 376 | } |
|
| 377 | 377 | ||
| 378 | 378 | /// Test addition. |
|
| 379 | - | fn testAdd(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 379 | + | unsafe fn testAdd(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 380 | 380 | let result = evaluate("3 + 4", tokenBuf, exprBuf) else { |
|
| 381 | 381 | return 1; |
|
| 382 | 382 | }; |
|
| 383 | 383 | assert result == 7; |
|
| 384 | 384 | return 0; |
|
| 385 | 385 | } |
|
| 386 | 386 | ||
| 387 | 387 | /// Test precedence: multiplication before addition. |
|
| 388 | - | fn testPrecedence(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 388 | + | unsafe fn testPrecedence(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 389 | 389 | let result = evaluate("2 + 3 * 4", tokenBuf, exprBuf) else { |
|
| 390 | 390 | return 1; |
|
| 391 | 391 | }; |
|
| 392 | 392 | assert result == 14; |
|
| 393 | 393 | return 0; |
|
| 394 | 394 | } |
|
| 395 | 395 | ||
| 396 | 396 | /// Test parenthesized expression. |
|
| 397 | - | fn testParens(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 397 | + | unsafe fn testParens(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 398 | 398 | let result = evaluate("(2 + 3) * 4", tokenBuf, exprBuf) else { |
|
| 399 | 399 | return 1; |
|
| 400 | 400 | }; |
|
| 401 | 401 | assert result == 20; |
|
| 402 | 402 | return 0; |
|
| 403 | 403 | } |
|
| 404 | 404 | ||
| 405 | 405 | /// Test negation. |
|
| 406 | - | fn testNeg(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 406 | + | unsafe fn testNeg(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 407 | 407 | let result = evaluate("-5 + 8", tokenBuf, exprBuf) else { |
|
| 408 | 408 | return 1; |
|
| 409 | 409 | }; |
|
| 410 | 410 | assert result == 3; |
|
| 411 | 411 | return 0; |
|
| 412 | 412 | } |
|
| 413 | 413 | ||
| 414 | 414 | /// Test complex expression. |
|
| 415 | - | fn testComplex(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 415 | + | unsafe fn testComplex(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 416 | 416 | // (10 - 3) * (2 + 1) = 7 * 3 = 21 |
|
| 417 | 417 | let result = evaluate("(10 - 3) * (2 + 1)", tokenBuf, exprBuf) else { |
|
| 418 | 418 | return 1; |
|
| 419 | 419 | }; |
|
| 420 | 420 | assert result == 21; |
|
| 421 | 421 | return 0; |
|
| 422 | 422 | } |
|
| 423 | 423 | ||
| 424 | 424 | /// Test chained operations. |
|
| 425 | - | fn testChained(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 425 | + | unsafe fn testChained(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 426 | 426 | // 100 - 20 - 30 - 10 = 40 |
|
| 427 | 427 | let result = evaluate("100 - 20 - 30 - 10", tokenBuf, exprBuf) else { |
|
| 428 | 428 | return 1; |
|
| 429 | 429 | }; |
|
| 430 | 430 | assert result == 40; |
|
| 431 | 431 | return 0; |
|
| 432 | 432 | } |
|
| 433 | 433 | ||
| 434 | 434 | /// Test token counting via for-in. |
|
| 435 | - | fn testTokenCount(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 435 | + | unsafe fn testTokenCount(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 436 | 436 | let mut lex = Lexer { source: "1 + 2 * 3", pos: 0 }; |
|
| 437 | 437 | let mut list = TokenList { tokens: tokenBuf, count: 0 }; |
|
| 438 | 438 | assert tokenize(&mut lex, &mut list); |
|
| 439 | 439 | ||
| 440 | 440 | // Should be: 1, +, 2, *, 3, Eof = 6 tokens |
| 455 | 455 | ||
| 456 | 456 | return 0; |
|
| 457 | 457 | } |
|
| 458 | 458 | ||
| 459 | 459 | /// Test division and mixed operations. |
|
| 460 | - | fn testDivision(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 460 | + | unsafe fn testDivision(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 461 | 461 | // 20 / 4 + 3 = 5 + 3 = 8 |
|
| 462 | 462 | let result = evaluate("20 / 4 + 3", tokenBuf, exprBuf) else { |
|
| 463 | 463 | return 1; |
|
| 464 | 464 | }; |
|
| 465 | 465 | assert result == 8; |
|
| 466 | 466 | return 0; |
|
| 467 | 467 | } |
|
| 468 | 468 | ||
| 469 | 469 | /// Test deeply nested parentheses. |
|
| 470 | - | fn testDeepNesting(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 470 | + | unsafe fn testDeepNesting(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 471 | 471 | // ((((5)))) = 5 |
|
| 472 | 472 | let result = evaluate("((((5))))", tokenBuf, exprBuf) else { |
|
| 473 | 473 | return 1; |
|
| 474 | 474 | }; |
|
| 475 | 475 | assert result == 5; |
|
| 476 | 476 | return 0; |
|
| 477 | 477 | } |
|
| 478 | 478 | ||
| 479 | 479 | /// Test node count of complex expression. |
|
| 480 | - | fn testNodeCount(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 480 | + | unsafe fn testNodeCount(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 481 | 481 | let mut lex = Lexer { source: "1 + 2 * 3", pos: 0 }; |
|
| 482 | 482 | let mut list = TokenList { tokens: tokenBuf, count: 0 }; |
|
| 483 | 483 | assert tokenize(&mut lex, &mut list); |
|
| 484 | 484 | ||
| 485 | 485 | let mut pool = ExprPool { nodes: exprBuf, count: 0 }; |
| 498 | 498 | let count = countNodes(exprBuf, root); |
|
| 499 | 499 | assert count == 5; |
|
| 500 | 500 | return 0; |
|
| 501 | 501 | } |
|
| 502 | 502 | ||
| 503 | - | @default fn main() -> i32 { |
|
| 503 | + | @default unsafe fn main() -> i32 { |
|
| 504 | 504 | let mut tokenBuf: [Token; 128] = [Token::Eof; 128]; |
|
| 505 | 505 | let mut exprBuf: [Expr; 128] = [Expr::Num(0); 128]; |
|
| 506 | 506 | ||
| 507 | 507 | let r1 = testNumber(&mut tokenBuf[..], &mut exprBuf[..]); |
|
| 508 | 508 | if r1 <> 0 { |
test/tests/prog.vm.rad
+19 -19
| 81 | 81 | /// Too many nested calls. |
|
| 82 | 82 | CallOverflow, |
|
| 83 | 83 | } |
|
| 84 | 84 | ||
| 85 | 85 | /// Push a value onto the stack. |
|
| 86 | - | fn push(vm: *mut VM, value: i32) throws (VmError) { |
|
| 86 | + | unsafe fn push(vm: *mut VM, value: i32) throws (VmError) { |
|
| 87 | 87 | if vm.sp >= MAX_STACK { |
|
| 88 | 88 | throw VmError::StackOverflow; |
|
| 89 | 89 | } |
|
| 90 | 90 | set vm.stack[vm.sp] = value; |
|
| 91 | 91 | set vm.sp += 1; |
|
| 92 | 92 | } |
|
| 93 | 93 | ||
| 94 | 94 | /// Pop a value from the stack. |
|
| 95 | - | fn pop(vm: *mut VM) -> i32 throws (VmError) { |
|
| 95 | + | unsafe fn pop(vm: *mut VM) -> i32 throws (VmError) { |
|
| 96 | 96 | if vm.sp == 0 { |
|
| 97 | 97 | throw VmError::StackUnderflow; |
|
| 98 | 98 | } |
|
| 99 | 99 | set vm.sp -= 1; |
|
| 100 | 100 | return vm.stack[vm.sp]; |
|
| 101 | 101 | } |
|
| 102 | 102 | ||
| 103 | 103 | /// Peek at the top of the stack without removing. |
|
| 104 | - | fn peek(vm: *VM) -> i32 throws (VmError) { |
|
| 104 | + | unsafe fn peek(vm: *VM) -> i32 throws (VmError) { |
|
| 105 | 105 | if vm.sp == 0 { |
|
| 106 | 106 | throw VmError::StackUnderflow; |
|
| 107 | 107 | } |
|
| 108 | 108 | return vm.stack[vm.sp - 1]; |
|
| 109 | 109 | } |
|
| 110 | 110 | ||
| 111 | 111 | /// Execute the bytecode program. |
|
| 112 | - | fn execute(vm: *mut VM) -> i32 throws (VmError) { |
|
| 112 | + | unsafe fn execute(vm: *mut VM) -> i32 throws (VmError) { |
|
| 113 | 113 | while vm.pc < vm.codeLen { |
|
| 114 | 114 | let instr = vm.code[vm.pc]; |
|
| 115 | 115 | set vm.pc += 1; |
|
| 116 | 116 | ||
| 117 | 117 | match instr { |
| 234 | 234 | } |
|
| 235 | 235 | throw VmError::InvalidPC; |
|
| 236 | 236 | } |
|
| 237 | 237 | ||
| 238 | 238 | /// Helper to initialize VM and run a program. |
|
| 239 | - | fn runProgram( |
|
| 239 | + | unsafe fn runProgram( |
|
| 240 | 240 | code: *[Op], |
|
| 241 | 241 | codeLen: u32, |
|
| 242 | 242 | stackBuf: *mut [i32], |
|
| 243 | 243 | localsBuf: *mut [i32], |
|
| 244 | 244 | framesBuf: *mut [Frame] |
| 255 | 255 | }; |
|
| 256 | 256 | return try execute(&mut vm); |
|
| 257 | 257 | } |
|
| 258 | 258 | ||
| 259 | 259 | /// Test basic arithmetic: 3 + 4 * 2 = 11 |
|
| 260 | - | fn testArith(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 260 | + | unsafe fn testArith(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 261 | 261 | let mut code: [Op; 8] = [Op::Halt; 8]; |
|
| 262 | 262 | set code[0] = Op::Push(3); |
|
| 263 | 263 | set code[1] = Op::Push(4); |
|
| 264 | 264 | set code[2] = Op::Push(2); |
|
| 265 | 265 | set code[3] = Op::Mul; |
| 270 | 270 | assert result == 11; |
|
| 271 | 271 | return 0; |
|
| 272 | 272 | } |
|
| 273 | 273 | ||
| 274 | 274 | /// Test local variables: x = 5, y = 7, push x + y. |
|
| 275 | - | fn testLocals(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 275 | + | unsafe fn testLocals(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 276 | 276 | let mut code: [Op; 16] = [Op::Halt; 16]; |
|
| 277 | 277 | set code[0] = Op::Push(5); |
|
| 278 | 278 | set code[1] = Op::Store(0); // x = 5 |
|
| 279 | 279 | set code[2] = Op::Push(7); |
|
| 280 | 280 | set code[3] = Op::Store(1); // y = 7 |
| 287 | 287 | assert result == 12; |
|
| 288 | 288 | return 0; |
|
| 289 | 289 | } |
|
| 290 | 290 | ||
| 291 | 291 | /// Test conditional jump: if 3 > 2 then push 42 else push 99. |
|
| 292 | - | fn testConditional(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 292 | + | unsafe fn testConditional(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 293 | 293 | let mut code: [Op; 16] = [Op::Halt; 16]; |
|
| 294 | 294 | set code[0] = Op::Push(3); |
|
| 295 | 295 | set code[1] = Op::Push(2); |
|
| 296 | 296 | set code[2] = Op::Gt; // 3 > 2 => 1 |
|
| 297 | 297 | set code[3] = Op::JumpIfZero(6); // if false, jump to 6 |
| 305 | 305 | return 0; |
|
| 306 | 306 | } |
|
| 307 | 307 | ||
| 308 | 308 | /// Test loop: sum 1..5 using jumps. |
|
| 309 | 309 | /// local[0] = counter (starts at 1), local[1] = sum (starts at 0). |
|
| 310 | - | fn testLoop(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 310 | + | unsafe fn testLoop(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 311 | 311 | let mut code: [Op; 32] = [Op::Halt; 32]; |
|
| 312 | 312 | set code[0] = Op::Push(1); |
|
| 313 | 313 | set code[1] = Op::Store(0); // counter = 1 |
|
| 314 | 314 | set code[2] = Op::Push(0); |
|
| 315 | 315 | set code[3] = Op::Store(1); // sum = 0 |
| 335 | 335 | assert result == 15; |
|
| 336 | 336 | return 0; |
|
| 337 | 337 | } |
|
| 338 | 338 | ||
| 339 | 339 | /// Test function call: call a function that computes n*2+1 for n=10. |
|
| 340 | - | fn testCall(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 340 | + | unsafe fn testCall(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 341 | 341 | let mut code: [Op; 32] = [Op::Halt; 32]; |
|
| 342 | 342 | ||
| 343 | 343 | // Main: push argument on stack, call function, halt. |
|
| 344 | 344 | set code[0] = Op::Push(10); // push argument |
|
| 345 | 345 | set code[1] = Op::Call(5); // call function at 5 |
| 362 | 362 | assert result == 21; |
|
| 363 | 363 | return 0; |
|
| 364 | 364 | } |
|
| 365 | 365 | ||
| 366 | 366 | /// Test division by zero detection using try...catch with error binding. |
|
| 367 | - | fn testDivByZero(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 367 | + | unsafe fn testDivByZero(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 368 | 368 | let mut code: [Op; 8] = [Op::Halt; 8]; |
|
| 369 | 369 | set code[0] = Op::Push(42); |
|
| 370 | 370 | set code[1] = Op::Push(0); |
|
| 371 | 371 | set code[2] = Op::Div; |
|
| 372 | 372 | set code[3] = Op::Halt; |
| 382 | 382 | assert caught == 1; |
|
| 383 | 383 | return 0; |
|
| 384 | 384 | } |
|
| 385 | 385 | ||
| 386 | 386 | /// Test negation and equality. |
|
| 387 | - | fn testNegAndEq(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 387 | + | unsafe fn testNegAndEq(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 388 | 388 | let mut code: [Op; 16] = [Op::Halt; 16]; |
|
| 389 | 389 | set code[0] = Op::Push(5); |
|
| 390 | 390 | set code[1] = Op::Neg; // -5 |
|
| 391 | 391 | set code[2] = Op::Push(-5); |
|
| 392 | 392 | set code[3] = Op::Eq; // -5 == -5 => 1 |
| 396 | 396 | assert result == 1; |
|
| 397 | 397 | return 0; |
|
| 398 | 398 | } |
|
| 399 | 399 | ||
| 400 | 400 | /// Test factorial using recursive calls: fact(6) = 720. |
|
| 401 | - | fn testFactorial(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 401 | + | unsafe fn testFactorial(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 402 | 402 | let mut code: [Op; 32] = [Op::Halt; 32]; |
|
| 403 | 403 | ||
| 404 | 404 | // Main: push 6, call fact, halt. |
|
| 405 | 405 | set code[0] = Op::Push(6); |
|
| 406 | 406 | set code[1] = Op::Call(4); // call fact at 4 |
| 432 | 432 | assert result == 720; |
|
| 433 | 433 | return 0; |
|
| 434 | 434 | } |
|
| 435 | 435 | ||
| 436 | 436 | /// Test dup instruction. |
|
| 437 | - | fn testDup(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 437 | + | unsafe fn testDup(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 438 | 438 | let mut code: [Op; 8] = [Op::Halt; 8]; |
|
| 439 | 439 | set code[0] = Op::Push(7); |
|
| 440 | 440 | set code[1] = Op::Dup; |
|
| 441 | 441 | set code[2] = Op::Add; // 7 + 7 = 14 |
|
| 442 | 442 | set code[3] = Op::Halt; |
| 445 | 445 | assert result == 14; |
|
| 446 | 446 | return 0; |
|
| 447 | 447 | } |
|
| 448 | 448 | ||
| 449 | 449 | /// Test stack underflow detection using try...catch with error binding. |
|
| 450 | - | fn testStackUnderflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 450 | + | unsafe fn testStackUnderflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 451 | 451 | let mut code: [Op; 4] = [Op::Halt; 4]; |
|
| 452 | 452 | set code[0] = Op::Add; // nothing on stack |
|
| 453 | 453 | set code[1] = Op::Halt; |
|
| 454 | 454 | ||
| 455 | 455 | let mut caught: i32 = 0; |
| 463 | 463 | assert caught == 1; |
|
| 464 | 464 | return 0; |
|
| 465 | 465 | } |
|
| 466 | 466 | ||
| 467 | 467 | /// Test that try...catch on success path does not execute catch block. |
|
| 468 | - | fn testSuccessNoCatch(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 468 | + | unsafe fn testSuccessNoCatch(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 469 | 469 | let mut code: [Op; 4] = [Op::Halt; 4]; |
|
| 470 | 470 | set code[0] = Op::Push(99); |
|
| 471 | 471 | set code[1] = Op::Halt; |
|
| 472 | 472 | ||
| 473 | 473 | let mut caught: i32 = 0; |
| 481 | 481 | assert result == 99; |
|
| 482 | 482 | return 0; |
|
| 483 | 483 | } |
|
| 484 | 484 | ||
| 485 | 485 | /// Test call overflow detection by exhausting frames. |
|
| 486 | - | fn testCallOverflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 486 | + | unsafe fn testCallOverflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 487 | 487 | let mut code: [Op; 4] = [Op::Halt; 4]; |
|
| 488 | 488 | // Infinite recursion: function calls itself. |
|
| 489 | 489 | set code[0] = Op::Call(0); |
|
| 490 | 490 | set code[1] = Op::Halt; |
|
| 491 | 491 |
| 500 | 500 | assert caught == 1; |
|
| 501 | 501 | return 0; |
|
| 502 | 502 | } |
|
| 503 | 503 | ||
| 504 | 504 | /// Test that catch with no binding works (discard the error). |
|
| 505 | - | fn testCatchNoBinding(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 505 | + | unsafe fn testCatchNoBinding(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 506 | 506 | let mut code: [Op; 4] = [Op::Halt; 4]; |
|
| 507 | 507 | set code[0] = Op::Pop; // underflow |
|
| 508 | 508 | set code[1] = Op::Halt; |
|
| 509 | 509 | ||
| 510 | 510 | // Catch without binding - just swallow the error. |
|
| 511 | 511 | try runProgram(&code[..], 2, stackBuf, localsBuf, framesBuf) catch {}; |
|
| 512 | 512 | return 0; |
|
| 513 | 513 | } |
|
| 514 | 514 | ||
| 515 | - | @default fn main() -> i32 { |
|
| 515 | + | @default unsafe fn main() -> i32 { |
|
| 516 | 516 | let mut stackBuf: [i32; 64] = [0; 64]; |
|
| 517 | 517 | let mut localsBuf: [i32; 128] = [0; 128]; |
|
| 518 | 518 | let mut framesBuf: [Frame; 8] = [Frame { returnAddr: 0, localBase: 0 }; 8]; |
|
| 519 | 519 | ||
| 520 | 520 | let r1 = testArith(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]); |
test/tests/ptr.addressof.field.rad
+1 -1
| 1 | 1 | record Point { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | /// Takes the address of a record field. |
|
| 4 | - | fn addressOfField(p: *Point) -> *i32 { |
|
| 4 | + | unsafe fn addressOfField(p: *Point) -> *i32 { |
|
| 5 | 5 | return &p.y; |
|
| 6 | 6 | } |
test/tests/ptr.addressof.local.rad
+3 -3
| 1 | 1 | record Point { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | /// Address of a record local (aggregate) - value is already a pointer. |
|
| 4 | - | fn addressOfRecordLocal() -> *Point { |
|
| 4 | + | unsafe fn addressOfRecordLocal() -> *Point { |
|
| 5 | 5 | let p = Point { x: 1, y: 2 }; |
|
| 6 | 6 | return &p; |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | /// Address of a scalar local - requires stack allocation. |
|
| 10 | - | fn addressOfScalarLocal() -> *i32 { |
|
| 10 | + | unsafe fn addressOfScalarLocal() -> *i32 { |
|
| 11 | 11 | let x: i32 = 42; |
|
| 12 | 12 | return &x; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Address of dereference - should return the original pointer. |
|
| 16 | - | fn addressOfDeref(ptr: *i32) -> *i32 { |
|
| 16 | + | unsafe fn addressOfDeref(ptr: *i32) -> *i32 { |
|
| 17 | 17 | return &(*ptr); |
|
| 18 | 18 | } |
test/tests/ptr.addressof.rad
+1 -1
| 1 | 1 | /// Takes the address of an array element. |
|
| 2 | - | fn addressOfElem(arr: [i32; 4], idx: u32) -> *i32 { |
|
| 2 | + | unsafe fn addressOfElem(arr: [i32; 4], idx: u32) -> *i32 { |
|
| 3 | 3 | return &arr[idx]; |
|
| 4 | 4 | } |
test/tests/ptr.assign.rad
+1 -1
| 1 | 1 | //! returns: 42 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut x: i32 = 1; |
|
| 4 | 4 | let mut ptr: *mut i32 = &mut x; |
|
| 5 | 5 | ||
| 6 | 6 | set *ptr = 42; |
|
| 7 | 7 |
test/tests/ptr.deref.rad
+4 -4
| 1 | 1 | //! returns: 65 |
|
| 2 | 2 | //! Test pointer dereference in various contexts. |
|
| 3 | 3 | ||
| 4 | - | fn derefArrayIndex(ary: [i32; 3]) -> i32 { |
|
| 4 | + | unsafe fn derefArrayIndex(ary: [i32; 3]) -> i32 { |
|
| 5 | 5 | let x: *i32 = &ary[1]; |
|
| 6 | 6 | return *x; |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn derefSliceIndex(slc: *[i32]) -> i32 { |
|
| 9 | + | unsafe fn derefSliceIndex(slc: *[i32]) -> i32 { |
|
| 10 | 10 | let x: *i32 = &slc[2]; |
|
| 11 | 11 | return *x; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn derefBinop(x: i32, y: i32) -> i32 { |
|
| 14 | + | unsafe fn derefBinop(x: i32, y: i32) -> i32 { |
|
| 15 | 15 | let px: *i32 = &x; |
|
| 16 | 16 | let py: *i32 = &y; |
|
| 17 | 17 | ||
| 18 | 18 | return *px + *py; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | @default fn main() -> i32 { |
|
| 21 | + | @default unsafe fn main() -> i32 { |
|
| 22 | 22 | let x: i32 = 42; |
|
| 23 | 23 | let y: *i32 = &x; |
|
| 24 | 24 | let z: i32 = *y; // 42 |
|
| 25 | 25 | let r: i32 = derefBinop(3, 6); // 9 |
|
| 26 | 26 | let a: i32 = derefArrayIndex([7, 8, 9]); // 8 |
test/tests/ptr.deref.record.rad
+2 -2
| 1 | 1 | record Point { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | /// Dereferences a record pointer and accesses a field. |
|
| 4 | - | fn derefField(p: *Point) -> i32 { |
|
| 4 | + | unsafe fn derefField(p: *Point) -> i32 { |
|
| 5 | 5 | return (*p).x; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | /// Dereferences a record pointer and copies to a local. |
|
| 9 | - | fn derefCopy(p: *Point) -> i32 { |
|
| 9 | + | unsafe fn derefCopy(p: *Point) -> i32 { |
|
| 10 | 10 | let copy: Point = *p; |
|
| 11 | 11 | return copy.y; |
|
| 12 | 12 | } |
test/tests/ptr.eq.rad
+4 -4
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | // Test that pointer equality uses address comparison, not value comparison. |
|
| 9 | - | fn testPtrSameAddress() -> bool { |
|
| 9 | + | unsafe fn testPtrSameAddress() -> bool { |
|
| 10 | 10 | let p1 = Point { x: 1, y: 2 }; |
|
| 11 | 11 | let a = &p1; |
|
| 12 | 12 | let b = &p1; |
|
| 13 | 13 | ||
| 14 | 14 | return a == b; // Same address, should be equal. |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | fn testPtrDifferentAddressSameValues() -> bool { |
|
| 17 | + | unsafe fn testPtrDifferentAddressSameValues() -> bool { |
|
| 18 | 18 | let p1 = Point { x: 1, y: 2 }; |
|
| 19 | 19 | let p2 = Point { x: 1, y: 2 }; // Same values as p1, but different address. |
|
| 20 | 20 | let a = &p1; |
|
| 21 | 21 | let b = &p2; |
|
| 22 | 22 | ||
| 23 | 23 | return not (a == b); // Different addresses, should NOT be equal. |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | - | fn testPtrDifferentAddressDifferentValues() -> bool { |
|
| 26 | + | unsafe fn testPtrDifferentAddressDifferentValues() -> bool { |
|
| 27 | 27 | let p1 = Point { x: 1, y: 2 }; |
|
| 28 | 28 | let p2 = Point { x: 3, y: 4 }; |
|
| 29 | 29 | let a = &p1; |
|
| 30 | 30 | let b = &p2; |
|
| 31 | 31 | ||
| 32 | 32 | return not (a == b); // Different addresses, should NOT be equal. |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | - | @default fn main() -> i32 { |
|
| 35 | + | @default unsafe fn main() -> i32 { |
|
| 36 | 36 | assert testPtrSameAddress(); |
|
| 37 | 37 | assert testPtrDifferentAddressSameValues(); |
|
| 38 | 38 | assert testPtrDifferentAddressDifferentValues(); |
|
| 39 | 39 | return 0; |
|
| 40 | 40 | } |
test/tests/ptr.mutate.rad
+3 -3
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | fn mutate1(ptr: *mut i32) { |
|
| 3 | + | unsafe fn mutate1(ptr: *mut i32) { |
|
| 4 | 4 | set *ptr = 39; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn mutate2(ptr: *mut i32) { |
|
| 7 | + | unsafe fn mutate2(ptr: *mut i32) { |
|
| 8 | 8 | set *ptr += 2; |
|
| 9 | 9 | set *ptr += 1; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | @default fn main() -> i32 { |
|
| 12 | + | @default unsafe fn main() -> i32 { |
|
| 13 | 13 | let mut ptr: i32 = 0; |
|
| 14 | 14 | ||
| 15 | 15 | mutate1(&mut ptr); |
|
| 16 | 16 | mutate2(&mut ptr); |
|
| 17 | 17 |
test/tests/ptr.opaque.rad
+7 -7
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test basic opaque pointer usage (automatic coercion). |
|
| 3 | - | fn testOpaqueCasting() -> bool { |
|
| 3 | + | unsafe fn testOpaqueCasting() -> bool { |
|
| 4 | 4 | let x: u32 = 42; |
|
| 5 | 5 | let ptr: *u8 = &x as *u8; |
|
| 6 | 6 | let opq: *opaque = ptr; // Automatic coercion from *u8 to *opaque. |
|
| 7 | 7 | let back: *u8 = opq as *u8; |
|
| 8 | 8 | ||
| 9 | 9 | return back == ptr; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// Test that opaque can be used in function parameters. |
|
| 13 | - | fn takesOpaque(ptr: *opaque, orig: *u8) -> bool { |
|
| 13 | + | unsafe fn takesOpaque(ptr: *opaque, orig: *u8) -> bool { |
|
| 14 | 14 | let back: *u8 = ptr as *u8; |
|
| 15 | 15 | return back == orig; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | fn testOpaqueParams() -> bool { |
|
| 18 | + | unsafe fn testOpaqueParams() -> bool { |
|
| 19 | 19 | let x: u32 = 42; |
|
| 20 | 20 | let ptr: *u8 = &x as *u8; |
|
| 21 | 21 | return takesOpaque(ptr, ptr); // Automatic coercion in function call. |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | /// Test that opaque can be used in return types. |
|
| 25 | - | fn returnsOpaque(ptr: *u8) -> *opaque { |
|
| 25 | + | unsafe fn returnsOpaque(ptr: *u8) -> *opaque { |
|
| 26 | 26 | return ptr; // Automatic coercion in return. |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | - | fn testOpaqueReturn() -> bool { |
|
| 29 | + | unsafe fn testOpaqueReturn() -> bool { |
|
| 30 | 30 | let x: u32 = 42; |
|
| 31 | 31 | let ptr: *u8 = &x as *u8; |
|
| 32 | 32 | let opq: *opaque = returnsOpaque(ptr); |
|
| 33 | 33 | return opq as *u8 == ptr; |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | 36 | /// Test nullable opaque pointers. |
|
| 37 | - | fn testNullableOpaque() -> bool { |
|
| 37 | + | unsafe fn testNullableOpaque() -> bool { |
|
| 38 | 38 | let mut opt: ?*opaque = nil; |
|
| 39 | 39 | if opt == nil { |
|
| 40 | 40 | let x: u32 = 42; |
|
| 41 | 41 | let ptr: *u8 = &x as *u8; |
|
| 42 | 42 | set opt = ptr; // Automatic coercion in assignment. |
| 46 | 46 | } |
|
| 47 | 47 | } |
|
| 48 | 48 | return false; |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | - | @default fn main() -> u32 { |
|
| 51 | + | @default unsafe fn main() -> u32 { |
|
| 52 | 52 | assert testOpaqueCasting(); |
|
| 53 | 53 | assert testOpaqueParams(); |
|
| 54 | 54 | assert testOpaqueReturn(); |
|
| 55 | 55 | assert testNullableOpaque(); |
|
| 56 | 56 | return 0; |
test/tests/ptr.subscript.assign.rad
+1 -1
| 1 | 1 | /// Writes to a slice element through subscript. |
|
| 2 | - | fn subscriptAssign(arr: *mut [i32], idx: u32, val: i32) { |
|
| 2 | + | unsafe fn subscriptAssign(arr: *mut [i32], idx: u32, val: i32) { |
|
| 3 | 3 | set arr[idx] = val; |
|
| 4 | 4 | } |
test/tests/range.arithmetic.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 4 | let arr: [i32; 6] = [10, 20, 30, 40, 50, 60]; |
|
| 5 | 5 | let len: u32 = 6; |
|
| 6 | 6 | ||
| 7 | 7 | // Arithmetic in range start (no parens needed). |
|
| 8 | 8 | let s = &arr[len - 3..len]; |
test/tests/record.copy.rad
+3 -3
| 69 | 69 | assert a[2].x == 561; |
|
| 70 | 70 | assert a[3].x == 12; |
|
| 71 | 71 | return 0; |
|
| 72 | 72 | } |
|
| 73 | 73 | ||
| 74 | - | fn func4(s: S) -> i32 { |
|
| 74 | + | unsafe fn func4(s: S) -> i32 { |
|
| 75 | 75 | let mut a: [S; 2] = undefined; |
|
| 76 | 76 | set a[0] = s; |
|
| 77 | 77 | ||
| 78 | 78 | assert a[0].x == 561; |
|
| 79 | 79 | assert a[0].y == 938; |
|
| 80 | 80 | assert a[0].z == 102; |
|
| 81 | 81 | return 0; |
|
| 82 | 82 | } |
|
| 83 | 83 | ||
| 84 | - | fn func5(s: S) -> i32 { |
|
| 84 | + | unsafe fn func5(s: S) -> i32 { |
|
| 85 | 85 | let mut a: [S; 2] = undefined; |
|
| 86 | 86 | let t: S = makeS(s.x, s.y, s.z); |
|
| 87 | 87 | set a[0] = t; |
|
| 88 | 88 | ||
| 89 | 89 | assert a[0].x == 561; |
|
| 90 | 90 | assert a[0].y == 938; |
|
| 91 | 91 | assert a[0].z == 102; |
|
| 92 | 92 | return 0; |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | - | @default fn main() -> i32 { |
|
| 95 | + | @default unsafe fn main() -> i32 { |
|
| 96 | 96 | let s: S = S { x: 561, y: 938, z: 102 }; |
|
| 97 | 97 | ||
| 98 | 98 | let r1: i32 = func1(s); |
|
| 99 | 99 | if r1 <> 0 { |
|
| 100 | 100 | return 10 + r1; |
test/tests/record.ptr.access.rad
+1 -1
| 3 | 3 | record Point { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let mut p: Point = Point { x: 11, y: 31 }; |
|
| 10 | 10 | let mut r: *Point = &p; |
|
| 11 | 11 | ||
| 12 | 12 | let mut x: i32 = r.x; |
|
| 13 | 13 | let mut y: i32 = r.y; |
test/tests/record.ptr.mutate.rad
+2 -2
| 3 | 3 | record Point { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn mutate(p: *mut Point) { |
|
| 8 | + | unsafe fn mutate(p: *mut Point) { |
|
| 9 | 9 | set p.x = 4; |
|
| 10 | 10 | set p.y = 38; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | @default fn main() -> i32 { |
|
| 13 | + | @default unsafe fn main() -> i32 { |
|
| 14 | 14 | let mut p: Point = Point { x: 0, y: 0 }; |
|
| 15 | 15 | ||
| 16 | 16 | mutate(&mut p); |
|
| 17 | 17 | ||
| 18 | 18 | return p.x + p.y; |
test/tests/ref.if.bug.rad
+3 -3
| 2 | 2 | //! Regression test: address-of inside an if branch. |
|
| 3 | 3 | //! |
|
| 4 | 4 | //! When `&mut var` appears in only one branch of an if/else, the merge |
|
| 5 | 5 | //! block's phi merges the original integer value with a stack pointer. |
|
| 6 | 6 | ||
| 7 | - | fn store(ptr: *mut u32, val: u32) { |
|
| 7 | + | unsafe fn store(ptr: *mut u32, val: u32) { |
|
| 8 | 8 | set *ptr = val; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | fn testIfBranch(cond: bool) -> u32 { |
|
| 11 | + | unsafe fn testIfBranch(cond: bool) -> u32 { |
|
| 12 | 12 | let mut val: u32 = 42; |
|
| 13 | 13 | if cond { |
|
| 14 | 14 | store(&mut val, 99); |
|
| 15 | 15 | } |
|
| 16 | 16 | return val; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | @default fn main() -> i32 { |
|
| 19 | + | @default unsafe fn main() -> i32 { |
|
| 20 | 20 | assert testIfBranch(false) == 42; |
|
| 21 | 21 | assert testIfBranch(true) == 99; |
|
| 22 | 22 | return 0; |
|
| 23 | 23 | } |
test/tests/ref.immut.loop.bug.rad
+3 -3
| 4 | 4 | //! Even though the variable is not `mut`, taking its address inside a |
|
| 5 | 5 | //! loop can produce the same SSA/pointer phi conflict as with mutable |
|
| 6 | 6 | //! variables: the loop header merges the original integer with a pointer |
|
| 7 | 7 | //! from the stack slot created by `&x`. |
|
| 8 | 8 | ||
| 9 | - | fn read(ptr: *u32) -> u32 { |
|
| 9 | + | unsafe fn read(ptr: *u32) -> u32 { |
|
| 10 | 10 | return *ptr; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn testZeroIter(n: u32) -> u32 { |
|
| 13 | + | unsafe fn testZeroIter(n: u32) -> u32 { |
|
| 14 | 14 | let val: u32 = 42; |
|
| 15 | 15 | let mut i: u32 = 0; |
|
| 16 | 16 | while i < n { |
|
| 17 | 17 | let _ = read(&val); |
|
| 18 | 18 | set i += 1; |
|
| 19 | 19 | } |
|
| 20 | 20 | return val; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | @default fn main() -> i32 { |
|
| 23 | + | @default unsafe fn main() -> i32 { |
|
| 24 | 24 | assert testZeroIter(0) == 42; |
|
| 25 | 25 | assert testZeroIter(3) == 42; |
|
| 26 | 26 | return 0; |
|
| 27 | 27 | } |
test/tests/ref.mut.ptr.rad
+2 -2
| 1 | 1 | //! returns: 84 |
|
| 2 | 2 | //! Test mutable pointer references. |
|
| 3 | 3 | ||
| 4 | - | fn store(ptr: *mut i32) { |
|
| 4 | + | unsafe fn store(ptr: *mut i32) { |
|
| 5 | 5 | set *ptr = 42; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let mut a: i32 = 0; |
|
| 10 | 10 | let p: *mut i32 = &mut a; |
|
| 11 | 11 | store(p); |
|
| 12 | 12 | ||
| 13 | 13 | let mut b: i32 = 0; |
test/tests/reference.array.repeat.rad
+1 -1
| 1 | 1 | /// Taking a reference to an array repeat literal yields a static slice. |
|
| 2 | - | fn repeatedSlice() -> *[u32] { |
|
| 2 | + | unsafe fn repeatedSlice() -> *[u32] { |
|
| 3 | 3 | return &[(7 as u32); 3]; |
|
| 4 | 4 | } |
test/tests/slice.alloc.loop.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test returning a slice from a function and iterating over it. |
|
| 3 | 3 | //! Exercises the same code path as createBlock's vars initialization. |
|
| 4 | 4 | //! The slice is returned through a return buffer (> 8 bytes). |
|
| 5 | 5 | ||
| 6 | - | fn makeSlice(buf: *mut [u8], count: u32) -> *mut [u8] { |
|
| 6 | + | unsafe fn makeSlice(buf: *mut [u8], count: u32) -> *mut [u8] { |
|
| 7 | 7 | if count == 0 { |
|
| 8 | 8 | return &mut []; |
|
| 9 | 9 | } |
|
| 10 | 10 | return @sliceOf(&mut buf[0], count); |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn initSlice(buf: *mut [u8], count: u32) -> i32 { |
|
| 13 | + | unsafe fn initSlice(buf: *mut [u8], count: u32) -> i32 { |
|
| 14 | 14 | let s = makeSlice(buf, count); |
|
| 15 | 15 | ||
| 16 | 16 | // This loop pattern matches createBlock's vars initialization. |
|
| 17 | 17 | for i in 0..s.len { |
|
| 18 | 18 | set s[i] = 0; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | return s.len as i32; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | @default fn main() -> i32 { |
|
| 24 | + | @default unsafe fn main() -> i32 { |
|
| 25 | 25 | let mut buf: [u8; 64] = undefined; |
|
| 26 | 26 | ||
| 27 | 27 | let n = initSlice(&mut buf[..], 5); |
|
| 28 | 28 | assert n == 5; |
|
| 29 | 29 |
test/tests/slice.append.rad
+9 -7
| 5 | 5 | record Arena { |
|
| 6 | 6 | data: *mut [u8], |
|
| 7 | 7 | offset: u32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | fn newArena(data: *mut [u8]) -> Arena { |
|
| 10 | + | unsafe 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: *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 |
| 28 | 28 | func: fn(*mut opaque, u32, u32) -> *mut opaque, |
|
| 29 | 29 | ctx: *mut opaque, |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | fn arenaAllocFn(ctx: *mut opaque, size: u32, al: u32) -> *mut opaque { |
|
| 33 | - | let arena = ctx as *mut Arena; |
|
| 34 | - | return arenaAlloc(arena, size, al); |
|
| 33 | + | unsafe { |
|
| 34 | + | let arena = ctx as *mut Arena; |
|
| 35 | + | return arenaAlloc(arena, size, al); |
|
| 36 | + | } |
|
| 35 | 37 | } |
|
| 36 | 38 | ||
| 37 | - | fn arenaAllocator(arena: *mut Arena) -> Allocator { |
|
| 39 | + | unsafe fn arenaAllocator(arena: *mut Arena) -> Allocator { |
|
| 38 | 40 | return Allocator { |
|
| 39 | 41 | func: arenaAllocFn, |
|
| 40 | 42 | ctx: arena as *mut opaque, |
|
| 41 | 43 | }; |
|
| 42 | 44 | } |
|
| 43 | 45 | ||
| 44 | - | static BUF: [u8; 4096] = undefined; |
|
| 46 | + | unsafe static BUF: [u8; 4096] = undefined; |
|
| 45 | 47 | ||
| 46 | - | @default fn main() -> i32 { |
|
| 48 | + | @default unsafe fn main() -> i32 { |
|
| 47 | 49 | let mut arena = newArena(&mut BUF[..]); |
|
| 48 | 50 | let a = arenaAllocator(&mut arena); |
|
| 49 | 51 | ||
| 50 | 52 | // Allocate initial capacity of 4. |
|
| 51 | 53 | let ptr = arenaAlloc(&mut arena, @sizeOf(i32) * 4, @alignOf(i32)); |
test/tests/slice.assign.rad
+1 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | ||
| 3 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 3 | // Fill entire array. |
|
| 5 | 4 | let mut a: [i32; 4] = [1, 2, 3, 4]; |
|
| 6 | 5 | set a[..] = 0; |
|
| 7 | 6 | assert a == [0, 0, 0, 0]; |
|
| 8 | 7 |
test/tests/slice.basic.rad
+6 -6
| 1 | 1 | /// Returns the length field from a slice header. |
|
| 2 | - | fn sliceLen(s: *[i32]) -> u32 { |
|
| 2 | + | unsafe fn sliceLen(s: *[i32]) -> u32 { |
|
| 3 | 3 | return s.len; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | /// Returns the capacity field from a slice header. |
|
| 7 | - | fn sliceCap(s: *[i32]) -> u32 { |
|
| 7 | + | unsafe fn sliceCap(s: *[i32]) -> u32 { |
|
| 8 | 8 | return s.cap; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | /// Returns the pointer field from a slice header. |
|
| 12 | - | fn slicePtr(s: *[i32]) -> *i32 { |
|
| 12 | + | unsafe fn slicePtr(s: *[i32]) -> *i32 { |
|
| 13 | 13 | return s.ptr; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | /// Copies a slice header into a new binding and returns it. |
|
| 17 | - | fn sliceCopy(s: *[i32]) -> *[i32] { |
|
| 17 | + | unsafe fn sliceCopy(s: *[i32]) -> *[i32] { |
|
| 18 | 18 | let c: *[i32] = s; |
|
| 19 | 19 | return c; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// Builds a slice header from a pointer and length. |
|
| 23 | - | fn sliceOf(ptr: *i32, len: u32) -> *[i32] { |
|
| 23 | + | unsafe fn sliceOf(ptr: *i32, len: u32) -> *[i32] { |
|
| 24 | 24 | return @sliceOf(ptr, len); |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Builds a slice header from pointer, length, and capacity. |
|
| 28 | - | fn sliceOfWithCap(ptr: *i32, len: u32, cap: u32) -> *[i32] { |
|
| 28 | + | unsafe fn sliceOfWithCap(ptr: *i32, len: u32, cap: u32) -> *[i32] { |
|
| 29 | 29 | return @sliceOf(ptr, len, cap); |
|
| 30 | 30 | } |
test/tests/slice.cap.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test slice capacity field and @sliceOf builtin. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | // Test that regular slices have cap == len. |
|
| 6 | 6 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 7 | 7 | let s = &mut arr[..]; |
|
| 8 | 8 | ||
| 9 | 9 | if s.cap <> 4 { |
test/tests/slice.delete.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test slice .delete() method. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | let mut arr: [i32; 5] = [10, 20, 30, 40, 50]; |
|
| 6 | 6 | let mut s = &mut arr[..]; |
|
| 7 | 7 | ||
| 8 | 8 | // Delete middle element (index 2: value 30). |
|
| 9 | 9 | s.delete(2); |
test/tests/slice.empty.suffix.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | fn checkArraySuffix() { |
|
| 3 | + | unsafe fn checkArraySuffix() { |
|
| 4 | 4 | let xs: [u8; 3] = [10, 20, 30]; |
|
| 5 | 5 | let empty = &xs[xs.len..xs.len]; |
|
| 6 | 6 | ||
| 7 | 7 | assert empty.len == 0; |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | fn checkSliceSuffix() { |
|
| 10 | + | unsafe fn checkSliceSuffix() { |
|
| 11 | 11 | let xs: [u8; 3] = [10, 20, 30]; |
|
| 12 | 12 | let slice: *[u8] = &xs[..]; |
|
| 13 | 13 | let empty = &slice[slice.len..slice.len]; |
|
| 14 | 14 | ||
| 15 | 15 | assert empty.len == 0; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | fn checkStringSuffix() { |
|
| 18 | + | unsafe fn checkStringSuffix() { |
|
| 19 | 19 | let text = "abc"; |
|
| 20 | 20 | let empty = &text[text.len..text.len]; |
|
| 21 | 21 | ||
| 22 | 22 | assert empty.len == 0; |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | - | @default fn main() -> i32 { |
|
| 25 | + | @default unsafe fn main() -> i32 { |
|
| 26 | 26 | checkArraySuffix(); |
|
| 27 | 27 | checkSliceSuffix(); |
|
| 28 | 28 | checkStringSuffix(); |
|
| 29 | 29 | return 0; |
|
| 30 | 30 | } |
test/tests/slice.eq.rad
+1 -1
| 1 | 1 | // Returns true when the slice pointer and length both match. |
|
| 2 | - | fn sliceEq(a: *[i32], b: *[i32]) -> bool { |
|
| 2 | + | unsafe fn sliceEq(a: *[i32], b: *[i32]) -> bool { |
|
| 3 | 3 | return a == b; |
|
| 4 | 4 | } |
test/tests/slice.index.rad
+1 -1
| 1 | 1 | /// Returns an element from the slice. |
|
| 2 | - | fn sliceIndex(s: *[i32], idx: u32) -> i32 { |
|
| 2 | + | fn sliceIndex(s: &[i32], idx: u32) -> i32 { |
|
| 3 | 3 | return s[idx]; |
|
| 4 | 4 | } |
test/tests/slice.mutable.rad
+3 -3
| 1 | 1 | // Test mutable slice operations |
|
| 2 | 2 | ||
| 3 | - | fn mutSliceStore(s: *mut [i32], idx: u32, val: i32) { |
|
| 3 | + | fn mutSliceStore(s: &mut [i32], idx: u32, val: i32) { |
|
| 4 | 4 | set s[idx] = val; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn mutSliceIncrement(s: *mut [i32], idx: u32) { |
|
| 7 | + | fn mutSliceIncrement(s: &mut [i32], idx: u32) { |
|
| 8 | 8 | set s[idx] += 1; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | fn mutSliceSwap(s: *mut [i32], i: u32, j: u32) { |
|
| 11 | + | fn mutSliceSwap(s: &mut [i32], i: u32, j: u32) { |
|
| 12 | 12 | let tmp = s[i]; |
|
| 13 | 13 | set s[i] = s[j]; |
|
| 14 | 14 | set s[j] = tmp; |
|
| 15 | 15 | } |
test/tests/slice.of.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that @sliceOf produces a fat pointer with the correct length. |
|
| 3 | 3 | ||
| 4 | - | fn makeSlice(ptr: *mut i32, count: u32) -> *mut [i32] { |
|
| 4 | + | unsafe fn makeSlice(ptr: *mut i32, count: u32) -> *mut [i32] { |
|
| 5 | 5 | return @sliceOf(ptr, count); |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 10 | 10 | let s = makeSlice(&mut arr[0], 4); |
|
| 11 | 11 | ||
| 12 | 12 | assert s.len == 4; |
|
| 13 | 13 | assert s[0] == 10; |
test/tests/slice.range.bounds.check.rad
+1 -1
| 3 | 3 | ||
| 4 | 4 | fn id(n: u32) -> u32 { |
|
| 5 | 5 | return n; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let xs: [i32; 3] = [1, 2, 3]; |
|
| 10 | 10 | let slice: *[i32] = &xs[..]; |
|
| 11 | 11 | let bad = &slice[id(4)..id(4)]; |
|
| 12 | 12 | ||
| 13 | 13 | return bad.len as i32; |
test/tests/slice.range.dynamic.rad
+1 -1
| 2 | 2 | ||
| 3 | 3 | fn id(n: u32) -> u32 { |
|
| 4 | 4 | return n; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | @default fn main() -> i32 { |
|
| 7 | + | @default unsafe fn main() -> i32 { |
|
| 8 | 8 | let xs: [u8; 5] = [10, 20, 30, 40, 50]; |
|
| 9 | 9 | let slice: *[u8] = &xs[..]; |
|
| 10 | 10 | ||
| 11 | 11 | let start = id(1); |
|
| 12 | 12 | let end = id(4); |
test/tests/slice.range.order.check.rad
+1 -1
| 3 | 3 | ||
| 4 | 4 | fn id(n: u32) -> u32 { |
|
| 5 | 5 | return n; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let xs: [i32; 3] = [1, 2, 3]; |
|
| 10 | 10 | let slice: *[i32] = &xs[..]; |
|
| 11 | 11 | let bad = &slice[id(2)..id(1)]; |
|
| 12 | 12 | ||
| 13 | 13 | return bad.len as i32; |
test/tests/slice.range.rad
+5 -5
| 1 | 1 | /// Returns a subslice with explicit bounds. |
|
| 2 | - | fn sliceRange(s: *[i32], start: u32, end: u32) -> *[i32] { |
|
| 2 | + | unsafe fn sliceRange(s: *[i32], start: u32, end: u32) -> *[i32] { |
|
| 3 | 3 | return &s[start..end]; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | /// Returns a subslice with an open end bound. |
|
| 7 | - | fn sliceRangeOpenEnd(s: *[i32], start: u32) -> *[i32] { |
|
| 7 | + | unsafe fn sliceRangeOpenEnd(s: *[i32], start: u32) -> *[i32] { |
|
| 8 | 8 | return &s[start..]; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | /// Returns a subslice with an open start bound. |
|
| 12 | - | fn sliceRangeOpenStart(s: *[i32], end: u32) -> *[i32] { |
|
| 12 | + | unsafe fn sliceRangeOpenStart(s: *[i32], end: u32) -> *[i32] { |
|
| 13 | 13 | return &s[..end]; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | /// Returns a full reslice of a slice. |
|
| 17 | - | fn sliceRangeFull(s: *[i32]) -> *[i32] { |
|
| 17 | + | unsafe fn sliceRangeFull(s: *[i32]) -> *[i32] { |
|
| 18 | 18 | return &s[..]; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | /// Returns a subslice from an array parameter. |
|
| 22 | - | fn sliceArray(a: [i32; 4]) -> *[i32] { |
|
| 22 | + | unsafe fn sliceArray(a: [i32; 4]) -> *[i32] { |
|
| 23 | 23 | return &a[1..3]; |
|
| 24 | 24 | } |
test/tests/slice.runtime.i32.rad
+3 -3
| 1 | 1 | // Test runtime slice literal with i32 elements |
|
| 2 | 2 | ||
| 3 | - | fn sliceWithVar(c: i32) -> *[i32] { |
|
| 3 | + | unsafe fn sliceWithVar(c: i32) -> *[i32] { |
|
| 4 | 4 | return &[c]; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn sliceWithVars(a: i32, b: i32) -> *[i32] { |
|
| 7 | + | unsafe fn sliceWithVars(a: i32, b: i32) -> *[i32] { |
|
| 8 | 8 | return &[a, b]; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | fn sliceWithMixed(a: i32) -> *[i32] { |
|
| 11 | + | unsafe fn sliceWithMixed(a: i32) -> *[i32] { |
|
| 12 | 12 | return &[a, 42, a]; |
|
| 13 | 13 | } |
test/tests/slice.runtime.literal.rad
+2 -2
| 1 | 1 | /// Create a slice literal with a runtime (non-constant) element. |
|
| 2 | - | fn sliceWithVar(c: u8) -> *[u8] { |
|
| 2 | + | unsafe fn sliceWithVar(c: u8) -> *[u8] { |
|
| 3 | 3 | return &[c]; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | /// Create a slice literal with multiple runtime elements. |
|
| 7 | - | fn sliceWithVars(a: u8, b: u8) -> *[u8] { |
|
| 7 | + | unsafe fn sliceWithVars(a: u8, b: u8) -> *[u8] { |
|
| 8 | 8 | return &[a, b]; |
|
| 9 | 9 | } |
test/tests/slice.subslice.rad
+5 -5
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | fn testBasicSubslice() -> bool { |
|
| 3 | + | unsafe fn testBasicSubslice() -> bool { |
|
| 4 | 4 | let arr: [i32; 5] = [10, 20, 30, 40, 50]; |
|
| 5 | 5 | let slice: *[i32] = &arr[..]; // Full slice [10, 20, 30, 40, 50] |
|
| 6 | 6 | let subslice: *[i32] = &slice[1..4]; // Sub-slice [20, 30, 40] |
|
| 7 | 7 | ||
| 8 | 8 | return subslice[0] == 20 and subslice[1] == 30 and subslice[2] == 40; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | fn testNestedSubslice() -> bool { |
|
| 11 | + | unsafe fn testNestedSubslice() -> bool { |
|
| 12 | 12 | let arr: [i32; 6] = [100, 200, 300, 400, 500, 600]; |
|
| 13 | 13 | let slice1: *[i32] = &arr[1..5]; // [200, 300, 400, 500] |
|
| 14 | 14 | let slice2: *[i32] = &slice1[1..3]; // [300, 400] |
|
| 15 | 15 | let slice3: *[i32] = &slice2[0..1]; // [300] |
|
| 16 | 16 | ||
| 17 | 17 | return slice3[0] == 300; |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | fn testSubsliceLength() -> bool { |
|
| 20 | + | unsafe fn testSubsliceLength() -> bool { |
|
| 21 | 21 | let arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 22 | 22 | let slice: *[i32] = &arr[..]; |
|
| 23 | 23 | let subslice: *[i32] = &slice[1..3]; |
|
| 24 | 24 | ||
| 25 | 25 | return subslice.len == 2; |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn testEdgeCases() -> bool { |
|
| 28 | + | unsafe fn testEdgeCases() -> bool { |
|
| 29 | 29 | let arr: [i32; 3] = [7, 8, 9]; |
|
| 30 | 30 | let slice: *[i32] = &arr[..]; |
|
| 31 | 31 | ||
| 32 | 32 | // Test single element subslice |
|
| 33 | 33 | let single: *[i32] = &slice[1..2]; |
| 41 | 41 | return false; |
|
| 42 | 42 | } |
|
| 43 | 43 | return true; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | - | @default fn main() -> i32 { |
|
| 46 | + | @default unsafe fn main() -> i32 { |
|
| 47 | 47 | if ( |
|
| 48 | 48 | testBasicSubslice() and |
|
| 49 | 49 | testNestedSubslice() and |
|
| 50 | 50 | testSubsliceLength() and |
|
| 51 | 51 | testEdgeCases() |
test/tests/spill.blockarg.clobber.rad
+3 -3
| 36 | 36 | record State { |
|
| 37 | 37 | h: [u32; 8], |
|
| 38 | 38 | w: [u32; 64], |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | - | fn prepareSchedule(s: *mut State, block: *[u32]) { |
|
| 41 | + | unsafe fn prepareSchedule(s: *mut State, block: *[u32]) { |
|
| 42 | 42 | let mut i: u32 = 0; |
|
| 43 | 43 | while i < 16 { |
|
| 44 | 44 | set s.w[i] = block[i]; |
|
| 45 | 45 | set i += 1; |
|
| 46 | 46 | } |
| 50 | 50 | } |
|
| 51 | 51 | } |
|
| 52 | 52 | ||
| 53 | 53 | /// SHA-256 compression: 64 rounds with 8 rotating working variables. |
|
| 54 | 54 | /// Exercises heavy register pressure and spilled block-argument shuffles. |
|
| 55 | - | fn compress(s: *mut State, k: *[u32]) { |
|
| 55 | + | unsafe fn compress(s: *mut State, k: *[u32]) { |
|
| 56 | 56 | let mut a: u32 = s.h[0]; |
|
| 57 | 57 | let mut b: u32 = s.h[1]; |
|
| 58 | 58 | let mut c: u32 = s.h[2]; |
|
| 59 | 59 | let mut d: u32 = s.h[3]; |
|
| 60 | 60 | let mut e: u32 = s.h[4]; |
| 85 | 85 | set s.h[5] += f; |
|
| 86 | 86 | set s.h[6] += g; |
|
| 87 | 87 | set s.h[7] += hh; |
|
| 88 | 88 | } |
|
| 89 | 89 | ||
| 90 | - | @default fn main() -> i32 { |
|
| 90 | + | @default unsafe fn main() -> i32 { |
|
| 91 | 91 | let k: [u32; 64] = [ |
|
| 92 | 92 | 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, |
|
| 93 | 93 | 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, |
|
| 94 | 94 | 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, |
|
| 95 | 95 | 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, |
test/tests/spill.loop.rad
+1 -1
| 29 | 29 | return n; |
|
| 30 | 30 | } |
|
| 31 | 31 | return (n + a - 1) & (0 - a); |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | - | fn computeRecordLayout(tags: *[u8]) -> Layout { |
|
| 34 | + | fn computeRecordLayout(tags: &[u8]) -> Layout { |
|
| 35 | 35 | let mut currentOffset: u32 = 0; |
|
| 36 | 36 | let mut maxAlignment: u32 = 1; |
|
| 37 | 37 | for i in 0..tags.len { |
|
| 38 | 38 | let layout = getLayout(tags[i]); |
|
| 39 | 39 | set currentOffset = alignUp(currentOffset, layout.alignment); |
test/tests/static.slice.index.assign.rad
+2 -2
| 7 | 7 | record Container { |
|
| 8 | 8 | pad: i32, |
|
| 9 | 9 | items: [Inner; 3], |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | static container: Container = undefined; |
|
| 12 | + | unsafe static container: Container = undefined; |
|
| 13 | 13 | ||
| 14 | - | @default fn main() -> i32 { |
|
| 14 | + | @default unsafe fn main() -> i32 { |
|
| 15 | 15 | set container.items[0].value = 10; |
|
| 16 | 16 | set container.items[1].value = 20; |
|
| 17 | 17 | set container.items[2].value = 30; |
|
| 18 | 18 | ||
| 19 | 19 | let slice: *mut [Inner] = &mut container.items[..]; |
test/tests/static.slice.offset.rad
+2 -2
| 15 | 15 | static SCRATCH: [Entry; 1] = [Entry { a: 0, b: 0 }]; |
|
| 16 | 16 | static STORAGE: [Entry; 2] = [ |
|
| 17 | 17 | Entry { a: 0, b: 0 }, |
|
| 18 | 18 | Entry { a: 0, b: 0 }, |
|
| 19 | 19 | ]; |
|
| 20 | - | static TBL: Table = undefined; |
|
| 20 | + | unsafe static TBL: Table = undefined; |
|
| 21 | 21 | ||
| 22 | - | @default fn main() -> i32 { |
|
| 22 | + | @default unsafe fn main() -> i32 { |
|
| 23 | 23 | set TBL.scratch = &mut SCRATCH[..]; |
|
| 24 | 24 | set TBL.entries = &mut STORAGE[..]; |
|
| 25 | 25 | set TBL.len = 0; |
|
| 26 | 26 | ||
| 27 | 27 | set TBL.entries[TBL.len] = Entry { a: 7, b: 9 }; |
test/tests/static.zero.bss.rad
+2 -2
| 16 | 16 | /// Field that forces padding after `tag`. |
|
| 17 | 17 | value: u32, |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | /// Partially initialized aggregate where all concrete bytes are zero. |
|
| 21 | - | static POOL: Pool = Pool { table: undefined, count: 0 }; |
|
| 21 | + | unsafe static POOL: Pool = Pool { table: undefined, count: 0 }; |
|
| 22 | 22 | ||
| 23 | 23 | /// Repeated scalar zeros. |
|
| 24 | 24 | static FLAGS: [bool; 4] = [false; 4]; |
|
| 25 | 25 | ||
| 26 | 26 | /// Explicit zero fields with undefined padding. |
|
| 27 | 27 | static PADDED: Padded = Padded { tag: 0, value: 0 }; |
|
| 28 | 28 | ||
| 29 | - | @default fn main() -> i32 { |
|
| 29 | + | @default unsafe fn main() -> i32 { |
|
| 30 | 30 | assert POOL.count == 0; |
|
| 31 | 31 | assert FLAGS[0] == false; |
|
| 32 | 32 | assert FLAGS[3] == false; |
|
| 33 | 33 | assert PADDED.tag == 0; |
|
| 34 | 34 | assert PADDED.value == 0; |
test/tests/string.basic.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | // Test string literals. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> u32 { |
|
| 4 | + | @default unsafe fn main() -> u32 { |
|
| 5 | 5 | let s: *[u8] = "fnord"; |
|
| 6 | 6 | ||
| 7 | 7 | // Return the length of the string. |
|
| 8 | 8 | return (s.len) - 5; |
|
| 9 | 9 | } |
test/tests/string.escape.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test string escape sequences. |
|
| 3 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 4 | let s1: *[u8] = "Hello\tWorld!\n"; |
|
| 5 | 5 | assert s1.len == 13; |
|
| 6 | 6 | ||
| 7 | 7 | let s2: *[u8] = "\""; |
|
| 8 | 8 | assert s2.len == 1; |
test/tests/string.index.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | // Test string indexing. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> u8 { |
|
| 4 | + | @default unsafe fn main() -> u8 { |
|
| 5 | 5 | let s: *[u8] = "fnord"; |
|
| 6 | 6 | ||
| 7 | 7 | return (s[1] + s[2]) - 221; |
|
| 8 | 8 | } |
test/tests/trait.aggregate.ret.rad
+23 -15
| 14 | 14 | y: i32, |
|
| 15 | 15 | z: i32, |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | trait Geometry { |
|
| 19 | - | fn (*Geometry) origin() -> Point; |
|
| 20 | - | fn (*Geometry) center() -> Vec3; |
|
| 21 | - | fn (*Geometry) maybe() -> ?i32; |
|
| 19 | + | fn (&Geometry) origin() -> Point; |
|
| 20 | + | fn (&Geometry) center() -> Vec3; |
|
| 21 | + | fn (&Geometry) maybe() -> ?i32; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | record Circle { |
|
| 25 | 25 | cx: i32, |
|
| 26 | 26 | cy: i32, |
|
| 27 | 27 | radius: i32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | instance Geometry for Circle { |
|
| 31 | - | fn (c: *Circle) origin() -> Point { |
|
| 31 | + | fn (c: &Circle) origin() -> Point { |
|
| 32 | 32 | return Point { x: c.cx, y: c.cy }; |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | - | fn (c: *Circle) center() -> Vec3 { |
|
| 35 | + | fn (c: &Circle) center() -> Vec3 { |
|
| 36 | 36 | return Vec3 { x: c.cx, y: c.cy, z: 0 }; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | - | fn (c: *Circle) maybe() -> ?i32 { |
|
| 39 | + | fn (c: &Circle) maybe() -> ?i32 { |
|
| 40 | 40 | if c.radius > 0 { |
|
| 41 | 41 | return c.radius; |
|
| 42 | 42 | } |
|
| 43 | 43 | return nil; |
|
| 44 | 44 | } |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | - | @default fn main() -> i32 { |
|
| 48 | - | let c = Circle { cx: 10, cy: 20, radius: 5 }; |
|
| 49 | - | let g: *opaque Geometry = &c; |
|
| 50 | - | ||
| 47 | + | fn checkCircle(g: &opaque Geometry) -> i32 { |
|
| 51 | 48 | // Small struct return (Point, 8 bytes = pointer size). |
|
| 52 | 49 | let p = g.origin(); |
|
| 53 | 50 | assert p.x == 10; |
|
| 54 | 51 | assert p.y == 20; |
|
| 55 | 52 |
| 64 | 61 | if let val = m { |
|
| 65 | 62 | assert val == 5; |
|
| 66 | 63 | } else { |
|
| 67 | 64 | return 7; |
|
| 68 | 65 | } |
|
| 66 | + | return 0; |
|
| 67 | + | } |
|
| 69 | 68 | ||
| 69 | + | fn checkEmptyCircle(g: &opaque Geometry) -> i32 { |
|
| 70 | 70 | // Optional return - None case. |
|
| 71 | - | let c2 = Circle { cx: 0, cy: 0, radius: 0 }; |
|
| 72 | - | let g2: *opaque Geometry = &c2; |
|
| 73 | - | let m2 = g2.maybe(); |
|
| 74 | - | if let _ = m2 { |
|
| 71 | + | let m = g.maybe(); |
|
| 72 | + | if let _ = m { |
|
| 75 | 73 | return 8; |
|
| 76 | 74 | } |
|
| 77 | - | ||
| 78 | 75 | return 0; |
|
| 79 | 76 | } |
|
| 77 | + | ||
| 78 | + | @default fn main() -> i32 { |
|
| 79 | + | let c = Circle { cx: 10, cy: 20, radius: 5 }; |
|
| 80 | + | let result = checkCircle(&c); |
|
| 81 | + | if result <> 0 { |
|
| 82 | + | return result; |
|
| 83 | + | } |
|
| 84 | + | ||
| 85 | + | let c2 = Circle { cx: 0, cy: 0, radius: 0 }; |
|
| 86 | + | return checkEmptyCircle(&c2); |
|
| 87 | + | } |
test/tests/trait.array.optional.rad
+6 -6
| 11 | 11 | record Multiplier { |
|
| 12 | 12 | n: i32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | trait Transform { |
|
| 16 | - | fn (*Transform) apply(x: i32) -> i32; |
|
| 16 | + | unsafe fn (*Transform) apply(x: i32) -> i32; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | instance Transform for Adder { |
|
| 20 | - | fn (a: *Adder) apply(x: i32) -> i32 { |
|
| 20 | + | unsafe fn (a: *Adder) apply(x: i32) -> i32 { |
|
| 21 | 21 | return x + a.n; |
|
| 22 | 22 | } |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | instance Transform for Multiplier { |
|
| 26 | - | fn (m: *Multiplier) apply(x: i32) -> i32 { |
|
| 26 | + | unsafe fn (m: *Multiplier) apply(x: i32) -> i32 { |
|
| 27 | 27 | return x * m.n; |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | /// Apply a chain of transforms to a value. |
|
| 32 | - | fn applyAll(transforms: *[*opaque Transform], value: i32) -> i32 { |
|
| 32 | + | unsafe fn applyAll(transforms: *[*opaque Transform], value: i32) -> i32 { |
|
| 33 | 33 | let mut result = value; |
|
| 34 | 34 | for i in 0..transforms.len { |
|
| 35 | 35 | set result = transforms[i].apply(result); |
|
| 36 | 36 | } |
|
| 37 | 37 | return result; |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Apply an optional transform, returning the original value if nil. |
|
| 41 | - | fn applyMaybe(t: ?*opaque Transform, value: i32) -> i32 { |
|
| 41 | + | unsafe fn applyMaybe(t: ?*opaque Transform, value: i32) -> i32 { |
|
| 42 | 42 | if let tr = t { |
|
| 43 | 43 | return tr.apply(value); |
|
| 44 | 44 | } |
|
| 45 | 45 | return value; |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | - | @default fn main() -> i32 { |
|
| 48 | + | @default unsafe fn main() -> i32 { |
|
| 49 | 49 | let a1 = Adder { n: 10 }; |
|
| 50 | 50 | let m1 = Multiplier { n: 3 }; |
|
| 51 | 51 | let a2 = Adder { n: 5 }; |
|
| 52 | 52 | ||
| 53 | 53 | let t1: *opaque Transform = &a1; |
test/tests/trait.basic.rad
+8 -6
| 3 | 3 | record Counter { |
|
| 4 | 4 | value: i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | trait Adder { |
|
| 8 | - | fn (*mut Adder) add(n: i32) -> i32; |
|
| 8 | + | fn (&mut Adder) add(n: i32) -> i32; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | instance Adder for Counter { |
|
| 12 | - | fn (c: *mut Counter) add(n: i32) -> i32 { |
|
| 12 | + | fn (c: &mut Counter) add(n: i32) -> i32 { |
|
| 13 | 13 | set c.value = c.value + n; |
|
| 14 | 14 | return c.value; |
|
| 15 | 15 | } |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | @default fn main() -> i32 { |
|
| 19 | - | let mut c = Counter { value: 10 }; |
|
| 20 | - | let a: *mut opaque Adder = &mut c; |
|
| 21 | - | ||
| 18 | + | fn exerciseAdder(a: &mut opaque Adder) -> i32 { |
|
| 22 | 19 | let result = a.add(5); |
|
| 23 | 20 | // c.value should be 15 now. |
|
| 24 | 21 | assert result == 15; |
|
| 25 | 22 | let result2 = a.add(3); |
|
| 26 | 23 | // c.value should be 18 now. |
|
| 27 | 24 | assert result2 == 18; |
|
| 28 | 25 | return 0; |
|
| 29 | 26 | } |
|
| 27 | + | ||
| 28 | + | @default fn main() -> i32 { |
|
| 29 | + | let mut c = Counter { value: 10 }; |
|
| 30 | + | return exerciseAdder(&mut c); |
|
| 31 | + | } |
test/tests/trait.control.flow.rad
+10 -8
| 8 | 8 | record Counter { |
|
| 9 | 9 | value: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Stepper { |
|
| 13 | - | fn (*mut Stepper) step() -> i32; |
|
| 14 | - | fn (*Stepper) current() -> i32; |
|
| 13 | + | fn (&mut Stepper) step() -> i32; |
|
| 14 | + | fn (&Stepper) current() -> i32; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | instance Stepper for Counter { |
|
| 18 | - | fn (c: *mut Counter) step() -> i32 { |
|
| 18 | + | fn (c: &mut Counter) step() -> i32 { |
|
| 19 | 19 | set c.value = c.value + 1; |
|
| 20 | 20 | return c.value; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | fn (c: *Counter) current() -> i32 { |
|
| 23 | + | fn (c: &Counter) current() -> i32 { |
|
| 24 | 24 | return c.value; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | @default fn main() -> i32 { |
|
| 29 | - | let mut c = Counter { value: 0 }; |
|
| 30 | - | let s: *mut opaque Stepper = &mut c; |
|
| 31 | - | ||
| 28 | + | fn exerciseStepper(s: &mut opaque Stepper) -> i32 { |
|
| 32 | 29 | // Dispatch in a while loop. |
|
| 33 | 30 | let mut i: i32 = 0; |
|
| 34 | 31 | while i < 5 { |
|
| 35 | 32 | s.step(); |
|
| 36 | 33 | set i = i + 1; |
| 52 | 49 | // Dispatch result used in conditional expression. |
|
| 53 | 50 | let v = s.current(); |
|
| 54 | 51 | assert v == 10; |
|
| 55 | 52 | return 0; |
|
| 56 | 53 | } |
|
| 54 | + | ||
| 55 | + | @default fn main() -> i32 { |
|
| 56 | + | let mut c = Counter { value: 0 }; |
|
| 57 | + | return exerciseStepper(&mut c); |
|
| 58 | + | } |
test/tests/trait.dispatch.rad
+5 -5
| 1 | 1 | record Acc { |
|
| 2 | 2 | n: i32, |
|
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | trait Ops { |
|
| 6 | - | fn (*Ops) get() -> i32; |
|
| 7 | - | fn (*mut Ops) put(n: i32); |
|
| 6 | + | fn (&Ops) get() -> i32; |
|
| 7 | + | fn (&mut Ops) put(n: i32); |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | 10 | instance Ops for Acc { |
|
| 11 | - | fn (a: *Acc) get() -> i32 { |
|
| 11 | + | fn (a: &Acc) get() -> i32 { |
|
| 12 | 12 | return a.n; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn (a: *mut Acc) put(n: i32) { |
|
| 15 | + | fn (a: &mut Acc) put(n: i32) { |
|
| 16 | 16 | set a.n = n; |
|
| 17 | 17 | } |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | fn dispatch(o: *mut opaque Ops) -> i32 { |
|
| 20 | + | fn dispatch(o: &mut opaque Ops) -> i32 { |
|
| 21 | 21 | o.put(42); |
|
| 22 | 22 | return o.get(); |
|
| 23 | 23 | } |
test/tests/trait.fn.param.rad
+12 -15
| 11 | 11 | record Square { |
|
| 12 | 12 | side: i32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | trait Shape { |
|
| 16 | - | fn (*Shape) area() -> i32; |
|
| 16 | + | fn (&Shape) area() -> i32; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | trait Scalable { |
|
| 20 | - | fn (*mut Scalable) scale(factor: i32); |
|
| 20 | + | fn (&mut Scalable) scale(factor: i32); |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | instance Shape for Circle { |
|
| 24 | - | fn (c: *Circle) area() -> i32 { |
|
| 24 | + | fn (c: &Circle) area() -> i32 { |
|
| 25 | 25 | return c.radius * c.radius * 3; |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | instance Shape for Square { |
|
| 30 | - | fn (s: *Square) area() -> i32 { |
|
| 30 | + | fn (s: &Square) area() -> i32 { |
|
| 31 | 31 | return s.side * s.side; |
|
| 32 | 32 | } |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | instance Scalable for Circle { |
|
| 36 | - | fn (c: *mut Circle) scale(factor: i32) { |
|
| 36 | + | fn (c: &mut Circle) scale(factor: i32) { |
|
| 37 | 37 | set c.radius = c.radius * factor; |
|
| 38 | 38 | } |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Accept an immutable trait object parameter. |
|
| 42 | - | fn getArea(s: *opaque Shape) -> i32 { |
|
| 42 | + | fn getArea(s: &opaque Shape) -> i32 { |
|
| 43 | 43 | return s.area(); |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | /// Accept a mutable trait object parameter. |
|
| 47 | - | fn doubleSize(s: *mut opaque Scalable) { |
|
| 47 | + | fn doubleSize(s: &mut opaque Scalable) { |
|
| 48 | 48 | s.scale(2); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Accept two trait object parameters. |
|
| 52 | - | fn totalArea(a: *opaque Shape, b: *opaque Shape) -> i32 { |
|
| 52 | + | fn totalArea(a: &opaque Shape, b: &opaque Shape) -> i32 { |
|
| 53 | 53 | return a.area() + b.area(); |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | @default fn main() -> i32 { |
|
| 57 | 57 | let c = Circle { radius: 5 }; |
|
| 58 | 58 | let s = Square { side: 4 }; |
|
| 59 | 59 | ||
| 60 | 60 | // Pass immutable trait objects to function. |
|
| 61 | - | let cs: *opaque Shape = &c; |
|
| 62 | - | let ca = getArea(cs); |
|
| 61 | + | let ca = getArea(&c); |
|
| 63 | 62 | assert ca == 75; |
|
| 64 | 63 | ||
| 65 | - | let ss: *opaque Shape = &s; |
|
| 66 | - | let sa = getArea(ss); |
|
| 64 | + | let sa = getArea(&s); |
|
| 67 | 65 | assert sa == 16; |
|
| 68 | 66 | ||
| 69 | 67 | // Pass two different trait objects to same function. |
|
| 70 | - | let total = totalArea(cs, ss); |
|
| 68 | + | let total = totalArea(&c, &s); |
|
| 71 | 69 | assert total == 91; |
|
| 72 | 70 | ||
| 73 | 71 | // Pass mutable trait object to function. |
|
| 74 | 72 | let mut c2 = Circle { radius: 3 }; |
|
| 75 | - | let sc: *mut opaque Scalable = &mut c2; |
|
| 76 | - | doubleSize(sc); |
|
| 73 | + | doubleSize(&mut c2); |
|
| 77 | 74 | assert c2.radius == 6; |
|
| 78 | 75 | return 0; |
|
| 79 | 76 | } |
test/tests/trait.multiple.methods.rad
+14 -11
| 8 | 8 | record Accumulator { |
|
| 9 | 9 | total: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Collector { |
|
| 13 | - | fn (*mut Collector) add(n: i32) -> i32; |
|
| 14 | - | fn (*mut Collector) clear(); |
|
| 15 | - | fn (*mut Collector) isEmpty() -> bool; |
|
| 13 | + | fn (&mut Collector) add(n: i32) -> i32; |
|
| 14 | + | fn (&mut Collector) clear(); |
|
| 15 | + | fn (&Collector) isEmpty() -> bool; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | instance Collector for Accumulator { |
|
| 19 | - | fn (a: *mut Accumulator) add(n: i32) -> i32 { |
|
| 19 | + | fn (a: &mut Accumulator) add(n: i32) -> i32 { |
|
| 20 | 20 | set a.total = a.total + n; |
|
| 21 | 21 | return a.total; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | fn (a: *mut Accumulator) clear() { |
|
| 24 | + | fn (a: &mut Accumulator) clear() { |
|
| 25 | 25 | set a.total = 0; |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn (a: *mut Accumulator) isEmpty() -> bool { |
|
| 28 | + | fn (a: &Accumulator) isEmpty() -> bool { |
|
| 29 | 29 | return a.total == 0; |
|
| 30 | 30 | } |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | - | @default fn main() -> i32 { |
|
| 34 | - | let mut acc = Accumulator { total: 0 }; |
|
| 35 | - | let c: *mut opaque Collector = &mut acc; |
|
| 36 | - | ||
| 33 | + | fn exerciseCollector(c: &mut opaque Collector) -> i32 { |
|
| 37 | 34 | // Initially empty. |
|
| 38 | 35 | assert c.isEmpty(); |
|
| 39 | 36 | ||
| 40 | 37 | // Add returns running total. |
|
| 41 | 38 | let v1 = c.add(10); |
| 49 | 46 | return 4; |
|
| 50 | 47 | } |
|
| 51 | 48 | ||
| 52 | 49 | // Void return: clear. |
|
| 53 | 50 | c.clear(); |
|
| 54 | - | assert acc.total == 0; |
|
| 55 | 51 | assert c.isEmpty(); |
|
| 56 | 52 | return 0; |
|
| 57 | 53 | } |
|
| 54 | + | ||
| 55 | + | @default fn main() -> i32 { |
|
| 56 | + | let mut acc = Accumulator { total: 0 }; |
|
| 57 | + | let result = exerciseCollector(&mut acc); |
|
| 58 | + | assert acc.total == 0; |
|
| 59 | + | return result; |
|
| 60 | + | } |
test/tests/trait.multiple.traits.rad
+24 -14
| 8 | 8 | record Counter { |
|
| 9 | 9 | value: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Incrementable { |
|
| 13 | - | fn (*mut Incrementable) inc() -> i32; |
|
| 13 | + | fn (&mut Incrementable) inc() -> i32; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | trait Resettable { |
|
| 17 | - | fn (*mut Resettable) reset(); |
|
| 18 | - | fn (*mut Resettable) isZero() -> bool; |
|
| 17 | + | fn (&mut Resettable) reset(); |
|
| 18 | + | fn (&Resettable) isZero() -> bool; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | instance Incrementable for Counter { |
|
| 22 | - | fn (c: *mut Counter) inc() -> i32 { |
|
| 22 | + | fn (c: &mut Counter) inc() -> i32 { |
|
| 23 | 23 | set c.value = c.value + 1; |
|
| 24 | 24 | return c.value; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | instance Resettable for Counter { |
|
| 29 | - | fn (c: *mut Counter) reset() { |
|
| 29 | + | fn (c: &mut Counter) reset() { |
|
| 30 | 30 | set c.value = 0; |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | - | fn (c: *mut Counter) isZero() -> bool { |
|
| 33 | + | fn (c: &Counter) isZero() -> bool { |
|
| 34 | 34 | return c.value == 0; |
|
| 35 | 35 | } |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | + | fn increment(i: &mut opaque Incrementable) -> i32 { |
|
| 39 | + | return i.inc(); |
|
| 40 | + | } |
|
| 41 | + | ||
| 42 | + | fn resetCounter(r: &mut opaque Resettable) -> i32 { |
|
| 43 | + | if r.isZero() { |
|
| 44 | + | return 2; |
|
| 45 | + | } |
|
| 46 | + | r.reset(); |
|
| 47 | + | assert r.isZero(); |
|
| 48 | + | return 0; |
|
| 49 | + | } |
|
| 50 | + | ||
| 38 | 51 | @default fn main() -> i32 { |
|
| 39 | 52 | let mut c = Counter { value: 10 }; |
|
| 40 | 53 | ||
| 41 | 54 | // Dispatch through Incrementable. |
|
| 42 | - | let i: *mut opaque Incrementable = &mut c; |
|
| 43 | - | let v1 = i.inc(); |
|
| 55 | + | let v1 = increment(&mut c); |
|
| 44 | 56 | assert v1 == 11; |
|
| 45 | 57 | ||
| 46 | 58 | // Dispatch through Resettable. |
|
| 47 | - | let r: *mut opaque Resettable = &mut c; |
|
| 48 | - | if r.isZero() { |
|
| 49 | - | return 2; |
|
| 59 | + | let result = resetCounter(&mut c); |
|
| 60 | + | if result <> 0 { |
|
| 61 | + | return result; |
|
| 50 | 62 | } |
|
| 51 | - | r.reset(); |
|
| 52 | - | assert r.isZero(); |
|
| 53 | 63 | assert c.value == 0; |
|
| 54 | 64 | ||
| 55 | 65 | // Increment again after reset. |
|
| 56 | - | let v2 = i.inc(); |
|
| 66 | + | let v2 = increment(&mut c); |
|
| 57 | 67 | assert v2 == 1; |
|
| 58 | 68 | return 0; |
|
| 59 | 69 | } |
test/tests/trait.multiple.types.rad
+18 -17
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test multiple types implementing the same trait, and immutable receivers. |
|
| 3 | 3 | //! |
|
| 4 | 4 | //! Exercises: two different concrete types implementing the same trait, |
|
| 5 | - | //! coerced to the same trait object type. Also tests immutable `*Trait` |
|
| 5 | + | //! coerced to the same trait object type. Also tests immutable `&Trait` |
|
| 6 | 6 | //! receivers. Verifies each type dispatches through its own vtable. |
|
| 7 | 7 | ||
| 8 | 8 | record Dog { |
|
| 9 | 9 | age: i32, |
|
| 10 | 10 | } |
| 12 | 12 | record Cat { |
|
| 13 | 13 | lives: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | trait Speaker { |
|
| 17 | - | fn (*Speaker) speak() -> i32; |
|
| 18 | - | fn (*Speaker) isOld() -> bool; |
|
| 17 | + | fn (&Speaker) speak() -> i32; |
|
| 18 | + | fn (&Speaker) isOld() -> bool; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | instance Speaker for Dog { |
|
| 22 | - | fn (d: *Dog) speak() -> i32 { |
|
| 22 | + | fn (d: &Dog) speak() -> i32 { |
|
| 23 | 23 | return d.age; |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | - | fn (d: *Dog) isOld() -> bool { |
|
| 26 | + | fn (d: &Dog) isOld() -> bool { |
|
| 27 | 27 | return d.age > 10; |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | instance Speaker for Cat { |
|
| 32 | - | fn (c: *Cat) speak() -> i32 { |
|
| 32 | + | fn (c: &Cat) speak() -> i32 { |
|
| 33 | 33 | return c.lives * 10; |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | - | fn (c: *Cat) isOld() -> bool { |
|
| 36 | + | fn (c: &Cat) isOld() -> bool { |
|
| 37 | 37 | return c.lives < 5; |
|
| 38 | 38 | } |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | + | fn checkYoungSpeaker(s: &opaque Speaker, expected: i32) -> bool { |
|
| 42 | + | return s.speak() == expected and not s.isOld(); |
|
| 43 | + | } |
|
| 44 | + | ||
| 45 | + | fn checkOldSpeaker(s: &opaque Speaker) -> bool { |
|
| 46 | + | return s.isOld(); |
|
| 47 | + | } |
|
| 48 | + | ||
| 41 | 49 | @default fn main() -> i32 { |
|
| 42 | 50 | let d = Dog { age: 5 }; |
|
| 43 | 51 | let c = Cat { lives: 9 }; |
|
| 44 | 52 | ||
| 45 | 53 | // Immutable trait object from Dog. |
|
| 46 | - | let sd: *opaque Speaker = &d; |
|
| 47 | - | let v1 = sd.speak(); |
|
| 48 | - | assert v1 == 5; |
|
| 49 | - | if sd.isOld() { |
|
| 54 | + | if not checkYoungSpeaker(&d, 5) { |
|
| 50 | 55 | return 2; |
|
| 51 | 56 | } |
|
| 52 | 57 | ||
| 53 | 58 | // Immutable trait object from Cat. |
|
| 54 | - | let sc: *opaque Speaker = &c; |
|
| 55 | - | let v2 = sc.speak(); |
|
| 56 | - | assert v2 == 90; |
|
| 57 | - | if sc.isOld() { |
|
| 59 | + | if not checkYoungSpeaker(&c, 90) { |
|
| 58 | 60 | return 4; |
|
| 59 | 61 | } |
|
| 60 | 62 | ||
| 61 | 63 | // Old dog. |
|
| 62 | 64 | let oldDog = Dog { age: 15 }; |
|
| 63 | - | let so: *opaque Speaker = &oldDog; |
|
| 64 | - | assert so.isOld(); |
|
| 65 | + | assert checkOldSpeaker(&oldDog); |
|
| 65 | 66 | return 0; |
|
| 66 | 67 | } |
test/tests/trait.object.rad
+7 -4
| 1 | 1 | record Counter { |
|
| 2 | 2 | value: i32, |
|
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | trait Adder { |
|
| 6 | - | fn (*mut Adder) add(n: i32) -> i32; |
|
| 6 | + | fn (&mut Adder) add(n: i32) -> i32; |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | instance Adder for Counter { |
|
| 10 | - | fn (c: *mut Counter) add(n: i32) -> i32 { |
|
| 10 | + | fn (c: &mut Counter) add(n: i32) -> i32 { |
|
| 11 | 11 | set c.value = c.value + n; |
|
| 12 | 12 | return c.value; |
|
| 13 | 13 | } |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | + | fn addOne(a: &mut opaque Adder) -> i32 { |
|
| 17 | + | return a.add(1); |
|
| 18 | + | } |
|
| 19 | + | ||
| 16 | 20 | fn use_adder() -> i32 { |
|
| 17 | 21 | let mut c = Counter { value: 0 }; |
|
| 18 | - | let a: *mut opaque Adder = &mut c; |
|
| 19 | - | return a.add(1); |
|
| 22 | + | return addOne(&mut c); |
|
| 20 | 23 | } |
test/tests/trait.object.ril
+11 -5
| 9 | 9 | store w32 %3 %0 0; |
|
| 10 | 10 | sload w32 %4 %0 0; |
|
| 11 | 11 | ret %4; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | + | fn w32 $addOne(w64 %0) { |
|
| 15 | + | @entry0 |
|
| 16 | + | load w64 %1 %0 0; |
|
| 17 | + | load w64 %2 %0 8; |
|
| 18 | + | load w64 %3 %2 0; |
|
| 19 | + | call w32 %4 %3(%1, 1); |
|
| 20 | + | ret %4; |
|
| 21 | + | } |
|
| 22 | + | ||
| 14 | 23 | fn w32 $use_adder() { |
|
| 15 | 24 | @entry0 |
|
| 16 | 25 | reserve %0 4 4; |
|
| 17 | 26 | store w32 0 %0 0; |
|
| 18 | 27 | reserve %1 16 8; |
|
| 19 | 28 | store w64 %0 %1 0; |
|
| 20 | 29 | store w64 $"vtable::Counter::Adder" %1 8; |
|
| 21 | - | load w64 %2 %1 0; |
|
| 22 | - | load w64 %3 %1 8; |
|
| 23 | - | load w64 %4 %3 0; |
|
| 24 | - | call w32 %5 %4(%2, 1); |
|
| 25 | - | ret %5; |
|
| 30 | + | call w32 %2 $addOne(%1); |
|
| 31 | + | ret %2; |
|
| 26 | 32 | } |
test/tests/trait.supertrait.forward.rad
+10 -7
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! A supertrait declared later must contribute methods to its child trait. |
|
| 3 | 3 | ||
| 4 | 4 | trait Child: Parent { |
|
| 5 | - | fn (*Child) child() -> i32; |
|
| 5 | + | fn (&Child) child() -> i32; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | trait Parent { |
|
| 9 | - | fn (*Parent) parent() -> i32; |
|
| 9 | + | fn (&Parent) parent() -> i32; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | record Value { |
|
| 13 | 13 | n: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | instance Parent for Value { |
|
| 17 | - | fn (self: *Value) parent() -> i32 { |
|
| 17 | + | fn (self: &Value) parent() -> i32 { |
|
| 18 | 18 | return self.n; |
|
| 19 | 19 | } |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | instance Child for Value { |
|
| 23 | - | fn (self: *Value) child() -> i32 { |
|
| 23 | + | fn (self: &Value) child() -> i32 { |
|
| 24 | 24 | return self.n + 1; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | @default fn main() -> i32 { |
|
| 29 | - | let value = Value { n: 41 }; |
|
| 30 | - | let object: *opaque Child = &value; |
|
| 28 | + | fn checkChild(object: &opaque Child) { |
|
| 31 | 29 | assert object.parent() == 41; |
|
| 32 | 30 | assert object.child() == 42; |
|
| 31 | + | } |
|
| 32 | + | ||
| 33 | + | @default fn main() -> i32 { |
|
| 34 | + | let value = Value { n: 41 }; |
|
| 35 | + | checkChild(&value); |
|
| 33 | 36 | return 0; |
|
| 34 | 37 | } |
test/tests/trait.supertrait.rad
+24 -13
| 2 | 2 | //! Test supertrait bounds: a trait that requires its implementors to also |
|
| 3 | 3 | //! implement another trait. |
|
| 4 | 4 | //! |
|
| 5 | 5 | //! Exercises: trait Reader with a `read` method, trait Writer with a `write` |
|
| 6 | 6 | //! method, and trait ReadWriter: Reader + Writer that combines both. |
|
| 7 | - | //! A Socket type implements all three. Coercing a `*opaque ReadWriter` to |
|
| 8 | - | //! `*opaque Reader` or `*opaque Writer` should work, and dispatching through |
|
| 7 | + | //! A Socket type implements all three. Coercing an `&mut opaque ReadWriter` to |
|
| 8 | + | //! `&mut opaque Reader` or `&mut opaque Writer` should work, and dispatching through |
|
| 9 | 9 | //! any of the three trait objects should call the correct methods. |
|
| 10 | 10 | ||
| 11 | 11 | trait Reader { |
|
| 12 | - | fn (*mut Reader) read(buf: *mut [u8]) -> i32; |
|
| 12 | + | fn (&mut Reader) read(buf: &mut [u8]) -> i32; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | trait Writer { |
|
| 16 | - | fn (*mut Writer) write(data: *[u8]) -> i32; |
|
| 16 | + | fn (&mut Writer) write(data: &[u8]) -> i32; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | trait ReadWriter: Reader + Writer { |
|
| 20 | - | fn (*mut ReadWriter) flush() -> i32; |
|
| 20 | + | fn (&mut ReadWriter) flush() -> i32; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | record Socket { |
|
| 24 | 24 | rbuf: [u8; 32], |
|
| 25 | 25 | rpos: i32, |
| 27 | 27 | wbuf: [u8; 32], |
|
| 28 | 28 | wpos: i32, |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | instance Reader for Socket { |
|
| 32 | - | fn (s: *mut Socket) read(buf: *mut [u8]) -> i32 { |
|
| 32 | + | fn (s: &mut Socket) read(buf: &mut [u8]) -> i32 { |
|
| 33 | 33 | let mut i: u32 = 0; |
|
| 34 | 34 | while i < buf.len and s.rpos < s.rlen { |
|
| 35 | 35 | set buf[i] = s.rbuf[s.rpos as u32]; |
|
| 36 | 36 | set s.rpos = s.rpos + 1; |
|
| 37 | 37 | set i = i + 1; |
| 39 | 39 | return i as i32; |
|
| 40 | 40 | } |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | 43 | instance Writer for Socket { |
|
| 44 | - | fn (s: *mut Socket) write(data: *[u8]) -> i32 { |
|
| 44 | + | fn (s: &mut Socket) write(data: &[u8]) -> i32 { |
|
| 45 | 45 | let mut i: u32 = 0; |
|
| 46 | 46 | while i < data.len { |
|
| 47 | 47 | if s.wpos >= 32 { |
|
| 48 | 48 | return s.wpos; |
|
| 49 | 49 | } |
| 54 | 54 | return s.wpos; |
|
| 55 | 55 | } |
|
| 56 | 56 | } |
|
| 57 | 57 | ||
| 58 | 58 | instance ReadWriter for Socket { |
|
| 59 | - | fn (s: *mut Socket) flush() -> i32 { |
|
| 59 | + | fn (s: &mut Socket) flush() -> i32 { |
|
| 60 | 60 | let pos = s.wpos; |
|
| 61 | 61 | set s.wpos = 0; |
|
| 62 | 62 | return pos; |
|
| 63 | 63 | } |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | - | @default fn main() -> i32 { |
|
| 66 | + | fn writeThrough(rw: &mut opaque ReadWriter, data: &[u8]) -> i32 { |
|
| 67 | + | return rw.write(data); |
|
| 68 | + | } |
|
| 69 | + | ||
| 70 | + | fn readThrough(rw: &mut opaque ReadWriter, buf: &mut [u8]) -> i32 { |
|
| 71 | + | return rw.read(buf); |
|
| 72 | + | } |
|
| 73 | + | ||
| 74 | + | fn flushThrough(rw: &mut opaque ReadWriter) -> i32 { |
|
| 75 | + | return rw.flush(); |
|
| 76 | + | } |
|
| 77 | + | ||
| 78 | + | @default unsafe fn main() -> i32 { |
|
| 67 | 79 | let mut sock = Socket { |
|
| 68 | 80 | rbuf: undefined, |
|
| 69 | 81 | rpos: 0, |
|
| 70 | 82 | rlen: 5, |
|
| 71 | 83 | wbuf: undefined, |
| 77 | 89 | set sock.rbuf[2] = 'l' as u8; |
|
| 78 | 90 | set sock.rbuf[3] = 'l' as u8; |
|
| 79 | 91 | set sock.rbuf[4] = 'o' as u8; |
|
| 80 | 92 | ||
| 81 | 93 | // Test 1: Use as ReadWriter trait object. |
|
| 82 | - | let rw: *mut opaque ReadWriter = &mut sock; |
|
| 83 | 94 | ||
| 84 | 95 | // Test 2: Write through the ReadWriter (dispatches via Writer supertrait). |
|
| 85 | - | let wn = rw.write("abc"); |
|
| 96 | + | let wn = writeThrough(&mut sock, &"abc"[..]); |
|
| 86 | 97 | assert wn == 3; |
|
| 87 | 98 | assert sock.wpos == 3; |
|
| 88 | 99 | ||
| 89 | 100 | // Test 3: Read through the ReadWriter (dispatches via Reader supertrait). |
|
| 90 | 101 | let mut rbuf: [u8; 8] = undefined; |
|
| 91 | - | let rn = rw.read(&mut rbuf[0..8]); |
|
| 102 | + | let rn = readThrough(&mut sock, &mut rbuf[..]); |
|
| 92 | 103 | assert rn == 5; |
|
| 93 | 104 | assert rbuf[0] == 'h' as u8; |
|
| 94 | 105 | assert rbuf[4] == 'o' as u8; |
|
| 95 | 106 | ||
| 96 | 107 | // Test 4: Call ReadWriter's own method. |
|
| 97 | - | let flushed = rw.flush(); |
|
| 108 | + | let flushed = flushThrough(&mut sock); |
|
| 98 | 109 | assert flushed == 3; |
|
| 99 | 110 | assert sock.wpos == 0; |
|
| 100 | 111 | return 0; |
|
| 101 | 112 | } |
test/tests/trait.supertrait.ril
+59 -29
| 104 | 104 | sload w32 %1 %0 72; |
|
| 105 | 105 | store w32 0 %0 72; |
|
| 106 | 106 | ret %1; |
|
| 107 | 107 | } |
|
| 108 | 108 | ||
| 109 | + | fn w32 $writeThrough(w64 %0, w64 %1) { |
|
| 110 | + | @entry0 |
|
| 111 | + | load w64 %2 %0 0; |
|
| 112 | + | load w64 %3 %0 8; |
|
| 113 | + | load w64 %4 %3 8; |
|
| 114 | + | call w32 %5 %4(%2, %1); |
|
| 115 | + | ret %5; |
|
| 116 | + | } |
|
| 117 | + | ||
| 118 | + | fn w32 $readThrough(w64 %0, w64 %1) { |
|
| 119 | + | @entry0 |
|
| 120 | + | load w64 %2 %0 0; |
|
| 121 | + | load w64 %3 %0 8; |
|
| 122 | + | load w64 %4 %3 0; |
|
| 123 | + | call w32 %5 %4(%2, %1); |
|
| 124 | + | ret %5; |
|
| 125 | + | } |
|
| 126 | + | ||
| 127 | + | fn w32 $flushThrough(w64 %0) { |
|
| 128 | + | @entry0 |
|
| 129 | + | load w64 %1 %0 0; |
|
| 130 | + | load w64 %2 %0 8; |
|
| 131 | + | load w64 %3 %2 16; |
|
| 132 | + | call w32 %4 %3(%1); |
|
| 133 | + | ret %4; |
|
| 134 | + | } |
|
| 135 | + | ||
| 109 | 136 | fn w32 $main() { |
|
| 110 | 137 | @entry0 |
|
| 111 | 138 | reserve %0 76 4; |
|
| 112 | 139 | store w32 0 %0 32; |
|
| 113 | 140 | store w32 5 %0 36; |
| 122 | 149 | add w64 %4 %0 4; |
|
| 123 | 150 | store w8 111 %4 0; |
|
| 124 | 151 | reserve %5 16 8; |
|
| 125 | 152 | store w64 %0 %5 0; |
|
| 126 | 153 | store w64 $"vtable::Socket::ReadWriter" %5 8; |
|
| 127 | - | load w64 %6 %5 0; |
|
| 128 | - | load w64 %7 %5 8; |
|
| 129 | - | load w64 %8 %7 8; |
|
| 130 | - | copy %9 $main$literal$0; |
|
| 154 | + | copy %6 $main$literal$0; |
|
| 155 | + | reserve %7 16 8; |
|
| 156 | + | store w64 %6 %7 0; |
|
| 157 | + | store w32 3 %7 8; |
|
| 158 | + | store w32 3 %7 12; |
|
| 159 | + | load w64 %8 %7 0; |
|
| 160 | + | load w32 %9 %7 8; |
|
| 131 | 161 | reserve %10 16 8; |
|
| 132 | - | store w64 %9 %10 0; |
|
| 133 | - | store w32 3 %10 8; |
|
| 134 | - | store w32 3 %10 12; |
|
| 135 | - | call w32 %11 %8(%6, %10); |
|
| 162 | + | store w64 %8 %10 0; |
|
| 163 | + | store w32 %9 %10 8; |
|
| 164 | + | store w32 %9 %10 12; |
|
| 165 | + | call w32 %11 $writeThrough(%5, %10); |
|
| 136 | 166 | br.eq w32 %11 3 @assert.ok2 @assert.fail1; |
|
| 137 | 167 | @assert.fail1 |
|
| 138 | 168 | unreachable; |
|
| 139 | 169 | @assert.ok2 |
|
| 140 | 170 | sload w32 %12 %0 72; |
|
| 141 | 171 | br.eq w32 %12 3 @assert.ok4 @assert.fail3; |
|
| 142 | 172 | @assert.fail3 |
|
| 143 | 173 | unreachable; |
|
| 144 | 174 | @assert.ok4 |
|
| 145 | 175 | reserve %13 8 1; |
|
| 146 | - | load w64 %14 %5 0; |
|
| 147 | - | load w64 %15 %5 8; |
|
| 148 | - | load w64 %16 %15 0; |
|
| 149 | - | reserve %17 16 8; |
|
| 150 | - | store w64 %13 %17 0; |
|
| 151 | - | store w32 8 %17 8; |
|
| 152 | - | store w32 8 %17 12; |
|
| 153 | - | call w32 %18 %16(%14, %17); |
|
| 154 | - | br.eq w32 %18 5 @assert.ok6 @assert.fail5; |
|
| 176 | + | reserve %14 16 8; |
|
| 177 | + | store w64 %0 %14 0; |
|
| 178 | + | store w64 $"vtable::Socket::ReadWriter" %14 8; |
|
| 179 | + | reserve %15 16 8; |
|
| 180 | + | store w64 %13 %15 0; |
|
| 181 | + | store w32 8 %15 8; |
|
| 182 | + | store w32 8 %15 12; |
|
| 183 | + | call w32 %16 $readThrough(%14, %15); |
|
| 184 | + | br.eq w32 %16 5 @assert.ok6 @assert.fail5; |
|
| 155 | 185 | @assert.fail5 |
|
| 156 | 186 | unreachable; |
|
| 157 | 187 | @assert.ok6 |
|
| 158 | - | load w8 %19 %13 0; |
|
| 159 | - | br.eq w8 %19 104 @assert.ok8 @assert.fail7; |
|
| 188 | + | load w8 %17 %13 0; |
|
| 189 | + | br.eq w8 %17 104 @assert.ok8 @assert.fail7; |
|
| 160 | 190 | @assert.fail7 |
|
| 161 | 191 | unreachable; |
|
| 162 | 192 | @assert.ok8 |
|
| 163 | - | add w64 %20 %13 4; |
|
| 164 | - | load w8 %21 %20 0; |
|
| 165 | - | br.eq w8 %21 111 @assert.ok10 @assert.fail9; |
|
| 193 | + | add w64 %18 %13 4; |
|
| 194 | + | load w8 %19 %18 0; |
|
| 195 | + | br.eq w8 %19 111 @assert.ok10 @assert.fail9; |
|
| 166 | 196 | @assert.fail9 |
|
| 167 | 197 | unreachable; |
|
| 168 | 198 | @assert.ok10 |
|
| 169 | - | load w64 %22 %5 0; |
|
| 170 | - | load w64 %23 %5 8; |
|
| 171 | - | load w64 %24 %23 16; |
|
| 172 | - | call w32 %25 %24(%22); |
|
| 173 | - | br.eq w32 %25 3 @assert.ok12 @assert.fail11; |
|
| 199 | + | reserve %20 16 8; |
|
| 200 | + | store w64 %0 %20 0; |
|
| 201 | + | store w64 $"vtable::Socket::ReadWriter" %20 8; |
|
| 202 | + | call w32 %21 $flushThrough(%20); |
|
| 203 | + | br.eq w32 %21 3 @assert.ok12 @assert.fail11; |
|
| 174 | 204 | @assert.fail11 |
|
| 175 | 205 | unreachable; |
|
| 176 | 206 | @assert.ok12 |
|
| 177 | - | sload w32 %26 %0 72; |
|
| 178 | - | br.eq w32 %26 0 @assert.ok14 @assert.fail13; |
|
| 207 | + | sload w32 %22 %0 72; |
|
| 208 | + | br.eq w32 %22 0 @assert.ok14 @assert.fail13; |
|
| 179 | 209 | @assert.fail13 |
|
| 180 | 210 | unreachable; |
|
| 181 | 211 | @assert.ok14 |
|
| 182 | 212 | ret 0; |
|
| 183 | 213 | } |
test/tests/trait.throws.rad
+3 -3
| 9 | 9 | record StrictParser { |
|
| 10 | 10 | limit: i32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | trait Parser { |
|
| 14 | - | fn (*Parser) parse(n: i32) -> i32 throws (ParseError); |
|
| 14 | + | unsafe fn (*Parser) parse(n: i32) -> i32 throws (ParseError); |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | instance Parser for StrictParser { |
|
| 18 | - | fn (p: *StrictParser) parse(n: i32) -> i32 throws (ParseError) { |
|
| 18 | + | unsafe fn (p: *StrictParser) parse(n: i32) -> i32 throws (ParseError) { |
|
| 19 | 19 | if n < 0 { |
|
| 20 | 20 | throw ParseError::InvalidInput; |
|
| 21 | 21 | } |
|
| 22 | 22 | if n > p.limit { |
|
| 23 | 23 | throw ParseError::Overflow; |
|
| 24 | 24 | } |
|
| 25 | 25 | return n * 2; |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | - | @default fn main() -> i32 { |
|
| 29 | + | @default unsafe fn main() -> i32 { |
|
| 30 | 30 | let sp = StrictParser { limit: 100 }; |
|
| 31 | 31 | let p: *opaque Parser = &sp; |
|
| 32 | 32 | ||
| 33 | 33 | // Success path. |
|
| 34 | 34 | let r1 = try p.parse(5) catch { |
test/tests/trait.writer.rad
+8 -8
| 5 | 5 | //! A BufferWriter writes to an in-memory buffer; a CountingWriter wraps any |
|
| 6 | 6 | //! Writer and tracks how many bytes flow through it. This tests trait objects |
|
| 7 | 7 | //! as struct fields, dispatch chains, and a real-world composition pattern. |
|
| 8 | 8 | ||
| 9 | 9 | trait Writer { |
|
| 10 | - | fn (*mut Writer) write(data: *[u8]) -> i32; |
|
| 11 | - | fn (*Writer) total() -> i32; |
|
| 10 | + | unsafe fn (*mut Writer) write(data: *[u8]) -> i32; |
|
| 11 | + | unsafe fn (*Writer) total() -> i32; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Writes bytes into a fixed-size buffer. |
|
| 15 | 15 | record BufferWriter { |
|
| 16 | 16 | buf: [u8; 64], |
|
| 17 | 17 | pos: i32, |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | instance Writer for BufferWriter { |
|
| 21 | - | fn (w: *mut BufferWriter) write(data: *[u8]) -> i32 { |
|
| 21 | + | unsafe fn (w: *mut BufferWriter) write(data: *[u8]) -> i32 { |
|
| 22 | 22 | let mut i: u32 = 0; |
|
| 23 | 23 | while i < data.len { |
|
| 24 | 24 | if w.pos >= 64 { |
|
| 25 | 25 | return w.pos; |
|
| 26 | 26 | } |
| 29 | 29 | set i = i + 1; |
|
| 30 | 30 | } |
|
| 31 | 31 | return w.pos; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | - | fn (w: *BufferWriter) total() -> i32 { |
|
| 34 | + | unsafe fn (w: *BufferWriter) total() -> i32 { |
|
| 35 | 35 | return w.pos; |
|
| 36 | 36 | } |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Counts bytes written through it, forwarding to an inner writer. |
| 41 | 41 | inner: *mut opaque Writer, |
|
| 42 | 42 | count: i32, |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | 45 | instance Writer for CountingWriter { |
|
| 46 | - | fn (w: *mut CountingWriter) write(data: *[u8]) -> i32 { |
|
| 46 | + | unsafe fn (w: *mut CountingWriter) write(data: *[u8]) -> i32 { |
|
| 47 | 47 | set w.count = w.count + data.len as i32; |
|
| 48 | 48 | return w.inner.write(data); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | - | fn (w: *CountingWriter) total() -> i32 { |
|
| 51 | + | unsafe fn (w: *CountingWriter) total() -> i32 { |
|
| 52 | 52 | return w.count; |
|
| 53 | 53 | } |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Write a slice through any Writer. |
|
| 57 | - | fn emit(w: *mut opaque Writer, data: *[u8]) -> i32 { |
|
| 57 | + | unsafe fn emit(w: *mut opaque Writer, data: *[u8]) -> i32 { |
|
| 58 | 58 | return w.write(data); |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | - | @default fn main() -> i32 { |
|
| 61 | + | @default unsafe fn main() -> i32 { |
|
| 62 | 62 | // Direct BufferWriter usage through trait. |
|
| 63 | 63 | let mut buf = BufferWriter { buf: undefined, pos: 0 }; |
|
| 64 | 64 | let w: *mut opaque Writer = &mut buf; |
|
| 65 | 65 | emit(w, "hello"); |
|
| 66 | 66 | assert w.total() == 5; |
test/tests/type.unify.rad
+6 -6
| 45 | 45 | ||
| 46 | 46 | return true; |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Verifies assignment compatibility for pointers with identical target types. |
|
| 50 | - | fn testPointerUnification() -> bool { |
|
| 50 | + | unsafe fn testPointerUnification() -> bool { |
|
| 51 | 51 | let x: i32 = 42; |
|
| 52 | 52 | let y: i32 = 24; |
|
| 53 | 53 | let ptr1: *i32 = &x; |
|
| 54 | 54 | let ptr2: *i32 = &y; |
|
| 55 | 55 | let mut ptrResult: *i32 = ptr1; |
| 57 | 57 | ||
| 58 | 58 | return true; |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Verifies conversion from array references to slices. |
|
| 62 | - | fn testArrayToSlice() -> bool { |
|
| 62 | + | unsafe fn testArrayToSlice() -> bool { |
|
| 63 | 63 | let arr: [i32; 3] = [1, 2, 3]; |
|
| 64 | 64 | let slice: *[i32] = &arr[..]; |
|
| 65 | 65 | ||
| 66 | 66 | return true; |
|
| 67 | 67 | } |
| 97 | 97 | ||
| 98 | 98 | return true; |
|
| 99 | 99 | } |
|
| 100 | 100 | ||
| 101 | 101 | /// Verifies coercion from pointer values to optional pointer values. |
|
| 102 | - | fn testPointerToOptional() -> bool { |
|
| 102 | + | unsafe fn testPointerToOptional() -> bool { |
|
| 103 | 103 | let value: i32 = 42; |
|
| 104 | 104 | let ptr: *i32 = &value; |
|
| 105 | 105 | let optPtr: ?*i32 = ptr; |
|
| 106 | 106 | ||
| 107 | 107 | return true; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | 110 | /// Verifies creation of slices for different, but internally consistent, element types. |
|
| 111 | - | fn testSliceElementUnification() -> bool { |
|
| 111 | + | unsafe fn testSliceElementUnification() -> bool { |
|
| 112 | 112 | let arrSmall: [i8; 3] = [1, 2, 3]; |
|
| 113 | 113 | let sliceSmall: *[i8] = &arrSmall[..]; |
|
| 114 | 114 | ||
| 115 | 115 | let arrLarge: [i32; 3] = [10, 20, 30]; |
|
| 116 | 116 | let sliceLarge: *[i32] = &arrLarge[..]; |
| 136 | 136 | ||
| 137 | 137 | return true; |
|
| 138 | 138 | } |
|
| 139 | 139 | ||
| 140 | 140 | /// Verifies repeated pointer assignments across multiple values of the same type. |
|
| 141 | - | fn testMultiplePointerAssignments() -> bool { |
|
| 141 | + | unsafe fn testMultiplePointerAssignments() -> bool { |
|
| 142 | 142 | let value1: i32 = 42; |
|
| 143 | 143 | let value2: i32 = 24; |
|
| 144 | 144 | let value3: i32 = 100; |
|
| 145 | 145 | ||
| 146 | 146 | let ptr1: *i32 = &value1; |
| 162 | 162 | set result = b2; |
|
| 163 | 163 | ||
| 164 | 164 | return true; |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | - | @default fn main() -> i32 { |
|
| 167 | + | @default unsafe fn main() -> i32 { |
|
| 168 | 168 | let testResult: bool = |
|
| 169 | 169 | testNumericUnification() and |
|
| 170 | 170 | testOptionalUnification() and |
|
| 171 | 171 | testArrayUnification() and |
|
| 172 | 172 | testPointerUnification() and |
test/tests/undefined.aggregate.rad
+1 -1
| 1 | 1 | /// Test lowering of undefined for aggregate types. |
|
| 2 | 2 | record Point { x: i32, y: i32 } |
|
| 3 | 3 | ||
| 4 | - | fn test() -> i32 { |
|
| 4 | + | unsafe fn test() -> i32 { |
|
| 5 | 5 | let p: Point = undefined; |
|
| 6 | 6 | return p.x; |
|
| 7 | 7 | } |
test/tests/undefined.primitive.rad
+1 -1
| 1 | 1 | /// Test lowering of undefined for primitive types. |
|
| 2 | - | fn test() -> i32 { |
|
| 2 | + | unsafe fn test() -> i32 { |
|
| 3 | 3 | let x: i32 = undefined; |
|
| 4 | 4 | return x; |
|
| 5 | 5 | } |
test/tests/undefined.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test undefined values for arrays and assignment. |
|
| 3 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 4 | let mut ary: [u16; 32] = undefined; |
|
| 5 | 5 | let x: u32 = 8; |
|
| 6 | 6 | let y: u32 = 9; |
|
| 7 | 7 | ||
| 8 | 8 | set ary[0] = 1; |
test/tests/undefined.record.field.rad
+3 -3
| 20 | 20 | len: i32, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// Construct a record with one field `undefined` (named-field syntax). |
|
| 24 | 24 | /// The IL must not contain a `blit` or `store` for `x`. |
|
| 25 | - | fn partialInit() -> i32 { |
|
| 25 | + | unsafe fn partialInit() -> i32 { |
|
| 26 | 26 | let s = Small { x: undefined, y: 42 }; |
|
| 27 | 27 | return s.y; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Construct a record with an array field `undefined` (named-field syntax). |
|
| 31 | 31 | /// The IL must not contain a `blit` for `data`. |
|
| 32 | - | fn arrayFieldUndef() -> i32 { |
|
| 32 | + | unsafe fn arrayFieldUndef() -> i32 { |
|
| 33 | 33 | let w = WithArray { data: undefined, len: 3 }; |
|
| 34 | 34 | return w.len; |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Both fields defined - normal case for comparison. |
| 45 | 45 | B, |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | 48 | /// Union variant with record payload containing an `undefined` field. |
|
| 49 | 49 | /// This exercises `lowerRecordCtor` (positional constructor path). |
|
| 50 | - | fn unionPayloadUndef() -> i32 { |
|
| 50 | + | unsafe fn unionPayloadUndef() -> i32 { |
|
| 51 | 51 | let t = Tagged::A { data: undefined, tag: 99 }; |
|
| 52 | 52 | match t { |
|
| 53 | 53 | case Tagged::A { tag, .. } => return tag, |
|
| 54 | 54 | else => return -1, |
|
| 55 | 55 | } |
test/tests/union-tag.rad
+2 -2
| 23 | 23 | V18, |
|
| 24 | 24 | V19, |
|
| 25 | 25 | V20, |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn tag(u: *BigUnion) -> u8 { |
|
| 28 | + | unsafe fn tag(u: *BigUnion) -> u8 { |
|
| 29 | 29 | let p = u as *opaque as *u8; |
|
| 30 | 30 | return *p; |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | - | @default fn main() -> i32 { |
|
| 33 | + | @default unsafe fn main() -> i32 { |
|
| 34 | 34 | let v0 = BigUnion::V0; |
|
| 35 | 35 | assert tag(&v0) == 0; |
|
| 36 | 36 | let v7 = BigUnion::V7; |
|
| 37 | 37 | assert tag(&v7) == 7; |
|
| 38 | 38 | let v13 = BigUnion::V13; |
test/tests/union.edge.case.3.rad
+3 -3
| 9 | 9 | ||
| 10 | 10 | record Parser { |
|
| 11 | 11 | root: Node, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn node(p: *mut Parser, value: Node) -> *Node { |
|
| 14 | + | unsafe fn node(p: *mut Parser, value: Node) -> *Node { |
|
| 15 | 15 | set p.root = value; |
|
| 16 | 16 | return &p.root; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | fn nodeBool(p: *mut Parser, value: bool) -> *Node { |
|
| 19 | + | unsafe fn nodeBool(p: *mut Parser, value: bool) -> *Node { |
|
| 20 | 20 | return node(p, Node::Bool(value)); |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | @default fn main() -> u32 { |
|
| 23 | + | @default unsafe fn main() -> u32 { |
|
| 24 | 24 | let mut parser = Parser { root: undefined }; |
|
| 25 | 25 | ||
| 26 | 26 | match *nodeBool(&mut parser, true) { |
|
| 27 | 27 | case Node::Bool(v) => { |
|
| 28 | 28 | assert v == true; |
test/tests/union.match.ref.rad
+5 -5
| 3 | 3 | ||
| 4 | 4 | /// Non-void union for testing ref matching. |
|
| 5 | 5 | union Option { None, Some(u32) } |
|
| 6 | 6 | ||
| 7 | 7 | /// Match on a void union reference. |
|
| 8 | - | fn matchColorRef(ptr: *Color) -> u32 { |
|
| 8 | + | fn matchColorRef(ptr: &Color) -> u32 { |
|
| 9 | 9 | match ptr { |
|
| 10 | 10 | case Color::Red => return 0, |
|
| 11 | 11 | case Color::Green => return 1, |
|
| 12 | 12 | case Color::Blue => return 2, |
|
| 13 | 13 | } |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | /// Match on a non-void union reference. Bindings become pointers. |
|
| 17 | - | fn matchRef(ptr: *Option) -> u32 { |
|
| 17 | + | fn matchRef(ptr: &Option) -> u32 { |
|
| 18 | 18 | match ptr { |
|
| 19 | 19 | case Option::None => return 0, |
|
| 20 | 20 | case Option::Some(val) => return *val, |
|
| 21 | 21 | } |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | /// If-let on a union reference. |
|
| 25 | - | fn ifLetRef(ptr: *Option) -> u32 { |
|
| 25 | + | fn ifLetRef(ptr: &Option) -> u32 { |
|
| 26 | 26 | if let case Option::Some(val) = ptr { |
|
| 27 | 27 | return *val; |
|
| 28 | 28 | } |
|
| 29 | 29 | return 0; |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | /// While-let on a mutable union reference. |
|
| 33 | - | fn whileLetRef(ptr: *mut Option) -> u32 { |
|
| 33 | + | fn whileLetRef(ptr: &mut Option) -> u32 { |
|
| 34 | 34 | let mut sum: u32 = 0; |
|
| 35 | 35 | while let case Option::Some(val) = ptr { |
|
| 36 | 36 | set sum += *val; |
|
| 37 | 37 | set *ptr = Option::None; |
|
| 38 | 38 | } |
|
| 39 | 39 | return sum; |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// Match on an optional reference with binding pattern. |
|
| 43 | - | fn optionalRef(ptr: ?*u32) -> u32 { |
|
| 43 | + | unsafe fn optionalRef(ptr: ?*u32) -> u32 { |
|
| 44 | 44 | if let x = ptr { |
|
| 45 | 45 | return *x; |
|
| 46 | 46 | } |
|
| 47 | 47 | return 0; |
|
| 48 | 48 | } |
test/tests/union.mixed.assign.rad
+2 -2
| 10 | 10 | ||
| 11 | 11 | record Holder { |
|
| 12 | 12 | value: Mixed, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn storePayload(holder: *mut Holder, value: i32) { |
|
| 15 | + | fn storePayload(holder: &mut Holder, value: i32) { |
|
| 16 | 16 | set holder.value = Mixed::Payload(value); |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | fn storeFinal(holder: *mut Holder) { |
|
| 19 | + | fn storeFinal(holder: &mut Holder) { |
|
| 20 | 20 | set holder.value = Mixed::Final; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | fn checkIfLet(value: Mixed) -> i32 { |
|
| 24 | 24 | if let case Mixed::Payload(v) = value { |
test/tests/union.payload.mutref.rad
+2 -2
| 17 | 17 | padding1: i32, |
|
| 18 | 18 | padding2: i32, |
|
| 19 | 19 | state: Sealed, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | fn addItemViaMatchRef(blk: *mut Block, val: u32) -> bool { |
|
| 22 | + | unsafe fn addItemViaMatchRef(blk: &mut Block, val: u32) -> bool { |
|
| 23 | 23 | match &mut blk.state { |
|
| 24 | 24 | case Sealed::No { items } => { |
|
| 25 | 25 | set items.data[items.len] = val; |
|
| 26 | 26 | set items.len += 1; |
|
| 27 | 27 | return true; |
| 30 | 30 | return false; |
|
| 31 | 31 | }, |
|
| 32 | 32 | } |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | - | @default fn main() -> i32 { |
|
| 35 | + | @default unsafe fn main() -> i32 { |
|
| 36 | 36 | let mut buf: [u32; 8] = undefined; |
|
| 37 | 37 | let mut blk = Block { |
|
| 38 | 38 | padding1: 0, |
|
| 39 | 39 | padding2: 0, |
|
| 40 | 40 | state: Sealed::No { items: U32List { data: &mut buf[0..8], len: 0 } }, |