Restrict safe pointers to permanent storage
a9d3b2cff3482048ab6e8780e099708d68c5732f2435654c8485e6b571a13d65
Use borrows for call-scoped stack access and explicit unsafe pointers for retained storage. Migrate compiler, library, and test callers to the storage rules. Scope unsafe operations to individual functions and preserve call requirements in unsafe function types. Refresh the compiler seed and bootstrap inputs. Assisted-by: Codex:gpt-6-astra
1 parent
55e5d92e
Makefile
+3 -2
| 36 | 36 | endif |
|
| 37 | 37 | ||
| 38 | 38 | # Compiler build |
|
| 39 | 39 | ||
| 40 | 40 | SEED := seed/radiance.rv64 |
|
| 41 | - | SEED_OPTS := $(STD) -pkg radiance -mod compiler/radiance.rad -entry radiance |
|
| 41 | + | COMPILER_SRC := compiler/radiance.rad compiler/radiance/codegen.rad |
|
| 42 | + | SEED_OPTS := $(STD) -pkg radiance $(patsubst %,-mod %,$(COMPILER_SRC)) -entry radiance |
|
| 42 | 43 | ||
| 43 | - | $(RAD_BIN): $(STD_LIB) compiler/radiance.rad | $(BIN_DIR) |
|
| 44 | + | $(RAD_BIN): $(STD_LIB) $(COMPILER_SRC) | $(BIN_DIR) |
|
| 44 | 45 | @echo "radiance $(SEED) => $@" |
|
| 45 | 46 | @$(EMU) $(EMU_FLAGS) -run $(SEED) $(SEED_OPTS) -o $@ |
|
| 46 | 47 | ||
| 47 | 48 | $(BIN_DIR): |
|
| 48 | 49 | @mkdir -p $@ |
compiler/radiance.rad
+126 -140
| 1 | 1 | //! Radiance compiler front-end. |
|
| 2 | + | mod codegen; |
|
| 3 | + | ||
| 2 | 4 | use std::mem; |
|
| 3 | 5 | use std::fmt; |
|
| 4 | 6 | use std::io; |
|
| 5 | 7 | use std::lang::alloc; |
|
| 6 | 8 | use std::lang::ast; |
| 90 | 92 | static ASM_RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = undefined; |
|
| 91 | 93 | ||
| 92 | 94 | /// Assembly source file extension. |
|
| 93 | 95 | constant ASM_SOURCE_EXT: *[u8] = ".ras"; |
|
| 94 | 96 | /// Symbol name exported for startup code to call the semantic entry function. |
|
| 95 | - | constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default"; |
|
| 97 | + | export constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default"; |
|
| 96 | 98 | ||
| 97 | 99 | /// Usage string. |
|
| 98 | 100 | constant USAGE: *[u8] = |
|
| 99 | 101 | "usage: radiance -pkg <name> [-start <input.ras>] -mod <input>.. [-pkg <name> -mod <input>..] -entry <pkg> -o <output>\n"; |
|
| 100 | 102 |
| 167 | 169 | record RootModule: Copy { |
|
| 168 | 170 | entry: *module::ModuleEntry, |
|
| 169 | 171 | ast: *mut ast::Node, |
|
| 170 | 172 | } |
|
| 171 | 173 | ||
| 172 | - | /// State carried by the streaming lowerer/codegen callback. |
|
| 173 | - | record CodegenSinkContext: Copy { |
|
| 174 | - | /// RV64 generator receiving lowered functions. |
|
| 175 | - | generator: *mut rv64::Generator, |
|
| 176 | - | /// Arena holding the current function's lowered IL. |
|
| 177 | - | fnArena: *mut alloc::Arena, |
|
| 178 | - | } |
|
| 179 | - | ||
| 180 | 174 | /// Entry handling for streamed code generation. |
|
| 181 | 175 | union CodegenEntryMode: Copy { |
|
| 182 | 176 | /// Do not reserve an entry jump. |
|
| 183 | 177 | None, |
|
| 184 | 178 | /// Reserve and patch an entry jump to the `@default` function. |
| 194 | 188 | /// How the generated program should handle entry. |
|
| 195 | 189 | entryMode: CodegenEntryMode, |
|
| 196 | 190 | } |
|
| 197 | 191 | ||
| 198 | 192 | /// Print a driver error line. |
|
| 199 | - | fn error(msg: *[*[u8]]) -> Error { |
|
| 193 | + | fn error(msg: &[*[u8]]) -> Error { |
|
| 200 | 194 | io::printError("radiance: "); |
|
| 201 | 195 | ||
| 202 | 196 | for part, i in msg { |
|
| 203 | 197 | io::printError(part); |
|
| 204 | 198 | if i < msg.len - 1 { |
| 208 | 202 | io::printError("\n"); |
|
| 209 | 203 | return Error::Other; |
|
| 210 | 204 | } |
|
| 211 | 205 | ||
| 212 | 206 | /// Print a log line for the given package. |
|
| 213 | - | fn pkgLog(pkg: *package::Package, msg: *[*[u8]]) { |
|
| 207 | + | fn pkgLog(pkg: &package::Package, msg: &[*[u8]]) { |
|
| 214 | 208 | io::printError("radiance: "); |
|
| 215 | 209 | io::printError(pkg.name); |
|
| 216 | 210 | io::printError(": "); |
|
| 217 | 211 | ||
| 218 | 212 | for part, i in msg { |
| 244 | 238 | asmPathCount: 0, |
|
| 245 | 239 | }; |
|
| 246 | 240 | } |
|
| 247 | 241 | ||
| 248 | 242 | /// Register, load, and parse `path` within `pkg`. |
|
| 249 | - | fn processModule( |
|
| 250 | - | pkg: *mut package::Package, |
|
| 251 | - | graph: *mut module::ModuleGraph, |
|
| 243 | + | unsafe fn processModule( |
|
| 244 | + | pkg: *unsafe mut package::Package, |
|
| 245 | + | graph: &mut module::ModuleGraph, |
|
| 252 | 246 | path: *[u8], |
|
| 253 | - | nodeArena: *mut ast::NodeArena, |
|
| 254 | - | sourceArena: *mut alloc::Arena |
|
| 247 | + | nodeArena: &mut ast::NodeArena, |
|
| 248 | + | sourceArena: &mut alloc::Arena |
|
| 255 | 249 | ) throws (Error) { |
|
| 256 | - | pkgLog(pkg, &["parsing", "(", path, ")", ".."]); |
|
| 250 | + | pkgLog(&*pkg, &["parsing", "(", path, ")", ".."]); |
|
| 257 | 251 | ||
| 258 | - | let moduleId = try package::registerModule(pkg, graph, path) catch { |
|
| 252 | + | let moduleId = try package::registerModule(&mut *pkg, graph, path) catch { |
|
| 259 | 253 | throw error(&["error registering module"]); |
|
| 260 | 254 | }; |
|
| 261 | 255 | // Read file into remaining arena space. |
|
| 262 | 256 | let buffer = alloc::remainingBuf(sourceArena); |
|
| 263 | 257 | if buffer.len == 0 { |
|
| 264 | 258 | throw error(&["fatal:", "source arena exhausted"]); |
|
| 265 | 259 | } |
|
| 266 | - | let source = unix::readFile(path, buffer) else { |
|
| 260 | + | let sourceLen = unix::readFile(path, buffer) else { |
|
| 267 | 261 | throw error(&["error reading file"]); |
|
| 268 | 262 | }; |
|
| 263 | + | let source = &buffer[..sourceLen]; |
|
| 269 | 264 | if source.len == buffer.len { |
|
| 270 | 265 | throw error(&["fatal:", "source arena too small, file truncated:", path]); |
|
| 271 | 266 | } |
|
| 272 | 267 | // Commit only what was read. |
|
| 273 | 268 | alloc::commit(sourceArena, source.len); |
| 282 | 277 | throw error(&["error setting source"]); |
|
| 283 | 278 | }; |
|
| 284 | 279 | } |
|
| 285 | 280 | ||
| 286 | 281 | /// Consume the next argument, or print an error and throw. |
|
| 287 | - | fn nextArg(args: *[*[u8]], idx: *mut u32, msg: *[*[u8]]) -> *[u8] throws (Error) { |
|
| 282 | + | fn nextArg(args: *[*[u8]], idx: &mut u32, msg: &[*[u8]]) -> *[u8] throws (Error) { |
|
| 288 | 283 | set *idx += 1; |
|
| 289 | 284 | if *idx >= args.len { |
|
| 290 | 285 | throw error(msg); |
|
| 291 | 286 | } |
|
| 292 | 287 | return args[*idx]; |
|
| 293 | 288 | } |
|
| 294 | 289 | ||
| 295 | 290 | /// Parse CLI arguments and return compilation context. |
|
| 296 | - | fn processCommand( |
|
| 291 | + | unsafe fn processCommand( |
|
| 297 | 292 | args: *[*[u8]], |
|
| 298 | - | arena: *mut ast::NodeArena |
|
| 293 | + | arena: &mut ast::NodeArena |
|
| 299 | 294 | ) -> CompileContext throws (Error) { |
|
| 300 | 295 | let mut buildTest = false; |
|
| 301 | 296 | let mut debugEnabled = false; |
|
| 302 | 297 | let mut outputPath: ?*[u8] = nil; |
|
| 303 | 298 | let mut dump = Dump::None; |
| 327 | 322 | } else if mem::eq(arg, "-mod") { |
|
| 328 | 323 | try nextArg(args, &mut idx, &["`-mod` requires a module path"]); |
|
| 329 | 324 | let pkgIdx = currentPkgIdx else { |
|
| 330 | 325 | throw error(&["`-mod` must follow a `-pkg` argument"]); |
|
| 331 | 326 | }; |
|
| 332 | - | let input = &mut inputs[pkgIdx]; |
|
| 327 | + | let input: *unsafe mut PackageInput = &mut inputs[pkgIdx]; |
|
| 333 | 328 | if hasExtension(args[idx], ASM_SOURCE_EXT) { |
|
| 334 | 329 | if input.asmPathCount >= MAX_ASM_MODULES { |
|
| 335 | 330 | throw error(&["too many assembly modules specified"]); |
|
| 336 | 331 | } |
|
| 337 | 332 | set input.asmPaths[input.asmPathCount] = args[idx]; |
| 346 | 341 | } else if mem::eq(arg, "-start") { |
|
| 347 | 342 | try nextArg(args, &mut idx, &["`-start` requires an assembly path"]); |
|
| 348 | 343 | let pkgIdx = currentPkgIdx else { |
|
| 349 | 344 | throw error(&["`-start` must follow a `-pkg` argument"]); |
|
| 350 | 345 | }; |
|
| 351 | - | let input = &mut inputs[pkgIdx]; |
|
| 346 | + | let input: *unsafe mut PackageInput = &mut inputs[pkgIdx]; |
|
| 352 | 347 | if input.startupPath <> nil { |
|
| 353 | 348 | throw error(&["package", input.name, "has more than one startup file"]); |
|
| 354 | 349 | } |
|
| 355 | 350 | if not hasExtension(args[idx], ASM_SOURCE_EXT) { |
|
| 356 | 351 | throw error(&["`-start` requires a `.ras` assembly file"]); |
| 435 | 430 | debug: debugEnabled, |
|
| 436 | 431 | }; |
|
| 437 | 432 | // Initialize and parse all packages. |
|
| 438 | 433 | let mut sourceArena = alloc::new(&mut MODULE_SOURCES[..]); |
|
| 439 | 434 | for i in 0..pkgCount { |
|
| 440 | - | package::init(&mut ctx.packages[i], i as u16, ctx.inputs[i].name, &mut STRING_POOL); |
|
| 435 | + | let name = ctx.inputs[i].name; |
|
| 436 | + | package::init(&mut ctx.packages[i], i as u16, name, &mut STRING_POOL); |
|
| 441 | 437 | ||
| 442 | 438 | for j in 0..ctx.inputs[i].radPathCount { |
|
| 443 | 439 | let path = ctx.inputs[i].radPaths[j]; |
|
| 444 | 440 | try processModule(&mut ctx.packages[i], &mut ctx.graph, path, arena, &mut sourceArena); |
|
| 445 | 441 | } |
|
| 446 | 442 | } |
|
| 447 | 443 | return ctx; |
|
| 448 | 444 | } |
|
| 449 | 445 | ||
| 450 | 446 | /// Get the entry package from the context. |
|
| 451 | - | fn getEntryPackage(ctx: *CompileContext) -> *package::Package throws (Error) { |
|
| 447 | + | unsafe fn getEntryPackage(ctx: *unsafe CompileContext) -> *unsafe package::Package throws (Error) { |
|
| 452 | 448 | let entryIdx = ctx.entryPkgIdx else { |
|
| 453 | 449 | throw error(&["no entry package specified"]); |
|
| 454 | 450 | }; |
|
| 455 | 451 | return &ctx.packages[entryIdx]; |
|
| 456 | 452 | } |
|
| 457 | 453 | ||
| 458 | 454 | /// Return the startup assembly path for the entry package, if one was supplied. |
|
| 459 | - | fn getEntryStartupPath(ctx: *CompileContext) -> ?*[u8] { |
|
| 455 | + | fn getEntryStartupPath(ctx: &CompileContext) -> ?*[u8] { |
|
| 460 | 456 | let entryIdx = ctx.entryPkgIdx else { |
|
| 461 | 457 | panic "getEntryStartupPath: no entry package"; |
|
| 462 | 458 | }; |
|
| 463 | 459 | return ctx.inputs[entryIdx].startupPath; |
|
| 464 | 460 | } |
|
| 465 | 461 | ||
| 466 | 462 | /// Get root module info from a package. |
|
| 467 | - | fn getRootModule(pkg: *package::Package, graph: *module::ModuleGraph) -> RootModule throws (Error) { |
|
| 463 | + | fn getRootModule(pkg: &package::Package, graph: &module::ModuleGraph) -> RootModule throws (Error) { |
|
| 468 | 464 | let rootId = pkg.rootModuleId else { |
|
| 469 | 465 | throw error(&["no root module found"]); |
|
| 470 | 466 | }; |
|
| 471 | 467 | let rootEntry = module::get(graph, rootId) else { |
|
| 472 | 468 | throw error(&["root module entry not found"]); |
| 476 | 472 | }; |
|
| 477 | 473 | return RootModule { entry: rootEntry, ast: rootAst }; |
|
| 478 | 474 | } |
|
| 479 | 475 | ||
| 480 | 476 | /// Dump the module graph. |
|
| 481 | - | fn dumpGraph(ctx: *CompileContext) { |
|
| 477 | + | unsafe fn dumpGraph(ctx: &CompileContext) { |
|
| 482 | 478 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 483 | 479 | module::printer::printGraph(&ctx.graph, &mut arena); |
|
| 484 | 480 | } |
|
| 485 | 481 | ||
| 486 | 482 | /// Dump the parsed AST. |
|
| 487 | - | fn dumpAst(ctx: *CompileContext) throws (Error) { |
|
| 483 | + | unsafe fn dumpAst(ctx: *unsafe CompileContext) throws (Error) { |
|
| 488 | 484 | let pkg = try getEntryPackage(ctx); |
|
| 489 | - | let root = try getRootModule(pkg, &ctx.graph); |
|
| 485 | + | let root = try getRootModule(&*pkg, &ctx.graph); |
|
| 490 | 486 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 491 | 487 | ||
| 492 | 488 | ast::printer::printTree(root.ast, &mut arena); |
|
| 493 | 489 | } |
|
| 494 | 490 | ||
| 495 | 491 | /// Lower all packages into a single IL program. |
|
| 496 | 492 | /// Dependencies are lowered first, then the entry package. |
|
| 497 | - | fn lowerAllPackages( |
|
| 498 | - | ctx: *mut CompileContext, |
|
| 499 | - | res: *mut resolver::Resolver |
|
| 493 | + | unsafe fn lowerAllPackages( |
|
| 494 | + | ctx: *unsafe mut CompileContext, |
|
| 495 | + | res: *unsafe mut resolver::Resolver |
|
| 500 | 496 | ) -> il::Program throws (Error) { |
|
| 501 | 497 | let entryIdx = ctx.entryPkgIdx else { |
|
| 502 | 498 | panic "lowerAllPackages: no entry package"; |
|
| 503 | 499 | }; |
|
| 504 | 500 | let entryPkg = &ctx.packages[entryIdx]; |
| 513 | 509 | // Finalize and return the unified program. |
|
| 514 | 510 | return lower::finalize(&low); |
|
| 515 | 511 | } |
|
| 516 | 512 | ||
| 517 | 513 | /// Lower all packages into an existing lowerer. |
|
| 518 | - | fn lowerAllPackagesInto( |
|
| 519 | - | ctx: *mut CompileContext, |
|
| 520 | - | res: *mut resolver::Resolver, |
|
| 521 | - | low: *mut lower::Lowerer |
|
| 514 | + | unsafe fn lowerAllPackagesInto( |
|
| 515 | + | ctx: *unsafe mut CompileContext, |
|
| 516 | + | res: *unsafe mut resolver::Resolver, |
|
| 517 | + | low: &mut lower::Lowerer |
|
| 522 | 518 | ) throws (Error) { |
|
| 523 | 519 | let entryIdx = ctx.entryPkgIdx else { |
|
| 524 | 520 | panic "lowerAllPackagesInto: no entry package"; |
|
| 525 | 521 | }; |
|
| 526 | 522 | // Lower all packages except entry. |
|
| 527 | 523 | for i in 0..ctx.packageCount { |
|
| 528 | 524 | if i <> entryIdx { |
|
| 529 | - | try lowerPackage(ctx, res, low, &mut ctx.packages[i], false); |
|
| 525 | + | try lowerPackage(ctx, res, &mut *low, &mut ctx.packages[i], false); |
|
| 530 | 526 | } |
|
| 531 | 527 | } |
|
| 532 | 528 | // Lower entry package. |
|
| 533 | - | try lowerPackage(ctx, res, low, &mut ctx.packages[entryIdx], true); |
|
| 529 | + | try lowerPackage(ctx, res, &mut *low, &mut ctx.packages[entryIdx], true); |
|
| 534 | 530 | } |
|
| 535 | 531 | ||
| 536 | 532 | /// Lower all modules in a package into the lowerer accumulator. |
|
| 537 | - | fn lowerPackage( |
|
| 538 | - | ctx: *CompileContext, |
|
| 539 | - | res: *mut resolver::Resolver, |
|
| 540 | - | low: *mut lower::Lowerer, |
|
| 541 | - | pkg: *mut package::Package, |
|
| 533 | + | unsafe fn lowerPackage( |
|
| 534 | + | ctx: *unsafe CompileContext, |
|
| 535 | + | res: *unsafe mut resolver::Resolver, |
|
| 536 | + | low: &mut lower::Lowerer, |
|
| 537 | + | pkg: &mut package::Package, |
|
| 542 | 538 | isEntry: bool |
|
| 543 | 539 | ) throws (Error) { |
|
| 544 | 540 | let rootId = pkg.rootModuleId else { |
|
| 545 | 541 | throw error(&["no root module found"]); |
|
| 546 | 542 | }; |
|
| 547 | 543 | // Set lowerer's package context for qualified name generation. |
|
| 548 | 544 | // TODO: We shouldn't have to call this manually. |
|
| 549 | - | lower::setPackage(low, &ctx.graph, pkg.name); |
|
| 545 | + | lower::setPackage(&mut *low, &ctx.graph, pkg.name); |
|
| 550 | 546 | ||
| 551 | - | try lowerModuleTreeInto(ctx, low, &ctx.graph, rootId, isEntry, pkg); |
|
| 547 | + | try lowerModuleTreeInto(ctx, &mut *low, &ctx.graph, rootId, isEntry, pkg); |
|
| 552 | 548 | } |
|
| 553 | 549 | ||
| 554 | 550 | /// Recursively lower a module and all its children into the accumulator. |
|
| 555 | - | fn lowerModuleTreeInto( |
|
| 556 | - | ctx: *CompileContext, |
|
| 557 | - | low: *mut lower::Lowerer, |
|
| 558 | - | graph: *module::ModuleGraph, |
|
| 551 | + | unsafe fn lowerModuleTreeInto( |
|
| 552 | + | ctx: *unsafe CompileContext, |
|
| 553 | + | low: &mut lower::Lowerer, |
|
| 554 | + | graph: &module::ModuleGraph, |
|
| 559 | 555 | modId: u16, |
|
| 560 | 556 | isRoot: bool, |
|
| 561 | - | pkg: *package::Package |
|
| 557 | + | pkg: &package::Package |
|
| 562 | 558 | ) throws (Error) { |
|
| 563 | 559 | let entry = module::get(graph, modId) else { |
|
| 564 | 560 | throw error(&["module entry not found"]); |
|
| 565 | 561 | }; |
|
| 566 | 562 | let modAst = entry.ast else { |
|
| 567 | 563 | throw error(&["module has no AST"]); |
|
| 568 | 564 | }; |
|
| 569 | - | pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
|
| 565 | + | pkgLog(&*pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
|
| 570 | 566 | ||
| 571 | - | try lower::lowerModule(low, modId, modAst, isRoot) catch err { |
|
| 567 | + | try lower::lowerModule(&mut *low, modId, modAst, isRoot) catch err { |
|
| 572 | 568 | io::printError("radiance: "); |
|
| 573 | 569 | io::printError("internal error during lowering: "); |
|
| 574 | 570 | lower::printError(err); |
|
| 575 | 571 | io::printError("\n"); |
|
| 576 | 572 | ||
| 577 | 573 | throw Error::Other; |
|
| 578 | 574 | }; |
|
| 579 | 575 | // Recurse into children. |
|
| 580 | 576 | for i in 0..entry.childrenLen { |
|
| 581 | 577 | let childId = module::childAt(entry, i); |
|
| 582 | - | try lowerModuleTreeInto(ctx, low, graph, childId, false, pkg); |
|
| 578 | + | try lowerModuleTreeInto(ctx, &mut *low, graph, childId, false, pkg); |
|
| 583 | 579 | } |
|
| 584 | 580 | } |
|
| 585 | 581 | ||
| 586 | 582 | /// Build a scope access chain: a::b::c from a slice of identifiers. |
|
| 587 | - | fn synthScopeAccess(arena: *mut ast::NodeArena, path: *[*[u8]]) -> *ast::Node { |
|
| 583 | + | fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node { |
|
| 588 | 584 | let mut result = ast::synthNode( |
|
| 589 | 585 | arena, |
|
| 590 | 586 | ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, path[0])) |
|
| 591 | 587 | ); |
|
| 592 | 588 | for i in 1..path.len { |
| 600 | 596 | } |
|
| 601 | 597 | return result; |
|
| 602 | 598 | } |
|
| 603 | 599 | ||
| 604 | 600 | /// Check if a function declaration has the `@test` attribute and return its name if so. |
|
| 605 | - | fn getTestFnName(decl: *ast::FnDecl) -> ?*[u8] { |
|
| 601 | + | fn getTestFnName(decl: &ast::FnDecl) -> ?*[u8] { |
|
| 606 | 602 | let attrs = decl.attrs else { return nil; }; |
|
| 607 | 603 | if not ast::attributesContains(&attrs, ast::Attribute::Test) { |
|
| 608 | 604 | return nil; |
|
| 609 | 605 | } |
|
| 610 | 606 | let case ast::NodeValue::Ident(name) = decl.name.value |
| 614 | 610 | } |
|
| 615 | 611 | ||
| 616 | 612 | /// Scan a single module's AST for `@test` functions and append them to `tests`. |
|
| 617 | 613 | fn collectModuleTests( |
|
| 618 | 614 | entry: *module::ModuleEntry, |
|
| 619 | - | tests: *mut [TestDesc], |
|
| 620 | - | testCount: *mut u32 |
|
| 615 | + | tests: &mut [TestDesc], |
|
| 616 | + | testCount: &mut u32 |
|
| 621 | 617 | ) { |
|
| 622 | 618 | let modAst = entry.ast else { |
|
| 623 | 619 | return; |
|
| 624 | 620 | }; |
|
| 625 | 621 | let case ast::NodeValue::Block(block) = modAst.value else { |
| 640 | 636 | } |
|
| 641 | 637 | } |
|
| 642 | 638 | } |
|
| 643 | 639 | ||
| 644 | 640 | /// Synthesize a `testing::test("mod", "name", mod::fn)` call for one test. |
|
| 645 | - | fn synthTestCall(arena: *mut ast::NodeArena, desc: *TestDesc) -> *ast::Node { |
|
| 641 | + | unsafe fn synthTestCall(arena: &mut ast::NodeArena, desc: &TestDesc) -> *ast::Node { |
|
| 646 | 642 | let callee = synthScopeAccess(arena, &["testing", "test"]); |
|
| 647 | 643 | let modStr = il::formatQualifiedName( |
|
| 648 | 644 | &mut arena.arena, |
|
| 649 | 645 | &desc.modPath[..desc.modPath.len - 1], |
|
| 650 | 646 | desc.modPath[desc.modPath.len - 1] |
| 682 | 678 | /// ]); |
|
| 683 | 679 | /// } |
|
| 684 | 680 | /// ``` |
|
| 685 | 681 | /// |
|
| 686 | 682 | /// Uses `#`-prefixed names to avoid conflicts with user code. |
|
| 687 | - | fn generateTestRunner( |
|
| 688 | - | ctx: *mut CompileContext, |
|
| 689 | - | arena: *mut ast::NodeArena |
|
| 683 | + | unsafe fn generateTestRunner( |
|
| 684 | + | ctx: *unsafe mut CompileContext, |
|
| 685 | + | arena: &mut ast::NodeArena |
|
| 690 | 686 | ) throws (Error) { |
|
| 691 | 687 | let entryPkg = try getEntryPackage(ctx); |
|
| 692 | - | let root = try getRootModule(entryPkg, &ctx.graph); |
|
| 688 | + | let root = try getRootModule(&*entryPkg, &ctx.graph); |
|
| 693 | 689 | ||
| 694 | 690 | // Collect all test functions across all modules. |
|
| 695 | 691 | let mut tests: [TestDesc; MAX_TESTS] = undefined; |
|
| 696 | 692 | let mut testCount: u32 = 0; |
|
| 697 | 693 |
| 702 | 698 | } |
|
| 703 | 699 | if testCount == 0 { |
|
| 704 | 700 | throw error(&["fatal:", "no test functions found"]); |
|
| 705 | 701 | } |
|
| 706 | 702 | let mut countBuf: [u8; 10] = undefined; |
|
| 707 | - | let countStr = fmt::formatU32(testCount, &mut countBuf[..]); |
|
| 708 | - | pkgLog(entryPkg, &["found", countStr, "test(s)"]); |
|
| 703 | + | let start = fmt::formatU32(testCount, &mut countBuf[..]); |
|
| 704 | + | io::printError("radiance: "); |
|
| 705 | + | io::printError(entryPkg.name); |
|
| 706 | + | io::printError(": found "); |
|
| 707 | + | io::printError(&countBuf[start..]); |
|
| 708 | + | io::printError(" test(s)\n"); |
|
| 709 | 709 | ||
| 710 | 710 | // Synthesize the `@default` function and append to the root module. |
|
| 711 | 711 | let fnDecl = synthTestMainFn(arena, &tests[..testCount]); |
|
| 712 | 712 | ||
| 713 | 713 | injectIntoBlock(root.ast, arena, fnDecl); |
|
| 714 | 714 | } |
|
| 715 | 715 | ||
| 716 | 716 | /// Synthesize the test entry point. |
|
| 717 | - | fn synthTestMainFn(arena: *mut ast::NodeArena, tests: *[TestDesc]) -> *ast::Node { |
|
| 717 | + | unsafe fn synthTestMainFn(arena: &mut ast::NodeArena, tests: &[TestDesc]) -> *ast::Node { |
|
| 718 | 718 | // Build array literal: `[testing::test(...), ...]`. |
|
| 719 | 719 | let a = alloc::arenaAllocator(&mut arena.arena); |
|
| 720 | 720 | let mut elements = ast::nodeSlice(arena, tests.len as u32); |
|
| 721 | 721 | for i in 0..tests.len { |
|
| 722 | 722 | elements.append(synthTestCall(arena, &tests[i]), a); |
| 738 | 738 | // Build: `return testing::runAllTests(&[...]);` |
|
| 739 | 739 | let retStmt = ast::synthNode(arena, ast::NodeValue::Return { value: callExpr }); |
|
| 740 | 740 | let bodyStmts = ast::nodeSlice(arena, 1).append(retStmt, a); |
|
| 741 | 741 | let fnBody = ast::synthNode(arena, ast::NodeValue::Block(ast::Block { statements: bodyStmts })); |
|
| 742 | 742 | ||
| 743 | - | // Build: `fn #testMain() -> i32` |
|
| 743 | + | // Build: `unsafe fn #testMain() -> i32` |
|
| 744 | 744 | let fnName = ast::synthNode(arena, ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, "#testMain"))); |
|
| 745 | 745 | let returnType = ast::synthNode(arena, ast::NodeValue::TypeSig(ast::TypeSig::Integer { |
|
| 746 | 746 | width: 4, sign: ast::Signedness::Signed, |
|
| 747 | 747 | })); |
|
| 748 | 748 | let fnSig = ast::FnSig { |
|
| 749 | 749 | params: ast::nodeSlice(arena, 0), |
|
| 750 | 750 | returnType, |
|
| 751 | 751 | throwList: ast::nodeSlice(arena, 0), |
|
| 752 | 752 | }; |
|
| 753 | 753 | ||
| 754 | - | // `@default` attribute. |
|
| 754 | + | // Entry and function safety attributes. |
|
| 755 | 755 | let attrNode = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Default)); |
|
| 756 | - | let attrList = ast::nodeSlice(arena, 1).append(attrNode, a); |
|
| 756 | + | let unsafeAttr = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Unsafe)); |
|
| 757 | + | let attrList = ast::nodeSlice(arena, 2).append(attrNode, a).append(unsafeAttr, a); |
|
| 757 | 758 | let fnAttrs = ast::Attributes { list: attrList }; |
|
| 758 | 759 | ||
| 759 | 760 | return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl { |
|
| 760 | 761 | name: fnName, sig: fnSig, body: fnBody, attrs: fnAttrs, |
|
| 761 | 762 | })); |
|
| 762 | 763 | } |
|
| 763 | 764 | ||
| 764 | 765 | /// Append a declaration to a block node's statement list. |
|
| 765 | - | fn injectIntoBlock( |
|
| 766 | + | unsafe fn injectIntoBlock( |
|
| 766 | 767 | blockNode: *mut ast::Node, |
|
| 767 | - | arena: *mut ast::NodeArena, |
|
| 768 | + | arena: &mut ast::NodeArena, |
|
| 768 | 769 | decl: *ast::Node |
|
| 769 | 770 | ) { |
|
| 770 | 771 | let case ast::NodeValue::Block(block) = blockNode.value else { |
|
| 771 | 772 | panic "injectIntoBlock: expected Block node"; |
|
| 772 | 773 | }; |
|
| 773 | 774 | let stmts = block.statements.append(decl, alloc::arenaAllocator(&mut arena.arena)); |
|
| 774 | 775 | set blockNode.value = ast::NodeValue::Block(ast::Block { statements: stmts }); |
|
| 775 | 776 | } |
|
| 776 | 777 | ||
| 777 | 778 | /// Write a self-contained RV64 image containing text and data sections. |
|
| 778 | - | fn writeImage( |
|
| 779 | + | unsafe fn writeImage( |
|
| 779 | 780 | code: *[u32], |
|
| 780 | 781 | roData: *[u8], |
|
| 781 | 782 | rwData: *[u8], |
|
| 782 | 783 | path: *[u8] |
|
| 783 | 784 | ) -> bool { |
|
| 784 | 785 | let mut header = rv64::imageHeader(code.len * rv64::INSTR_SIZE as u32, roData.len, rwData.len); |
|
| 785 | - | let headerWords = &header[..]; |
|
| 786 | - | let headerBytes = @sliceOf(headerWords.ptr as *u8, headerWords.len * rv64::WORD_SIZE as u32); |
|
| 786 | + | let headerBytes: *unsafe [u8] = @sliceOf(&header[0] as *unsafe u8, header.len * rv64::WORD_SIZE as u32); |
|
| 787 | 787 | let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32); |
|
| 788 | 788 | ||
| 789 | - | return unix::writeFileParts(path, &[headerBytes, codeBytes, roData, rwData]); |
|
| 789 | + | let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420); |
|
| 790 | + | if fd < 0 { return false; } |
|
| 791 | + | let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, codeBytes) |
|
| 792 | + | and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData); |
|
| 793 | + | let closed = unix::close(fd) == 0; |
|
| 794 | + | return written and closed; |
|
| 790 | 795 | } |
|
| 791 | 796 | ||
| 792 | 797 | /// Write a data section to a file at `basePath` + `ext`. |
|
| 793 | 798 | /// Empty data truncates any stale sidecar left by an earlier build. |
|
| 794 | 799 | fn writeDataWithExt( |
| 811 | 816 | /// Serialize debug entries and write the `.debug` file. |
|
| 812 | 817 | /// Resolves module IDs to file paths via the module graph. |
|
| 813 | 818 | /// Format per entry is `{pc: u32, offset: u32, filePath: [u8], NULL}`. |
|
| 814 | 819 | fn writeDebugInfo( |
|
| 815 | 820 | entries: *[types::DebugEntry], |
|
| 816 | - | graph: *module::ModuleGraph, |
|
| 821 | + | graph: &module::ModuleGraph, |
|
| 817 | 822 | basePath: *[u8], |
|
| 818 | - | arena: *mut alloc::Arena |
|
| 823 | + | arena: &mut alloc::Arena |
|
| 819 | 824 | ) throws (Error) { |
|
| 820 | 825 | if entries.len == 0 { |
|
| 821 | 826 | return; |
|
| 822 | 827 | } |
|
| 823 | 828 | // Use remaining arena space as serialization buffer. |
| 838 | 843 | } |
|
| 839 | 844 | try writeDataWithExt(&buf[..pos], basePath, DEBUG_EXT); |
|
| 840 | 845 | } |
|
| 841 | 846 | ||
| 842 | 847 | /// Run the resolver on the parsed modules. |
|
| 843 | - | fn runResolver(ctx: *mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) { |
|
| 848 | + | unsafe fn runResolver(ctx: *unsafe mut CompileContext, nodeCount: u32) -> resolver::Resolver throws (Error) { |
|
| 844 | 849 | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 845 | 850 | let entryPkg = try getEntryPackage(ctx); |
|
| 846 | 851 | ||
| 847 | - | pkgLog(entryPkg, &["resolving", ".."]); |
|
| 852 | + | pkgLog(&*entryPkg, &["resolving", ".."]); |
|
| 848 | 853 | ||
| 849 | 854 | let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData); |
|
| 850 | 855 | let nodeDataPtr = try! alloc::alloc(&mut mainArena, nodeDataSize, @alignOf(resolver::NodeData)); |
|
| 851 | 856 | let nodeData = @sliceOf(nodeDataPtr as *mut resolver::NodeData, nodeCount); |
|
| 852 | 857 | let storage = resolver::ResolverStorage { |
| 860 | 865 | // Build the semantic package list consumed by the resolver. |
|
| 861 | 866 | let mut resolverPkgs: [resolver::Pkg; MAX_PACKAGES] = undefined; |
|
| 862 | 867 | let mut resolverPackageCount: u32 = 0; |
|
| 863 | 868 | for i in 0..ctx.packageCount { |
|
| 864 | 869 | let pkg = &ctx.packages[i]; |
|
| 865 | - | let root = try getRootModule(pkg, &ctx.graph); |
|
| 870 | + | let root = try getRootModule(&*pkg, &ctx.graph); |
|
| 866 | 871 | ||
| 867 | 872 | set resolverPkgs[resolverPackageCount] = resolver::Pkg { |
|
| 868 | 873 | rootEntry: root.entry, |
|
| 869 | 874 | rootAst: root.ast, |
|
| 870 | 875 | }; |
| 879 | 884 | throw Error::Other; |
|
| 880 | 885 | }; |
|
| 881 | 886 | if not resolver::success(&diags) { |
|
| 882 | 887 | resolver::printer::printDiagnostics(&diags, &res); |
|
| 883 | 888 | let mut countBuf: [u8; 10] = undefined; |
|
| 884 | - | let countStr = fmt::formatU32(diags.errors.len, &mut countBuf[..]); |
|
| 885 | - | throw error(&["failed:", countStr, "errors"]); |
|
| 889 | + | let start = fmt::formatU32(diags.errors.len, &mut countBuf[..]); |
|
| 890 | + | io::printError("radiance: failed: "); |
|
| 891 | + | io::printError(&countBuf[start..]); |
|
| 892 | + | io::printError(" errors\n"); |
|
| 893 | + | throw Error::Other; |
|
| 886 | 894 | } |
|
| 887 | 895 | return res; |
|
| 888 | 896 | } |
|
| 889 | 897 | ||
| 890 | - | /// Emit one lowered function to machine code and reclaim its IL arena. |
|
| 891 | - | fn generateLoweredFn(ctxPtr: *mut opaque, func: *il::Fn, role: lower::FnRole) { |
|
| 892 | - | let ctx = ctxPtr as *mut CodegenSinkContext; |
|
| 893 | - | ||
| 894 | - | match role { |
|
| 895 | - | case lower::FnRole::Default => { |
|
| 896 | - | rv64::recordFunctionAlias(ctx.generator, DEFAULT_ENTRY_SYMBOL); |
|
| 897 | - | match ctx.generator.entryPatch { |
|
| 898 | - | case rv64::EntryPatch::Reserved(_) => { |
|
| 899 | - | set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name); |
|
| 900 | - | } |
|
| 901 | - | // No entry jump was reserved: startup assembly calls the |
|
| 902 | - | // default function through `DEFAULT_ENTRY_SYMBOL` instead. |
|
| 903 | - | case rv64::EntryPatch::None => {} |
|
| 904 | - | } |
|
| 905 | - | } |
|
| 906 | - | else => {} |
|
| 907 | - | } |
|
| 908 | - | rv64::generateFunction(ctx.generator, func, ctx.fnArena); |
|
| 909 | - | alloc::reset(ctx.fnArena); |
|
| 910 | - | } |
|
| 911 | - | ||
| 912 | 898 | /// Assemble one `.ras` input and merge it into the active code generator. |
|
| 913 | 899 | /// |
|
| 914 | 900 | /// Text symbols are appended to `generator`. Data emitted by the assembler is |
|
| 915 | 901 | /// copied into `ASM_RO_DATA_BUF` at `*asmDataLen`, and `*asmDataLen` is advanced |
|
| 916 | 902 | /// so the next assembly module receives the correct rodata base address. |
|
| 917 | - | fn assembleAsmModule( |
|
| 918 | - | generator: *mut rv64::Generator, |
|
| 919 | - | pkg: *package::Package, |
|
| 903 | + | unsafe fn assembleAsmModule( |
|
| 904 | + | generator: &mut rv64::Generator, |
|
| 905 | + | pkg: &package::Package, |
|
| 920 | 906 | path: *[u8], |
|
| 921 | - | asmDataLen: *mut u32, |
|
| 922 | - | arena: *mut alloc::Arena |
|
| 907 | + | asmDataLen: &mut u32, |
|
| 908 | + | arena: &mut alloc::Arena |
|
| 923 | 909 | ) throws (Error) { |
|
| 924 | - | pkgLog(pkg, &["asm:", "parsing", "(", path, ")", ".."]); |
|
| 910 | + | pkgLog(&*pkg, &["asm:", "parsing", "(", path, ")", ".."]); |
|
| 925 | 911 | ||
| 926 | - | let source = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else { |
|
| 912 | + | let sourceLen = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else { |
|
| 927 | 913 | throw error(&["error reading assembly file"]); |
|
| 928 | 914 | }; |
|
| 915 | + | let source = &ASM_SOURCE_BUF[..sourceLen]; |
|
| 929 | 916 | if source.len == ASM_SOURCE_BUF.len { |
|
| 930 | 917 | throw error(&["fatal:", "assembly source too large:", path]); |
|
| 931 | 918 | } |
|
| 932 | 919 | let program = try asm::assemble( |
|
| 933 | 920 | asm::scanner::SourceKind::File { path }, |
| 948 | 935 | ||
| 949 | 936 | rv64::addAssembly(generator, program); |
|
| 950 | 937 | } |
|
| 951 | 938 | ||
| 952 | 939 | /// Assemble all inputs collected in the package inputs. |
|
| 953 | - | fn assembleAsmInputs( |
|
| 954 | - | ctx: *CompileContext, |
|
| 955 | - | generator: *mut rv64::Generator, |
|
| 956 | - | asmDataLen: *mut u32, |
|
| 957 | - | arena: *mut alloc::Arena |
|
| 940 | + | unsafe fn assembleAsmInputs( |
|
| 941 | + | ctx: &CompileContext, |
|
| 942 | + | generator: &mut rv64::Generator, |
|
| 943 | + | asmDataLen: &mut u32, |
|
| 944 | + | arena: &mut alloc::Arena |
|
| 958 | 945 | ) -> *[u8] throws (Error) { |
|
| 959 | 946 | for i in 0..ctx.packageCount { |
|
| 960 | - | let input = &ctx.inputs[i]; |
|
| 961 | - | for j in 0..input.asmPathCount { |
|
| 947 | + | for j in 0..ctx.inputs[i].asmPathCount { |
|
| 962 | 948 | try assembleAsmModule( |
|
| 963 | 949 | generator, |
|
| 964 | 950 | &ctx.packages[i], |
|
| 965 | - | input.asmPaths[j], |
|
| 951 | + | ctx.inputs[i].asmPaths[j], |
|
| 966 | 952 | asmDataLen, |
|
| 967 | 953 | arena |
|
| 968 | 954 | ); |
|
| 969 | 955 | } |
|
| 970 | 956 | } |
|
| 971 | 957 | return &ASM_RO_DATA_BUF[..*asmDataLen]; |
|
| 972 | 958 | } |
|
| 973 | 959 | ||
| 974 | 960 | /// Lower all packages while streaming each lowered function into RV64 codegen. |
|
| 975 | - | fn lowerAndGenerateAllPackages( |
|
| 976 | - | ctx: *mut CompileContext, |
|
| 977 | - | res: *mut resolver::Resolver, |
|
| 978 | - | fnArena: *mut alloc::Arena, |
|
| 961 | + | unsafe fn lowerAndGenerateAllPackages( |
|
| 962 | + | ctx: *unsafe mut CompileContext, |
|
| 963 | + | res: *unsafe mut resolver::Resolver, |
|
| 964 | + | fnArena: &mut alloc::Arena, |
|
| 979 | 965 | codegenOptions: CodegenOptions |
|
| 980 | 966 | ) -> rv64::Program throws (Error) { |
|
| 981 | 967 | let entryIdx = ctx.entryPkgIdx else { |
|
| 982 | 968 | panic "lowerAndGenerateAllPackages: no entry package"; |
|
| 983 | 969 | }; |
|
| 984 | 970 | let entryPkg = &ctx.packages[entryIdx]; |
|
| 985 | - | let startupPath = getEntryStartupPath(ctx); |
|
| 971 | + | let startupPath = getEntryStartupPath(&*ctx); |
|
| 986 | 972 | let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest }; |
|
| 987 | 973 | let storage = rv64::Storage { |
|
| 988 | 974 | dataSyms: &mut CODEGEN_DATA_SYMS[..], |
|
| 989 | 975 | dataSymEntries: &mut CODEGEN_DATA_SYM_ENTRIES[..], |
|
| 990 | 976 | }; |
| 997 | 983 | } |
|
| 998 | 984 | let mut generator = rv64::beginProgram( |
|
| 999 | 985 | rv64::ProgramOptions { entryPatch, debug: codegenOptions.debug }, |
|
| 1000 | 986 | &mut res.arena |
|
| 1001 | 987 | ); |
|
| 1002 | - | let mut codegenCtx = CodegenSinkContext { |
|
| 988 | + | let mut codegenCtx = codegen::Context { |
|
| 1003 | 989 | generator: &mut generator, |
|
| 1004 | - | fnArena, |
|
| 990 | + | fnArena: fnArena as *unsafe mut alloc::Arena, |
|
| 1005 | 991 | }; |
|
| 1006 | 992 | let mut low = lower::lowerer( |
|
| 1007 | - | res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena, options |
|
| 993 | + | res, &ctx.graph, entryPkg.name, &mut res.arena, fnArena as *unsafe mut alloc::Arena, options |
|
| 1008 | 994 | ); |
|
| 1009 | 995 | set low.output = lower::FnOutput::Stream(lower::FnSink { |
|
| 1010 | - | ctx: &mut codegenCtx as *mut opaque, |
|
| 1011 | - | emitFn: generateLoweredFn, |
|
| 996 | + | ctx: &mut codegenCtx as *unsafe mut opaque, |
|
| 997 | + | emitFn: codegen::emit, |
|
| 1012 | 998 | }); |
|
| 1013 | 999 | let mut asmDataLen: u32 = 0; |
|
| 1014 | 1000 | if let path = startupPath { |
|
| 1015 | - | try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, &mut res.arena); |
|
| 1001 | + | try assembleAsmModule(&mut generator, &*entryPkg, path, &mut asmDataLen, &mut res.arena); |
|
| 1016 | 1002 | } |
|
| 1017 | 1003 | try lowerAllPackagesInto(ctx, res, &mut low); |
|
| 1018 | - | let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, &mut res.arena); |
|
| 1004 | + | let asmData = try assembleAsmInputs(&*ctx, &mut generator, &mut asmDataLen, &mut res.arena); |
|
| 1019 | 1005 | ||
| 1020 | 1006 | match generator.entryPatch { |
|
| 1021 | 1007 | case rv64::EntryPatch::Reserved(targetName) => { |
|
| 1022 | 1008 | if targetName == nil { |
|
| 1023 | 1009 | throw error(&["fatal:", "no default function found"]); |
|
| 1024 | 1010 | } |
|
| 1025 | 1011 | } |
|
| 1026 | 1012 | else => {} |
|
| 1027 | 1013 | } |
|
| 1028 | 1014 | if let path = codegenOptions.logPath { |
|
| 1029 | - | pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]); |
|
| 1015 | + | pkgLog(&*entryPkg, &["generating code", "(", path, ")", ".."]); |
|
| 1030 | 1016 | } |
|
| 1031 | 1017 | return rv64::finishProgram(&mut generator, &low.data[..], storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]); |
|
| 1032 | 1018 | } |
|
| 1033 | 1019 | ||
| 1034 | 1020 | /// Lower, optionally dump, and optionally generate binary output. |
|
| 1035 | - | fn compile( |
|
| 1036 | - | ctx: *mut CompileContext, |
|
| 1037 | - | res: *mut resolver::Resolver, |
|
| 1038 | - | fnArena: *mut alloc::Arena |
|
| 1021 | + | unsafe fn compile( |
|
| 1022 | + | ctx: *unsafe mut CompileContext, |
|
| 1023 | + | res: *unsafe mut resolver::Resolver, |
|
| 1024 | + | fnArena: &mut alloc::Arena |
|
| 1039 | 1025 | ) throws (Error) { |
|
| 1040 | 1026 | let entryPkg = try getEntryPackage(ctx); |
|
| 1041 | 1027 | let mut out = sexpr::Output::Stdout; |
|
| 1042 | 1028 | ||
| 1043 | 1029 | if ctx.dump == Dump::Il { |
| 1061 | 1047 | // Generate binary output if path specified. |
|
| 1062 | 1048 | let outPath = ctx.outputPath else { |
|
| 1063 | 1049 | try lowerAllPackages(ctx, res); |
|
| 1064 | 1050 | return; |
|
| 1065 | 1051 | }; |
|
| 1066 | - | let startupPath = getEntryStartupPath(ctx); |
|
| 1052 | + | let startupPath = getEntryStartupPath(&*ctx); |
|
| 1067 | 1053 | let result = try lowerAndGenerateAllPackages(ctx, res, fnArena, CodegenOptions { |
|
| 1068 | 1054 | logPath: outPath, |
|
| 1069 | 1055 | debug: ctx.debug, |
|
| 1070 | 1056 | entryMode: CodegenEntryMode::None |
|
| 1071 | 1057 | if startupPath <> nil |
| 1083 | 1069 | ||
| 1084 | 1070 | // Write debug info file if enabled. |
|
| 1085 | 1071 | if ctx.debug { |
|
| 1086 | 1072 | try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, &mut res.arena); |
|
| 1087 | 1073 | } |
|
| 1088 | - | pkgLog(entryPkg, &["ok", "(", outPath, ")"]); |
|
| 1074 | + | pkgLog(&*entryPkg, &["ok", "(", outPath, ")"]); |
|
| 1089 | 1075 | } |
|
| 1090 | 1076 | ||
| 1091 | - | @default fn main(env: *sys::Env) -> i32 { |
|
| 1077 | + | @default unsafe fn main(env: *sys::Env) -> i32 { |
|
| 1092 | 1078 | let mut arena = ast::nodeArena(&mut TEMP_ARENA[..]); |
|
| 1093 | 1079 | let mut ctx = try processCommand(env.args, &mut arena) catch { |
|
| 1094 | 1080 | return 1; |
|
| 1095 | 1081 | }; |
|
| 1096 | 1082 | match ctx.dump { |
compiler/radiance/codegen.rad
added
+39 -0
| 1 | + | //! Streaming code generation with caller-managed context storage. |
|
| 2 | + | ||
| 3 | + | use std::arch::rv64; |
|
| 4 | + | use std::lang::alloc; |
|
| 5 | + | use std::lang::il; |
|
| 6 | + | use std::lang::lower; |
|
| 7 | + | ||
| 8 | + | /// State carried by the streaming lowerer/codegen callback. |
|
| 9 | + | export record Context: Copy { |
|
| 10 | + | /// Generator receiving functions. It must remain valid for every callback. |
|
| 11 | + | generator: *unsafe mut rv64::Generator, |
|
| 12 | + | /// Arena for function IL. It must remain valid for every callback. |
|
| 13 | + | fnArena: *unsafe mut alloc::Arena, |
|
| 14 | + | } |
|
| 15 | + | ||
| 16 | + | /// Emit one lowered function to machine code and reclaim its IL arena. |
|
| 17 | + | /// The context must point to a valid `Context` with live generator and arena storage. |
|
| 18 | + | export unsafe fn emit(ctxPtr: *unsafe mut opaque, func: *il::Fn, role: lower::FnRole) { |
|
| 19 | + | let ctx = ctxPtr as *unsafe mut Context; |
|
| 20 | + | ||
| 21 | + | match role { |
|
| 22 | + | case lower::FnRole::Default => { |
|
| 23 | + | rv64::recordFunctionAlias(&mut *ctx.generator, super::DEFAULT_ENTRY_SYMBOL); |
|
| 24 | + | match ctx.generator.entryPatch { |
|
| 25 | + | case rv64::EntryPatch::Reserved(_) => { |
|
| 26 | + | set ctx.generator.entryPatch = rv64::EntryPatch::Reserved(func.name); |
|
| 27 | + | } |
|
| 28 | + | // Startup assembly calls the default function through its |
|
| 29 | + | // default entry symbol when no entry jump is reserved. |
|
| 30 | + | case rv64::EntryPatch::None => {} |
|
| 31 | + | } |
|
| 32 | + | } |
|
| 33 | + | else => {} |
|
| 34 | + | } |
|
| 35 | + | let generator = ctx.generator; |
|
| 36 | + | let arena = ctx.fnArena; |
|
| 37 | + | rv64::generateFunction(&mut *generator, func, &mut *arena); |
|
| 38 | + | alloc::reset(&mut *ctx.fnArena); |
|
| 39 | + | } |
lib/std/arch/rv64.rad
+10 -9
| 209 | 209 | /// Entry jump patching state. |
|
| 210 | 210 | entryPatch: EntryPatch, |
|
| 211 | 211 | } |
|
| 212 | 212 | ||
| 213 | 213 | /// Begin RV64 code generation for a program's global state. |
|
| 214 | - | export fn beginProgram( |
|
| 214 | + | export unsafe fn beginProgram( |
|
| 215 | 215 | options: ProgramOptions, |
|
| 216 | - | arena: *mut alloc::Arena |
|
| 216 | + | arena: &mut alloc::Arena |
|
| 217 | 217 | ) -> Generator { |
|
| 218 | 218 | let mut e = try! emit::emitter(arena, options.debug); |
|
| 219 | 219 | ||
| 220 | 220 | // Emit placeholder entry jump when requested. |
|
| 221 | 221 | // We'll patch this at the end once we know where the function is. |
| 232 | 232 | entryPatch: options.entryPatch, |
|
| 233 | 233 | }; |
|
| 234 | 234 | } |
|
| 235 | 235 | ||
| 236 | 236 | /// Generate code for one IL function. |
|
| 237 | - | export fn generateFunction( |
|
| 238 | - | generator: *mut Generator, |
|
| 237 | + | export unsafe fn generateFunction( |
|
| 238 | + | generator: &mut Generator, |
|
| 239 | 239 | func: *il::Fn, |
|
| 240 | - | arena: *mut alloc::Arena |
|
| 240 | + | arena: &mut alloc::Arena |
|
| 241 | 241 | ) { |
|
| 242 | 242 | if func.isExtern { |
|
| 243 | 243 | return; |
|
| 244 | 244 | } |
|
| 245 | 245 | let checkpoint = alloc::save(arena); |
| 251 | 251 | // Reclaim unused memory after instruction selection. |
|
| 252 | 252 | alloc::restore(arena, checkpoint); |
|
| 253 | 253 | } |
|
| 254 | 254 | ||
| 255 | 255 | /// Record an alternate name for the next function emitted. |
|
| 256 | - | export fn recordFunctionAlias(generator: *mut Generator, name: *[u8]) { |
|
| 257 | - | emit::recordFuncOffsetAt(&mut generator.e, name, generator.e.codeLen); |
|
| 256 | + | export fn recordFunctionAlias(generator: &mut Generator, name: *[u8]) { |
|
| 257 | + | let codeLen = generator.e.codeLen; |
|
| 258 | + | emit::recordFuncOffsetAt(&mut generator.e, name, codeLen); |
|
| 258 | 259 | } |
|
| 259 | 260 | ||
| 260 | 261 | /// Add the text section of an assembled program to the generator. |
|
| 261 | 262 | /// |
|
| 262 | 263 | /// This function snapshots the generator's current code length as the base |
| 267 | 268 | /// separate assembly inputs may reuse the same local names. |
|
| 268 | 269 | /// |
|
| 269 | 270 | /// Non-text symbols are ignored here because assembled data is not appended to |
|
| 270 | 271 | /// the generator's text stream. The driver merges assembled data into the RO data |
|
| 271 | 272 | /// prefix separately and passes that data to [`finishProgram`]. |
|
| 272 | - | export fn addAssembly(generator: *mut Generator, program: asm::Program) { |
|
| 273 | + | export fn addAssembly(generator: &mut Generator, program: asm::Program) { |
|
| 273 | 274 | let baseIndex = generator.e.codeLen; |
|
| 274 | 275 | ||
| 275 | 276 | for symbol in program.symbols { |
|
| 276 | 277 | if symbol.section == asm::Section::Text { |
|
| 277 | 278 | let index = baseIndex + ((symbol.offset as u32) / INSTR_SIZE as u32); |
| 297 | 298 | } |
|
| 298 | 299 | } |
|
| 299 | 300 | ||
| 300 | 301 | /// Finish RV64 code generation and return the emitted program. |
|
| 301 | 302 | export fn finishProgram( |
|
| 302 | - | generator: *mut Generator, |
|
| 303 | + | generator: &mut Generator, |
|
| 303 | 304 | globalData: *[il::Data], |
|
| 304 | 305 | storage: Storage, |
|
| 305 | 306 | roDataPrefix: *[u8], |
|
| 306 | 307 | roDataBuf: *mut [u8], |
|
| 307 | 308 | rwDataBuf: *mut [u8] |
lib/std/arch/rv64/asm.rad
+4 -4
| 452 | 452 | } |
|
| 453 | 453 | ||
| 454 | 454 | /// Parser and emission state. |
|
| 455 | 455 | export record Assembler: Copy { |
|
| 456 | 456 | /// Allocation arena for temporary assembler state. |
|
| 457 | - | arena: *mut alloc::Arena, |
|
| 457 | + | arena: *unsafe mut alloc::Arena, |
|
| 458 | 458 | /// Assembler lexical scanner. |
|
| 459 | 459 | scan: scanner::Scanner, |
|
| 460 | 460 | /// Output text buffer. |
|
| 461 | 461 | text: *mut [u32], |
|
| 462 | 462 | /// Output data buffer. |
| 478 | 478 | /// Absolute runtime address of data-section offset zero. |
|
| 479 | 479 | dataBase: u32, |
|
| 480 | 480 | } |
|
| 481 | 481 | ||
| 482 | 482 | /// Assemble source using `dataBase` as the runtime address of the data-section. |
|
| 483 | - | export fn assemble( |
|
| 483 | + | export unsafe fn assemble( |
|
| 484 | 484 | sourceKind: scanner::SourceKind, |
|
| 485 | 485 | source: *[u8], |
|
| 486 | 486 | textBuf: *mut [u32], |
|
| 487 | 487 | dataBuf: *mut [u8], |
|
| 488 | - | arena: *mut alloc::Arena, |
|
| 488 | + | arena: &mut alloc::Arena, |
|
| 489 | 489 | pool: *mut strings::Pool, |
|
| 490 | 490 | dataBase: u32 |
|
| 491 | 491 | ) -> Program throws (Error) { |
|
| 492 | 492 | let slotCap = source.len + SOURCE_CAP_PADDING; |
|
| 493 | 493 | let tableCap = nextPowerOfTwo(slotCap * TABLE_CAPACITY_SCALE); |
| 498 | 498 | let entries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
|
| 499 | 499 | let constEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
|
| 500 | 500 | let exportEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
|
| 501 | 501 | ||
| 502 | 502 | let mut a = Assembler { |
|
| 503 | - | arena, |
|
| 503 | + | arena: arena as *unsafe mut alloc::Arena, |
|
| 504 | 504 | scan: scanner::scanner(sourceKind, source, pool), |
|
| 505 | 505 | text: @sliceOf(textBuf.ptr, 0, textBuf.len), |
|
| 506 | 506 | data: @sliceOf(dataBuf.ptr, 0, dataBuf.len), |
|
| 507 | 507 | section: Section::Text, |
|
| 508 | 508 | symbols: @sliceOf((symbols as *mut [Symbol]).ptr, 0, (symbols as *mut [Symbol]).len), |
lib/std/arch/rv64/asm/emit.rad
+20 -20
| 7 | 7 | use std::collections::dict; |
|
| 8 | 8 | use std::lang::alloc; |
|
| 9 | 9 | use std::lang::gen; |
|
| 10 | 10 | ||
| 11 | 11 | /// Define a symbol at the current text or data offset. |
|
| 12 | - | export fn defineSymbol(a: *mut super::Assembler, name: *[u8]) { |
|
| 12 | + | export unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8]) { |
|
| 13 | 13 | let idx = a.symbols.len; |
|
| 14 | 14 | let offset: i32 = a.data.len as i32 |
|
| 15 | 15 | if a.section == super::Section::Data |
|
| 16 | 16 | else a.text.len as i32 * rv64::INSTR_SIZE; |
|
| 17 | 17 | ||
| 18 | 18 | a.symbols.append(super::Symbol { |
|
| 19 | 19 | name, |
|
| 20 | 20 | section: a.section, |
|
| 21 | 21 | offset, |
|
| 22 | 22 | isExported: dict::get(&a.exportMap, name) <> nil, |
|
| 23 | - | }, alloc::arenaAllocator(a.arena)); |
|
| 23 | + | }, alloc::arenaAllocator(&mut *a.arena)); |
|
| 24 | 24 | dict::insert(&mut a.symbolMap, name, idx as i32); |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Append one encoded instruction word to the text section. |
|
| 28 | - | export fn emitText(a: *mut super::Assembler, word: u32) throws (super::Error) { |
|
| 29 | - | a.text.append(word, alloc::arenaAllocator(a.arena)); |
|
| 28 | + | export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) { |
|
| 29 | + | a.text.append(word, alloc::arenaAllocator(&mut *a.arena)); |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | /// Append `words` no-op instructions to the text section. |
|
| 33 | - | export fn emitTextPadding(a: *mut super::Assembler, words: u32) throws (super::Error) { |
|
| 33 | + | export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) { |
|
| 34 | 34 | for _ in 0..words { |
|
| 35 | 35 | try emitText(a, encode::nop()); |
|
| 36 | 36 | } |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Append one byte to the data section. |
|
| 40 | - | export fn emitByte(a: *mut super::Assembler, byte: u8) throws (super::Error) { |
|
| 41 | - | a.data.append(byte, alloc::arenaAllocator(a.arena)); |
|
| 40 | + | export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) { |
|
| 41 | + | a.data.append(byte, alloc::arenaAllocator(&mut *a.arena)); |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Emit a little-endian integer with `bytes` bytes. |
|
| 45 | - | fn emitDataInt(a: *mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) { |
|
| 45 | + | unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) { |
|
| 46 | 46 | for i in 0..bytes { |
|
| 47 | 47 | try emitByte(a, ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8); |
|
| 48 | 48 | } |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Patch a little-endian integer with `bytes` bytes. |
|
| 52 | - | fn patchDataInt(a: *mut super::Assembler, offset: u32, bits: u64, bytes: u32) { |
|
| 52 | + | fn patchDataInt(a: &mut super::Assembler, offset: u32, bits: u64, bytes: u32) { |
|
| 53 | 53 | for i in 0..bytes { |
|
| 54 | 54 | set a.data[offset + i] = ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8; |
|
| 55 | 55 | } |
|
| 56 | 56 | } |
|
| 57 | 57 | ||
| 58 | 58 | /// Emit an integer data directive value. |
|
| 59 | - | export fn emitDataValue(a: *mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) { |
|
| 59 | + | export unsafe fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) { |
|
| 60 | 60 | match width { |
|
| 61 | 61 | case super::DataWidth::Word => try emitDataInt(a, value as u64, rv64::WORD_SIZE as u32), |
|
| 62 | 62 | case super::DataWidth::Dword => try emitDataInt(a, value as u64, rv64::DWORD_SIZE as u32), |
|
| 63 | 63 | } |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Record a data-section symbol fixup and reserve its bytes. |
|
| 67 | - | export fn recordDataFixup(a: *mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) { |
|
| 67 | + | export unsafe fn recordDataFixup(a: &mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) { |
|
| 68 | 68 | let offset = a.data.len; |
|
| 69 | 69 | match width { |
|
| 70 | 70 | case super::DataWidth::Word => { |
|
| 71 | 71 | recordFixup(a, target, super::FixupInfo::Word { offset }); |
|
| 72 | 72 | try emitDataInt(a, 0, rv64::WORD_SIZE as u32); |
| 77 | 77 | } |
|
| 78 | 78 | } |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Record a pending symbol fixup. |
|
| 82 | - | fn recordFixup(a: *mut super::Assembler, symbol: *[u8], info: super::FixupInfo) { |
|
| 83 | - | a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(a.arena)); |
|
| 82 | + | unsafe fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) { |
|
| 83 | + | a.fixups.append(super::Fixup { symbol, info }, alloc::arenaAllocator(&mut *a.arena)); |
|
| 84 | 84 | } |
|
| 85 | 85 | ||
| 86 | 86 | /// Record a text fixup that must be resolved after all program text is known. |
|
| 87 | - | fn recordExternalFixup(a: *mut super::Assembler, fixup: super::Fixup) { |
|
| 88 | - | a.externalFixups.append(fixup, alloc::arenaAllocator(a.arena)); |
|
| 87 | + | unsafe fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) { |
|
| 88 | + | a.externalFixups.append(fixup, alloc::arenaAllocator(&mut *a.arena)); |
|
| 89 | 89 | } |
|
| 90 | 90 | ||
| 91 | 91 | /// Record a text-section symbol fixup and reserve its instruction words. |
|
| 92 | - | export fn recordTextFixup(a: *mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) { |
|
| 92 | + | export unsafe fn recordTextFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) { |
|
| 93 | 93 | recordFixup(a, symbol, info); |
|
| 94 | 94 | try emitTextPadding(a, words); |
|
| 95 | 95 | } |
|
| 96 | 96 | ||
| 97 | 97 | /// Find a previously defined symbol by name. |
|
| 98 | - | fn findSymbol(a: *super::Assembler, name: *[u8]) -> ?super::Symbol { |
|
| 98 | + | fn findSymbol(a: &super::Assembler, name: *[u8]) -> ?super::Symbol { |
|
| 99 | 99 | let idx = dict::get(&a.symbolMap, name) |
|
| 100 | 100 | else return nil; |
|
| 101 | 101 | return a.symbols[idx as u32]; |
|
| 102 | 102 | } |
|
| 103 | 103 | ||
| 104 | 104 | /// Return the final address for a data symbol. |
|
| 105 | - | fn dataSymbolAddr(a: *super::Assembler, symbol: super::Symbol) -> i32 throws (super::Error) { |
|
| 105 | + | fn dataSymbolAddr(a: &super::Assembler, symbol: super::Symbol) -> i32 throws (super::Error) { |
|
| 106 | 106 | if symbol.section <> super::Section::Data { |
|
| 107 | 107 | throw super::Error::Invalid { offset: 0, message: "data address target must be in data section" }; |
|
| 108 | 108 | } |
|
| 109 | 109 | return symbol.offset + (a.dataBase as i32); |
|
| 110 | 110 | } |
|
| 111 | 111 | ||
| 112 | 112 | /// Resolve final symbol references and patch all delayed output. |
|
| 113 | - | export fn finishProgram(a: *mut super::Assembler) throws (super::Error) { |
|
| 113 | + | export unsafe fn finishProgram(a: &mut super::Assembler) throws (super::Error) { |
|
| 114 | 114 | for i in 0..a.fixups.len { |
|
| 115 | 115 | let fixup = a.fixups[i]; |
|
| 116 | 116 | let symbol = findSymbol(a, fixup.symbol) else { |
|
| 117 | 117 | match fixup.info { |
|
| 118 | 118 | case super::FixupInfo::Jal { .. }, super::FixupInfo::Addr { .. } => { |
| 185 | 185 | case super::BranchOp::Bgt => return encode::bgt(rs1, rs2, imm), |
|
| 186 | 186 | } |
|
| 187 | 187 | } |
|
| 188 | 188 | ||
| 189 | 189 | /// Decode string literal escapes and emit the resulting data bytes. |
|
| 190 | - | export fn emitDecodedString(a: *mut super::Assembler, literal: *[u8]) throws (super::Error) { |
|
| 190 | + | export unsafe fn emitDecodedString(a: &mut super::Assembler, literal: *[u8]) throws (super::Error) { |
|
| 191 | 191 | let raw = &literal[super::QUOTE_DELIM_LEN..literal.len - super::QUOTE_DELIM_LEN]; |
|
| 192 | 192 | let mut i: u32 = 0; |
|
| 193 | 193 | ||
| 194 | 194 | while i < raw.len { |
|
| 195 | 195 | if raw[i] == '\\' and i + 1 < raw.len { |
lib/std/arch/rv64/asm/parser.rad
+62 -62
| 19 | 19 | /// Signed byte offset preceding the base register. |
|
| 20 | 20 | offset: i32, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// Parse assembler source into the supplied assembler state. |
|
| 24 | - | export fn parseProgram(a: *mut super::Assembler) throws (super::Error) { |
|
| 24 | + | export unsafe fn parseProgram(a: &mut super::Assembler) throws (super::Error) { |
|
| 25 | 25 | advance(a); |
|
| 26 | 26 | ||
| 27 | 27 | while a.scan.current.kind <> scanner::TokenKind::Eof { |
|
| 28 | 28 | try parseItem(a); |
|
| 29 | 29 | } |
| 37 | 37 | } |
|
| 38 | 38 | return mem::alignUp(value, alignment); |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Advance the parser by one token, preserving the previous token. |
|
| 42 | - | fn advance(a: *mut super::Assembler) { |
|
| 42 | + | fn advance(a: &mut super::Assembler) { |
|
| 43 | 43 | set a.scan.previous = a.scan.current; |
|
| 44 | 44 | set a.scan.current = scanner::next(&mut a.scan); |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | 47 | /// Consume the current token when it has `kind`. |
|
| 48 | - | fn consume(a: *mut super::Assembler, kind: scanner::TokenKind) -> bool { |
|
| 48 | + | fn consume(a: &mut super::Assembler, kind: scanner::TokenKind) -> bool { |
|
| 49 | 49 | if a.scan.current.kind == kind { |
|
| 50 | 50 | advance(a); |
|
| 51 | 51 | return true; |
|
| 52 | 52 | } |
|
| 53 | 53 | return false; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Create an error at the current token. |
|
| 57 | - | fn fail(a: *super::Assembler, message: *[u8]) -> super::Error { |
|
| 57 | + | fn fail(a: &super::Assembler, message: *[u8]) -> super::Error { |
|
| 58 | 58 | return super::Error::Invalid { offset: a.scan.current.offset, message }; |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Create an error at `tok`. |
|
| 62 | 62 | fn failOnToken(tok: scanner::Token, message: *[u8]) -> super::Error { |
|
| 63 | 63 | return super::Error::Invalid { offset: tok.offset, message }; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Require that a data directive appears while assembling the data section. |
|
| 67 | - | fn expectDataSection(a: *super::Assembler, tok: scanner::Token) throws (super::Error) { |
|
| 67 | + | fn expectDataSection(a: &super::Assembler, tok: scanner::Token) throws (super::Error) { |
|
| 68 | 68 | if a.section <> super::Section::Data { |
|
| 69 | 69 | throw failOnToken(tok, "data directive is only valid in the data section"); |
|
| 70 | 70 | } |
|
| 71 | 71 | } |
|
| 72 | 72 | ||
| 73 | 73 | /// Consume `kind` or throw `message` at the current token. |
|
| 74 | - | fn expect(a: *mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) throws (super::Error) { |
|
| 74 | + | fn expect(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) throws (super::Error) { |
|
| 75 | 75 | if not consume(a, kind) { |
|
| 76 | 76 | throw fail(a, message); |
|
| 77 | 77 | } |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// Consume `kind` and return the consumed token. |
|
| 81 | - | fn expectToken(a: *mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) -> scanner::Token throws (super::Error) { |
|
| 81 | + | fn expectToken(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) -> scanner::Token throws (super::Error) { |
|
| 82 | 82 | try expect(a, kind, message); |
|
| 83 | 83 | return a.scan.previous; |
|
| 84 | 84 | } |
|
| 85 | 85 | ||
| 86 | 86 | /// Require that the current item has reached its semicolon terminator. |
|
| 87 | - | fn expectTerminator(a: *super::Assembler, message: *[u8]) throws (super::Error) { |
|
| 87 | + | fn expectTerminator(a: &super::Assembler, message: *[u8]) throws (super::Error) { |
|
| 88 | 88 | if a.scan.current.kind <> scanner::TokenKind::Semicolon { |
|
| 89 | 89 | throw fail(a, message); |
|
| 90 | 90 | } |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | 93 | /// Require that `value` fits in i32. |
|
| 94 | - | fn expectI32Value(a: *super::Assembler, value: i64, message: *[u8]) -> i32 throws (super::Error) { |
|
| 94 | + | fn expectI32Value(a: &super::Assembler, value: i64, message: *[u8]) -> i32 throws (super::Error) { |
|
| 95 | 95 | if value < -super::I32_MIN_MAGNITUDE or value > super::I32_MAX_VALUE { |
|
| 96 | 96 | throw fail(a, message); |
|
| 97 | 97 | } |
|
| 98 | 98 | return value as i32; |
|
| 99 | 99 | } |
|
| 100 | 100 | ||
| 101 | 101 | /// Require that `value` fits in a signed 12-bit immediate field. |
|
| 102 | - | fn expectSmallImmValue(a: *super::Assembler, value: i64) -> i32 throws (super::Error) { |
|
| 102 | + | fn expectSmallImmValue(a: &super::Assembler, value: i64) -> i32 throws (super::Error) { |
|
| 103 | 103 | if not encode::isSmallImm64(value) { |
|
| 104 | 104 | throw fail(a, "immediate out of range"); |
|
| 105 | 105 | } |
|
| 106 | 106 | return value as i32; |
|
| 107 | 107 | } |
|
| 108 | 108 | ||
| 109 | 109 | /// Define a label at the current text or data offset. |
|
| 110 | - | fn defineSymbol(a: *mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
|
| 110 | + | unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
|
| 111 | 111 | if dict::get(&a.symbolMap, name) <> nil { |
|
| 112 | 112 | throw failOnToken(tok, "duplicate label"); |
|
| 113 | 113 | } |
|
| 114 | 114 | emit::defineSymbol(a, name); |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | 117 | /// Emit a parsed integer data value after applying source-level range checks. |
|
| 118 | - | fn emitDataValue(a: *mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) { |
|
| 118 | + | unsafe fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) { |
|
| 119 | 119 | match width { |
|
| 120 | 120 | case super::DataWidth::Word => |
|
| 121 | 121 | try emit::emitDataValue(a, (try expectI32Value(a, value, "word literal out of range")) as i64, width), |
|
| 122 | 122 | case super::DataWidth::Dword => |
|
| 123 | 123 | try emit::emitDataValue(a, value, width), |
|
| 124 | 124 | } |
|
| 125 | 125 | } |
|
| 126 | 126 | ||
| 127 | 127 | /// Parse a possibly scoped name from one or more `::`-separated segments. |
|
| 128 | 128 | fn parseScopedName( |
|
| 129 | - | a: *mut super::Assembler, |
|
| 129 | + | a: &mut super::Assembler, |
|
| 130 | 130 | kind: scanner::TokenKind, |
|
| 131 | 131 | message: *[u8], |
|
| 132 | 132 | trimPrefix: u32 |
|
| 133 | 133 | ) -> *[u8] throws (super::Error) { |
|
| 134 | 134 | let first = try expectToken(a, kind, message); |
| 141 | 141 | } |
|
| 142 | 142 | return strings::intern(a.scan.pool, &a.scan.source[start..end]); |
|
| 143 | 143 | } |
|
| 144 | 144 | ||
| 145 | 145 | /// Parse a bare symbol name. |
|
| 146 | - | fn parseSymbolName(a: *mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 146 | + | fn parseSymbolName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 147 | 147 | return try parseScopedName(a, scanner::TokenKind::Ident, "expected symbol name", 0); |
|
| 148 | 148 | } |
|
| 149 | 149 | ||
| 150 | 150 | /// Return `true` when [`tok`] is any label token form. |
|
| 151 | 151 | fn isLabel(tok: scanner::TokenKind) -> bool { |
|
| 152 | 152 | return tok == scanner::TokenKind::Label or tok == scanner::TokenKind::QuotedLabel; |
|
| 153 | 153 | } |
|
| 154 | 154 | ||
| 155 | 155 | /// Parse the contents of a quoted label token, decoding escapes as needed. |
|
| 156 | - | fn parseQuotedLabelName(a: *mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 156 | + | unsafe fn parseQuotedLabelName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 157 | 157 | let tok = try expectToken(a, scanner::TokenKind::QuotedLabel, "expected label name"); |
|
| 158 | 158 | let rawStart = super::LABEL_SIGIL_LEN + super::QUOTE_DELIM_LEN; |
|
| 159 | 159 | let raw = &tok.source[rawStart..tok.source.len - super::QUOTE_DELIM_LEN]; |
|
| 160 | - | let storage = try alloc::allocSlice(a.arena, 1, 1, raw.len) catch { |
|
| 160 | + | let storage = try alloc::allocSlice(&mut *a.arena, 1, 1, raw.len) catch { |
|
| 161 | 161 | panic "asm: out of memory allocating quoted label"; |
|
| 162 | 162 | } as *mut [u8]; |
|
| 163 | 163 | let len = fmt::unescapeString(raw, storage); |
|
| 164 | 164 | ||
| 165 | 165 | return strings::intern(a.scan.pool, &storage[..len]); |
|
| 166 | 166 | } |
|
| 167 | 167 | ||
| 168 | 168 | /// Parse a label reference or definition name. |
|
| 169 | - | fn parseLabelName(a: *mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 169 | + | unsafe fn parseLabelName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 170 | 170 | if a.scan.current.kind == scanner::TokenKind::QuotedLabel { |
|
| 171 | 171 | return try parseQuotedLabelName(a); |
|
| 172 | 172 | } |
|
| 173 | 173 | return try parseScopedName(a, scanner::TokenKind::Label, "expected label name", super::LABEL_SIGIL_LEN); |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Parse a directive name without its leading `.`. |
|
| 177 | - | fn parseDirectiveName(a: *mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 177 | + | fn parseDirectiveName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
|
| 178 | 178 | let name = try expectToken(a, scanner::TokenKind::Directive, "expected directive name"); |
|
| 179 | 179 | return &name.source[super::DIRECTIVE_SIGIL_LEN..]; |
|
| 180 | 180 | } |
|
| 181 | 181 | ||
| 182 | 182 | /// Parse one top-level assembler item. |
|
| 183 | - | fn parseItem(a: *mut super::Assembler) throws (super::Error) { |
|
| 183 | + | unsafe fn parseItem(a: &mut super::Assembler) throws (super::Error) { |
|
| 184 | 184 | match a.scan.current.kind { |
|
| 185 | 185 | case scanner::TokenKind::Ident => { |
|
| 186 | 186 | let tok = a.scan.current; |
|
| 187 | 187 | let name = try parseSymbolName(a); |
|
| 188 | 188 | try parseInstruction(a, name, tok); |
| 277 | 277 | }; |
|
| 278 | 278 | return super::CSRS[index].csr; |
|
| 279 | 279 | } |
|
| 280 | 280 | ||
| 281 | 281 | /// Parse an instruction after its mnemonic has already been consumed. |
|
| 282 | - | fn parseInstruction(a: *mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
|
| 282 | + | unsafe fn parseInstruction(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
|
| 283 | 283 | if a.section <> super::Section::Text { |
|
| 284 | 284 | throw failOnToken(tok, "instructions are only valid in the text section"); |
|
| 285 | 285 | } |
|
| 286 | 286 | let form = lookupInstruction(name) else { |
|
| 287 | 287 | throw failOnToken(tok, "unknown instruction"); |
| 316 | 316 | case super::InstructionEncoder::Upper { enc } => return try parseUpper(a, enc), |
|
| 317 | 317 | } |
|
| 318 | 318 | } |
|
| 319 | 319 | ||
| 320 | 320 | /// Parse the `li` pseudo-instruction. |
|
| 321 | - | fn parseLi(a: *mut super::Assembler) throws (super::Error) { |
|
| 321 | + | unsafe fn parseLi(a: &mut super::Assembler) throws (super::Error) { |
|
| 322 | 322 | let rd = try parseRegister(a); |
|
| 323 | 323 | let value = try parseValue(a); |
|
| 324 | 324 | if encode::isSmallImm64(value) { |
|
| 325 | 325 | try emit::emitText(a, encode::addi(rd, rv64::ZERO, value as i32)); |
|
| 326 | 326 | return; |
| 331 | 331 | try emit::emitText(a, encode::lui(rd, split.hi)); |
|
| 332 | 332 | try emit::emitText(a, encode::addi(rd, rd, split.lo)); |
|
| 333 | 333 | } |
|
| 334 | 334 | ||
| 335 | 335 | /// Parse the `la` pseudo-instruction. |
|
| 336 | - | fn parseLa(a: *mut super::Assembler) throws (super::Error) { |
|
| 336 | + | unsafe fn parseLa(a: &mut super::Assembler) throws (super::Error) { |
|
| 337 | 337 | let rd = try parseRegister(a); |
|
| 338 | 338 | let target = try parseLabelName(a); |
|
| 339 | 339 | let index = a.text.len; |
|
| 340 | 340 | ||
| 341 | 341 | try emit::recordTextFixup(a, target, super::FixupInfo::Addr { rd, index }, 2); |
|
| 342 | 342 | } |
|
| 343 | 343 | ||
| 344 | 344 | /// Parse a CSR read-like instruction with destination register then CSR. |
|
| 345 | - | fn parseRdCsr(a: *mut super::Assembler, enc: fn(gen::Reg, u32) -> u32) throws (super::Error) { |
|
| 345 | + | unsafe fn parseRdCsr(a: &mut super::Assembler, enc: fn(gen::Reg, u32) -> u32) throws (super::Error) { |
|
| 346 | 346 | let rd = try parseRegister(a); |
|
| 347 | 347 | let csr = try parseCsr(a); |
|
| 348 | 348 | ||
| 349 | 349 | try emit::emitText(a, enc(rd, csr)); |
|
| 350 | 350 | } |
|
| 351 | 351 | ||
| 352 | 352 | /// Parse a CSR write-like instruction with CSR then source register. |
|
| 353 | - | fn parseCsrRs1(a: *mut super::Assembler, enc: fn(u32, gen::Reg) -> u32) throws (super::Error) { |
|
| 353 | + | unsafe fn parseCsrRs1(a: &mut super::Assembler, enc: fn(u32, gen::Reg) -> u32) throws (super::Error) { |
|
| 354 | 354 | let csr = try parseCsr(a); |
|
| 355 | 355 | let rs1 = try parseRegister(a); |
|
| 356 | 356 | ||
| 357 | 357 | try emit::emitText(a, enc(csr, rs1)); |
|
| 358 | 358 | } |
|
| 359 | 359 | ||
| 360 | 360 | /// Parse `csrrw`. |
|
| 361 | - | fn parseCsrrw(a: *mut super::Assembler) throws (super::Error) { |
|
| 361 | + | unsafe fn parseCsrrw(a: &mut super::Assembler) throws (super::Error) { |
|
| 362 | 362 | let rd = try parseRegister(a); |
|
| 363 | 363 | let csr = try parseCsr(a); |
|
| 364 | 364 | let rs1 = try parseRegister(a); |
|
| 365 | 365 | ||
| 366 | 366 | try emit::emitText(a, encode::csrrw(rd, csr, rs1)); |
|
| 367 | 367 | } |
|
| 368 | 368 | ||
| 369 | 369 | /// Parse a CSR immediate instruction. |
|
| 370 | - | fn parseCsrsi(a: *mut super::Assembler) throws (super::Error) { |
|
| 370 | + | unsafe fn parseCsrsi(a: &mut super::Assembler) throws (super::Error) { |
|
| 371 | 371 | let csr = try parseCsr(a); |
|
| 372 | 372 | let imm = try parseValue(a); |
|
| 373 | 373 | if imm < 0 or imm >= super::CSR_IMM_LIMIT { |
|
| 374 | 374 | throw fail(a, "CSR immediate out of range"); |
|
| 375 | 375 | } |
|
| 376 | 376 | try emit::emitText(a, encode::csrsi(csr, imm as u32)); |
|
| 377 | 377 | } |
|
| 378 | 378 | ||
| 379 | 379 | /// Parse a two-register instruction. |
|
| 380 | - | fn parseRR(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg) -> u32) throws (super::Error) { |
|
| 380 | + | unsafe fn parseRR(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg) -> u32) throws (super::Error) { |
|
| 381 | 381 | let rd = try parseRegister(a); |
|
| 382 | 382 | let rs = try parseRegister(a); |
|
| 383 | 383 | ||
| 384 | 384 | try emit::emitText(a, enc(rd, rs)); |
|
| 385 | 385 | } |
|
| 386 | 386 | ||
| 387 | 387 | /// Parse a three-register instruction. |
|
| 388 | - | fn parseRRR(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg, gen::Reg) -> u32) throws (super::Error) { |
|
| 388 | + | unsafe fn parseRRR(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, gen::Reg) -> u32) throws (super::Error) { |
|
| 389 | 389 | let rd = try parseRegister(a); |
|
| 390 | 390 | let rs1 = try parseRegister(a); |
|
| 391 | 391 | let rs2 = try parseRegister(a); |
|
| 392 | 392 | ||
| 393 | 393 | try emit::emitText(a, enc(rd, rs1, rs2)); |
|
| 394 | 394 | } |
|
| 395 | 395 | ||
| 396 | 396 | /// Parse a register-register-immediate instruction. |
|
| 397 | - | fn parseRRI(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
|
| 397 | + | unsafe fn parseRRI(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
|
| 398 | 398 | let rd = try parseRegister(a); |
|
| 399 | 399 | let rs1 = try parseRegister(a); |
|
| 400 | 400 | let imm = try parseSmallImm(a); |
|
| 401 | 401 | ||
| 402 | 402 | try emit::emitText(a, enc(rd, rs1, imm)); |
|
| 403 | 403 | } |
|
| 404 | 404 | ||
| 405 | 405 | /// Parse a shift-immediate instruction and enforce its RV64 shift bound. |
|
| 406 | - | fn parseShift( |
|
| 407 | - | a: *mut super::Assembler, |
|
| 406 | + | unsafe fn parseShift( |
|
| 407 | + | a: &mut super::Assembler, |
|
| 408 | 408 | enc: fn(gen::Reg, gen::Reg, i32) -> u32, |
|
| 409 | 409 | limit: i32, |
|
| 410 | 410 | message: *[u8] |
|
| 411 | 411 | ) throws (super::Error) { |
|
| 412 | 412 | let rd = try parseRegister(a); |
| 423 | 423 | ||
| 424 | 424 | try emit::emitText(a, enc(rd, rs1, shamt)); |
|
| 425 | 425 | } |
|
| 426 | 426 | ||
| 427 | 427 | /// Parse a load instruction with a memory operand. |
|
| 428 | - | fn parseLoad(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
|
| 428 | + | unsafe fn parseLoad(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
|
| 429 | 429 | let rd = try parseRegister(a); |
|
| 430 | 430 | let memop = try parseMemory(a); |
|
| 431 | 431 | ||
| 432 | 432 | try emit::emitText(a, enc(rd, memop.base, memop.offset)); |
|
| 433 | 433 | } |
|
| 434 | 434 | ||
| 435 | 435 | /// Parse a store instruction with a memory operand. |
|
| 436 | - | fn parseStore(a: *mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
|
| 436 | + | unsafe fn parseStore(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
|
| 437 | 437 | let rs2 = try parseRegister(a); |
|
| 438 | 438 | let memop = try parseMemory(a); |
|
| 439 | 439 | ||
| 440 | 440 | try emit::emitText(a, enc(rs2, memop.base, memop.offset)); |
|
| 441 | 441 | } |
|
| 442 | 442 | ||
| 443 | 443 | /// Parse a two-register branch instruction. |
|
| 444 | - | fn parseBranch(a: *mut super::Assembler, op: super::BranchOp) throws (super::Error) { |
|
| 444 | + | unsafe fn parseBranch(a: &mut super::Assembler, op: super::BranchOp) throws (super::Error) { |
|
| 445 | 445 | let rs1 = try parseRegister(a); |
|
| 446 | 446 | let rs2 = try parseRegister(a); |
|
| 447 | 447 | ||
| 448 | 448 | try parseBranchLabel(a, op, rs1, rs2); |
|
| 449 | 449 | } |
|
| 450 | 450 | ||
| 451 | 451 | /// Parse an optional label operand. |
|
| 452 | - | fn parseOptionalLabel(a: *mut super::Assembler) -> ?*[u8] throws (super::Error) { |
|
| 452 | + | unsafe fn parseOptionalLabel(a: &mut super::Assembler) -> ?*[u8] throws (super::Error) { |
|
| 453 | 453 | if not isLabel(a.scan.current.kind) { |
|
| 454 | 454 | return nil; |
|
| 455 | 455 | } |
|
| 456 | 456 | return try parseLabelName(a); |
|
| 457 | 457 | } |
|
| 458 | 458 | ||
| 459 | 459 | /// Parse a branch target as either a label fixup or immediate offset. |
|
| 460 | - | fn parseBranchLabel(a: *mut super::Assembler, op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg) throws (super::Error) { |
|
| 460 | + | unsafe fn parseBranchLabel(a: &mut super::Assembler, op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg) throws (super::Error) { |
|
| 461 | 461 | let index = a.text.len; |
|
| 462 | 462 | if let target = try parseOptionalLabel(a) { |
|
| 463 | 463 | try emit::recordTextFixup(a, target, super::FixupInfo::Branch { op, rs1, rs2, index }, 1); |
|
| 464 | 464 | return; |
|
| 465 | 465 | } |
|
| 466 | 466 | let imm = try parseBranchImm(a); |
|
| 467 | 467 | try emit::emitText(a, emit::encodeBranch(op, rs1, rs2, imm)); |
|
| 468 | 468 | } |
|
| 469 | 469 | ||
| 470 | 470 | /// Parse a branch-to-zero pseudo-instruction. |
|
| 471 | - | fn parseBranchZero(a: *mut super::Assembler, op: super::BranchOp) throws (super::Error) { |
|
| 471 | + | unsafe fn parseBranchZero(a: &mut super::Assembler, op: super::BranchOp) throws (super::Error) { |
|
| 472 | 472 | let rs = try parseRegister(a); |
|
| 473 | 473 | try parseBranchLabel(a, op, rs, rv64::ZERO); |
|
| 474 | 474 | } |
|
| 475 | 475 | ||
| 476 | 476 | /// Parse `jal` with an explicit destination register. |
|
| 477 | - | fn parseJal(a: *mut super::Assembler) throws (super::Error) { |
|
| 477 | + | unsafe fn parseJal(a: &mut super::Assembler) throws (super::Error) { |
|
| 478 | 478 | let rd = try parseRegister(a); |
|
| 479 | 479 | try parseJ(a, rd); |
|
| 480 | 480 | } |
|
| 481 | 481 | ||
| 482 | 482 | /// Parse a jump target for `jal` or a jump pseudo-instruction. |
|
| 483 | - | fn parseJ(a: *mut super::Assembler, rd: gen::Reg) throws (super::Error) { |
|
| 483 | + | unsafe fn parseJ(a: &mut super::Assembler, rd: gen::Reg) throws (super::Error) { |
|
| 484 | 484 | let index = a.text.len; |
|
| 485 | 485 | if let target = try parseOptionalLabel(a) { |
|
| 486 | 486 | try emit::recordTextFixup(a, target, super::FixupInfo::Jal { rd, index }, 1); |
|
| 487 | 487 | return; |
|
| 488 | 488 | } |
|
| 489 | 489 | let imm = try parseJumpImm(a); |
|
| 490 | 490 | try emit::emitText(a, encode::jal(rd, imm)); |
|
| 491 | 491 | } |
|
| 492 | 492 | ||
| 493 | 493 | /// Parse an upper-immediate instruction. |
|
| 494 | - | fn parseUpper(a: *mut super::Assembler, enc: fn(gen::Reg, i32) -> u32) throws (super::Error) { |
|
| 494 | + | unsafe fn parseUpper(a: &mut super::Assembler, enc: fn(gen::Reg, i32) -> u32) throws (super::Error) { |
|
| 495 | 495 | let rd = try parseRegister(a); |
|
| 496 | 496 | let imm64 = try parseValue(a); |
|
| 497 | 497 | if imm64 < 0 or imm64 > super::UPPER_IMM_MAX_VALUE { |
|
| 498 | 498 | throw fail(a, "upper immediate out of range"); |
|
| 499 | 499 | } |
|
| 500 | 500 | try emit::emitText(a, enc(rd, imm64 as i32)); |
|
| 501 | 501 | } |
|
| 502 | 502 | ||
| 503 | 503 | /// Parse a directive after its name has already been consumed. |
|
| 504 | - | fn parseDirective(a: *mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
|
| 504 | + | unsafe fn parseDirective(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
|
| 505 | 505 | let directive = classifyDirective(name) else { |
|
| 506 | 506 | throw failOnToken(tok, "unknown directive"); |
|
| 507 | 507 | }; |
|
| 508 | 508 | match directive { |
|
| 509 | 509 | case super::DirectiveKind::Text => { |
| 544 | 544 | } |
|
| 545 | 545 | } |
|
| 546 | 546 | } |
|
| 547 | 547 | ||
| 548 | 548 | /// Parse a `.constant` directive. |
|
| 549 | - | fn parseConstantDirective(a: *mut super::Assembler) throws (super::Error) { |
|
| 549 | + | fn parseConstantDirective(a: &mut super::Assembler) throws (super::Error) { |
|
| 550 | 550 | let name = try parseSymbolName(a); |
|
| 551 | 551 | let value = try expectI32Value(a, try parseExpr(a), "constant out of range"); |
|
| 552 | 552 | ||
| 553 | 553 | dict::insert(&mut a.constMap, name, value); |
|
| 554 | 554 | } |
|
| 555 | 555 | ||
| 556 | 556 | /// Parse a `.export` directive. |
|
| 557 | - | fn parseExportDirective(a: *mut super::Assembler) throws (super::Error) { |
|
| 557 | + | unsafe fn parseExportDirective(a: &mut super::Assembler) throws (super::Error) { |
|
| 558 | 558 | let name = try parseLabelName(a); |
|
| 559 | 559 | dict::insert(&mut a.exportMap, name, 1); |
|
| 560 | 560 | if let idx = dict::get(&a.symbolMap, name) { |
|
| 561 | 561 | set a.symbols[idx as u32].isExported = true; |
|
| 562 | 562 | } |
|
| 563 | 563 | } |
|
| 564 | 564 | ||
| 565 | 565 | /// Parse a `.space` directive. |
|
| 566 | - | fn parseSpaceDirective(a: *mut super::Assembler) throws (super::Error) { |
|
| 566 | + | unsafe fn parseSpaceDirective(a: &mut super::Assembler) throws (super::Error) { |
|
| 567 | 567 | let count = try parseValue(a); |
|
| 568 | 568 | if count < 0 { |
|
| 569 | 569 | throw fail(a, "space size must be non-negative"); |
|
| 570 | 570 | } |
|
| 571 | 571 | // The data section grows on demand; only reject sizes that cannot be |
| 577 | 577 | try emit::emitByte(a, 0); |
|
| 578 | 578 | } |
|
| 579 | 579 | } |
|
| 580 | 580 | ||
| 581 | 581 | /// Parse an `.align` directive for the current section. |
|
| 582 | - | fn parseAlignDirective(a: *mut super::Assembler) throws (super::Error) { |
|
| 582 | + | unsafe fn parseAlignDirective(a: &mut super::Assembler) throws (super::Error) { |
|
| 583 | 583 | let amount64 = try parseValue(a); |
|
| 584 | 584 | if amount64 <= 0 { |
|
| 585 | 585 | throw fail(a, "alignment must be positive"); |
|
| 586 | 586 | } |
|
| 587 | 587 | if amount64 > super::U32_MAX_VALUE { |
| 613 | 613 | } |
|
| 614 | 614 | } |
|
| 615 | 615 | } |
|
| 616 | 616 | ||
| 617 | 617 | /// Parse a `.byte` directive. |
|
| 618 | - | fn parseByteDirective(a: *mut super::Assembler) throws (super::Error) { |
|
| 618 | + | unsafe fn parseByteDirective(a: &mut super::Assembler) throws (super::Error) { |
|
| 619 | 619 | loop { |
|
| 620 | 620 | if a.scan.current.kind == scanner::TokenKind::Char { |
|
| 621 | 621 | let ch = parseCharLiteral(a.scan.current) else { |
|
| 622 | 622 | throw fail(a, "invalid char literal"); |
|
| 623 | 623 | }; |
| 635 | 635 | } |
|
| 636 | 636 | } |
|
| 637 | 637 | } |
|
| 638 | 638 | ||
| 639 | 639 | /// Parse a fixed-width integer data directive. |
|
| 640 | - | fn parseIntDirective(a: *mut super::Assembler, width: super::DataWidth) throws (super::Error) { |
|
| 640 | + | unsafe fn parseIntDirective(a: &mut super::Assembler, width: super::DataWidth) throws (super::Error) { |
|
| 641 | 641 | loop { |
|
| 642 | 642 | if isLabel(a.scan.current.kind) { |
|
| 643 | 643 | let target = try parseLabelName(a); |
|
| 644 | 644 | try emit::recordDataFixup(a, target, width); |
|
| 645 | 645 | } else if a.scan.current.kind == scanner::TokenKind::Char { |
| 656 | 656 | } |
|
| 657 | 657 | } |
|
| 658 | 658 | } |
|
| 659 | 659 | ||
| 660 | 660 | /// Parse a `.ascii` string literal list. |
|
| 661 | - | fn parseStringDirective(a: *mut super::Assembler) throws (super::Error) { |
|
| 661 | + | unsafe fn parseStringDirective(a: &mut super::Assembler) throws (super::Error) { |
|
| 662 | 662 | loop { |
|
| 663 | 663 | let literal = try expectToken(a, scanner::TokenKind::String, "expected string literal"); |
|
| 664 | 664 | try emit::emitDecodedString(a, literal.source); |
|
| 665 | 665 | if not consume(a, scanner::TokenKind::Comma) { |
|
| 666 | 666 | return; |
|
| 667 | 667 | } |
|
| 668 | 668 | } |
|
| 669 | 669 | } |
|
| 670 | 670 | ||
| 671 | 671 | /// Parse and resolve a register operand. |
|
| 672 | - | fn parseRegister(a: *mut super::Assembler) -> gen::Reg throws (super::Error) { |
|
| 672 | + | fn parseRegister(a: &mut super::Assembler) -> gen::Reg throws (super::Error) { |
|
| 673 | 673 | let tok = try expectToken(a, scanner::TokenKind::Register, "expected register"); |
|
| 674 | 674 | let reg = lookupRegister(&tok.source[1..]) else { |
|
| 675 | 675 | throw super::Error::Invalid { offset: tok.offset, message: "unknown register" }; |
|
| 676 | 676 | }; |
|
| 677 | 677 | return reg; |
|
| 678 | 678 | } |
|
| 679 | 679 | ||
| 680 | 680 | /// Parse a simple signed immediate or constant value. |
|
| 681 | - | fn parseValue(a: *mut super::Assembler) -> i64 throws (super::Error) { |
|
| 681 | + | fn parseValue(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 682 | 682 | if consume(a, scanner::TokenKind::Minus) { |
|
| 683 | 683 | return -(try parseValuePrimary(a)); |
|
| 684 | 684 | } |
|
| 685 | 685 | return try parseValuePrimary(a); |
|
| 686 | 686 | } |
|
| 687 | 687 | ||
| 688 | 688 | /// Parse the primary form used by simple immediate values. |
|
| 689 | - | fn parseValuePrimary(a: *mut super::Assembler) -> i64 throws (super::Error) { |
|
| 689 | + | fn parseValuePrimary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 690 | 690 | if a.scan.current.kind == scanner::TokenKind::Number { |
|
| 691 | 691 | return try parseInteger(a); |
|
| 692 | 692 | } |
|
| 693 | 693 | if a.scan.current.kind == scanner::TokenKind::Ident { |
|
| 694 | 694 | return try parseConstantValue(a); |
|
| 695 | 695 | } |
|
| 696 | 696 | throw fail(a, "expected number or constant"); |
|
| 697 | 697 | } |
|
| 698 | 698 | ||
| 699 | 699 | /// Parse an additive constant expression. |
|
| 700 | - | fn parseExpr(a: *mut super::Assembler) -> i64 throws (super::Error) { |
|
| 700 | + | fn parseExpr(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 701 | 701 | let mut value = try parseExprMul(a); |
|
| 702 | 702 | ||
| 703 | 703 | while a.scan.current.kind == scanner::TokenKind::Plus or a.scan.current.kind == scanner::TokenKind::Minus { |
|
| 704 | 704 | let op = a.scan.current.kind; |
|
| 705 | 705 | advance(a); |
| 713 | 713 | } |
|
| 714 | 714 | return value; |
|
| 715 | 715 | } |
|
| 716 | 716 | ||
| 717 | 717 | /// Parse multiplicative expression operators. |
|
| 718 | - | fn parseExprMul(a: *mut super::Assembler) -> i64 throws (super::Error) { |
|
| 718 | + | fn parseExprMul(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 719 | 719 | let mut value = try parseExprUnary(a); |
|
| 720 | 720 | ||
| 721 | 721 | while a.scan.current.kind == scanner::TokenKind::Star or a.scan.current.kind == scanner::TokenKind::Slash { |
|
| 722 | 722 | let op = a.scan.current.kind; |
|
| 723 | 723 | advance(a); |
| 734 | 734 | } |
|
| 735 | 735 | return value; |
|
| 736 | 736 | } |
|
| 737 | 737 | ||
| 738 | 738 | /// Parse unary expression operators. |
|
| 739 | - | fn parseExprUnary(a: *mut super::Assembler) -> i64 throws (super::Error) { |
|
| 739 | + | fn parseExprUnary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 740 | 740 | if consume(a, scanner::TokenKind::Minus) { |
|
| 741 | 741 | return -(try parseExprUnary(a)); |
|
| 742 | 742 | } |
|
| 743 | 743 | if consume(a, scanner::TokenKind::Plus) { |
|
| 744 | 744 | return try parseExprUnary(a); |
|
| 745 | 745 | } |
|
| 746 | 746 | return try parseExprPrimary(a); |
|
| 747 | 747 | } |
|
| 748 | 748 | ||
| 749 | 749 | /// Parse expression atoms. |
|
| 750 | - | fn parseExprPrimary(a: *mut super::Assembler) -> i64 throws (super::Error) { |
|
| 750 | + | fn parseExprPrimary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 751 | 751 | if consume(a, scanner::TokenKind::LParen) { |
|
| 752 | 752 | let value = try parseExpr(a); |
|
| 753 | 753 | try expect(a, scanner::TokenKind::RParen, "expected `)`"); |
|
| 754 | 754 | return value; |
|
| 755 | 755 | } |
| 761 | 761 | } |
|
| 762 | 762 | throw fail(a, "expected expression"); |
|
| 763 | 763 | } |
|
| 764 | 764 | ||
| 765 | 765 | /// Parse and resolve a named assembler constant. |
|
| 766 | - | fn parseConstantValue(a: *mut super::Assembler) -> i64 throws (super::Error) { |
|
| 766 | + | fn parseConstantValue(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 767 | 767 | let name = try parseSymbolName(a); |
|
| 768 | 768 | let value = dict::get(&a.constMap, name) else { |
|
| 769 | 769 | throw super::Error::Invalid { offset: a.scan.previous.offset, message: "undefined constant" }; |
|
| 770 | 770 | }; |
|
| 771 | 771 | return value as i64; |
|
| 772 | 772 | } |
|
| 773 | 773 | ||
| 774 | 774 | /// Parse and resolve a CSR operand. |
|
| 775 | - | fn parseCsr(a: *mut super::Assembler) -> u32 throws (super::Error) { |
|
| 775 | + | fn parseCsr(a: &mut super::Assembler) -> u32 throws (super::Error) { |
|
| 776 | 776 | let name = try parseSymbolName(a); |
|
| 777 | 777 | let csr = lookupCsr(name) else { |
|
| 778 | 778 | throw super::Error::Invalid { offset: a.scan.previous.offset, message: "unknown CSR" }; |
|
| 779 | 779 | }; |
|
| 780 | 780 | return csr; |
|
| 781 | 781 | } |
|
| 782 | 782 | ||
| 783 | 783 | /// Parse an offset(base) memory operand. |
|
| 784 | - | fn parseMemory(a: *mut super::Assembler) -> MemOperand throws (super::Error) { |
|
| 784 | + | fn parseMemory(a: &mut super::Assembler) -> MemOperand throws (super::Error) { |
|
| 785 | 785 | let mut offset: i32 = 0; |
|
| 786 | 786 | if a.scan.current.kind <> scanner::TokenKind::LParen { |
|
| 787 | 787 | set offset = try expectSmallImmValue(a, try parseValue(a)); |
|
| 788 | 788 | } |
|
| 789 | 789 | try expect(a, scanner::TokenKind::LParen, "expected `(`"); |
| 792 | 792 | ||
| 793 | 793 | return MemOperand { base, offset }; |
|
| 794 | 794 | } |
|
| 795 | 795 | ||
| 796 | 796 | /// Parse an immediate value that fits in a signed 12-bit field. |
|
| 797 | - | fn parseSmallImm(a: *mut super::Assembler) -> i32 throws (super::Error) { |
|
| 797 | + | fn parseSmallImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 798 | 798 | return try expectSmallImmValue(a, try parseValue(a)); |
|
| 799 | 799 | } |
|
| 800 | 800 | ||
| 801 | 801 | /// Parse and validate a branch immediate. |
|
| 802 | - | fn parseBranchImm(a: *mut super::Assembler) -> i32 throws (super::Error) { |
|
| 802 | + | fn parseBranchImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 803 | 803 | let value = try expectI32Value(a, try parseValue(a), "branch immediate out of range"); |
|
| 804 | 804 | if not encode::isBranchImm(value) { |
|
| 805 | 805 | throw fail(a, "branch immediate out of range"); |
|
| 806 | 806 | } |
|
| 807 | 807 | return value; |
|
| 808 | 808 | } |
|
| 809 | 809 | ||
| 810 | 810 | /// Parse and validate a jump immediate. |
|
| 811 | - | fn parseJumpImm(a: *mut super::Assembler) -> i32 throws (super::Error) { |
|
| 811 | + | fn parseJumpImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
|
| 812 | 812 | let value = try expectI32Value(a, try parseValue(a), "jump immediate out of range"); |
|
| 813 | 813 | if not encode::isJumpImm(value) { |
|
| 814 | 814 | throw fail(a, "jump immediate out of range"); |
|
| 815 | 815 | } |
|
| 816 | 816 | return value; |
|
| 817 | 817 | } |
|
| 818 | 818 | ||
| 819 | 819 | /// Parse an integer token as an i64. |
|
| 820 | - | fn parseInteger(a: *mut super::Assembler) -> i64 throws (super::Error) { |
|
| 820 | + | fn parseInteger(a: &mut super::Assembler) -> i64 throws (super::Error) { |
|
| 821 | 821 | let tok = try expectToken(a, scanner::TokenKind::Number, "expected number"); |
|
| 822 | 822 | let value = parseIntegerText(tok.source) else { |
|
| 823 | 823 | throw failOnToken(tok, "invalid integer literal"); |
|
| 824 | 824 | }; |
|
| 825 | 825 | return value; |
lib/std/arch/rv64/asm/scanner.rad
+17 -17
| 95 | 95 | export fn invalid(offset: u32, message: *[u8]) -> Token { |
|
| 96 | 96 | return Token { kind: TokenKind::Invalid, source: message, offset }; |
|
| 97 | 97 | } |
|
| 98 | 98 | ||
| 99 | 99 | /// Return `true` when the scanner has consumed all input. |
|
| 100 | - | export fn isEof(s: *Scanner) -> bool { |
|
| 100 | + | export fn isEof(s: &Scanner) -> bool { |
|
| 101 | 101 | return s.cursor >= s.source.len; |
|
| 102 | 102 | } |
|
| 103 | 103 | ||
| 104 | 104 | /// Return the current character without advancing. |
|
| 105 | - | fn current(s: *Scanner) -> ?u8 { |
|
| 105 | + | fn current(s: &Scanner) -> ?u8 { |
|
| 106 | 106 | if isEof(s) { |
|
| 107 | 107 | return nil; |
|
| 108 | 108 | } |
|
| 109 | 109 | return s.source[s.cursor]; |
|
| 110 | 110 | } |
|
| 111 | 111 | ||
| 112 | 112 | /// Return the next character without advancing. |
|
| 113 | - | fn peek(s: *Scanner) -> ?u8 { |
|
| 113 | + | fn peek(s: &Scanner) -> ?u8 { |
|
| 114 | 114 | if s.cursor + 1 >= s.source.len { |
|
| 115 | 115 | return nil; |
|
| 116 | 116 | } |
|
| 117 | 117 | return s.source[s.cursor + 1]; |
|
| 118 | 118 | } |
|
| 119 | 119 | ||
| 120 | 120 | /// Advance the scanner cursor and return the consumed character. |
|
| 121 | - | fn advance(s: *mut Scanner) -> u8 { |
|
| 121 | + | fn advance(s: &mut Scanner) -> u8 { |
|
| 122 | 122 | set s.cursor += 1; |
|
| 123 | 123 | return s.source[s.cursor - 1]; |
|
| 124 | 124 | } |
|
| 125 | 125 | ||
| 126 | 126 | /// Consume `expected` when it is present at the current cursor. |
|
| 127 | - | fn consume(s: *mut Scanner, expected: u8) -> bool { |
|
| 127 | + | fn consume(s: &mut Scanner, expected: u8) -> bool { |
|
| 128 | 128 | if let ch = current(s); ch == expected { |
|
| 129 | 129 | advance(s); |
|
| 130 | 130 | return true; |
|
| 131 | 131 | } |
|
| 132 | 132 | return false; |
|
| 133 | 133 | } |
|
| 134 | 134 | ||
| 135 | 135 | /// Skip spaces, newlines, tabs, and `//` line comments. |
|
| 136 | - | fn skipWhitespace(s: *mut Scanner) { |
|
| 136 | + | fn skipWhitespace(s: &mut Scanner) { |
|
| 137 | 137 | while let ch = current(s) { |
|
| 138 | 138 | match ch { |
|
| 139 | 139 | case ' ', '\n', '\r', '\t' => advance(s), |
|
| 140 | 140 | case '/' => { |
|
| 141 | 141 | if let nextCh = peek(s); nextCh == '/' { |
| 150 | 150 | } |
|
| 151 | 151 | } |
|
| 152 | 152 | } |
|
| 153 | 153 | ||
| 154 | 154 | /// Return the next assembler token. |
|
| 155 | - | export fn next(s: *mut Scanner) -> Token { |
|
| 155 | + | export fn next(s: &mut Scanner) -> Token { |
|
| 156 | 156 | skipWhitespace(s); |
|
| 157 | 157 | set s.token = s.cursor; |
|
| 158 | 158 | ||
| 159 | 159 | if isEof(s) { |
|
| 160 | 160 | return tok(s, TokenKind::Eof); |
| 191 | 191 | else => return invalid(s.token, "unexpected character"), |
|
| 192 | 192 | } |
|
| 193 | 193 | } |
|
| 194 | 194 | ||
| 195 | 195 | /// Create a token spanning the current scanner range. |
|
| 196 | - | fn tok(s: *Scanner, kind: TokenKind) -> Token { |
|
| 196 | + | fn tok(s: &Scanner, kind: TokenKind) -> Token { |
|
| 197 | 197 | return Token { kind, source: &s.source[s.token..s.cursor], offset: s.token }; |
|
| 198 | 198 | } |
|
| 199 | 199 | ||
| 200 | 200 | /// Scan the identifier continuation characters that follow the current token start. |
|
| 201 | - | fn scanIdentifierBody(s: *mut Scanner) { |
|
| 201 | + | fn scanIdentifierBody(s: &mut Scanner) { |
|
| 202 | 202 | while let ch = current(s); char::isAlpha(ch) or char::isDigit(ch) or ch == '_' { |
|
| 203 | 203 | advance(s); |
|
| 204 | 204 | } |
|
| 205 | 205 | } |
|
| 206 | 206 | ||
| 207 | 207 | /// Scan a signed number when `+` or `-` is followed by a digit, otherwise return the punctuation token. |
|
| 208 | - | fn scanSignedNumberOrToken(s: *mut Scanner, kind: TokenKind) -> Token { |
|
| 208 | + | fn scanSignedNumberOrToken(s: &mut Scanner, kind: TokenKind) -> Token { |
|
| 209 | 209 | if let nextCh = current(s); char::isDigit(nextCh) { |
|
| 210 | 210 | return scanNumber(s); |
|
| 211 | 211 | } |
|
| 212 | 212 | return tok(s, kind); |
|
| 213 | 213 | } |
|
| 214 | 214 | ||
| 215 | 215 | /// Scan a numeric literal. |
|
| 216 | - | fn scanNumber(s: *mut Scanner) -> Token { |
|
| 216 | + | fn scanNumber(s: &mut Scanner) -> Token { |
|
| 217 | 217 | let first = s.source[s.cursor - 1]; |
|
| 218 | 218 | if first == '-' or first == '+' { |
|
| 219 | 219 | advance(s); |
|
| 220 | 220 | } |
|
| 221 | 221 | if s.source[s.cursor - 1] == '0' { |
| 235 | 235 | } |
|
| 236 | 236 | return tok(s, TokenKind::Number); |
|
| 237 | 237 | } |
|
| 238 | 238 | ||
| 239 | 239 | /// Scan a printable token terminated by `delim`. |
|
| 240 | - | fn scanCharsUntil(s: *mut Scanner, delim: u8, kind: TokenKind) -> ?Token { |
|
| 240 | + | fn scanCharsUntil(s: &mut Scanner, delim: u8, kind: TokenKind) -> ?Token { |
|
| 241 | 241 | while let ch = current(s); ch <> delim { |
|
| 242 | 242 | if not char::isPrint(ch) { |
|
| 243 | 243 | return invalid(s.token, "invalid character"); |
|
| 244 | 244 | } |
|
| 245 | 245 | if consume(s, '\\') { |
| 254 | 254 | } |
|
| 255 | 255 | return tok(s, kind); |
|
| 256 | 256 | } |
|
| 257 | 257 | ||
| 258 | 258 | /// Scan a string literal. |
|
| 259 | - | fn scanString(s: *mut Scanner) -> Token { |
|
| 259 | + | fn scanString(s: &mut Scanner) -> Token { |
|
| 260 | 260 | if let token = scanCharsUntil(s, '"', TokenKind::String) { |
|
| 261 | 261 | return token; |
|
| 262 | 262 | } |
|
| 263 | 263 | return invalid(s.token, "unterminated string"); |
|
| 264 | 264 | } |
|
| 265 | 265 | ||
| 266 | 266 | /// Scan a character literal. |
|
| 267 | - | fn scanChar(s: *mut Scanner) -> Token { |
|
| 267 | + | fn scanChar(s: &mut Scanner) -> Token { |
|
| 268 | 268 | if let token = scanCharsUntil(s, '\'', TokenKind::Char) { |
|
| 269 | 269 | return token; |
|
| 270 | 270 | } |
|
| 271 | 271 | return invalid(s.token, "unterminated character"); |
|
| 272 | 272 | } |
|
| 273 | 273 | ||
| 274 | 274 | /// Scan an identifier-shaped token of the given kind. |
|
| 275 | - | fn scanIdentToken(s: *mut Scanner, kind: TokenKind) -> Token { |
|
| 275 | + | fn scanIdentToken(s: &mut Scanner, kind: TokenKind) -> Token { |
|
| 276 | 276 | scanIdentifierBody(s); |
|
| 277 | 277 | ||
| 278 | 278 | return Token { |
|
| 279 | 279 | kind, |
|
| 280 | 280 | source: strings::intern(s.pool, &s.source[s.token..s.cursor]), |
|
| 281 | 281 | offset: s.token, |
|
| 282 | 282 | }; |
|
| 283 | 283 | } |
|
| 284 | 284 | ||
| 285 | 285 | /// Scan a sigil-prefixed identifier-shaped token. |
|
| 286 | - | fn scanPrefixedToken(s: *mut Scanner, kind: TokenKind, message: *[u8]) -> Token { |
|
| 286 | + | fn scanPrefixedToken(s: &mut Scanner, kind: TokenKind, message: *[u8]) -> Token { |
|
| 287 | 287 | let ch = current(s) else { |
|
| 288 | 288 | return invalid(s.token, message); |
|
| 289 | 289 | }; |
|
| 290 | 290 | if not char::isAlpha(ch) and ch <> '_' { |
|
| 291 | 291 | return invalid(s.token, message); |
| 298 | 298 | offset: s.token, |
|
| 299 | 299 | }; |
|
| 300 | 300 | } |
|
| 301 | 301 | ||
| 302 | 302 | /// Scan an assembler label token, accepting either `@name` or `@"quoted"` syntax. |
|
| 303 | - | fn scanLabelToken(s: *mut Scanner) -> Token { |
|
| 303 | + | fn scanLabelToken(s: &mut Scanner) -> Token { |
|
| 304 | 304 | let ch = current(s) else { |
|
| 305 | 305 | return invalid(s.token, "expected label after `@`"); |
|
| 306 | 306 | }; |
|
| 307 | 307 | if ch == '"' { |
|
| 308 | 308 | advance(s); |
lib/std/arch/rv64/asm/tests.rad
+15 -15
| 16 | 16 | static ASM_DATA_STORAGE: [u8; 1024] = undefined; |
|
| 17 | 17 | static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
|
| 18 | 18 | static PRINT_ARENA_STORAGE: [u8; 1024] = undefined; |
|
| 19 | 19 | static PRINT_BUFFER: [u8; 128] = undefined; |
|
| 20 | 20 | ||
| 21 | - | fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) { |
|
| 21 | + | unsafe fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) { |
|
| 22 | 22 | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
|
| 23 | 23 | return try super::assemble( |
|
| 24 | 24 | scanner::SourceKind::String, |
|
| 25 | 25 | source, |
|
| 26 | 26 | &mut ASM_TEXT_STORAGE[..], |
| 31 | 31 | ) catch { |
|
| 32 | 32 | throw testing::TestError::Failed; |
|
| 33 | 33 | }; |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | - | fn expectAssembleFail(source: *[u8]) throws (testing::TestError) { |
|
| 36 | + | unsafe fn expectAssembleFail(source: *[u8]) throws (testing::TestError) { |
|
| 37 | 37 | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
|
| 38 | 38 | try super::assemble( |
|
| 39 | 39 | scanner::SourceKind::String, |
|
| 40 | 40 | source, |
|
| 41 | 41 | &mut ASM_TEXT_STORAGE[..], |
| 47 | 47 | return; |
|
| 48 | 48 | }; |
|
| 49 | 49 | throw testing::TestError::Failed; |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | - | fn printInstrText(instr: u32) -> *[u8] { |
|
| 52 | + | unsafe fn printInstrText(instr: u32) -> *[u8] { |
|
| 53 | 53 | let mut arena = alloc::new(&mut PRINT_ARENA_STORAGE[..]); |
|
| 54 | 54 | let mut pos: u32 = 0; |
|
| 55 | 55 | let mut out = sexpr::Output::Buffer { buf: &mut PRINT_BUFFER[..], pos: &mut pos }; |
|
| 56 | 56 | printer::printInstr(&mut out, &mut arena, instr); |
|
| 57 | 57 | return &PRINT_BUFFER[..pos]; |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | - | @test fn testAssemblePercentPrefixedRegisters() throws (testing::TestError) { |
|
| 60 | + | @test unsafe fn testAssemblePercentPrefixedRegisters() throws (testing::TestError) { |
|
| 61 | 61 | let program = try assembleSource( |
|
| 62 | 62 | ".text;\naddi %a0 %zero 42;\nsd %a0 8(%sp);\n" |
|
| 63 | 63 | ); |
|
| 64 | 64 | try testing::expect(program.text.len == 2); |
|
| 65 | 65 | try testing::expect(program.text[0] == encode::addi(rv64::A0, rv64::ZERO, 42)); |
|
| 66 | 66 | try testing::expect(program.text[1] == encode::sd(rv64::A0, rv64::SP, 8)); |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | - | @test fn testAssembleDataAddressUsesRoDataBase() throws (testing::TestError) { |
|
| 69 | + | @test unsafe fn testAssembleDataAddressUsesRoDataBase() throws (testing::TestError) { |
|
| 70 | 70 | let program = try assembleSource( |
|
| 71 | 71 | ".text;\nla %t0 @value;\n.data;\n.byte 0;\n@value\n.byte 1;\n" |
|
| 72 | 72 | ); |
|
| 73 | 73 | try testing::expect(program.text.len == 2); |
|
| 74 | 74 | try testing::expect(program.text[0] == encode::lui(rv64::T0, 0x10)); |
|
| 75 | 75 | try testing::expect(program.text[1] == encode::addi(rv64::T0, rv64::T0, 1)); |
|
| 76 | 76 | } |
|
| 77 | 77 | ||
| 78 | - | @test fn testAssembleTextAddressUsesPcRelative() throws (testing::TestError) { |
|
| 78 | + | @test unsafe fn testAssembleTextAddressUsesPcRelative() throws (testing::TestError) { |
|
| 79 | 79 | let program = try assembleSource( |
|
| 80 | 80 | ".text;\nla %t0 @target;\n@target\nret;\n" |
|
| 81 | 81 | ); |
|
| 82 | 82 | try testing::expect(program.text.len == 3); |
|
| 83 | 83 | try testing::expect(program.text[0] == encode::auipc(rv64::T0, 0)); |
|
| 84 | 84 | try testing::expect(program.text[1] == encode::addi(rv64::T0, rv64::T0, 8)); |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | - | @test fn testAssembleQuotedLabelNames() throws (testing::TestError) { |
|
| 87 | + | @test unsafe fn testAssembleQuotedLabelNames() throws (testing::TestError) { |
|
| 88 | 88 | let program = try assembleSource( |
|
| 89 | 89 | ".text;\nj @\"foo.bar.baz\";\n@\"foo.bar.baz\"\nret;\n" |
|
| 90 | 90 | ); |
|
| 91 | 91 | try testing::expect(program.text.len == 2); |
|
| 92 | 92 | try testing::expect(program.text[0] == encode::jal(rv64::ZERO, 4)); |
|
| 93 | 93 | try testing::expect(program.text[1] == encode::jalr(rv64::ZERO, rv64::RA, 0)); |
|
| 94 | 94 | } |
|
| 95 | 95 | ||
| 96 | - | @test fn testAssembleGlobalMarksOnlyDeclaredSymbols() throws (testing::TestError) { |
|
| 96 | + | @test unsafe fn testAssembleGlobalMarksOnlyDeclaredSymbols() throws (testing::TestError) { |
|
| 97 | 97 | let program = try assembleSource( |
|
| 98 | 98 | ".text;\n.export @exported;\n@local\nret;\n@exported\nret;\n@late\n.export @late;\nret;\n" |
|
| 99 | 99 | ); |
|
| 100 | 100 | try testing::expect(program.symbols.len == 3); |
|
| 101 | 101 | try testing::expect(not program.symbols[0].isExported); |
|
| 102 | 102 | try testing::expect(program.symbols[1].isExported); |
|
| 103 | 103 | try testing::expect(program.symbols[2].isExported); |
|
| 104 | 104 | } |
|
| 105 | 105 | ||
| 106 | - | @test fn testAssembleExternalTextFixups() throws (testing::TestError) { |
|
| 106 | + | @test unsafe fn testAssembleExternalTextFixups() throws (testing::TestError) { |
|
| 107 | 107 | let program = try assembleSource( |
|
| 108 | 108 | ".text;\ntail @\"::default\";\nla %t0 @\"::default\";\n" |
|
| 109 | 109 | ); |
|
| 110 | 110 | try testing::expect(program.externalFixups.len == 2); |
|
| 111 | 111 |
| 122 | 122 | try testing::expect(mem::eq(program.externalFixups[1].symbol, "::default")); |
|
| 123 | 123 | try testing::expect(addrRd == rv64::T0); |
|
| 124 | 124 | try testing::expect(addrIndex == 1); |
|
| 125 | 125 | } |
|
| 126 | 126 | ||
| 127 | - | @test fn testAssembleInvalidOperandsFail() throws (testing::TestError) { |
|
| 127 | + | @test unsafe fn testAssembleInvalidOperandsFail() throws (testing::TestError) { |
|
| 128 | 128 | try expectAssembleFail( |
|
| 129 | 129 | ".text;\nbeq %a0 %a1 @missing;\n" |
|
| 130 | 130 | ); |
|
| 131 | 131 | try expectAssembleFail( |
|
| 132 | 132 | ".text;\naddi a0 zero 1;\n" |
| 143 | 143 | try expectAssembleFail( |
|
| 144 | 144 | ".data;\n.dword @missing;\n" |
|
| 145 | 145 | ); |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | - | @test fn testAssembleInvalidSyntaxFails() throws (testing::TestError) { |
|
| 148 | + | @test unsafe fn testAssembleInvalidSyntaxFails() throws (testing::TestError) { |
|
| 149 | 149 | try expectAssembleFail( |
|
| 150 | 150 | ".text;\n@dup\n@dup\nret;\n" |
|
| 151 | 151 | ); |
|
| 152 | 152 | try expectAssembleFail( |
|
| 153 | 153 | ".text;\naddi %a0, %zero, 1\n" |
| 161 | 161 | try expectAssembleFail( |
|
| 162 | 162 | ".export @kernel::main, @data::sym;\n" |
|
| 163 | 163 | ); |
|
| 164 | 164 | } |
|
| 165 | 165 | ||
| 166 | - | @test fn testAssembleInvalidSectionsFail() throws (testing::TestError) { |
|
| 166 | + | @test unsafe fn testAssembleInvalidSectionsFail() throws (testing::TestError) { |
|
| 167 | 167 | try expectAssembleFail( |
|
| 168 | 168 | ".data;\n.dword @target;\n.text;\n@target\nret;\n" |
|
| 169 | 169 | ); |
|
| 170 | 170 | try expectAssembleFail( |
|
| 171 | 171 | ".data;\naddi %a0 %zero 1;\n" |
| 185 | 185 | try expectAssembleFail( |
|
| 186 | 186 | ".data;\n@value\n.byte 1;\n.text;\nj @value;\n" |
|
| 187 | 187 | ); |
|
| 188 | 188 | } |
|
| 189 | 189 | ||
| 190 | - | @test fn testAssembleInvalidDirectivesFail() throws (testing::TestError) { |
|
| 190 | + | @test unsafe fn testAssembleInvalidDirectivesFail() throws (testing::TestError) { |
|
| 191 | 191 | try expectAssembleFail( |
|
| 192 | 192 | ".data;\n.ascii 'x';\n" |
|
| 193 | 193 | ); |
|
| 194 | 194 | try expectAssembleFail( |
|
| 195 | 195 | ".data;\n.byte 1 + 2;\n" |
| 212 | 212 | try expectAssembleFail( |
|
| 213 | 213 | ".data;\n.align 4294967296;\n" |
|
| 214 | 214 | ); |
|
| 215 | 215 | } |
|
| 216 | 216 | ||
| 217 | - | @test fn testAssembleInvalidImmediateRangesFail() throws (testing::TestError) { |
|
| 217 | + | @test unsafe fn testAssembleInvalidImmediateRangesFail() throws (testing::TestError) { |
|
| 218 | 218 | try expectAssembleFail( |
|
| 219 | 219 | ".text;\nslli %a0 %a1 64;\n" |
|
| 220 | 220 | ); |
|
| 221 | 221 | try expectAssembleFail( |
|
| 222 | 222 | ".text;\nslli %a0 %a1 4294967296;\n" |
| 227 | 227 | try expectAssembleFail( |
|
| 228 | 228 | ".text;\ncsrsi mstatus 32;\n" |
|
| 229 | 229 | ); |
|
| 230 | 230 | } |
|
| 231 | 231 | ||
| 232 | - | @test fn testPrintInstrUsesPercentPrefixedRegisters() throws (testing::TestError) { |
|
| 232 | + | @test unsafe fn testPrintInstrUsesPercentPrefixedRegisters() throws (testing::TestError) { |
|
| 233 | 233 | let text = printInstrText(encode::addi(rv64::A0, rv64::SP, 42)); |
|
| 234 | 234 | try testing::expect(mem::eq(text, "addi %a0, %sp, 42")); |
|
| 235 | 235 | } |
lib/std/arch/rv64/emit.rad
+43 -40
| 180 | 180 | } |
|
| 181 | 181 | return frame; |
|
| 182 | 182 | } |
|
| 183 | 183 | ||
| 184 | 184 | /// Create a new emitter. |
|
| 185 | - | export fn emitter(arena: *mut alloc::Arena, debug: bool) -> Emitter throws (alloc::AllocError) { |
|
| 185 | + | export unsafe fn emitter(arena: &mut alloc::Arena, debug: bool) -> Emitter throws (alloc::AllocError) { |
|
| 186 | 186 | let code = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_INSTRS); |
|
| 187 | 187 | let pendingBranches = try alloc::allocSlice(arena, @sizeOf(PendingBranch), @alignOf(PendingBranch), MAX_PENDING); |
|
| 188 | 188 | let pendingCalls = try alloc::allocSlice(arena, @sizeOf(PendingCall), @alignOf(PendingCall), MAX_PENDING); |
|
| 189 | 189 | let pendingJumps = try alloc::allocSlice(arena, @sizeOf(PendingJump), @alignOf(PendingJump), MAX_PENDING); |
|
| 190 | 190 | let pendingAddrLoads = try alloc::allocSlice(arena, @sizeOf(PendingAddrLoad), @alignOf(PendingAddrLoad), MAX_PENDING); |
| 216 | 216 | /////////////////////// |
|
| 217 | 217 | // Emission Helpers // |
|
| 218 | 218 | /////////////////////// |
|
| 219 | 219 | ||
| 220 | 220 | /// Emit a single instruction. |
|
| 221 | - | export fn emit(e: *mut Emitter, instr: u32) { |
|
| 221 | + | export fn emit(e: &mut Emitter, instr: u32) { |
|
| 222 | 222 | assert e.codeLen < e.code.len, "emit: code buffer full"; |
|
| 223 | 223 | set e.code[e.codeLen] = instr; |
|
| 224 | 224 | set e.codeLen += 1; |
|
| 225 | 225 | } |
|
| 226 | 226 | ||
| 227 | 227 | /// Compute branch offset to a function by name. |
|
| 228 | - | export fn branchOffsetToFunc(e: *Emitter, srcIndex: u32, name: *[u8]) -> i32 { |
|
| 228 | + | export fn branchOffsetToFunc(e: &Emitter, srcIndex: u32, name: *[u8]) -> i32 { |
|
| 229 | 229 | return labels::branchToFunc(&e.labels, srcIndex, name, super::INSTR_SIZE); |
|
| 230 | 230 | } |
|
| 231 | 231 | ||
| 232 | 232 | /// Patch an instruction at a given index. |
|
| 233 | - | export fn patch(e: *mut Emitter, index: u32, instr: u32) { |
|
| 233 | + | export fn patch(e: &mut Emitter, index: u32, instr: u32) { |
|
| 234 | 234 | set e.code[index] = instr; |
|
| 235 | 235 | } |
|
| 236 | 236 | ||
| 237 | 237 | /// Record a block's address for branch resolution. |
|
| 238 | - | export fn recordBlock(e: *mut Emitter, blockIdx: u32) { |
|
| 238 | + | export fn recordBlock(e: &mut Emitter, blockIdx: u32) { |
|
| 239 | 239 | assert e.codeLen <= MAX_CODE_LEN; |
|
| 240 | 240 | labels::recordBlock(&mut e.labels, blockIdx, e.codeLen as i32 * super::INSTR_SIZE); |
|
| 241 | 241 | } |
|
| 242 | 242 | ||
| 243 | 243 | /// Record a function's code offset for call resolution. |
|
| 244 | - | export fn recordFuncOffset(e: *mut Emitter, name: *[u8]) { |
|
| 245 | - | recordFuncOffsetAt(e, name, e.codeLen); |
|
| 244 | + | export fn recordFuncOffset(e: &mut Emitter, name: *[u8]) { |
|
| 245 | + | let codeLen = e.codeLen; |
|
| 246 | + | recordFuncOffsetAt(e, name, codeLen); |
|
| 246 | 247 | } |
|
| 247 | 248 | ||
| 248 | 249 | /// Record a function's code offset at `index` for call resolution. |
|
| 249 | - | export fn recordFuncOffsetAt(e: *mut Emitter, name: *[u8], index: u32) { |
|
| 250 | + | export fn recordFuncOffsetAt(e: &mut Emitter, name: *[u8], index: u32) { |
|
| 250 | 251 | assert index <= MAX_CODE_LEN; |
|
| 251 | 252 | dict::insert(&mut e.labels.funcs, name, index as i32 * super::INSTR_SIZE); |
|
| 252 | 253 | } |
|
| 253 | 254 | ||
| 254 | 255 | /// Record a function's start position for printing. |
|
| 255 | - | export fn recordFunc(e: *mut Emitter, name: *[u8]) { |
|
| 256 | - | recordFuncAt(e, name, e.codeLen); |
|
| 256 | + | export fn recordFunc(e: &mut Emitter, name: *[u8]) { |
|
| 257 | + | let codeLen = e.codeLen; |
|
| 258 | + | recordFuncAt(e, name, codeLen); |
|
| 257 | 259 | } |
|
| 258 | 260 | ||
| 259 | 261 | /// Record a function's start position at `index` for printing. |
|
| 260 | - | export fn recordFuncAt(e: *mut Emitter, name: *[u8], index: u32) { |
|
| 262 | + | export fn recordFuncAt(e: &mut Emitter, name: *[u8], index: u32) { |
|
| 261 | 263 | e.funcs.append(types::FuncAddr { name, index }, e.allocator); |
|
| 262 | 264 | } |
|
| 263 | 265 | ||
| 264 | 266 | /// Record a local branch needing later patching. |
|
| 265 | 267 | /// Unconditional jumps use a single slot (J-type, +-1MB range). |
|
| 266 | 268 | /// Conditional branches use two slots (B-type has only +-4KB range, |
|
| 267 | 269 | /// so large functions may need the inverted-branch + JAL fallback). |
|
| 268 | - | export fn recordBranch(e: *mut Emitter, targetBlock: u32, kind: BranchKind) { |
|
| 270 | + | export fn recordBranch(e: &mut Emitter, targetBlock: u32, kind: BranchKind) { |
|
| 269 | 271 | e.pendingBranches.append(PendingBranch { |
|
| 270 | 272 | index: e.codeLen, |
|
| 271 | 273 | target: targetBlock, |
|
| 272 | 274 | kind: kind, |
|
| 273 | 275 | }, e.allocator); |
| 281 | 283 | } |
|
| 282 | 284 | ||
| 283 | 285 | /// Record a function call needing later patching. |
|
| 284 | 286 | /// Emits placeholder instructions that will be patched later. |
|
| 285 | 287 | /// Uses two slots to support long-distance calls. |
|
| 286 | - | export fn recordCall(e: *mut Emitter, target: *[u8]) { |
|
| 288 | + | export fn recordCall(e: &mut Emitter, target: *[u8]) { |
|
| 287 | 289 | e.pendingCalls.append(PendingCall { |
|
| 288 | 290 | index: e.codeLen, |
|
| 289 | 291 | target, |
|
| 290 | 292 | }, e.allocator); |
|
| 291 | 293 | ||
| 292 | 294 | emit(e, encode::nop()); // Placeholder for AUIPC. |
|
| 293 | 295 | emit(e, encode::nop()); // Placeholder for JALR. |
|
| 294 | 296 | } |
|
| 295 | 297 | ||
| 296 | 298 | /// Record a jump emitted by assembly that needs whole-program patching. |
|
| 297 | - | export fn recordJumpAt(e: *mut Emitter, target: *[u8], rd: gen::Reg, index: u32) { |
|
| 299 | + | export fn recordJumpAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) { |
|
| 298 | 300 | e.pendingJumps.append(PendingJump { |
|
| 299 | 301 | index, |
|
| 300 | 302 | target, |
|
| 301 | 303 | rd, |
|
| 302 | 304 | }, e.allocator); |
|
| 303 | 305 | } |
|
| 304 | 306 | ||
| 305 | 307 | /// Record a function address load needing later patching. |
|
| 306 | 308 | /// Emits placeholder instructions that will be patched to load the function's address. |
|
| 307 | 309 | /// Uses two slots to compute long-distance addresses. |
|
| 308 | - | export fn recordAddrLoad(e: *mut Emitter, target: *[u8], rd: gen::Reg) { |
|
| 309 | - | recordAddrLoadAt(e, target, rd, e.codeLen); |
|
| 310 | + | export fn recordAddrLoad(e: &mut Emitter, target: *[u8], rd: gen::Reg) { |
|
| 311 | + | let codeLen = e.codeLen; |
|
| 312 | + | recordAddrLoadAt(e, target, rd, codeLen); |
|
| 310 | 313 | ||
| 311 | 314 | emit(e, encode::nop()); // Placeholder for AUIPC. |
|
| 312 | 315 | emit(e, encode::nop()); // Placeholder for ADDI. |
|
| 313 | 316 | } |
|
| 314 | 317 | ||
| 315 | 318 | /// Record a function address load already reserved by assembly. |
|
| 316 | - | export fn recordAddrLoadAt(e: *mut Emitter, target: *[u8], rd: gen::Reg, index: u32) { |
|
| 319 | + | export fn recordAddrLoadAt(e: &mut Emitter, target: *[u8], rd: gen::Reg, index: u32) { |
|
| 317 | 320 | e.pendingAddrLoads.append(PendingAddrLoad { |
|
| 318 | 321 | index, |
|
| 319 | 322 | target, |
|
| 320 | 323 | rd: rd, |
|
| 321 | 324 | isData: false, |
|
| 322 | 325 | }, e.allocator); |
|
| 323 | 326 | } |
|
| 324 | 327 | ||
| 325 | 328 | /// Record a data address load needing later patching. |
|
| 326 | 329 | /// Uses an absolute 32-bit load sequence matching the current data memory map. |
|
| 327 | - | export fn recordDataAddrLoad(e: *mut Emitter, target: *[u8], rd: gen::Reg) { |
|
| 330 | + | export fn recordDataAddrLoad(e: &mut Emitter, target: *[u8], rd: gen::Reg) { |
|
| 328 | 331 | e.pendingAddrLoads.append(PendingAddrLoad { |
|
| 329 | 332 | index: e.codeLen, |
|
| 330 | 333 | target, |
|
| 331 | 334 | rd: rd, |
|
| 332 | 335 | isData: true, |
| 340 | 343 | /// |
|
| 341 | 344 | /// Called after each function. |
|
| 342 | 345 | /// |
|
| 343 | 346 | /// Uses two-instruction sequences: short branches use `branch` and `nop`, |
|
| 344 | 347 | /// long branches use inverted branch and `jal` or `auipc` and `jalr`. |
|
| 345 | - | export fn patchLocalBranches(e: *mut Emitter) { |
|
| 348 | + | export fn patchLocalBranches(e: &mut Emitter) { |
|
| 346 | 349 | for i in 0..e.pendingBranches.len { |
|
| 347 | 350 | let p = e.pendingBranches[i]; |
|
| 348 | 351 | let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE); |
|
| 349 | 352 | match p.kind { |
|
| 350 | 353 | case BranchKind::Cond { op, rs1, rs2 } => { |
| 397 | 400 | } |
|
| 398 | 401 | } |
|
| 399 | 402 | ||
| 400 | 403 | /// Patch all pending function calls. |
|
| 401 | 404 | /// Called after all functions have been generated. |
|
| 402 | - | export fn patchCalls(e: *mut Emitter) { |
|
| 405 | + | export fn patchCalls(e: &mut Emitter) { |
|
| 403 | 406 | for i in 0..e.pendingCalls.len { |
|
| 404 | 407 | let p = e.pendingCalls[i]; |
|
| 405 | 408 | let offset = branchOffsetToFunc(e, p.index, p.target); |
|
| 406 | 409 | let s = splitImm(offset); |
|
| 407 | 410 |
| 411 | 414 | patch(e, p.index + 1, encode::jalr(super::RA, super::SCRATCH1, s.lo)); |
|
| 412 | 415 | } |
|
| 413 | 416 | } |
|
| 414 | 417 | ||
| 415 | 418 | /// Patch all pending assembly jumps. |
|
| 416 | - | export fn patchJumps(e: *mut Emitter) { |
|
| 419 | + | export fn patchJumps(e: &mut Emitter) { |
|
| 417 | 420 | for i in 0..e.pendingJumps.len { |
|
| 418 | 421 | let p = e.pendingJumps[i]; |
|
| 419 | 422 | let offset = branchOffsetToFunc(e, p.index, p.target); |
|
| 420 | 423 | ||
| 421 | 424 | assert encode::isJumpImm(offset), "patchJumps: jump offset too large"; |
| 423 | 426 | } |
|
| 424 | 427 | } |
|
| 425 | 428 | ||
| 426 | 429 | /// Patch all pending function and data address loads. |
|
| 427 | 430 | /// Called after all functions have been generated and data layout is known. |
|
| 428 | - | export fn patchAddrLoads(e: *mut Emitter, dataSymMap: *data::DataSymMap) { |
|
| 431 | + | export fn patchAddrLoads(e: &mut Emitter, dataSymMap: &data::DataSymMap) { |
|
| 429 | 432 | for i in 0..e.pendingAddrLoads.len { |
|
| 430 | 433 | let p = e.pendingAddrLoads[i]; |
|
| 431 | 434 | if p.isData { |
|
| 432 | 435 | let addr = data::lookupAddr(dataSymMap, p.target) else { |
|
| 433 | 436 | panic "patchAddrLoads: data symbol not found"; |
| 479 | 482 | /// Adjust a large offset by loading *hi* bits into [`super::ADDR_SCRATCH`]. |
|
| 480 | 483 | /// Returns adjusted base register and remaining offset. |
|
| 481 | 484 | /// |
|
| 482 | 485 | /// When the offset fits a 12-bit signed immediate, returns it unchanged. |
|
| 483 | 486 | /// Otherwise uses [`super::ADDR_SCRATCH`] for the LUI+ADD decomposition. |
|
| 484 | - | fn adjustOffset(e: *mut Emitter, base: gen::Reg, offset: i32) -> AdjustedOffset { |
|
| 487 | + | fn adjustOffset(e: &mut Emitter, base: gen::Reg, offset: i32) -> AdjustedOffset { |
|
| 485 | 488 | if offset >= super::MIN_IMM and offset <= super::MAX_IMM { |
|
| 486 | 489 | return AdjustedOffset { base, offset }; |
|
| 487 | 490 | } |
|
| 488 | 491 | let s = splitImm(offset); |
|
| 489 | 492 | emit(e, encode::lui(super::ADDR_SCRATCH, s.hi)); |
| 495 | 498 | /// Load an immediate value into a register. |
|
| 496 | 499 | /// Handles the full range of 64-bit immediates. |
|
| 497 | 500 | /// For values fitting in 12 bits, uses a single `ADDI`. |
|
| 498 | 501 | /// For values fitting in 32 bits, uses `LUI` + `ADDIW`. |
|
| 499 | 502 | /// For wider values, loads upper and lower halves then combines with shift and add. |
|
| 500 | - | export fn loadImm(e: *mut Emitter, rd: gen::Reg, imm: i64) { |
|
| 503 | + | export fn loadImm(e: &mut Emitter, rd: gen::Reg, imm: i64) { |
|
| 501 | 504 | let immMin = super::MIN_IMM as i64; |
|
| 502 | 505 | let immMax = super::MAX_IMM as i64; |
|
| 503 | 506 | ||
| 504 | 507 | if imm >= immMin and imm <= immMax { |
|
| 505 | 508 | emit(e, encode::addi(rd, super::ZERO, imm as i32)); |
| 543 | 546 | emit(e, encode::addi(rd, rd, chunkLo)); |
|
| 544 | 547 | } |
|
| 545 | 548 | } |
|
| 546 | 549 | ||
| 547 | 550 | /// Emit add-immediate, handling large immediates. |
|
| 548 | - | export fn emitAddImm(e: *mut Emitter, rd: gen::Reg, rs: gen::Reg, imm: i32) { |
|
| 551 | + | export fn emitAddImm(e: &mut Emitter, rd: gen::Reg, rs: gen::Reg, imm: i32) { |
|
| 549 | 552 | if imm >= super::MIN_IMM and imm <= super::MAX_IMM { |
|
| 550 | 553 | emit(e, encode::addi(rd, rs, imm)); |
|
| 551 | 554 | } else { |
|
| 552 | 555 | loadImm(e, super::SCRATCH1, imm as i64); |
|
| 553 | 556 | emit(e, encode::add(rd, rs, super::SCRATCH1)); |
| 557 | 560 | //////////////////////// |
|
| 558 | 561 | // Load/Store Helpers // |
|
| 559 | 562 | //////////////////////// |
|
| 560 | 563 | ||
| 561 | 564 | /// Emit unsigned load with automatic offset adjustment. |
|
| 562 | - | export fn emitLoad(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 565 | + | export fn emitLoad(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 563 | 566 | let adj = adjustOffset(e, base, offset); |
|
| 564 | 567 | match typ { |
|
| 565 | 568 | case il::Type::W8 => emit(e, encode::lbu(rd, adj.base, adj.offset)), |
|
| 566 | 569 | case il::Type::W16 => emit(e, encode::lhu(rd, adj.base, adj.offset)), |
|
| 567 | 570 | case il::Type::W32 => emit(e, encode::lwu(rd, adj.base, adj.offset)), |
|
| 568 | 571 | case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)), |
|
| 569 | 572 | } |
|
| 570 | 573 | } |
|
| 571 | 574 | ||
| 572 | 575 | /// Emit signed load with automatic offset adjustment. |
|
| 573 | - | export fn emitSload(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 576 | + | export fn emitSload(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 574 | 577 | let adj = adjustOffset(e, base, offset); |
|
| 575 | 578 | match typ { |
|
| 576 | 579 | case il::Type::W8 => emit(e, encode::lb(rd, adj.base, adj.offset)), |
|
| 577 | 580 | case il::Type::W16 => emit(e, encode::lh(rd, adj.base, adj.offset)), |
|
| 578 | 581 | case il::Type::W32 => emit(e, encode::lw(rd, adj.base, adj.offset)), |
|
| 579 | 582 | case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)), |
|
| 580 | 583 | } |
|
| 581 | 584 | } |
|
| 582 | 585 | ||
| 583 | 586 | /// Emit store with automatic offset adjustment. |
|
| 584 | - | export fn emitStore(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 587 | + | export fn emitStore(e: &mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 585 | 588 | let adj = adjustOffset(e, base, offset); |
|
| 586 | 589 | match typ { |
|
| 587 | 590 | case il::Type::W8 => emit(e, encode::sb(rs, adj.base, adj.offset)), |
|
| 588 | 591 | case il::Type::W16 => emit(e, encode::sh(rs, adj.base, adj.offset)), |
|
| 589 | 592 | case il::Type::W32 => emit(e, encode::sw(rs, adj.base, adj.offset)), |
|
| 590 | 593 | case il::Type::W64 => emit(e, encode::sd(rs, adj.base, adj.offset)), |
|
| 591 | 594 | } |
|
| 592 | 595 | } |
|
| 593 | 596 | ||
| 594 | 597 | /// Emit 64-bit load with automatic offset adjustment. |
|
| 595 | - | export fn emitLd(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 598 | + | export fn emitLd(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 596 | 599 | let adj = adjustOffset(e, base, offset); |
|
| 597 | 600 | emit(e, encode::ld(rd, adj.base, adj.offset)); |
|
| 598 | 601 | } |
|
| 599 | 602 | ||
| 600 | 603 | /// Emit 64-bit store with automatic offset adjustment. |
|
| 601 | - | export fn emitSd(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 604 | + | export fn emitSd(e: &mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 602 | 605 | let adj = adjustOffset(e, base, offset); |
|
| 603 | 606 | emit(e, encode::sd(rs, adj.base, adj.offset)); |
|
| 604 | 607 | } |
|
| 605 | 608 | ||
| 606 | 609 | /// Emit 32-bit load with automatic offset adjustment. |
|
| 607 | - | export fn emitLw(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 610 | + | export fn emitLw(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 608 | 611 | let adj = adjustOffset(e, base, offset); |
|
| 609 | 612 | emit(e, encode::lw(rd, adj.base, adj.offset)); |
|
| 610 | 613 | } |
|
| 611 | 614 | ||
| 612 | 615 | /// Emit 32-bit store with automatic offset adjustment. |
|
| 613 | - | export fn emitSw(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 616 | + | export fn emitSw(e: &mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 614 | 617 | let adj = adjustOffset(e, base, offset); |
|
| 615 | 618 | emit(e, encode::sw(rs, adj.base, adj.offset)); |
|
| 616 | 619 | } |
|
| 617 | 620 | ||
| 618 | 621 | /// Emit 8-bit load with automatic offset adjustment. |
|
| 619 | - | export fn emitLb(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 622 | + | export fn emitLb(e: &mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 620 | 623 | let adj = adjustOffset(e, base, offset); |
|
| 621 | 624 | emit(e, encode::lb(rd, adj.base, adj.offset)); |
|
| 622 | 625 | } |
|
| 623 | 626 | ||
| 624 | 627 | /// Emit 8-bit store with automatic offset adjustment. |
|
| 625 | - | export fn emitSb(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 628 | + | export fn emitSb(e: &mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 626 | 629 | let adj = adjustOffset(e, base, offset); |
|
| 627 | 630 | emit(e, encode::sb(rs, adj.base, adj.offset)); |
|
| 628 | 631 | } |
|
| 629 | 632 | ||
| 630 | 633 | ////////////////////////// |
|
| 631 | 634 | // Prologue / Epilogue // |
|
| 632 | 635 | ////////////////////////// |
|
| 633 | 636 | ||
| 634 | 637 | /// Emit function prologue. |
|
| 635 | 638 | /// Allocate the frame and save registers. Save FP only for dynamic frames. |
|
| 636 | - | export fn emitPrologue(e: *mut Emitter, frame: *Frame) { |
|
| 639 | + | export fn emitPrologue(e: &mut Emitter, frame: &Frame) { |
|
| 637 | 640 | // Fast path: leaf function with no locals. |
|
| 638 | 641 | if frame.totalSize == 0 { |
|
| 639 | 642 | return; |
|
| 640 | 643 | } |
|
| 641 | 644 | let totalSize = frame.totalSize; |
| 663 | 666 | emitSd(e, sr.reg, super::SP, sr.offset); |
|
| 664 | 667 | } |
|
| 665 | 668 | } |
|
| 666 | 669 | ||
| 667 | 670 | /// Emit a return: jump to epilogue, or emit `ret` directly for leaf functions. |
|
| 668 | - | export fn emitReturn(e: *mut Emitter, frame: *Frame) { |
|
| 671 | + | export fn emitReturn(e: &mut Emitter, frame: &Frame) { |
|
| 669 | 672 | if frame.totalSize == 0 { |
|
| 670 | 673 | // Leaf function: no frame to tear down, emit ret directly. |
|
| 671 | 674 | emit(e, encode::ret()); |
|
| 672 | 675 | return; |
|
| 673 | 676 | } |
|
| 674 | 677 | recordBranch(e, frame.epilogueBlock, BranchKind::Jump); |
|
| 675 | 678 | } |
|
| 676 | 679 | ||
| 677 | 680 | /// Emit function epilogue. |
|
| 678 | 681 | /// Restore saved registers and release the frame. Restore FP only for dynamic frames. |
|
| 679 | - | export fn emitEpilogue(e: *mut Emitter, frame: *Frame) { |
|
| 682 | + | export fn emitEpilogue(e: &mut Emitter, frame: &Frame) { |
|
| 680 | 683 | // Record epilogue block address for return jumps. |
|
| 681 | 684 | recordBlock(e, frame.epilogueBlock); |
|
| 682 | 685 | ||
| 683 | 686 | // Fast path: leaf function with no locals. |
|
| 684 | 687 | if frame.totalSize == 0 { |
| 713 | 716 | ////////////////// |
|
| 714 | 717 | // Code Access // |
|
| 715 | 718 | ////////////////// |
|
| 716 | 719 | ||
| 717 | 720 | /// Get emitted code as a slice. |
|
| 718 | - | export fn getCode(e: *Emitter) -> *[u32] { |
|
| 721 | + | export fn getCode(e: &Emitter) -> *[u32] { |
|
| 719 | 722 | return &e.code[..e.codeLen]; |
|
| 720 | 723 | } |
|
| 721 | 724 | ||
| 722 | 725 | /// Record a debug entry mapping the current PC to a source location. |
|
| 723 | 726 | /// Deduplicates consecutive entries with the same location. |
|
| 724 | - | export fn recordSrcLoc(e: *mut Emitter, loc: il::SrcLoc) { |
|
| 727 | + | export fn recordSrcLoc(e: &mut Emitter, loc: il::SrcLoc) { |
|
| 725 | 728 | let pc = e.codeLen * super::INSTR_SIZE as u32; |
|
| 726 | 729 | ||
| 727 | 730 | // Skip if this is the same location as the previous entry. |
|
| 728 | 731 | if e.debugEntriesLen > 0 { |
|
| 729 | 732 | let prev = &e.debugEntries[e.debugEntriesLen - 1]; |
| 739 | 742 | }; |
|
| 740 | 743 | set e.debugEntriesLen += 1; |
|
| 741 | 744 | } |
|
| 742 | 745 | ||
| 743 | 746 | /// Get debug entries as a slice. |
|
| 744 | - | export fn getDebugEntries(e: *Emitter) -> *[types::DebugEntry] { |
|
| 747 | + | export fn getDebugEntries(e: &Emitter) -> *[types::DebugEntry] { |
|
| 745 | 748 | return &e.debugEntries[..e.debugEntriesLen]; |
|
| 746 | 749 | } |
lib/std/arch/rv64/isel.rad
+155 -153
| 84 | 84 | //////////////////// |
|
| 85 | 85 | ||
| 86 | 86 | /// Instruction selector state. |
|
| 87 | 87 | export record Selector: Copy { |
|
| 88 | 88 | /// Emitter for outputting instructions. |
|
| 89 | - | e: *mut emit::Emitter, |
|
| 89 | + | e: *unsafe mut emit::Emitter, |
|
| 90 | 90 | /// Register allocation result. |
|
| 91 | - | ralloc: *regalloc::AllocResult, |
|
| 91 | + | ralloc: *unsafe regalloc::AllocResult, |
|
| 92 | 92 | /// Total stack frame size. |
|
| 93 | 93 | frameSize: i32, |
|
| 94 | 94 | /// Running offset into the reserve region of the frame. |
|
| 95 | 95 | /// Tracks current position within the pre-allocated reserve slots. |
|
| 96 | 96 | reserveOffset: i32, |
| 105 | 105 | ///////////////////////// |
|
| 106 | 106 | // Register Allocation // |
|
| 107 | 107 | ///////////////////////// |
|
| 108 | 108 | ||
| 109 | 109 | /// Get the physical register for an already-allocated SSA register. |
|
| 110 | - | fn getReg(s: *Selector, ssa: il::Reg) -> gen::Reg { |
|
| 110 | + | unsafe fn getReg(s: &Selector, ssa: il::Reg) -> gen::Reg { |
|
| 111 | 111 | let phys = s.ralloc.assignments[ssa.n] else { |
|
| 112 | 112 | panic "getReg: spilled register has no physical assignment"; |
|
| 113 | 113 | }; |
|
| 114 | 114 | return phys; |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | 117 | /// Compute the offset for a spill slot. |
|
| 118 | 118 | /// When using FP (dynamic): offset from FP = `slot - totalSize`. |
|
| 119 | 119 | /// When using SP: offset from SP = `slot`. |
|
| 120 | - | fn spillOffset(s: *Selector, slot: i32) -> i32 { |
|
| 120 | + | fn spillOffset(s: &Selector, slot: i32) -> i32 { |
|
| 121 | 121 | if s.isDynamic { |
|
| 122 | 122 | return slot - s.frameSize; |
|
| 123 | 123 | } |
|
| 124 | 124 | return slot; |
|
| 125 | 125 | } |
|
| 126 | 126 | ||
| 127 | 127 | /// Get the base register for spill slot addressing (FP or SP). |
|
| 128 | - | fn spillBase(s: *Selector) -> gen::Reg { |
|
| 128 | + | fn spillBase(s: &Selector) -> gen::Reg { |
|
| 129 | 129 | if s.isDynamic { |
|
| 130 | 130 | return super::FP; |
|
| 131 | 131 | } |
|
| 132 | 132 | return super::SP; |
|
| 133 | 133 | } |
|
| 134 | 134 | ||
| 135 | 135 | /// Get the destination register for an SSA register. |
|
| 136 | 136 | /// If the register is spilled, records a pending spill and returns the scratch |
|
| 137 | 137 | /// register. The pending spill is auto-committed by [`selectBlock`] after each |
|
| 138 | 138 | /// instruction. If not spilled, returns the physical register. |
|
| 139 | - | fn getDstReg(s: *mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg { |
|
| 139 | + | unsafe fn getDstReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg { |
|
| 140 | 140 | if let _ = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) { |
|
| 141 | 141 | set s.pendingSpill = PendingSpill { ssa, rd: scratch }; |
|
| 142 | 142 | return scratch; |
|
| 143 | 143 | } |
|
| 144 | 144 | return getReg(s, ssa); |
|
| 145 | 145 | } |
|
| 146 | 146 | ||
| 147 | 147 | /// Get the source register for an SSA register. |
|
| 148 | 148 | /// If the register is spilled, loads the value from the spill slot into the |
|
| 149 | 149 | /// scratch register and returns it. Otherwise returns the physical register. |
|
| 150 | - | fn getSrcReg(s: *mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg { |
|
| 150 | + | unsafe fn getSrcReg(s: &mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg { |
|
| 151 | 151 | if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) { |
|
| 152 | - | emit::emitLd(s.e, scratch, spillBase(s), spillOffset(s, slot)); |
|
| 152 | + | emit::emitLd(&mut *s.e, scratch, spillBase(s), spillOffset(s, slot)); |
|
| 153 | 153 | return scratch; |
|
| 154 | 154 | } |
|
| 155 | 155 | return getReg(s, ssa); |
|
| 156 | 156 | } |
|
| 157 | 157 | ||
| 158 | 158 | /// Resolve an IL value to the physical register holding it. |
|
| 159 | 159 | /// For non-spilled register values, returns the physical register directly. |
|
| 160 | 160 | /// For immediates, symbols, and spilled registers, materializes into `scratch`. |
|
| 161 | - | fn resolveVal(s: *mut Selector, scratch: gen::Reg, val: il::Val) -> gen::Reg { |
|
| 161 | + | unsafe fn resolveVal(s: &mut Selector, scratch: gen::Reg, val: il::Val) -> gen::Reg { |
|
| 162 | 162 | match val { |
|
| 163 | 163 | case il::Val::Reg(r) => { |
|
| 164 | 164 | return getSrcReg(s, r, scratch); |
|
| 165 | 165 | }, |
|
| 166 | 166 | case il::Val::Imm(imm) => { |
|
| 167 | 167 | if imm == 0 { |
|
| 168 | 168 | return super::ZERO; |
|
| 169 | 169 | } |
|
| 170 | - | emit::loadImm(s.e, scratch, imm); |
|
| 170 | + | emit::loadImm(&mut *s.e, scratch, imm); |
|
| 171 | 171 | return scratch; |
|
| 172 | 172 | }, |
|
| 173 | 173 | case il::Val::DataSym(name) => { |
|
| 174 | - | emit::recordDataAddrLoad(s.e, name, scratch); |
|
| 174 | + | emit::recordDataAddrLoad(&mut *s.e, name, scratch); |
|
| 175 | 175 | return scratch; |
|
| 176 | 176 | }, |
|
| 177 | 177 | case il::Val::FnAddr(name) => { |
|
| 178 | - | emit::recordAddrLoad(s.e, name, scratch); |
|
| 178 | + | emit::recordAddrLoad(&mut *s.e, name, scratch); |
|
| 179 | 179 | return scratch; |
|
| 180 | 180 | }, |
|
| 181 | 181 | case il::Val::Undef => { |
|
| 182 | 182 | return scratch; |
|
| 183 | 183 | } |
|
| 184 | 184 | } |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | 187 | /// Load an IL value into a specific physical register. |
|
| 188 | 188 | /// Like [`resolveVal`], but ensures the value ends up in `rd`. |
|
| 189 | - | fn loadVal(s: *mut Selector, rd: gen::Reg, val: il::Val) -> gen::Reg { |
|
| 189 | + | unsafe fn loadVal(s: &mut Selector, rd: gen::Reg, val: il::Val) -> gen::Reg { |
|
| 190 | 190 | let rs = resolveVal(s, rd, val); |
|
| 191 | 191 | emitMv(s, rd, rs); |
|
| 192 | 192 | return rd; |
|
| 193 | 193 | } |
|
| 194 | 194 | ||
| 195 | 195 | /// Emit a move instruction if source and destination differ. |
|
| 196 | - | fn emitMv(s: *mut Selector, rd: gen::Reg, rs: gen::Reg) { |
|
| 196 | + | unsafe fn emitMv(s: &mut Selector, rd: gen::Reg, rs: gen::Reg) { |
|
| 197 | 197 | if *rd <> *rs { |
|
| 198 | - | emit::emit(s.e, encode::mv(rd, rs)); |
|
| 198 | + | emit::emit(&mut *s.e, encode::mv(rd, rs)); |
|
| 199 | 199 | } |
|
| 200 | 200 | } |
|
| 201 | 201 | ||
| 202 | 202 | /// Emit zero-extension from a sub-word type to the full register width. |
|
| 203 | - | fn emitZext(e: *mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) { |
|
| 203 | + | fn emitZext(e: &mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) { |
|
| 204 | 204 | match typ { |
|
| 205 | 205 | case il::Type::W8 => emit::emit(e, encode::andi(rd, rs, MASK_W8)), |
|
| 206 | 206 | case il::Type::W16 => { |
|
| 207 | 207 | emit::emit(e, encode::slli(rd, rs, SHIFT_W16)); |
|
| 208 | 208 | emit::emit(e, encode::srli(rd, rd, SHIFT_W16)); |
| 214 | 214 | case il::Type::W64 => {} |
|
| 215 | 215 | } |
|
| 216 | 216 | } |
|
| 217 | 217 | ||
| 218 | 218 | /// Emit sign-extension from a sub-word type to the full register width. |
|
| 219 | - | fn emitSext(e: *mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) { |
|
| 219 | + | fn emitSext(e: &mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) { |
|
| 220 | 220 | match typ { |
|
| 221 | 221 | case il::Type::W8 => { |
|
| 222 | 222 | emit::emit(e, encode::slli(rd, rs, SHIFT_W8)); |
|
| 223 | 223 | emit::emit(e, encode::srai(rd, rd, SHIFT_W8)); |
|
| 224 | 224 | }, |
| 233 | 233 | } |
|
| 234 | 234 | } |
|
| 235 | 235 | ||
| 236 | 236 | /// Resolve a divisor in its declared width, trap if it becomes zero, and |
|
| 237 | 237 | /// return the canonicalized register. |
|
| 238 | - | fn resolveAndTrapIfZero( |
|
| 239 | - | s: *mut Selector, |
|
| 238 | + | unsafe fn resolveAndTrapIfZero( |
|
| 239 | + | s: &mut Selector, |
|
| 240 | 240 | b: il::Val, |
|
| 241 | 241 | typ: il::Type, |
|
| 242 | 242 | signed: bool |
|
| 243 | 243 | ) -> gen::Reg { |
|
| 244 | 244 | let mut divisor = b; |
|
| 245 | 245 | if let case il::Val::Imm(imm) = b { |
|
| 246 | 246 | set divisor = il::Val::Imm(canonicalCmpImm(imm, typ, signed)); |
|
| 247 | 247 | } |
|
| 248 | 248 | let rs2 = resolveVal(s, super::SCRATCH2, divisor); |
|
| 249 | 249 | if not isExtendedImm(divisor, typ, signed) { |
|
| 250 | - | emitCmpExt(s.e, rs2, rs2, typ, signed); |
|
| 250 | + | emitCmpExt(&mut *s.e, rs2, rs2, typ, signed); |
|
| 251 | 251 | } |
|
| 252 | 252 | let mut knownNonZero = false; |
|
| 253 | 253 | if let case il::Val::Imm(imm) = divisor { |
|
| 254 | 254 | set knownNonZero = imm <> 0; |
|
| 255 | 255 | } |
|
| 256 | 256 | if not knownNonZero { |
|
| 257 | - | emit::emit(s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2)); |
|
| 258 | - | emit::emit(s.e, encode::ebreak()); |
|
| 257 | + | emit::emit(&mut *s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2)); |
|
| 258 | + | emit::emit(&mut *s.e, encode::ebreak()); |
|
| 259 | 259 | } |
|
| 260 | 260 | return rs2; |
|
| 261 | 261 | } |
|
| 262 | 262 | ||
| 263 | 263 | //////////////////////// |
| 296 | 296 | } |
|
| 297 | 297 | return ReserveInfo { size: offset, isDynamic }; |
|
| 298 | 298 | } |
|
| 299 | 299 | ||
| 300 | 300 | /// Select instructions for a function. |
|
| 301 | - | export fn selectFn( |
|
| 302 | - | e: *mut emit::Emitter, |
|
| 303 | - | ralloc: *regalloc::AllocResult, |
|
| 301 | + | export unsafe fn selectFn( |
|
| 302 | + | e: &mut emit::Emitter, |
|
| 303 | + | ralloc: ®alloc::AllocResult, |
|
| 304 | 304 | func: *il::Fn |
|
| 305 | 305 | ) { |
|
| 306 | 306 | // Reset block offsets for this function. |
|
| 307 | 307 | labels::resetBlocks(&mut e.labels); |
|
| 308 | 308 | // Pre-scan for constant-sized reserves to promote to fixed frame slots. |
| 316 | 316 | isLeaf, |
|
| 317 | 317 | reserveInfo.isDynamic |
|
| 318 | 318 | ); |
|
| 319 | 319 | // Synthetic block indices start after real blocks and the epilogue block. |
|
| 320 | 320 | let mut s = Selector { |
|
| 321 | - | e, ralloc, frameSize: frame.totalSize, |
|
| 321 | + | e: e as *unsafe mut emit::Emitter, |
|
| 322 | + | ralloc: ralloc as *unsafe regalloc::AllocResult, |
|
| 323 | + | frameSize: frame.totalSize, |
|
| 322 | 324 | reserveOffset: 0, pendingSpill: nil, |
|
| 323 | 325 | nextSynthBlock: func.blocks.len + 1, |
|
| 324 | 326 | isDynamic: frame.isDynamic, |
|
| 325 | 327 | }; |
|
| 326 | 328 | // Record function name for printing. |
|
| 327 | - | emit::recordFunc(s.e, func.name); |
|
| 329 | + | emit::recordFunc(&mut *s.e, func.name); |
|
| 328 | 330 | // Record function code offset for call patching. |
|
| 329 | - | emit::recordFuncOffset(s.e, func.name); |
|
| 331 | + | emit::recordFuncOffset(&mut *s.e, func.name); |
|
| 330 | 332 | // Emit prologue. |
|
| 331 | - | emit::emitPrologue(s.e, &frame); |
|
| 333 | + | emit::emitPrologue(&mut *s.e, &frame); |
|
| 332 | 334 | ||
| 333 | 335 | // Move function params from arg registers to assigned registers. |
|
| 334 | 336 | // Cross-call params may have been assigned to callee-saved registers |
|
| 335 | 337 | // instead of their natural arg registers. Spilled params are stored |
|
| 336 | 338 | // directly to their spill slots. |
| 339 | 341 | let param = funcParam.value; |
|
| 340 | 342 | let argReg = super::ARG_REGS[i]; |
|
| 341 | 343 | ||
| 342 | 344 | if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) { |
|
| 343 | 345 | // Spilled parameter: store arg register to spill slot. |
|
| 344 | - | emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot)); |
|
| 346 | + | emit::emitSd(&mut *s.e, argReg, spillBase(&s), spillOffset(&s, slot)); |
|
| 345 | 347 | } else if let assigned = ralloc.assignments[param.n] { |
|
| 346 | 348 | emitMv(&mut s, assigned, argReg); |
|
| 347 | 349 | } |
|
| 348 | 350 | } |
|
| 349 | 351 | } |
| 351 | 353 | // Emit each block. |
|
| 352 | 354 | for i in 0..func.blocks.len { |
|
| 353 | 355 | selectBlock(&mut s, i, &func.blocks[i], &frame, func); |
|
| 354 | 356 | } |
|
| 355 | 357 | // Emit epilogue. |
|
| 356 | - | emit::emitEpilogue(s.e, &frame); |
|
| 358 | + | emit::emitEpilogue(&mut *s.e, &frame); |
|
| 357 | 359 | // Patch local branches now that all blocks are emitted. |
|
| 358 | - | emit::patchLocalBranches(s.e); |
|
| 360 | + | emit::patchLocalBranches(&mut *s.e); |
|
| 359 | 361 | } |
|
| 360 | 362 | ||
| 361 | 363 | /// Select instructions for a block. |
|
| 362 | - | fn selectBlock(s: *mut Selector, blockIdx: u32, block: *il::Block, frame: *emit::Frame, func: *il::Fn) { |
|
| 364 | + | unsafe fn selectBlock(s: &mut Selector, blockIdx: u32, block: *il::Block, frame: &emit::Frame, func: *il::Fn) { |
|
| 363 | 365 | // Record block address for branch patching. |
|
| 364 | - | emit::recordBlock(s.e, blockIdx); |
|
| 366 | + | emit::recordBlock(&mut *s.e, blockIdx); |
|
| 365 | 367 | ||
| 366 | 368 | // Block parameters are handled at jump sites (in `Jmp`/`Br`). |
|
| 367 | 369 | // By the time we enter the block, the arguments have already been |
|
| 368 | 370 | // moved to the parameter registers by the predecessor's terminator. |
|
| 369 | 371 | ||
| 370 | 372 | // Process each instruction, auto-committing any pending spill after each. |
|
| 371 | 373 | let hasLocs = block.locs.len > 0; |
|
| 372 | 374 | for instr, i in block.instrs { |
|
| 373 | 375 | // Record debug location before emitting machine instructions. |
|
| 374 | 376 | if hasLocs { |
|
| 375 | - | emit::recordSrcLoc(s.e, block.locs[i]); |
|
| 377 | + | emit::recordSrcLoc(&mut *s.e, block.locs[i]); |
|
| 376 | 378 | } |
|
| 377 | 379 | set s.pendingSpill = nil; |
|
| 378 | 380 | selectInstr(s, blockIdx, instr, frame, func); |
|
| 379 | 381 | ||
| 380 | 382 | // Flush the pending spill store, if any. |
|
| 381 | 383 | if let p = s.pendingSpill { |
|
| 382 | 384 | if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, p.ssa) { |
|
| 383 | - | emit::emitSd(s.e, p.rd, spillBase(s), spillOffset(s, slot)); |
|
| 385 | + | emit::emitSd(&mut *s.e, p.rd, spillBase(s), spillOffset(s, slot)); |
|
| 384 | 386 | } |
|
| 385 | 387 | set s.pendingSpill = nil; |
|
| 386 | 388 | } |
|
| 387 | 389 | } |
|
| 388 | 390 | } |
|
| 389 | 391 | ||
| 390 | 392 | /// Select instructions for a single IL instruction. |
|
| 391 | - | fn selectInstr(s: *mut Selector, blockIdx: u32, instr: il::Instr, frame: *emit::Frame, func: *il::Fn) { |
|
| 393 | + | unsafe fn selectInstr(s: &mut Selector, blockIdx: u32, instr: il::Instr, frame: &emit::Frame, func: *il::Fn) { |
|
| 392 | 394 | match instr { |
|
| 393 | 395 | case il::Instr::BinOp { op, typ, dst, a, b } => { |
|
| 394 | 396 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 395 | 397 | let rs1 = resolveVal(s, super::SCRATCH1, a); |
|
| 396 | 398 | selectAluBinOp(s, op, typ, rd, rs1, b); |
| 401 | 403 | selectAluUnOp(s, op, typ, rd, rs); |
|
| 402 | 404 | }, |
|
| 403 | 405 | case il::Instr::Load { typ, dst, src, offset } => { |
|
| 404 | 406 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 405 | 407 | let base = getSrcReg(s, src, super::SCRATCH2); |
|
| 406 | - | emit::emitLoad(s.e, rd, base, offset, typ); |
|
| 408 | + | emit::emitLoad(&mut *s.e, rd, base, offset, typ); |
|
| 407 | 409 | }, |
|
| 408 | 410 | case il::Instr::Sload { typ, dst, src, offset } => { |
|
| 409 | 411 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 410 | 412 | let base = getSrcReg(s, src, super::SCRATCH2); |
|
| 411 | - | emit::emitSload(s.e, rd, base, offset, typ); |
|
| 413 | + | emit::emitSload(&mut *s.e, rd, base, offset, typ); |
|
| 412 | 414 | }, |
|
| 413 | 415 | case il::Instr::Store { typ, src, dst, offset } => { |
|
| 414 | 416 | let base = getSrcReg(s, dst, super::SCRATCH2); |
|
| 415 | 417 | let rs = resolveVal(s, super::SCRATCH1, src); |
|
| 416 | - | emit::emitStore(s.e, rs, base, offset, typ); |
|
| 418 | + | emit::emitStore(&mut *s.e, rs, base, offset, typ); |
|
| 417 | 419 | }, |
|
| 418 | 420 | case il::Instr::Copy { dst, val } => { |
|
| 419 | 421 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 420 | 422 | let rs = resolveVal(s, super::SCRATCH1, val); |
|
| 421 | 423 | emitMv(s, rd, rs); |
| 428 | 430 | let aligned: i32 = mem::alignUpI32(s.reserveOffset, alignment as i32); |
|
| 429 | 431 | let base = spillBase(s); |
|
| 430 | 432 | let offset = s.ralloc.spill.frameSize + aligned |
|
| 431 | 433 | - (s.frameSize if s.isDynamic else 0); |
|
| 432 | 434 | ||
| 433 | - | emit::emitAddImm(s.e, rd, base, offset); |
|
| 435 | + | emit::emitAddImm(&mut *s.e, rd, base, offset); |
|
| 434 | 436 | set s.reserveOffset = aligned + (sz as i32); |
|
| 435 | 437 | }, |
|
| 436 | 438 | case il::Val::Reg(r) => { |
|
| 437 | 439 | // Dynamic-sized reserve: runtime SP adjustment. |
|
| 438 | 440 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 439 | 441 | let rs = getSrcReg(s, r, super::SCRATCH2); |
|
| 440 | 442 | ||
| 441 | - | emit::emit(s.e, encode::sub(super::SP, super::SP, rs)); |
|
| 443 | + | emit::emit(&mut *s.e, encode::sub(super::SP, super::SP, rs)); |
|
| 442 | 444 | ||
| 443 | 445 | if alignment > 1 { |
|
| 444 | 446 | let mask = 0 - alignment as i32; |
|
| 445 | 447 | assert encode::isSmallImm(mask); |
|
| 446 | 448 | ||
| 447 | - | emit::emit(s.e, encode::andi(super::SP, super::SP, mask)); |
|
| 449 | + | emit::emit(&mut *s.e, encode::andi(super::SP, super::SP, mask)); |
|
| 448 | 450 | } |
|
| 449 | - | emit::emit(s.e, encode::mv(rd, super::SP)); |
|
| 451 | + | emit::emit(&mut *s.e, encode::mv(rd, super::SP)); |
|
| 450 | 452 | }, |
|
| 451 | 453 | else => |
|
| 452 | 454 | panic "selectInstr: invalid reserve operand", |
|
| 453 | 455 | } |
|
| 454 | 456 | }, |
| 474 | 476 | panic "selectInstr: blit dst not spilled"; |
|
| 475 | 477 | }; |
|
| 476 | 478 | let srcSlot = regalloc::spill::spillSlot(&s.ralloc.spill, src) else { |
|
| 477 | 479 | panic "selectInstr: blit src not spilled"; |
|
| 478 | 480 | }; |
|
| 479 | - | emit::emitLd(s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot)); |
|
| 481 | + | emit::emitLd(&mut *s.e, super::SCRATCH2, spillBase(s), spillOffset(s, dstSlot)); |
|
| 480 | 482 | set srcReload = spillOffset(s, srcSlot); |
|
| 481 | 483 | } else { |
|
| 482 | 484 | set rdst = getSrcReg(s, dst, super::SCRATCH2); |
|
| 483 | 485 | set rsrc = getSrcReg(s, src, super::SCRATCH2); |
|
| 484 | 486 | } |
| 491 | 493 | let canLoop = not bothSpilled |
|
| 492 | 494 | and *rsrc <> *super::SCRATCH1 and *rsrc <> *super::SCRATCH2 |
|
| 493 | 495 | and *rdst <> *super::SCRATCH1 and *rdst <> *super::SCRATCH2; |
|
| 494 | 496 | ||
| 495 | 497 | if canLoop and dwordBytes >= super::BLIT_LOOP_THRESHOLD { |
|
| 496 | - | emit::emitAddImm(s.e, super::SCRATCH1, rsrc, dwordBytes); |
|
| 498 | + | emit::emitAddImm(&mut *s.e, super::SCRATCH1, rsrc, dwordBytes); |
|
| 497 | 499 | ||
| 498 | 500 | let loopStart = s.e.codeLen; |
|
| 499 | 501 | ||
| 500 | - | emit::emitLd(s.e, super::SCRATCH2, rsrc, 0); |
|
| 501 | - | emit::emitSd(s.e, super::SCRATCH2, rdst, 0); |
|
| 502 | - | emit::emit(s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE)); |
|
| 502 | + | emit::emitLd(&mut *s.e, super::SCRATCH2, rsrc, 0); |
|
| 503 | + | emit::emitSd(&mut *s.e, super::SCRATCH2, rdst, 0); |
|
| 504 | + | emit::emit(&mut *s.e, encode::addi(rsrc, rsrc, super::DWORD_SIZE)); |
|
| 503 | 505 | ||
| 504 | 506 | if *rdst <> *rsrc { |
|
| 505 | - | emit::emit(s.e, encode::addi(rdst, rdst, super::DWORD_SIZE)); |
|
| 507 | + | emit::emit(&mut *s.e, encode::addi(rdst, rdst, super::DWORD_SIZE)); |
|
| 506 | 508 | } |
|
| 507 | 509 | let brOff = (loopStart as i32 - s.e.codeLen as i32) * super::INSTR_SIZE; |
|
| 508 | 510 | ||
| 509 | - | emit::emit(s.e, encode::bne(rsrc, super::SCRATCH1, brOff)); |
|
| 511 | + | emit::emit(&mut *s.e, encode::bne(rsrc, super::SCRATCH1, brOff)); |
|
| 510 | 512 | set remaining -= dwordBytes; |
|
| 511 | 513 | } |
|
| 512 | 514 | ||
| 513 | 515 | // Copy remaining: 8 bytes, then 4 bytes, then 1 byte at a time. |
|
| 514 | 516 | // Before each load/store pair, check whether the offset is |
|
| 515 | 517 | // about to exceed the 12-bit signed immediate range. When |
|
| 516 | 518 | // it does, advance the base registers by the accumulated |
|
| 517 | 519 | // offset and reset to zero. |
|
| 518 | 520 | while remaining >= super::DWORD_SIZE { |
|
| 519 | 521 | if offset > super::MAX_IMM - super::DWORD_SIZE { |
|
| 520 | - | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 522 | + | emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset); |
|
| 521 | 523 | if *rdst <> *rsrc { |
|
| 522 | - | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 524 | + | emit::emitAddImm(&mut *s.e, rdst, rdst, offset); |
|
| 523 | 525 | } |
|
| 524 | 526 | set offset = 0; |
|
| 525 | 527 | } |
|
| 526 | 528 | if let off = srcReload { |
|
| 527 | - | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 528 | - | emit::emitLd(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 529 | + | emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off); |
|
| 530 | + | emit::emitLd(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 529 | 531 | } else { |
|
| 530 | - | emit::emitLd(s.e, super::SCRATCH1, rsrc, offset); |
|
| 532 | + | emit::emitLd(&mut *s.e, super::SCRATCH1, rsrc, offset); |
|
| 531 | 533 | } |
|
| 532 | - | emit::emitSd(s.e, super::SCRATCH1, rdst, offset); |
|
| 534 | + | emit::emitSd(&mut *s.e, super::SCRATCH1, rdst, offset); |
|
| 533 | 535 | set offset += super::DWORD_SIZE; |
|
| 534 | 536 | set remaining -= super::DWORD_SIZE; |
|
| 535 | 537 | } |
|
| 536 | 538 | if remaining >= super::WORD_SIZE { |
|
| 537 | 539 | if offset > super::MAX_IMM - super::WORD_SIZE { |
|
| 538 | - | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 540 | + | emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset); |
|
| 539 | 541 | if *rdst <> *rsrc { |
|
| 540 | - | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 542 | + | emit::emitAddImm(&mut *s.e, rdst, rdst, offset); |
|
| 541 | 543 | } |
|
| 542 | 544 | set offset = 0; |
|
| 543 | 545 | } |
|
| 544 | 546 | if let off = srcReload { |
|
| 545 | - | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 546 | - | emit::emitLw(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 547 | + | emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off); |
|
| 548 | + | emit::emitLw(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 547 | 549 | } else { |
|
| 548 | - | emit::emitLw(s.e, super::SCRATCH1, rsrc, offset); |
|
| 550 | + | emit::emitLw(&mut *s.e, super::SCRATCH1, rsrc, offset); |
|
| 549 | 551 | } |
|
| 550 | - | emit::emitSw(s.e, super::SCRATCH1, rdst, offset); |
|
| 552 | + | emit::emitSw(&mut *s.e, super::SCRATCH1, rdst, offset); |
|
| 551 | 553 | set offset += super::WORD_SIZE; |
|
| 552 | 554 | set remaining -= super::WORD_SIZE; |
|
| 553 | 555 | } |
|
| 554 | 556 | while remaining > 0 { |
|
| 555 | 557 | if offset > super::MAX_IMM - 1 { |
|
| 556 | - | emit::emitAddImm(s.e, rsrc, rsrc, offset); |
|
| 558 | + | emit::emitAddImm(&mut *s.e, rsrc, rsrc, offset); |
|
| 557 | 559 | if *rdst <> *rsrc { |
|
| 558 | - | emit::emitAddImm(s.e, rdst, rdst, offset); |
|
| 560 | + | emit::emitAddImm(&mut *s.e, rdst, rdst, offset); |
|
| 559 | 561 | } |
|
| 560 | 562 | set offset = 0; |
|
| 561 | 563 | } |
|
| 562 | 564 | if let off = srcReload { |
|
| 563 | - | emit::emitLd(s.e, super::SCRATCH1, spillBase(s), off); |
|
| 564 | - | emit::emitLb(s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 565 | + | emit::emitLd(&mut *s.e, super::SCRATCH1, spillBase(s), off); |
|
| 566 | + | emit::emitLb(&mut *s.e, super::SCRATCH1, super::SCRATCH1, offset); |
|
| 565 | 567 | } else { |
|
| 566 | - | emit::emitLb(s.e, super::SCRATCH1, rsrc, offset); |
|
| 568 | + | emit::emitLb(&mut *s.e, super::SCRATCH1, rsrc, offset); |
|
| 567 | 569 | } |
|
| 568 | - | emit::emitSb(s.e, super::SCRATCH1, rdst, offset); |
|
| 570 | + | emit::emitSb(&mut *s.e, super::SCRATCH1, rdst, offset); |
|
| 569 | 571 | set offset += 1; |
|
| 570 | 572 | set remaining -= 1; |
|
| 571 | 573 | } |
|
| 572 | 574 | // Restore base registers if they were advanced (never happens |
|
| 573 | 575 | // in the both-spilled case since size <= MAX_IMM). |
|
| 574 | 576 | if not bothSpilled { |
|
| 575 | 577 | let advanced = staticSize as i32 - offset; |
|
| 576 | 578 | if advanced <> 0 { |
|
| 577 | - | emit::emitAddImm(s.e, rsrc, rsrc, 0 - advanced); |
|
| 579 | + | emit::emitAddImm(&mut *s.e, rsrc, rsrc, 0 - advanced); |
|
| 578 | 580 | if *rdst <> *rsrc { |
|
| 579 | - | emit::emitAddImm(s.e, rdst, rdst, 0 - advanced); |
|
| 581 | + | emit::emitAddImm(&mut *s.e, rdst, rdst, 0 - advanced); |
|
| 580 | 582 | } |
|
| 581 | 583 | } |
|
| 582 | 584 | } |
|
| 583 | 585 | }, |
|
| 584 | 586 | case il::Instr::Zext { typ, dst, val } => { |
|
| 585 | 587 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 586 | 588 | let rs = resolveVal(s, super::SCRATCH1, val); |
|
| 587 | - | emitZext(s.e, rd, rs, typ); |
|
| 589 | + | emitZext(&mut *s.e, rd, rs, typ); |
|
| 588 | 590 | }, |
|
| 589 | 591 | case il::Instr::Sext { typ, dst, val } => { |
|
| 590 | 592 | let rd = getDstReg(s, dst, super::SCRATCH1); |
|
| 591 | 593 | let rs = resolveVal(s, super::SCRATCH1, val); |
|
| 592 | - | emitSext(s.e, rd, rs, typ); |
|
| 594 | + | emitSext(&mut *s.e, rd, rs, typ); |
|
| 593 | 595 | }, |
|
| 594 | 596 | case il::Instr::Ret { val } => { |
|
| 595 | 597 | if let v = val { |
|
| 596 | 598 | let rs = resolveVal(s, super::SCRATCH1, v); |
|
| 597 | 599 | emitMv(s, super::A0, rs); |
| 599 | 601 | // Skip the jump to epilogue if this RET is in the last block, |
|
| 600 | 602 | // since the epilogue immediately follows. |
|
| 601 | 603 | if frame.totalSize <> 0 and blockIdx + 1 == frame.epilogueBlock { |
|
| 602 | 604 | // Epilogue is the next block; fallthrough is sufficient. |
|
| 603 | 605 | } else { |
|
| 604 | - | emit::emitReturn(s.e, frame); |
|
| 606 | + | emit::emitReturn(&mut *s.e, frame); |
|
| 605 | 607 | } |
|
| 606 | 608 | }, |
|
| 607 | 609 | case il::Instr::Jmp { target, args } => { |
|
| 608 | 610 | // Move arguments to target block's parameter registers. |
|
| 609 | 611 | emitBlockArgs(s, func, target, args); |
|
| 610 | 612 | // Skip branch if target is the next block (fallthrough). |
|
| 611 | 613 | if target <> blockIdx + 1 { |
|
| 612 | - | emit::recordBranch(s.e, target, emit::BranchKind::Jump); |
|
| 614 | + | emit::recordBranch(&mut *s.e, target, emit::BranchKind::Jump); |
|
| 613 | 615 | } |
|
| 614 | 616 | }, |
|
| 615 | 617 | case il::Instr::Br { op, typ, a, b, thenTarget, thenArgs, elseTarget, elseArgs } => { |
|
| 616 | 618 | // Use zero register directly for immediate `0` operands. |
|
| 617 | 619 | let aIsZero = isZeroImm(a); |
| 634 | 636 | if let case il::CmpOp::Slt = op { |
|
| 635 | 637 | set signed = true; |
|
| 636 | 638 | } |
|
| 637 | 639 | let useSext = cmpUsesSext(typ, signed); |
|
| 638 | 640 | if not aIsZero and not isExtendedImm(a, typ, useSext) { |
|
| 639 | - | emitCmpExt(s.e, rs1, rs1, typ, useSext); |
|
| 641 | + | emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext); |
|
| 640 | 642 | } |
|
| 641 | 643 | if not bIsZero and not isExtendedImm(b, typ, useSext) { |
|
| 642 | - | emitCmpExt(s.e, rs2, rs2, typ, useSext); |
|
| 644 | + | emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext); |
|
| 643 | 645 | } |
|
| 644 | 646 | // Block-argument moves must only execute on the taken path. |
|
| 645 | 647 | // When `thenArgs` is non-empty, invert the branch so that the |
|
| 646 | 648 | // then-moves land on the fall-through (taken) side. |
|
| 647 | 649 | // |
| 650 | 652 | // conditional branch to skip to the *other* target and letting |
|
| 651 | 653 | // execution fall through. |
|
| 652 | 654 | if thenArgs.len > 0 and elseArgs.len > 0 { |
|
| 653 | 655 | panic "selectInstr: both `then` and `else` have block arguments"; |
|
| 654 | 656 | } else if thenArgs.len > 0 { |
|
| 655 | - | emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 }); |
|
| 657 | + | emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 }); |
|
| 656 | 658 | emitBlockArgs(s, func, thenTarget, thenArgs); |
|
| 657 | 659 | // Skip trailing jump if then is the next block (fallthrough). |
|
| 658 | 660 | if thenTarget <> blockIdx + 1 { |
|
| 659 | - | emit::recordBranch(s.e, thenTarget, emit::BranchKind::Jump); |
|
| 661 | + | emit::recordBranch(&mut *s.e, thenTarget, emit::BranchKind::Jump); |
|
| 660 | 662 | } |
|
| 661 | 663 | } else if thenTarget == blockIdx + 1 and elseArgs.len == 0 { |
|
| 662 | 664 | // Then is the next block and no else args: invert the |
|
| 663 | 665 | // condition to branch to else and fall through to then. |
|
| 664 | - | emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 }); |
|
| 666 | + | emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 }); |
|
| 665 | 667 | } else { |
|
| 666 | - | emit::recordBranch(s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 }); |
|
| 668 | + | emit::recordBranch(&mut *s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 }); |
|
| 667 | 669 | emitBlockArgs(s, func, elseTarget, elseArgs); |
|
| 668 | 670 | // Skip trailing jump if else is the next block (fallthrough). |
|
| 669 | 671 | if elseTarget <> blockIdx + 1 { |
|
| 670 | - | emit::recordBranch(s.e, elseTarget, emit::BranchKind::Jump); |
|
| 672 | + | emit::recordBranch(&mut *s.e, elseTarget, emit::BranchKind::Jump); |
|
| 671 | 673 | } |
|
| 672 | 674 | } |
|
| 673 | 675 | }, |
|
| 674 | 676 | case il::Instr::Switch { val, defaultTarget, defaultArgs, cases } => { |
|
| 675 | 677 | let rs1 = resolveVal(s, super::SCRATCH1, val); |
|
| 676 | 678 | // When a case has block args, invert the branch to skip past |
|
| 677 | 679 | // the arg moves. |
|
| 678 | 680 | for c in cases { |
|
| 679 | - | emit::loadImm(s.e, super::SCRATCH2, c.value); |
|
| 681 | + | emit::loadImm(&mut *s.e, super::SCRATCH2, c.value); |
|
| 680 | 682 | ||
| 681 | 683 | if c.args.len > 0 { |
|
| 682 | 684 | let skip = s.nextSynthBlock; |
|
| 683 | 685 | set s.nextSynthBlock = skip + 1; |
|
| 684 | 686 | ||
| 685 | - | emit::recordBranch(s.e, skip, emit::BranchKind::InvertedCond { |
|
| 687 | + | emit::recordBranch(&mut *s.e, skip, emit::BranchKind::InvertedCond { |
|
| 686 | 688 | op: il::CmpOp::Eq, rs1, rs2: super::SCRATCH2, |
|
| 687 | 689 | }); |
|
| 688 | 690 | emitBlockArgs(s, func, c.target, c.args); |
|
| 689 | - | emit::recordBranch(s.e, c.target, emit::BranchKind::Jump); |
|
| 690 | - | emit::recordBlock(s.e, skip); |
|
| 691 | + | emit::recordBranch(&mut *s.e, c.target, emit::BranchKind::Jump); |
|
| 692 | + | emit::recordBlock(&mut *s.e, skip); |
|
| 691 | 693 | } else { |
|
| 692 | - | emit::recordBranch(s.e, c.target, emit::BranchKind::Cond { |
|
| 694 | + | emit::recordBranch(&mut *s.e, c.target, emit::BranchKind::Cond { |
|
| 693 | 695 | op: il::CmpOp::Eq, rs1, rs2: super::SCRATCH2, |
|
| 694 | 696 | }); |
|
| 695 | 697 | } |
|
| 696 | 698 | } |
|
| 697 | 699 | // Fall through to default. |
|
| 698 | 700 | emitBlockArgs(s, func, defaultTarget, defaultArgs); |
|
| 699 | - | emit::recordBranch(s.e, defaultTarget, emit::BranchKind::Jump); |
|
| 701 | + | emit::recordBranch(&mut *s.e, defaultTarget, emit::BranchKind::Jump); |
|
| 700 | 702 | }, |
|
| 701 | 703 | case il::Instr::Unreachable => { |
|
| 702 | - | emit::emit(s.e, encode::ebreak()); |
|
| 704 | + | emit::emit(&mut *s.e, encode::ebreak()); |
|
| 703 | 705 | }, |
|
| 704 | 706 | case il::Instr::Call { retTy, dst, func, args } => { |
|
| 705 | 707 | // For indirect calls, save target to scratch register before arg |
|
| 706 | 708 | // setup can clobber it. |
|
| 707 | 709 | if let case il::Val::Reg(r) = func { |
| 713 | 715 | emitParallelMoves(s, &super::ARG_REGS[..], args); |
|
| 714 | 716 | ||
| 715 | 717 | // Emit call. |
|
| 716 | 718 | match func { |
|
| 717 | 719 | case il::Val::FnAddr(name) => { |
|
| 718 | - | emit::recordCall(s.e, name); |
|
| 720 | + | emit::recordCall(&mut *s.e, name); |
|
| 719 | 721 | }, |
|
| 720 | 722 | case il::Val::Reg(_) => { |
|
| 721 | - | emit::emit(s.e, encode::jalr(super::RA, super::SCRATCH2, 0)); |
|
| 723 | + | emit::emit(&mut *s.e, encode::jalr(super::RA, super::SCRATCH2, 0)); |
|
| 722 | 724 | }, |
|
| 723 | 725 | else => { |
|
| 724 | 726 | panic "selectInstr: invalid call target"; |
|
| 725 | 727 | } |
|
| 726 | 728 | } |
| 736 | 738 | // support constant-evaluating struct/union values in them. |
|
| 737 | 739 | let ecallDsts: [gen::Reg; 5] = [super::A7, super::A0, super::A1, super::A2, super::A3]; |
|
| 738 | 740 | let ecallArgs: [il::Val; 5] = [num, a0, a1, a2, a3]; |
|
| 739 | 741 | ||
| 740 | 742 | emitParallelMoves(s, &ecallDsts[..], &ecallArgs[..]); |
|
| 741 | - | emit::emit(s.e, encode::ecall()); |
|
| 743 | + | emit::emit(&mut *s.e, encode::ecall()); |
|
| 742 | 744 | ||
| 743 | 745 | // Result in A0. |
|
| 744 | 746 | let ecallRd = getDstReg(s, dst, super::SCRATCH1); |
|
| 745 | 747 | emitMv(s, ecallRd, super::A0); |
|
| 746 | 748 | }, |
|
| 747 | 749 | case il::Instr::Ebreak => { |
|
| 748 | - | emit::emit(s.e, encode::ebreak()); |
|
| 750 | + | emit::emit(&mut *s.e, encode::ebreak()); |
|
| 749 | 751 | }, |
|
| 750 | 752 | case il::Instr::MemoryFence => { |
|
| 751 | - | emit::emit(s.e, encode::fence()); |
|
| 753 | + | emit::emit(&mut *s.e, encode::fence()); |
|
| 752 | 754 | }, |
|
| 753 | 755 | } |
|
| 754 | 756 | } |
|
| 755 | 757 | ||
| 756 | 758 | /// Choose the cheapest canonical representation that preserves the comparison. |
| 760 | 762 | return signed or typ == il::Type::W32; |
|
| 761 | 763 | } |
|
| 762 | 764 | ||
| 763 | 765 | /// Extend a comparison operand to its selected canonical representation. |
|
| 764 | 766 | fn emitCmpExt( |
|
| 765 | - | e: *mut emit::Emitter, |
|
| 767 | + | e: &mut emit::Emitter, |
|
| 766 | 768 | rd: gen::Reg, |
|
| 767 | 769 | rs: gen::Reg, |
|
| 768 | 770 | typ: il::Type, |
|
| 769 | 771 | useSext: bool |
|
| 770 | 772 | ) { |
| 830 | 832 | return false; |
|
| 831 | 833 | } |
|
| 832 | 834 | ||
| 833 | 835 | /// Select a binary ALU operation, dispatching to the appropriate |
|
| 834 | 836 | /// instruction pattern based on the operation kind and type. |
|
| 835 | - | fn selectAluBinOp(s: *mut Selector, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) { |
|
| 837 | + | unsafe fn selectAluBinOp(s: &mut Selector, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) { |
|
| 836 | 838 | match op { |
|
| 837 | 839 | case il::BinOp::Add => { |
|
| 838 | 840 | if typ == il::Type::W32 { |
|
| 839 | 841 | // Inline W32 ADD with immediate optimization. |
|
| 840 | 842 | if let case il::Val::Imm(imm) = b { |
|
| 841 | 843 | if encode::isSmallImm64(imm) { |
|
| 842 | - | emit::emit(s.e, encode::addiw(rd, rs1, imm as i32)); |
|
| 844 | + | emit::emit(&mut *s.e, encode::addiw(rd, rs1, imm as i32)); |
|
| 843 | 845 | return; |
|
| 844 | 846 | } |
|
| 845 | 847 | } |
|
| 846 | 848 | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 847 | - | emit::emit(s.e, encode::addw(rd, rs1, rs2)); |
|
| 849 | + | emit::emit(&mut *s.e, encode::addw(rd, rs1, rs2)); |
|
| 848 | 850 | } else { |
|
| 849 | 851 | selectBinOp(s, rd, rs1, b, BinOp::Add, super::SCRATCH2); |
|
| 850 | 852 | } |
|
| 851 | 853 | } |
|
| 852 | 854 | case il::BinOp::Sub => { |
|
| 853 | 855 | // Optimize subtraction by small immediate: use ADDI with negated value. |
|
| 854 | 856 | if let case il::Val::Imm(imm) = b { |
|
| 855 | 857 | let neg = -imm; |
|
| 856 | 858 | if neg >= super::MIN_IMM as i64 and neg <= super::MAX_IMM as i64 { |
|
| 857 | - | emit::emit(s.e, |
|
| 859 | + | emit::emit(&mut *s.e, |
|
| 858 | 860 | encode::addiw(rd, rs1, neg as i32) |
|
| 859 | 861 | if typ == il::Type::W32 else |
|
| 860 | 862 | encode::addi(rd, rs1, neg as i32)); |
|
| 861 | 863 | return; |
|
| 862 | 864 | } |
|
| 863 | 865 | } |
|
| 864 | 866 | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 865 | 867 | ||
| 866 | - | emit::emit(s.e, |
|
| 868 | + | emit::emit(&mut *s.e, |
|
| 867 | 869 | encode::subw(rd, rs1, rs2) |
|
| 868 | 870 | if typ == il::Type::W32 else |
|
| 869 | 871 | encode::sub(rd, rs1, rs2)); |
|
| 870 | 872 | } |
|
| 871 | 873 | case il::BinOp::Mul => { |
|
| 872 | 874 | // Strength-reduce multiplication by known constants. |
|
| 873 | 875 | if let case il::Val::Imm(imm) = b { |
|
| 874 | 876 | if imm == 0 { |
|
| 875 | - | emit::emit(s.e, encode::mv(rd, super::ZERO)); |
|
| 877 | + | emit::emit(&mut *s.e, encode::mv(rd, super::ZERO)); |
|
| 876 | 878 | return; |
|
| 877 | 879 | } else if imm == 1 { |
|
| 878 | 880 | emitMv(s, rd, rs1); |
|
| 879 | 881 | return; |
|
| 880 | 882 | } else if imm == 2 { |
|
| 881 | - | emit::emit(s.e, encode::slli(rd, rs1, 1)); |
|
| 883 | + | emit::emit(&mut *s.e, encode::slli(rd, rs1, 1)); |
|
| 882 | 884 | return; |
|
| 883 | 885 | } else if imm == 4 { |
|
| 884 | - | emit::emit(s.e, encode::slli(rd, rs1, 2)); |
|
| 886 | + | emit::emit(&mut *s.e, encode::slli(rd, rs1, 2)); |
|
| 885 | 887 | return; |
|
| 886 | 888 | } else if imm == 8 { |
|
| 887 | - | emit::emit(s.e, encode::slli(rd, rs1, 3)); |
|
| 889 | + | emit::emit(&mut *s.e, encode::slli(rd, rs1, 3)); |
|
| 888 | 890 | return; |
|
| 889 | 891 | } |
|
| 890 | 892 | } |
|
| 891 | 893 | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 892 | - | emit::emit(s.e, |
|
| 894 | + | emit::emit(&mut *s.e, |
|
| 893 | 895 | encode::mulw(rd, rs1, rs2) |
|
| 894 | 896 | if typ == il::Type::W32 else |
|
| 895 | 897 | encode::mul(rd, rs1, rs2)); |
|
| 896 | 898 | } |
|
| 897 | 899 | case il::BinOp::Sdiv => { |
|
| 898 | 900 | let rs2 = resolveAndTrapIfZero(s, b, typ, true); |
|
| 899 | - | emit::emit(s.e, |
|
| 901 | + | emit::emit(&mut *s.e, |
|
| 900 | 902 | encode::divw(rd, rs1, rs2) |
|
| 901 | 903 | if typ == il::Type::W32 else |
|
| 902 | 904 | encode::div(rd, rs1, rs2)); |
|
| 903 | 905 | } |
|
| 904 | 906 | case il::BinOp::Udiv => { |
|
| 905 | 907 | let rs2 = resolveAndTrapIfZero(s, b, typ, false); |
|
| 906 | - | emit::emit(s.e, |
|
| 908 | + | emit::emit(&mut *s.e, |
|
| 907 | 909 | encode::divuw(rd, rs1, rs2) |
|
| 908 | 910 | if typ == il::Type::W32 else |
|
| 909 | 911 | encode::divu(rd, rs1, rs2)); |
|
| 910 | 912 | } |
|
| 911 | 913 | case il::BinOp::Srem => { |
|
| 912 | 914 | let rs2 = resolveAndTrapIfZero(s, b, typ, true); |
|
| 913 | - | emit::emit(s.e, |
|
| 915 | + | emit::emit(&mut *s.e, |
|
| 914 | 916 | encode::remw(rd, rs1, rs2) |
|
| 915 | 917 | if typ == il::Type::W32 else |
|
| 916 | 918 | encode::rem(rd, rs1, rs2)); |
|
| 917 | 919 | } |
|
| 918 | 920 | case il::BinOp::Urem => { |
|
| 919 | 921 | let rs2 = resolveAndTrapIfZero(s, b, typ, false); |
|
| 920 | - | emit::emit(s.e, |
|
| 922 | + | emit::emit(&mut *s.e, |
|
| 921 | 923 | encode::remuw(rd, rs1, rs2) |
|
| 922 | 924 | if typ == il::Type::W32 else |
|
| 923 | 925 | encode::remu(rd, rs1, rs2)); |
|
| 924 | 926 | } |
|
| 925 | 927 | case il::BinOp::And => |
| 935 | 937 | case il::BinOp::Ushr => |
|
| 936 | 938 | selectShift(s, rd, rs1, b, ShiftOp::Srl, typ, super::SCRATCH2), |
|
| 937 | 939 | case il::BinOp::Eq, il::BinOp::Ne => { |
|
| 938 | 940 | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 939 | 941 | let useSext = cmpUsesSext(typ, false); |
|
| 940 | - | emitCmpExt(s.e, rs1, rs1, typ, useSext); |
|
| 942 | + | emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext); |
|
| 941 | 943 | if not isExtendedImm(b, typ, useSext) { |
|
| 942 | - | emitCmpExt(s.e, rs2, rs2, typ, useSext); |
|
| 944 | + | emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext); |
|
| 943 | 945 | } |
|
| 944 | - | emit::emit(s.e, encode::xor(rd, rs1, rs2)); |
|
| 946 | + | emit::emit(&mut *s.e, encode::xor(rd, rs1, rs2)); |
|
| 945 | 947 | if let case il::BinOp::Eq = op { |
|
| 946 | - | emit::emit(s.e, encode::sltiu(rd, rd, 1)); |
|
| 948 | + | emit::emit(&mut *s.e, encode::sltiu(rd, rd, 1)); |
|
| 947 | 949 | } else { |
|
| 948 | - | emit::emit(s.e, encode::sltu(rd, super::ZERO, rd)); |
|
| 950 | + | emit::emit(&mut *s.e, encode::sltu(rd, super::ZERO, rd)); |
|
| 949 | 951 | } |
|
| 950 | 952 | } |
|
| 951 | 953 | case il::BinOp::Slt => |
|
| 952 | 954 | selectCmp(s, typ, rd, rs1, b, CmpOp::Slt, false, super::SCRATCH2), |
|
| 953 | 955 | case il::BinOp::Ult => |
| 958 | 960 | selectCmp(s, typ, rd, rs1, b, CmpOp::Ult, true, super::SCRATCH2), |
|
| 959 | 961 | } |
|
| 960 | 962 | } |
|
| 961 | 963 | ||
| 962 | 964 | /// Select a unary ALU operation. |
|
| 963 | - | fn selectAluUnOp(s: *mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) { |
|
| 965 | + | unsafe fn selectAluUnOp(s: &mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) { |
|
| 964 | 966 | match op { |
|
| 965 | 967 | case il::UnOp::Neg => { |
|
| 966 | 968 | if typ == il::Type::W32 { |
|
| 967 | - | emit::emit(s.e, encode::subw(rd, super::ZERO, rs)); |
|
| 969 | + | emit::emit(&mut *s.e, encode::subw(rd, super::ZERO, rs)); |
|
| 968 | 970 | } else { |
|
| 969 | - | emit::emit(s.e, encode::neg(rd, rs)); |
|
| 971 | + | emit::emit(&mut *s.e, encode::neg(rd, rs)); |
|
| 970 | 972 | } |
|
| 971 | 973 | } |
|
| 972 | 974 | case il::UnOp::Not => |
|
| 973 | - | emit::emit(s.e, encode::not_(rd, rs)), |
|
| 975 | + | emit::emit(&mut *s.e, encode::not_(rd, rs)), |
|
| 974 | 976 | } |
|
| 975 | 977 | } |
|
| 976 | 978 | ||
| 977 | 979 | /// Select binary operation with immediate optimization. |
|
| 978 | - | fn selectBinOp(s: *mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) { |
|
| 980 | + | unsafe fn selectBinOp(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) { |
|
| 979 | 981 | // Try immediate optimization first. |
|
| 980 | 982 | if let case il::Val::Imm(imm) = b { |
|
| 981 | 983 | if encode::isSmallImm64(imm) { |
|
| 982 | 984 | let simm = imm as i32; |
|
| 983 | 985 | match op { |
|
| 984 | - | case BinOp::Add => emit::emit(s.e, encode::addi(rd, rs1, simm)), |
|
| 985 | - | case BinOp::And => emit::emit(s.e, encode::andi(rd, rs1, simm)), |
|
| 986 | - | case BinOp::Or => emit::emit(s.e, encode::ori(rd, rs1, simm)), |
|
| 987 | - | case BinOp::Xor => emit::emit(s.e, encode::xori(rd, rs1, simm)), |
|
| 986 | + | case BinOp::Add => emit::emit(&mut *s.e, encode::addi(rd, rs1, simm)), |
|
| 987 | + | case BinOp::And => emit::emit(&mut *s.e, encode::andi(rd, rs1, simm)), |
|
| 988 | + | case BinOp::Or => emit::emit(&mut *s.e, encode::ori(rd, rs1, simm)), |
|
| 989 | + | case BinOp::Xor => emit::emit(&mut *s.e, encode::xori(rd, rs1, simm)), |
|
| 988 | 990 | } |
|
| 989 | 991 | return; |
|
| 990 | 992 | } |
|
| 991 | 993 | } |
|
| 992 | 994 | // Fallback: load into register. |
|
| 993 | 995 | let rs2 = resolveVal(s, scratch, b); |
|
| 994 | 996 | match op { |
|
| 995 | - | case BinOp::Add => emit::emit(s.e, encode::add(rd, rs1, rs2)), |
|
| 996 | - | case BinOp::And => emit::emit(s.e, encode::and_(rd, rs1, rs2)), |
|
| 997 | - | case BinOp::Or => emit::emit(s.e, encode::or_(rd, rs1, rs2)), |
|
| 998 | - | case BinOp::Xor => emit::emit(s.e, encode::xor(rd, rs1, rs2)), |
|
| 997 | + | case BinOp::Add => emit::emit(&mut *s.e, encode::add(rd, rs1, rs2)), |
|
| 998 | + | case BinOp::And => emit::emit(&mut *s.e, encode::and_(rd, rs1, rs2)), |
|
| 999 | + | case BinOp::Or => emit::emit(&mut *s.e, encode::or_(rd, rs1, rs2)), |
|
| 1000 | + | case BinOp::Xor => emit::emit(&mut *s.e, encode::xor(rd, rs1, rs2)), |
|
| 999 | 1001 | } |
|
| 1000 | 1002 | } |
|
| 1001 | 1003 | ||
| 1002 | 1004 | /// Select shift operation with immediate optimization. |
|
| 1003 | 1005 | /// For 32-bit operations, uses the `*w` variants that operate on the lower 32 bits |
|
| 1004 | 1006 | /// and sign-extend the result. |
|
| 1005 | - | fn selectShift(s: *mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) { |
|
| 1007 | + | unsafe fn selectShift(s: &mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) { |
|
| 1006 | 1008 | let isW32: bool = typ == il::Type::W32; |
|
| 1007 | 1009 | ||
| 1008 | 1010 | // Try immediate optimization first. |
|
| 1009 | 1011 | if let case il::Val::Imm(shamt) = b { |
|
| 1010 | 1012 | // Keep immediate forms only for encodable shift amounts. |
|
| 1011 | 1013 | // Otherwise fall back to register shifts, which naturally mask the count. |
|
| 1012 | 1014 | if shamt >= 0 and ((isW32 and shamt < 32) or (not isW32 and shamt < 64)) { |
|
| 1013 | 1015 | let sa = shamt as i32; |
|
| 1014 | 1016 | if isW32 { |
|
| 1015 | 1017 | match op { |
|
| 1016 | - | case ShiftOp::Sll => emit::emit(s.e, encode::slliw(rd, rs1, sa)), |
|
| 1017 | - | case ShiftOp::Srl => emit::emit(s.e, encode::srliw(rd, rs1, sa)), |
|
| 1018 | - | case ShiftOp::Sra => emit::emit(s.e, encode::sraiw(rd, rs1, sa)), |
|
| 1018 | + | case ShiftOp::Sll => emit::emit(&mut *s.e, encode::slliw(rd, rs1, sa)), |
|
| 1019 | + | case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srliw(rd, rs1, sa)), |
|
| 1020 | + | case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sraiw(rd, rs1, sa)), |
|
| 1019 | 1021 | } |
|
| 1020 | 1022 | } else { |
|
| 1021 | 1023 | match op { |
|
| 1022 | - | case ShiftOp::Sll => emit::emit(s.e, encode::slli(rd, rs1, sa)), |
|
| 1023 | - | case ShiftOp::Srl => emit::emit(s.e, encode::srli(rd, rs1, sa)), |
|
| 1024 | - | case ShiftOp::Sra => emit::emit(s.e, encode::srai(rd, rs1, sa)), |
|
| 1024 | + | case ShiftOp::Sll => emit::emit(&mut *s.e, encode::slli(rd, rs1, sa)), |
|
| 1025 | + | case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srli(rd, rs1, sa)), |
|
| 1026 | + | case ShiftOp::Sra => emit::emit(&mut *s.e, encode::srai(rd, rs1, sa)), |
|
| 1025 | 1027 | } |
|
| 1026 | 1028 | } |
|
| 1027 | 1029 | return; |
|
| 1028 | 1030 | } |
|
| 1029 | 1031 | } |
|
| 1030 | 1032 | // Fallback: load into register. |
|
| 1031 | 1033 | let rs2 = resolveVal(s, scratch, b); |
|
| 1032 | 1034 | if isW32 { |
|
| 1033 | 1035 | match op { |
|
| 1034 | - | case ShiftOp::Sll => emit::emit(s.e, encode::sllw(rd, rs1, rs2)), |
|
| 1035 | - | case ShiftOp::Srl => emit::emit(s.e, encode::srlw(rd, rs1, rs2)), |
|
| 1036 | - | case ShiftOp::Sra => emit::emit(s.e, encode::sraw(rd, rs1, rs2)), |
|
| 1036 | + | case ShiftOp::Sll => emit::emit(&mut *s.e, encode::sllw(rd, rs1, rs2)), |
|
| 1037 | + | case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srlw(rd, rs1, rs2)), |
|
| 1038 | + | case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sraw(rd, rs1, rs2)), |
|
| 1037 | 1039 | } |
|
| 1038 | 1040 | } else { |
|
| 1039 | 1041 | match op { |
|
| 1040 | - | case ShiftOp::Sll => emit::emit(s.e, encode::sll(rd, rs1, rs2)), |
|
| 1041 | - | case ShiftOp::Srl => emit::emit(s.e, encode::srl(rd, rs1, rs2)), |
|
| 1042 | - | case ShiftOp::Sra => emit::emit(s.e, encode::sra(rd, rs1, rs2)), |
|
| 1042 | + | case ShiftOp::Sll => emit::emit(&mut *s.e, encode::sll(rd, rs1, rs2)), |
|
| 1043 | + | case ShiftOp::Srl => emit::emit(&mut *s.e, encode::srl(rd, rs1, rs2)), |
|
| 1044 | + | case ShiftOp::Sra => emit::emit(&mut *s.e, encode::sra(rd, rs1, rs2)), |
|
| 1043 | 1045 | } |
|
| 1044 | 1046 | } |
|
| 1045 | 1047 | } |
|
| 1046 | 1048 | ||
| 1047 | 1049 | /// Resolve parallel moves from IL values to physical destination registers. |
| 1053 | 1055 | /// 1. Identifies "ready" moves. |
|
| 1054 | 1056 | /// 2. Executes ready moves. |
|
| 1055 | 1057 | /// 3. Breaks cycles using scratch register. |
|
| 1056 | 1058 | /// |
|
| 1057 | 1059 | /// Entries with `ZERO` destination are skipped, as they are handled by caller. |
|
| 1058 | - | fn emitParallelMoves(s: *mut Selector, dsts: *[gen::Reg], args: *[il::Val]) { |
|
| 1060 | + | unsafe fn emitParallelMoves(s: &mut Selector, dsts: &[gen::Reg], args: &[il::Val]) { |
|
| 1059 | 1061 | let n: u32 = args.len; |
|
| 1060 | 1062 | if n == 0 { |
|
| 1061 | 1063 | return; |
|
| 1062 | 1064 | } |
|
| 1063 | 1065 | assert n <= MAX_BLOCK_ARGS, "emitParallelMoves: too many arguments"; |
| 1164 | 1166 | /// Emit moves from block arguments to target block's parameter registers. |
|
| 1165 | 1167 | /// |
|
| 1166 | 1168 | /// Handles spilled destinations directly, then delegates to [`emitParallelMoves`] |
|
| 1167 | 1169 | /// for the remaining register-to-register parallel move resolution. Edges that |
|
| 1168 | 1170 | /// would overwrite an unconsumed spill source are unsupported. |
|
| 1169 | - | fn emitBlockArgs(s: *mut Selector, func: *il::Fn, target: u32, args: *mut [il::Val]) { |
|
| 1171 | + | unsafe fn emitBlockArgs(s: &mut Selector, func: *il::Fn, target: u32, args: &[il::Val]) { |
|
| 1170 | 1172 | if args.len == 0 { |
|
| 1171 | 1173 | return; |
|
| 1172 | 1174 | } |
|
| 1173 | 1175 | let block = &func.blocks[target]; |
|
| 1174 | 1176 | assert args.len == block.params.len, "emitBlockArgs: argument/parameter count mismatch"; |
| 1220 | 1222 | if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, param) { |
|
| 1221 | 1223 | if let case il::Val::Undef = arg { |
|
| 1222 | 1224 | // Undefined values don't need any move. |
|
| 1223 | 1225 | } else { |
|
| 1224 | 1226 | let rs = resolveVal(s, super::SCRATCH1, arg); |
|
| 1225 | - | emit::emitSd(s.e, rs, spillBase(s), spillOffset(s, slot)); |
|
| 1227 | + | emit::emitSd(&mut *s.e, rs, spillBase(s), spillOffset(s, slot)); |
|
| 1226 | 1228 | } |
|
| 1227 | 1229 | } else { |
|
| 1228 | 1230 | set dsts[i] = getReg(s, param); |
|
| 1229 | 1231 | } |
|
| 1230 | 1232 | } |
|
| 1231 | 1233 | emitParallelMoves(s, &dsts[..], args); |
|
| 1232 | 1234 | } |
|
| 1233 | 1235 | ||
| 1234 | 1236 | /// Select a comparison with immediate optimization. |
|
| 1235 | - | fn selectCmp( |
|
| 1236 | - | s: *mut Selector, |
|
| 1237 | + | unsafe fn selectCmp( |
|
| 1238 | + | s: &mut Selector, |
|
| 1237 | 1239 | typ: il::Type, |
|
| 1238 | 1240 | rd: gen::Reg, |
|
| 1239 | 1241 | rs1: gen::Reg, |
|
| 1240 | 1242 | b: il::Val, |
|
| 1241 | 1243 | op: CmpOp, |
| 1245 | 1247 | let mut signed = false; |
|
| 1246 | 1248 | if let case CmpOp::Slt = op { |
|
| 1247 | 1249 | set signed = true; |
|
| 1248 | 1250 | } |
|
| 1249 | 1251 | let useSext = cmpUsesSext(typ, signed); |
|
| 1250 | - | emitCmpExt(s.e, rs1, rs1, typ, useSext); |
|
| 1252 | + | emitCmpExt(&mut *s.e, rs1, rs1, typ, useSext); |
|
| 1251 | 1253 | ||
| 1252 | 1254 | // Canonicalizing the immediate can expose an immediate instruction even |
|
| 1253 | 1255 | // when the IL value used a different representation for the same width. |
|
| 1254 | 1256 | let mut rhs = b; |
|
| 1255 | 1257 | if let case il::Val::Imm(imm) = b { |
|
| 1256 | 1258 | let canonical = canonicalCmpImm(imm, typ, useSext); |
|
| 1257 | 1259 | set rhs = il::Val::Imm(canonical); |
|
| 1258 | 1260 | if encode::isSmallImm64(canonical) { |
|
| 1259 | 1261 | let simm = canonical as i32; |
|
| 1260 | 1262 | match op { |
|
| 1261 | - | case CmpOp::Slt => emit::emit(s.e, encode::slti(rd, rs1, simm)), |
|
| 1262 | - | case CmpOp::Ult => emit::emit(s.e, encode::sltiu(rd, rs1, simm)), |
|
| 1263 | + | case CmpOp::Slt => emit::emit(&mut *s.e, encode::slti(rd, rs1, simm)), |
|
| 1264 | + | case CmpOp::Ult => emit::emit(&mut *s.e, encode::sltiu(rd, rs1, simm)), |
|
| 1263 | 1265 | } |
|
| 1264 | 1266 | if invert { |
|
| 1265 | - | emit::emit(s.e, encode::xori(rd, rd, 1)); |
|
| 1267 | + | emit::emit(&mut *s.e, encode::xori(rd, rd, 1)); |
|
| 1266 | 1268 | } |
|
| 1267 | 1269 | return; |
|
| 1268 | 1270 | } |
|
| 1269 | 1271 | } |
|
| 1270 | 1272 | ||
| 1271 | 1273 | let rs2 = resolveVal(s, scratch, rhs); |
|
| 1272 | 1274 | if not isExtendedImm(rhs, typ, useSext) { |
|
| 1273 | - | emitCmpExt(s.e, rs2, rs2, typ, useSext); |
|
| 1275 | + | emitCmpExt(&mut *s.e, rs2, rs2, typ, useSext); |
|
| 1274 | 1276 | } |
|
| 1275 | 1277 | match op { |
|
| 1276 | - | case CmpOp::Slt => emit::emit(s.e, encode::slt(rd, rs1, rs2)), |
|
| 1277 | - | case CmpOp::Ult => emit::emit(s.e, encode::sltu(rd, rs1, rs2)), |
|
| 1278 | + | case CmpOp::Slt => emit::emit(&mut *s.e, encode::slt(rd, rs1, rs2)), |
|
| 1279 | + | case CmpOp::Ult => emit::emit(&mut *s.e, encode::sltu(rd, rs1, rs2)), |
|
| 1278 | 1280 | } |
|
| 1279 | 1281 | if invert { |
|
| 1280 | - | emit::emit(s.e, encode::xori(rd, rd, 1)); |
|
| 1282 | + | emit::emit(&mut *s.e, encode::xori(rd, rd, 1)); |
|
| 1281 | 1283 | } |
|
| 1282 | 1284 | } |
lib/std/arch/rv64/printer.rad
+24 -24
| 37 | 37 | /////////////////////// |
|
| 38 | 38 | // Output Helpers // |
|
| 39 | 39 | /////////////////////// |
|
| 40 | 40 | ||
| 41 | 41 | /// Write a string to output. |
|
| 42 | - | fn write(out: *mut sexpr::Output, s: *[u8]) { |
|
| 42 | + | unsafe fn write(out: &mut sexpr::Output, s: &[u8]) { |
|
| 43 | 43 | sexpr::write(out, s); |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | /// Format `i32` into arena. |
|
| 47 | - | fn formatI32(a: *mut alloc::Arena, val: i32) -> *[u8] { |
|
| 47 | + | fn formatI32(a: &mut alloc::Arena, val: i32) -> *[u8] { |
|
| 48 | 48 | let mut digits: [u8; 12] = undefined; |
|
| 49 | - | let text = fmt::formatI32(val, &mut digits[..]); |
|
| 50 | - | let slice = try! alloc::allocSlice(a, 1, 1, text.len) as *mut [u8]; |
|
| 51 | - | try! mem::copy(slice, text); |
|
| 49 | + | let start = fmt::formatI32(val, &mut digits[..]); |
|
| 50 | + | let slice = try! alloc::allocSlice(a, 1, 1, digits.len - start) as *mut [u8]; |
|
| 51 | + | try! mem::copy(slice, &digits[start..]); |
|
| 52 | 52 | ||
| 53 | 53 | return slice; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Format `u32` into arena. |
|
| 57 | - | fn formatU32(a: *mut alloc::Arena, val: u32) -> *[u8] { |
|
| 57 | + | fn formatU32(a: &mut alloc::Arena, val: u32) -> *[u8] { |
|
| 58 | 58 | let mut digits: [u8; 10] = undefined; |
|
| 59 | - | let text = fmt::formatU32(val, &mut digits[..]); |
|
| 60 | - | let slice = try! alloc::allocSlice(a, 1, 1, text.len) as *mut [u8]; |
|
| 61 | - | try! mem::copy(slice, text); |
|
| 59 | + | let start = fmt::formatU32(val, &mut digits[..]); |
|
| 60 | + | let slice = try! alloc::allocSlice(a, 1, 1, digits.len - start) as *mut [u8]; |
|
| 61 | + | try! mem::copy(slice, &digits[start..]); |
|
| 62 | 62 | ||
| 63 | 63 | return slice; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /////////////////////////////// |
| 69 | 69 | ||
| 70 | 70 | /// Mnemonic column width for alignment. |
|
| 71 | 71 | constant MNEMONIC_WIDTH: u32 = 8; |
|
| 72 | 72 | ||
| 73 | 73 | /// Write text wrapped in parentheses. |
|
| 74 | - | fn writeParens(out: *mut sexpr::Output, s: *[u8]) { |
|
| 74 | + | unsafe fn writeParens(out: &mut sexpr::Output, s: &[u8]) { |
|
| 75 | 75 | write(out, "("); |
|
| 76 | 76 | write(out, s); |
|
| 77 | 77 | write(out, ")"); |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// Write strings separated by ", ". |
|
| 81 | - | fn writeDelim(out: *mut sexpr::Output, parts: *[*[u8]]) { |
|
| 81 | + | unsafe fn writeDelim(out: &mut sexpr::Output, parts: &[*[u8]]) { |
|
| 82 | 82 | for part, i in parts { |
|
| 83 | 83 | if i > 0 { |
|
| 84 | 84 | write(out, ", "); |
|
| 85 | 85 | } |
|
| 86 | 86 | write(out, part); |
|
| 87 | 87 | } |
|
| 88 | 88 | } |
|
| 89 | 89 | ||
| 90 | 90 | /// Write mnemonic with padding for alignment. |
|
| 91 | - | fn writeMnem(out: *mut sexpr::Output, m: *[u8]) { |
|
| 91 | + | unsafe fn writeMnem(out: &mut sexpr::Output, m: *[u8]) { |
|
| 92 | 92 | write(out, m); |
|
| 93 | 93 | let mut i = m.len; |
|
| 94 | 94 | while i < MNEMONIC_WIDTH { |
|
| 95 | 95 | write(out, " "); |
|
| 96 | 96 | set i += 1; |
| 100 | 100 | //////////////////////////////// |
|
| 101 | 101 | // Instruction Format Helpers // |
|
| 102 | 102 | //////////////////////////////// |
|
| 103 | 103 | ||
| 104 | 104 | /// R-type: `op rd, rs1, rs2`. |
|
| 105 | - | fn fmtR(out: *mut sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) { |
|
| 105 | + | unsafe fn fmtR(out: &mut sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) { |
|
| 106 | 106 | writeMnem(out, m); |
|
| 107 | 107 | writeDelim(out, &[regNameR(rd), regNameR(rs1), regNameR(rs2)]); |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | 110 | /// I-type: `op rd, rs1, imm`. |
|
| 111 | - | fn fmtI(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) { |
|
| 111 | + | unsafe fn fmtI(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) { |
|
| 112 | 112 | writeMnem(out, m); |
|
| 113 | 113 | writeDelim(out, &[regNameR(rd), regNameR(rs1), formatI32(a, imm)]); |
|
| 114 | 114 | } |
|
| 115 | 115 | ||
| 116 | 116 | /// 2-reg: `op rd, rs`. |
|
| 117 | - | fn fmt2R(out: *mut sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) { |
|
| 117 | + | unsafe fn fmt2R(out: &mut sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) { |
|
| 118 | 118 | writeMnem(out, m); |
|
| 119 | 119 | writeDelim(out, &[regNameR(rd), regNameR(rs)]); |
|
| 120 | 120 | } |
|
| 121 | 121 | ||
| 122 | 122 | /// reg + imm: `op rd, imm`. |
|
| 123 | - | fn fmtRI(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, imm: i32) { |
|
| 123 | + | unsafe fn fmtRI(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, imm: i32) { |
|
| 124 | 124 | writeMnem(out, m); |
|
| 125 | 125 | writeDelim(out, &[regNameR(rd), formatI32(a, imm)]); |
|
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | /// imm only: `op imm`. |
|
| 129 | - | fn fmtImm(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], imm: i32) { |
|
| 129 | + | unsafe fn fmtImm(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], imm: i32) { |
|
| 130 | 130 | writeMnem(out, m); |
|
| 131 | 131 | write(out, formatI32(a, imm)); |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | 134 | /// 1-reg: `op rs`. |
|
| 135 | - | fn fmt1R(out: *mut sexpr::Output, m: *[u8], rs: gen::Reg) { |
|
| 135 | + | unsafe fn fmt1R(out: &mut sexpr::Output, m: *[u8], rs: gen::Reg) { |
|
| 136 | 136 | writeMnem(out, m); |
|
| 137 | 137 | write(out, regNameR(rs)); |
|
| 138 | 138 | } |
|
| 139 | 139 | ||
| 140 | 140 | /// Load: `op rd, imm(rs1)`. |
|
| 141 | - | fn fmtLoad(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) { |
|
| 141 | + | unsafe fn fmtLoad(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) { |
|
| 142 | 142 | writeMnem(out, m); |
|
| 143 | 143 | writeDelim(out, &[regNameR(rd), formatI32(a, imm)]); |
|
| 144 | 144 | writeParens(out, regNameR(rs1)); |
|
| 145 | 145 | } |
|
| 146 | 146 | ||
| 147 | 147 | /// Store: `op rs2, imm(rs1)`. |
|
| 148 | - | fn fmtStore(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) { |
|
| 148 | + | unsafe fn fmtStore(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) { |
|
| 149 | 149 | writeMnem(out, m); |
|
| 150 | 150 | writeDelim(out, &[regNameR(rs2), formatI32(a, imm)]); |
|
| 151 | 151 | writeParens(out, regNameR(rs1)); |
|
| 152 | 152 | } |
|
| 153 | 153 | ||
| 154 | 154 | /// Branch: `op rs1, rs2, imm`. |
|
| 155 | - | fn fmtB(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) { |
|
| 155 | + | unsafe fn fmtB(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) { |
|
| 156 | 156 | writeMnem(out, m); |
|
| 157 | 157 | writeDelim(out, &[regNameR(rs1), regNameR(rs2), formatI32(a, imm)]); |
|
| 158 | 158 | } |
|
| 159 | 159 | ||
| 160 | 160 | /// Branch zero: `op rs1, imm`. |
|
| 161 | - | fn fmtBz(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs1: gen::Reg, imm: i32) { |
|
| 161 | + | unsafe fn fmtBz(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, imm: i32) { |
|
| 162 | 162 | writeMnem(out, m); |
|
| 163 | 163 | writeDelim(out, &[regNameR(rs1), formatI32(a, imm)]); |
|
| 164 | 164 | } |
|
| 165 | 165 | ||
| 166 | 166 | /// Print a single instruction to output buffer. |
|
| 167 | - | export fn printInstr(out: *mut sexpr::Output, a: *mut alloc::Arena, instr: u32) { |
|
| 167 | + | export unsafe fn printInstr(out: &mut sexpr::Output, a: &mut alloc::Arena, instr: u32) { |
|
| 168 | 168 | let decoded = decode::decode(instr); |
|
| 169 | 169 | ||
| 170 | 170 | match decoded { |
|
| 171 | 171 | case decode::Instr::Lui { rd, imm } => fmtRI(out, a, "lui", rd, imm), |
|
| 172 | 172 | case decode::Instr::Auipc { rd, imm } => fmtRI(out, a, "auipc", rd, imm), |
| 304 | 304 | }, |
|
| 305 | 305 | } |
|
| 306 | 306 | } |
|
| 307 | 307 | ||
| 308 | 308 | /// Print code with labels to the given output. |
|
| 309 | - | export fn printCodeTo(out: *mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: *mut alloc::Arena) { |
|
| 309 | + | export unsafe fn printCodeTo(out: &mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: &mut alloc::Arena) { |
|
| 310 | 310 | // Package header. |
|
| 311 | 311 | write(out, "# package `"); |
|
| 312 | 312 | write(out, pkgName); |
|
| 313 | 313 | write(out, "`\n\n"); |
|
| 314 | 314 |
lib/std/arch/rv64/tests.rad
+1 -1
| 16 | 16 | /// Helper to check encoding equals expected value. |
|
| 17 | 17 | fn expectEncoding(actual: u32, expected: u32) throws (testing::TestError) { |
|
| 18 | 18 | try testing::expect(actual == expected); |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | @test fn testAddAssemblyExportsOnlyGlobalTextSymbols() throws (testing::TestError) { |
|
| 21 | + | @test unsafe fn testAddAssemblyExportsOnlyGlobalTextSymbols() throws (testing::TestError) { |
|
| 22 | 22 | let mut arena = alloc::new(&mut ASSEMBLY_ARENA_STORAGE[..]); |
|
| 23 | 23 | let symbols = try alloc::allocSlice(&mut arena, @sizeOf(asm::Symbol), @alignOf(asm::Symbol), 2) catch { |
|
| 24 | 24 | throw testing::TestError::Failed; |
|
| 25 | 25 | }; |
|
| 26 | 26 | let mut symbolSlice = @sliceOf((symbols as *mut [asm::Symbol]).ptr, 2, 2); |
lib/std/collections/dict.rad
+2 -2
| 29 | 29 | } |
|
| 30 | 30 | return Dict { entries, count: 0 }; |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | 33 | /// Insert or update a key-value pair. Panics if the table exceeds 50% load. |
|
| 34 | - | export fn insert(m: *mut Dict, key: *[u8], value: i32) { |
|
| 34 | + | export fn insert(m: &mut Dict, key: *[u8], value: i32) { |
|
| 35 | 35 | let mask = m.entries.len - 1; |
|
| 36 | 36 | let mut idx = hash(key) & mask; |
|
| 37 | 37 | ||
| 38 | 38 | loop { |
|
| 39 | 39 | let entry = &m.entries[idx]; |
| 50 | 50 | set idx = (idx + 1) & mask; |
|
| 51 | 51 | } |
|
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | 54 | /// Look up a value by key. Returns `nil` if not found. |
|
| 55 | - | export fn get(m: *Dict, key: *[u8]) -> ?i32 { |
|
| 55 | + | export fn get(m: &Dict, key: *[u8]) -> ?i32 { |
|
| 56 | 56 | let mask = m.entries.len - 1; |
|
| 57 | 57 | let mut idx = hash(key) & mask; |
|
| 58 | 58 | ||
| 59 | 59 | loop { |
|
| 60 | 60 | let entry = &m.entries[idx]; |
lib/std/fmt.rad
+31 -29
| 42 | 42 | magnitude: u64, |
|
| 43 | 43 | /// Radix used by the literal. |
|
| 44 | 44 | radix: Radix, |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | - | /// Format a u32 by writing it to the provided buffer. |
|
| 48 | - | export fn formatU32(val: u32, buffer: *mut [u8]) -> *[u8] { |
|
| 47 | + | /// Write a u32 at the end of the buffer and return its start offset. |
|
| 48 | + | export fn formatU32(val: u32, buffer: &mut [u8]) -> u32 { |
|
| 49 | 49 | assert buffer.len >= U32_STR_LEN; |
|
| 50 | 50 | ||
| 51 | 51 | let mut x: u32 = val; |
|
| 52 | 52 | let mut i: u32 = buffer.len; |
|
| 53 | 53 |
| 61 | 61 | set i -= 1; |
|
| 62 | 62 | set buffer[i] = ('0' + (x % 10) as u8); |
|
| 63 | 63 | set x /= 10; |
|
| 64 | 64 | } |
|
| 65 | 65 | } |
|
| 66 | - | // Return the slice from the start of the written number to |
|
| 67 | - | // the end of the buffer. |
|
| 68 | - | return &buffer[i..]; |
|
| 66 | + | // Return the offset of the first written byte. |
|
| 67 | + | return i; |
|
| 69 | 68 | } |
|
| 70 | 69 | ||
| 71 | - | /// Format a i32 by writing it to the provided buffer. |
|
| 72 | - | export fn formatI32(val: i32, buffer: *mut [u8]) -> *[u8] { |
|
| 70 | + | /// Write a i32 at the end of the buffer and return its start offset. |
|
| 71 | + | export fn formatI32(val: i32, buffer: &mut [u8]) -> u32 { |
|
| 73 | 72 | assert buffer.len >= I32_STR_LEN; |
|
| 74 | 73 | ||
| 75 | 74 | let neg: bool = val < 0; |
|
| 76 | 75 | let mut x: u32 = -val as u32 if neg else val as u32; |
|
| 77 | 76 | let mut i: u32 = buffer.len; |
| 90 | 89 | if neg { |
|
| 91 | 90 | set i -= 1; |
|
| 92 | 91 | set buffer[i] = '-'; |
|
| 93 | 92 | } |
|
| 94 | 93 | } |
|
| 95 | - | return &buffer[i..]; |
|
| 94 | + | return i; |
|
| 96 | 95 | } |
|
| 97 | 96 | ||
| 98 | - | /// Format a u64 by writing it to the provided buffer. |
|
| 99 | - | export fn formatU64(val: u64, buffer: *mut [u8]) -> *[u8] { |
|
| 97 | + | /// Write a u64 at the end of the buffer and return its start offset. |
|
| 98 | + | export fn formatU64(val: u64, buffer: &mut [u8]) -> u32 { |
|
| 100 | 99 | assert buffer.len >= U64_STR_LEN; |
|
| 101 | 100 | ||
| 102 | 101 | let mut x: u64 = val; |
|
| 103 | 102 | let mut i: u32 = buffer.len; |
|
| 104 | 103 |
| 110 | 109 | set i -= 1; |
|
| 111 | 110 | set buffer[i] = ('0' + (x % 10) as u8); |
|
| 112 | 111 | set x /= 10; |
|
| 113 | 112 | } |
|
| 114 | 113 | } |
|
| 115 | - | return &buffer[i..]; |
|
| 114 | + | return i; |
|
| 116 | 115 | } |
|
| 117 | 116 | ||
| 118 | - | /// Format a i64 by writing it to the provided buffer. |
|
| 119 | - | export fn formatI64(val: i64, buffer: *mut [u8]) -> *[u8] { |
|
| 117 | + | /// Write a i64 at the end of the buffer and return its start offset. |
|
| 118 | + | export fn formatI64(val: i64, buffer: &mut [u8]) -> u32 { |
|
| 120 | 119 | assert buffer.len >= I64_STR_LEN; |
|
| 121 | 120 | ||
| 122 | 121 | let neg: bool = val < 0; |
|
| 123 | 122 | let mut x: u64 = -val as u64 if neg else val as u64; |
|
| 124 | 123 | let mut i: u32 = buffer.len; |
| 134 | 133 | if neg { |
|
| 135 | 134 | set i -= 1; |
|
| 136 | 135 | set buffer[i] = '-'; |
|
| 137 | 136 | } |
|
| 138 | 137 | } |
|
| 139 | - | return &buffer[i..]; |
|
| 138 | + | return i; |
|
| 140 | 139 | } |
|
| 141 | 140 | ||
| 142 | - | /// Format a i8 by writing it to the provided buffer. |
|
| 143 | - | export fn formatI8(val: i8, buffer: *mut [u8]) -> *[u8] { |
|
| 141 | + | /// Write a i8 at the end of the buffer and return its start offset. |
|
| 142 | + | export fn formatI8(val: i8, buffer: &mut [u8]) -> u32 { |
|
| 144 | 143 | return formatI32(val as i32, buffer); |
|
| 145 | 144 | } |
|
| 146 | 145 | ||
| 147 | - | /// Format a i16 by writing it to the provided buffer. |
|
| 148 | - | export fn formatI16(val: i16, buffer: *mut [u8]) -> *[u8] { |
|
| 146 | + | /// Write a i16 at the end of the buffer and return its start offset. |
|
| 147 | + | export fn formatI16(val: i16, buffer: &mut [u8]) -> u32 { |
|
| 149 | 148 | return formatI32(val as i32, buffer); |
|
| 150 | 149 | } |
|
| 151 | 150 | ||
| 152 | - | /// Format a u8 by writing it to the provided buffer. |
|
| 153 | - | export fn formatU8(val: u8, buffer: *mut [u8]) -> *[u8] { |
|
| 151 | + | /// Write a u8 at the end of the buffer and return its start offset. |
|
| 152 | + | export fn formatU8(val: u8, buffer: &mut [u8]) -> u32 { |
|
| 154 | 153 | return formatU32(val as u32, buffer); |
|
| 155 | 154 | } |
|
| 156 | 155 | ||
| 157 | - | /// Format a u16 by writing it to the provided buffer. |
|
| 158 | - | export fn formatU16(val: u16, buffer: *mut [u8]) -> *[u8] { |
|
| 156 | + | /// Write a u16 at the end of the buffer and return its start offset. |
|
| 157 | + | export fn formatU16(val: u16, buffer: &mut [u8]) -> u32 { |
|
| 159 | 158 | return formatU32(val as u32, buffer); |
|
| 160 | 159 | } |
|
| 161 | 160 | ||
| 162 | - | /// Format a bool by writing it to the provided buffer. |
|
| 163 | - | export fn formatBool(val: bool, buffer: *mut [u8]) -> *[u8] { |
|
| 161 | + | /// Write a bool at the end of the buffer and return its start offset. |
|
| 162 | + | export fn formatBool(val: bool, buffer: &mut [u8]) -> u32 { |
|
| 163 | + | assert buffer.len >= BOOL_STR_LEN; |
|
| 164 | 164 | if val { |
|
| 165 | - | try! mem::copy(buffer, "true"); |
|
| 166 | - | return &buffer[..4]; |
|
| 165 | + | let start = buffer.len - 4; |
|
| 166 | + | try! mem::copy(&mut buffer[start..], "true"); |
|
| 167 | + | return start; |
|
| 167 | 168 | } else { |
|
| 168 | - | try! mem::copy(buffer, "false"); |
|
| 169 | - | return &buffer[..5]; |
|
| 169 | + | let start = buffer.len - 5; |
|
| 170 | + | try! mem::copy(&mut buffer[start..], "false"); |
|
| 171 | + | return start; |
|
| 170 | 172 | } |
|
| 171 | 173 | } |
|
| 172 | 174 | ||
| 173 | 175 | /// Convert a single ASCII digit into its numeric value for the given radix. |
|
| 174 | 176 | export fn digitFromAscii(ch: u8, radix: u32) -> ?u32 { |
| 250 | 252 | return IntLiteral { text, magnitude: value, radix: radixType }; |
|
| 251 | 253 | } |
|
| 252 | 254 | ||
| 253 | 255 | /// Process escape sequences in a raw string, writing the result into `dst`. |
|
| 254 | 256 | /// Returns the number of bytes written. |
|
| 255 | - | export fn unescapeString(raw: *[u8], dst: *mut [u8]) -> u32 { |
|
| 257 | + | export fn unescapeString(raw: *[u8], dst: &mut [u8]) -> u32 { |
|
| 256 | 258 | let mut i: u32 = 0; |
|
| 257 | 259 | let mut j: u32 = 0; |
|
| 258 | 260 | ||
| 259 | 261 | while i < raw.len { |
|
| 260 | 262 | if raw[i] == '\\' and i + 1 < raw.len { |
lib/std/io.rad
+17 -10
| 1 | 1 | //! Input/output utilities. |
|
| 2 | 2 | use std::fmt; |
|
| 3 | 3 | use std::intrinsics; |
|
| 4 | 4 | ||
| 5 | - | export fn print(str: *[u8]) { |
|
| 5 | + | /// Write the bytes to standard output. |
|
| 6 | + | export fn print(str: &[u8]) { |
|
| 6 | 7 | intrinsics::ecall(64, 1, str.ptr as i64, str.len as i64, 0); |
|
| 7 | 8 | } |
|
| 8 | 9 | ||
| 9 | - | export fn printError(str: *[u8]) { |
|
| 10 | + | /// Write the bytes to standard error. |
|
| 11 | + | export fn printError(str: &[u8]) { |
|
| 10 | 12 | intrinsics::ecall(64, 2, str.ptr as i64, str.len as i64, 0); |
|
| 11 | 13 | } |
|
| 12 | 14 | ||
| 13 | - | export fn printLn(str: *[u8]) { |
|
| 15 | + | /// Write the bytes and a newline to standard output. |
|
| 16 | + | export fn printLn(str: &[u8]) { |
|
| 14 | 17 | print(str); |
|
| 15 | 18 | print("\n"); |
|
| 16 | 19 | } |
|
| 17 | 20 | ||
| 21 | + | /// Write a signed decimal integer to standard output. |
|
| 18 | 22 | export fn printI32(val: i32) { |
|
| 19 | 23 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 20 | - | let result: *[u8] = fmt::formatI32(val, &mut buffer[..]); |
|
| 21 | - | print(result); |
|
| 24 | + | let start = fmt::formatI32(val, &mut buffer[..]); |
|
| 25 | + | print(&buffer[start..]); |
|
| 22 | 26 | } |
|
| 23 | 27 | ||
| 28 | + | /// Write an unsigned decimal integer to standard output. |
|
| 24 | 29 | export fn printU32(val: u32) { |
|
| 25 | 30 | let mut buffer: [u8; 10] = [0; 10]; |
|
| 26 | - | let result: *[u8] = fmt::formatU32(val, &mut buffer[..]); |
|
| 27 | - | print(result); |
|
| 31 | + | let start = fmt::formatU32(val, &mut buffer[..]); |
|
| 32 | + | print(&buffer[start..]); |
|
| 28 | 33 | } |
|
| 29 | 34 | ||
| 35 | + | /// Write a Boolean value to standard output. |
|
| 30 | 36 | export fn printBool(val: bool) { |
|
| 31 | 37 | let mut buffer: [u8; 5] = [0; 5]; |
|
| 32 | - | let result: *[u8] = fmt::formatBool(val, &mut buffer[..]); |
|
| 33 | - | print(result); |
|
| 38 | + | let start = fmt::formatBool(val, &mut buffer[..]); |
|
| 39 | + | print(&buffer[start..]); |
|
| 34 | 40 | } |
|
| 35 | 41 | ||
| 36 | - | export fn read(buf: *mut [u8]) -> u32 { |
|
| 42 | + | /// Read standard input into the buffer and return the system call result. |
|
| 43 | + | export fn read(buf: &mut [u8]) -> u32 { |
|
| 37 | 44 | return intrinsics::ecall(63, 0, buf.ptr as i64, buf.len as i64, 0) as u32; |
|
| 38 | 45 | } |
|
| 39 | 46 | ||
| 40 | 47 | export fn readToEnd(buf: *mut [u8]) -> *[u8] { |
|
| 41 | 48 | let mut total: u32 = 0; |
lib/std/lang/alloc.rad
+17 -17
| 33 | 33 | /// Allocate `size` bytes with the given alignment. |
|
| 34 | 34 | /// |
|
| 35 | 35 | /// Returns an opaque pointer to the allocated memory. Throws `AllocError` if |
|
| 36 | 36 | /// the arena is exhausted. The caller is responsible for casting to the |
|
| 37 | 37 | /// appropriate type and initializing the memory. |
|
| 38 | - | export fn alloc(arena: *mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
|
| 38 | + | export fn alloc(arena: &mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
|
| 39 | 39 | assert alignment > 0; |
|
| 40 | 40 | assert size > 0; |
|
| 41 | 41 | ||
| 42 | 42 | let aligned = mem::alignUp(arena.offset, alignment); |
|
| 43 | 43 | let newOffset = aligned + size; |
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | 54 | /// Reset the arena, allowing all memory to be reused. |
|
| 55 | 55 | /// |
|
| 56 | 56 | /// Does not zero the memory. |
|
| 57 | - | export fn reset(arena: *mut Arena) { |
|
| 57 | + | export fn reset(arena: &mut Arena) { |
|
| 58 | 58 | set arena.offset = 0; |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Save the current arena state for later restoration. |
|
| 62 | - | export fn save(arena: *Arena) -> u32 { |
|
| 62 | + | export fn save(arena: &Arena) -> u32 { |
|
| 63 | 63 | return arena.offset; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Restore the arena to a previously saved state, reclaiming all |
|
| 67 | 67 | /// allocations made since that point. |
|
| 68 | - | export fn restore(arena: *mut Arena, savedOffset: u32) { |
|
| 68 | + | export fn restore(arena: &mut Arena, savedOffset: u32) { |
|
| 69 | 69 | set arena.offset = savedOffset; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Returns the number of bytes currently allocated. |
|
| 73 | - | export fn used(arena: *Arena) -> u32 { |
|
| 73 | + | export fn used(arena: &Arena) -> u32 { |
|
| 74 | 74 | return arena.offset; |
|
| 75 | 75 | } |
|
| 76 | 76 | ||
| 77 | 77 | /// Returns the number of bytes remaining in the arena. |
|
| 78 | - | export fn remaining(arena: *Arena) -> u32 { |
|
| 78 | + | export fn remaining(arena: &Arena) -> u32 { |
|
| 79 | 79 | return arena.data.len as u32 - arena.offset; |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | 82 | /// Returns the remaining buffer as a mutable slice. |
|
| 83 | - | export fn remainingBuf(arena: *mut Arena) -> *mut [u8] { |
|
| 83 | + | export fn remainingBuf(arena: &mut Arena) -> *mut [u8] { |
|
| 84 | 84 | return &mut arena.data[arena.offset..]; |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Commits `size` bytes of allocation, advancing the offset. |
|
| 88 | 88 | /// Use after writing to the buffer returned by [`remainingBuf`]. |
|
| 89 | - | export fn commit(arena: *mut Arena, size: u32) { |
|
| 89 | + | export fn commit(arena: &mut Arena, size: u32) { |
|
| 90 | 90 | set arena.offset += size; |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | 93 | /// Allocate a slice of `count` elements, each of `size` bytes with given alignment. |
|
| 94 | 94 | /// |
|
| 95 | 95 | /// Returns a type-erased slice that should be cast to the appropriate `*[T]`. |
|
| 96 | 96 | /// The slice length is set to `count` (element count, not bytes). |
|
| 97 | 97 | /// Throws `AllocError` if the arena is exhausted. |
|
| 98 | - | export fn allocSlice(arena: *mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
|
| 98 | + | export fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
|
| 99 | 99 | if count == 0 { |
|
| 100 | 100 | return &mut []; |
|
| 101 | 101 | } |
|
| 102 | 102 | let ptr = try alloc(arena, size * count, alignment); |
|
| 103 | 103 |
| 112 | 112 | /// pointer, a byte size and an alignment, and must return a pointer to |
|
| 113 | 113 | /// the allocated memory or panic on failure. |
|
| 114 | 114 | export record Allocator: Copy { |
|
| 115 | 115 | /// Allocation function. Returns a pointer to `size` bytes |
|
| 116 | 116 | /// aligned to `alignment`, or panics on failure. |
|
| 117 | - | func: fn(*mut opaque, u32, u32) -> *mut opaque, |
|
| 117 | + | func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, |
|
| 118 | 118 | /// Opaque context pointer passed to `func`. |
|
| 119 | - | ctx: *mut opaque, |
|
| 119 | + | ctx: *unsafe mut opaque, |
|
| 120 | 120 | } |
|
| 121 | 121 | ||
| 122 | - | /// Create an `Allocator` backed by an `Arena`. |
|
| 123 | - | export fn arenaAllocator(arena: *mut Arena) -> Allocator { |
|
| 122 | + | /// Create an allocator whose arena must outlive all uses of the allocator. |
|
| 123 | + | export unsafe fn arenaAllocator(arena: &mut Arena) -> Allocator { |
|
| 124 | 124 | return Allocator { |
|
| 125 | 125 | func: arenaAllocFn, |
|
| 126 | - | ctx: arena as *mut opaque, |
|
| 126 | + | ctx: (arena as *unsafe mut Arena) as *unsafe mut opaque, |
|
| 127 | 127 | }; |
|
| 128 | 128 | } |
|
| 129 | 129 | ||
| 130 | 130 | /// Arena allocation function conforming to the `Allocator` interface. |
|
| 131 | - | fn arenaAllocFn(ctx: *mut opaque, size: u32, alignment: u32) -> *mut opaque { |
|
| 132 | - | let arena = ctx as *mut Arena; |
|
| 133 | - | return try! alloc(arena, size, alignment); |
|
| 131 | + | unsafe fn arenaAllocFn(ctx: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque { |
|
| 132 | + | let arena = ctx as *unsafe mut Arena; |
|
| 133 | + | return try! alloc(&mut *arena, size, alignment); |
|
| 134 | 134 | } |
lib/std/lang/alloc/tests.rad
+1 -1
| 103 | 103 | }; |
|
| 104 | 104 | try testing::expect(failed); |
|
| 105 | 105 | } |
|
| 106 | 106 | ||
| 107 | 107 | /// Test the Allocator interface backed by an arena. |
|
| 108 | - | @test fn testAllocator() throws (testing::TestError) { |
|
| 108 | + | @test unsafe fn testAllocator() throws (testing::TestError) { |
|
| 109 | 109 | static STORAGE: [u8; 256] = undefined; |
|
| 110 | 110 | let mut arena = super::new(&mut STORAGE[..]); |
|
| 111 | 111 | let a = super::arenaAllocator(&mut arena); |
|
| 112 | 112 | ||
| 113 | 113 | // Allocate through the Allocator indirection. |
lib/std/lang/ast.rad
+12 -7
| 26 | 26 | nextId: 0, |
|
| 27 | 27 | }; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Create an empty `*mut [*Node]` slice with the given capacity. |
|
| 31 | - | export fn nodeSlice(arena: *mut NodeArena, capacity: u32) -> *mut [*Node] { |
|
| 31 | + | export fn nodeSlice(arena: &mut NodeArena, capacity: u32) -> *mut [*Node] { |
|
| 32 | 32 | if capacity == 0 { |
|
| 33 | 33 | return &mut []; |
|
| 34 | 34 | } |
|
| 35 | 35 | let ptr = try! alloc::allocSlice(&mut arena.arena, @sizeOf(*Node), @alignOf(*Node), capacity); |
|
| 36 | 36 |
| 57 | 57 | export record Attributes: Copy { |
|
| 58 | 58 | list: *mut [*Node], |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Check if an attributes list contains an attribute. |
|
| 62 | - | export fn attributesContains(self: *Attributes, attr: Attribute) -> bool { |
|
| 62 | + | export fn attributesContains(self: &Attributes, attr: Attribute) -> bool { |
|
| 63 | 63 | for node in self.list { |
|
| 64 | 64 | if let case NodeValue::Attribute(a) = node.value; a == attr { |
|
| 65 | 65 | return true; |
|
| 66 | 66 | } |
|
| 67 | 67 | } |
| 206 | 206 | fields: *mut [*Node], |
|
| 207 | 207 | /// Whether this record has labeled fields. |
|
| 208 | 208 | labeled: bool, |
|
| 209 | 209 | }, |
|
| 210 | 210 | /// Anonymous function type. |
|
| 211 | - | Fn(FnSig), |
|
| 211 | + | Fn { |
|
| 212 | + | /// Parameter, return, and error types. |
|
| 213 | + | sig: FnSig, |
|
| 214 | + | /// Whether a call requires an unsafe function body. |
|
| 215 | + | isUnsafe: bool, |
|
| 216 | + | }, |
|
| 212 | 217 | /// Trait object type, eg. `*opaque Allocator`, `&opaque Allocator`, or |
|
| 213 | 218 | /// `*unsafe opaque Allocator`. |
|
| 214 | 219 | TraitObject { |
|
| 215 | 220 | /// Ownership and safety class. |
|
| 216 | 221 | class: types::PointerClass, |
| 818 | 823 | else => return false, |
|
| 819 | 824 | } |
|
| 820 | 825 | } |
|
| 821 | 826 | ||
| 822 | 827 | /// Allocate a new AST node from the arena with the given span and value. |
|
| 823 | - | export fn allocNode(arena: *mut NodeArena, span: Span, value: NodeValue) -> *mut Node { |
|
| 828 | + | export fn allocNode(arena: &mut NodeArena, span: Span, value: NodeValue) -> *mut Node { |
|
| 824 | 829 | let p = try! alloc::alloc(&mut arena.arena, @sizeOf(Node), @alignOf(Node)); |
|
| 825 | 830 | let node = p as *mut Node; |
|
| 826 | 831 | let nodeId = arena.nextId; |
|
| 827 | 832 | set arena.nextId = nodeId + 1; |
|
| 828 | 833 |
| 830 | 835 | ||
| 831 | 836 | return node; |
|
| 832 | 837 | } |
|
| 833 | 838 | ||
| 834 | 839 | /// Allocate a synthetic AST node with a zero-length span. |
|
| 835 | - | export fn synthNode(arena: *mut NodeArena, value: NodeValue) -> *mut Node { |
|
| 840 | + | export fn synthNode(arena: &mut NodeArena, value: NodeValue) -> *mut Node { |
|
| 836 | 841 | return allocNode(arena, Span { offset: 0, length: 0 }, value); |
|
| 837 | 842 | } |
|
| 838 | 843 | ||
| 839 | 844 | /// Synthetic module with a single function in it. |
|
| 840 | 845 | record SynthFnMod: Copy { |
| 843 | 848 | /// The function block. |
|
| 844 | 849 | fnBody: *Node |
|
| 845 | 850 | } |
|
| 846 | 851 | ||
| 847 | 852 | /// Synthesize a module with a function in it with the given name and statements. |
|
| 848 | - | export fn synthFnModule( |
|
| 849 | - | arena: *mut NodeArena, name: *[u8], bodyStmts: *mut [*Node] |
|
| 853 | + | export unsafe fn synthFnModule( |
|
| 854 | + | arena: &mut NodeArena, name: *[u8], bodyStmts: *mut [*Node] |
|
| 850 | 855 | ) -> SynthFnMod { |
|
| 851 | 856 | let a = alloc::arenaAllocator(&mut arena.arena); |
|
| 852 | 857 | let fnName = synthNode(arena, NodeValue::Ident(name)); |
|
| 853 | 858 | let params: *mut [*Node] = &mut []; |
|
| 854 | 859 | let throwList: *mut [*Node] = &mut []; |
lib/std/lang/ast/printer.rad
+17 -16
| 82 | 82 | case types::PointerClass::Unsafe => return unsafeHead, |
|
| 83 | 83 | } |
|
| 84 | 84 | } |
|
| 85 | 85 | ||
| 86 | 86 | /// Convert a type signature to an S-expression. |
|
| 87 | - | fn typeSigToExpr(a: *mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr { |
|
| 87 | + | fn typeSigToExpr(a: &mut alloc::Arena, sig: super::TypeSig) -> sexpr::Expr { |
|
| 88 | 88 | match sig { |
|
| 89 | 89 | case super::TypeSig::Void => return sexpr::sym("void"), |
|
| 90 | 90 | case super::TypeSig::Opaque => return sexpr::sym("opaque"), |
|
| 91 | 91 | case super::TypeSig::Bool => return sexpr::sym("bool"), |
|
| 92 | 92 | case super::TypeSig::Integer { width, sign } => return sexpr::sym(intTypeName(width, sign)), |
| 105 | 105 | case super::TypeSig::Optional { valueType } => |
|
| 106 | 106 | return sexpr::list(a, "?", &[toExpr(a, valueType)]), |
|
| 107 | 107 | case super::TypeSig::Nominal(name) => return toExpr(a, name), |
|
| 108 | 108 | case super::TypeSig::Record { fields, .. } => |
|
| 109 | 109 | return sexpr::list(a, "record", nodeListToExprs(a, &fields[..])), |
|
| 110 | - | case super::TypeSig::Fn(sig) => { |
|
| 110 | + | case super::TypeSig::Fn { sig, isUnsafe } => { |
|
| 111 | 111 | let mut ret = sexpr::sym("void"); |
|
| 112 | 112 | if let rt = sig.returnType { |
|
| 113 | 113 | set ret = toExpr(a, rt); |
|
| 114 | 114 | } |
|
| 115 | - | return sexpr::list(a, "fn", &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])), ret]); |
|
| 115 | + | let head = "unsafe-fn" if isUnsafe else "fn"; |
|
| 116 | + | return sexpr::list(a, head, &[sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..])), ret]); |
|
| 116 | 117 | } |
|
| 117 | 118 | case super::TypeSig::TraitObject { class, traitName, mutable } => { |
|
| 118 | 119 | let head = pointerClassHead(class, "obj", "obj-ref", "unsafe-obj"); |
|
| 119 | 120 | return sexpr::list(a, head, &[sexpr::sym("mut"), toExpr(a, traitName)]) if mutable |
|
| 120 | 121 | else sexpr::list(a, head, &[toExpr(a, traitName)]); |
|
| 121 | 122 | } |
|
| 122 | 123 | } |
|
| 123 | 124 | } |
|
| 124 | 125 | ||
| 125 | 126 | /// Convert a node slice to a slice of expressions. |
|
| 126 | - | fn nodeListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 127 | + | fn nodeListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 127 | 128 | if nodes.len == 0 { |
|
| 128 | 129 | return &[]; |
|
| 129 | 130 | } |
|
| 130 | 131 | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 131 | 132 | for node, i in nodes { |
| 133 | 134 | } |
|
| 134 | 135 | return buf; |
|
| 135 | 136 | } |
|
| 136 | 137 | ||
| 137 | 138 | /// Convert optional attributes to an attribute list expression. |
|
| 138 | - | fn attributesToExpr(a: *mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr { |
|
| 139 | + | fn attributesToExpr(a: &mut alloc::Arena, attrs: ?super::Attributes) -> sexpr::Expr { |
|
| 139 | 140 | let mut exprs: *[sexpr::Expr] = &[]; |
|
| 140 | 141 | if let list = attrs { |
|
| 141 | 142 | set exprs = nodeListToExprs(a, &list.list[..]); |
|
| 142 | 143 | } |
|
| 143 | 144 | return sexpr::list(a, "attrs", exprs); |
|
| 144 | 145 | } |
|
| 145 | 146 | ||
| 146 | 147 | /// Convert an optional node to an expression, or return placeholder. |
|
| 147 | - | fn toExprOpt(a: *mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr { |
|
| 148 | + | fn toExprOpt(a: &mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr { |
|
| 148 | 149 | if let n = opt { |
|
| 149 | 150 | return toExpr(a, n); |
|
| 150 | 151 | } |
|
| 151 | 152 | return sexpr::sym("_"); |
|
| 152 | 153 | } |
|
| 153 | 154 | ||
| 154 | 155 | /// Convert an optional node to an expression, or return `Null`. |
|
| 155 | - | fn toExprOrNull(a: *mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr { |
|
| 156 | + | fn toExprOrNull(a: &mut alloc::Arena, opt: ?*super::Node) -> sexpr::Expr { |
|
| 156 | 157 | if let n = opt { |
|
| 157 | 158 | return toExpr(a, n); |
|
| 158 | 159 | } |
|
| 159 | 160 | return sexpr::Expr::Null; |
|
| 160 | 161 | } |
|
| 161 | 162 | ||
| 162 | 163 | /// Convert an optional guard. |
|
| 163 | - | fn guardExpr(a: *mut alloc::Arena, guard: ?*super::Node) -> sexpr::Expr { |
|
| 164 | + | fn guardExpr(a: &mut alloc::Arena, guard: ?*super::Node) -> sexpr::Expr { |
|
| 164 | 165 | if let g = guard { |
|
| 165 | 166 | return sexpr::list(a, "guard", &[toExpr(a, g)]); |
|
| 166 | 167 | } |
|
| 167 | 168 | return sexpr::Expr::Null; |
|
| 168 | 169 | } |
|
| 169 | 170 | ||
| 170 | 171 | /// Convert a list of match prongs to expressions. |
|
| 171 | - | fn prongListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 172 | + | fn prongListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 172 | 173 | if nodes.len == 0 { |
|
| 173 | 174 | return &[]; |
|
| 174 | 175 | } |
|
| 175 | 176 | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 176 | 177 | for prong, i in nodes { |
| 185 | 186 | } |
|
| 186 | 187 | return buf; |
|
| 187 | 188 | } |
|
| 188 | 189 | ||
| 189 | 190 | /// Convert a match prong to an S-expression. |
|
| 190 | - | fn prongToExpr(a: *mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr { |
|
| 191 | + | fn prongToExpr(a: &mut alloc::Arena, p: super::MatchProng) -> sexpr::Expr { |
|
| 191 | 192 | match p.arm { |
|
| 192 | 193 | case super::ProngArm::Case(patterns) => { |
|
| 193 | 194 | return sexpr::block(a, "case", &[ |
|
| 194 | 195 | sexpr::list(a, "patterns", nodeListToExprs(a, &patterns[..])), |
|
| 195 | 196 | guardExpr(a, p.guard) |
| 207 | 208 | } |
|
| 208 | 209 | } |
|
| 209 | 210 | ||
| 210 | 211 | /// Convert a record field declaration to an S-expression. |
|
| 211 | 212 | fn fieldToExpr( |
|
| 212 | - | a: *mut alloc::Arena, |
|
| 213 | + | a: &mut alloc::Arena, |
|
| 213 | 214 | field: ?*super::Node, |
|
| 214 | 215 | type: *super::Node, |
|
| 215 | 216 | value: ?*super::Node |
|
| 216 | 217 | ) -> sexpr::Expr { |
|
| 217 | 218 | return sexpr::list(a, ":", &[toExprOpt(a, field), toExpr(a, type), toExprOrNull(a, value)]); |
|
| 218 | 219 | } |
|
| 219 | 220 | ||
| 220 | 221 | /// Convert a list of record fields to expressions. |
|
| 221 | - | fn fieldListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 222 | + | fn fieldListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 222 | 223 | if nodes.len == 0 { |
|
| 223 | 224 | return &[]; |
|
| 224 | 225 | } |
|
| 225 | 226 | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 226 | 227 | for node, i in nodes { |
| 235 | 236 | } |
|
| 236 | 237 | return buf; |
|
| 237 | 238 | } |
|
| 238 | 239 | ||
| 239 | 240 | /// Convert a union variant to an S-expression. |
|
| 240 | - | fn variantToExpr(a: *mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr { |
|
| 241 | + | fn variantToExpr(a: &mut alloc::Arena, name: *super::Node, type: ?*super::Node) -> sexpr::Expr { |
|
| 241 | 242 | return sexpr::list(a, "variant", &[toExpr(a, name), toExprOrNull(a, type)]); |
|
| 242 | 243 | } |
|
| 243 | 244 | ||
| 244 | 245 | /// Convert a list of union variants to expressions. |
|
| 245 | - | fn variantListToExprs(a: *mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 246 | + | fn variantListToExprs(a: &mut alloc::Arena, nodes: *[*super::Node]) -> *[sexpr::Expr] { |
|
| 246 | 247 | if nodes.len == 0 { |
|
| 247 | 248 | return &[]; |
|
| 248 | 249 | } |
|
| 249 | 250 | let buf = try! sexpr::allocExprs(a, nodes.len as u32); |
|
| 250 | 251 | for node, i in nodes { |
| 259 | 260 | } |
|
| 260 | 261 | return buf; |
|
| 261 | 262 | } |
|
| 262 | 263 | ||
| 263 | 264 | /// Convert an AST node to an S-expression. |
|
| 264 | - | export fn toExpr(a: *mut alloc::Arena, node: *super::Node) -> sexpr::Expr { |
|
| 265 | + | export fn toExpr(a: &mut alloc::Arena, node: *super::Node) -> sexpr::Expr { |
|
| 265 | 266 | match node.value { |
|
| 266 | 267 | case super::NodeValue::Placeholder => return sexpr::sym("_"), |
|
| 267 | 268 | case super::NodeValue::Nil => return sexpr::sym("nil"), |
|
| 268 | 269 | case super::NodeValue::Undef => return sexpr::sym("undefined"), |
|
| 269 | 270 | case super::NodeValue::Bool(v) => { |
| 514 | 515 | else => return sexpr::sym("?"), |
|
| 515 | 516 | } |
|
| 516 | 517 | } |
|
| 517 | 518 | ||
| 518 | 519 | /// Dump the tree rooted at `root`, using the provided arena for allocation. |
|
| 519 | - | export fn printTree(root: *super::Node, arena: *mut alloc::Arena) { |
|
| 520 | + | export unsafe fn printTree(root: *super::Node, arena: &mut alloc::Arena) { |
|
| 520 | 521 | match root.value { |
|
| 521 | 522 | case super::NodeValue::Block(blk) => { |
|
| 522 | 523 | for stmt, i in blk.statements { |
|
| 523 | 524 | sexpr::print(toExpr(arena, stmt), 0); |
|
| 524 | 525 | if i < blk.statements.len - 1 { io::print("\n\n"); } |
lib/std/lang/gen/bitset.rad
+23 -20
| 19 | 19 | return (n + 31) / 32; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// A fixed-size bitset backed by an array of 32-bit words. |
|
| 23 | 23 | export record Bitset: Copy { |
|
| 24 | - | /// Backing storage for bits, organized as 32-bit words. |
|
| 25 | - | bits: *mut [u32], |
|
| 24 | + | /// Backing words. The storage must outlive the bitset and its iterators. |
|
| 25 | + | bits: *unsafe mut [u32], |
|
| 26 | 26 | /// Number of bits this bitset can hold. |
|
| 27 | 27 | len: u32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Create a new bitset backed by the given zero-initialized storage. |
|
| 31 | - | export fn new(bits: *mut [u32]) -> Bitset { |
|
| 32 | - | return Bitset { bits, len: bits.len * 32 }; |
|
| 31 | + | /// The storage must outlive the bitset and its iterators. |
|
| 32 | + | export unsafe fn new(bits: &mut [u32]) -> Bitset { |
|
| 33 | + | return Bitset { bits: bits as *unsafe mut [u32], len: bits.len * 32 }; |
|
| 33 | 34 | } |
|
| 34 | 35 | ||
| 35 | 36 | /// Create a new bitset backed by the given storage, zeroing it first. |
|
| 36 | - | export fn init(bits: *mut [u32]) -> Bitset { |
|
| 37 | + | /// The storage must outlive the bitset and its iterators. |
|
| 38 | + | export unsafe fn init(bits: &mut [u32]) -> Bitset { |
|
| 37 | 39 | for i in 0..bits.len { |
|
| 38 | 40 | set bits[i] = 0; |
|
| 39 | 41 | } |
|
| 40 | 42 | return new(bits); |
|
| 41 | 43 | } |
|
| 42 | 44 | ||
| 43 | 45 | /// Create a bitset from arena allocation. |
|
| 44 | - | export fn allocate(arena: *mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) { |
|
| 46 | + | export unsafe fn allocate(arena: &mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) { |
|
| 45 | 47 | let numWords = wordsFor(len); |
|
| 46 | 48 | let bits = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *mut [u32]; |
|
| 47 | 49 | ||
| 48 | 50 | return init(bits); |
|
| 49 | 51 | } |
|
| 50 | 52 | ||
| 51 | 53 | /// Set bit `n` in the bitset. |
|
| 52 | - | export fn put(bs: *mut Bitset, n: u32) { |
|
| 54 | + | export unsafe fn put(bs: &mut Bitset, n: u32) { |
|
| 53 | 55 | if n >= bs.len { |
|
| 54 | 56 | return; |
|
| 55 | 57 | } |
|
| 56 | 58 | let word = n / 32; |
|
| 57 | 59 | let b = n % 32; |
|
| 58 | 60 | ||
| 59 | 61 | set bs.bits[word] |= (1 << b); |
|
| 60 | 62 | } |
|
| 61 | 63 | ||
| 62 | 64 | /// Clear bit `n` in the bitset. |
|
| 63 | - | export fn clear(bs: *mut Bitset, n: u32) { |
|
| 65 | + | export unsafe fn clear(bs: &mut Bitset, n: u32) { |
|
| 64 | 66 | if n >= bs.len { |
|
| 65 | 67 | return; |
|
| 66 | 68 | } |
|
| 67 | 69 | let word = n / 32; |
|
| 68 | 70 | let b = n % 32; |
|
| 69 | 71 | ||
| 70 | 72 | set bs.bits[word] &= ~(1 << b); |
|
| 71 | 73 | } |
|
| 72 | 74 | ||
| 73 | 75 | /// Check if bit `n` is set. |
|
| 74 | - | export fn contains(bs: *Bitset, n: u32) -> bool { |
|
| 76 | + | export unsafe fn contains(bs: &Bitset, n: u32) -> bool { |
|
| 75 | 77 | if n >= bs.len { |
|
| 76 | 78 | return false; |
|
| 77 | 79 | } |
|
| 78 | 80 | let word = n / 32; |
|
| 79 | 81 | let b = n % 32; |
|
| 80 | 82 | ||
| 81 | 83 | return (bs.bits[word] & (1 << b)) <> 0; |
|
| 82 | 84 | } |
|
| 83 | 85 | ||
| 84 | 86 | /// Count the number of set bits. |
|
| 85 | - | export fn count(bs: *Bitset) -> u32 { |
|
| 87 | + | export unsafe fn count(bs: &Bitset) -> u32 { |
|
| 86 | 88 | let mut total: u32 = 0; |
|
| 87 | 89 | let numWords = bs.bits.len; |
|
| 88 | 90 | for i in 0..numWords { |
|
| 89 | 91 | let word = bs.bits[i]; |
|
| 90 | 92 | if word <> 0 { |
| 105 | 107 | ||
| 106 | 108 | return n & 0x3F; |
|
| 107 | 109 | } |
|
| 108 | 110 | ||
| 109 | 111 | /// Union: `dst = dst | src`. |
|
| 110 | - | export fn union_(dst: *mut Bitset, src: *Bitset) { |
|
| 112 | + | export unsafe fn union_(dst: &mut Bitset, src: &Bitset) { |
|
| 111 | 113 | let numWords = dst.bits.len; |
|
| 112 | 114 | let srcWords = src.bits.len; |
|
| 113 | 115 | let minWords = min(numWords, srcWords); |
|
| 114 | 116 | for i in 0..minWords { |
|
| 115 | 117 | set dst.bits[i] |= src.bits[i]; |
|
| 116 | 118 | } |
|
| 117 | 119 | } |
|
| 118 | 120 | ||
| 119 | 121 | /// Subtract: `dst = dst - src`. |
|
| 120 | - | export fn subtract(dst: *mut Bitset, src: *Bitset) { |
|
| 122 | + | export unsafe fn subtract(dst: &mut Bitset, src: &Bitset) { |
|
| 121 | 123 | let numWords = dst.bits.len; |
|
| 122 | 124 | let srcWords = src.bits.len; |
|
| 123 | 125 | let minWords = min(numWords, srcWords); |
|
| 124 | 126 | for i in 0..minWords { |
|
| 125 | 127 | set dst.bits[i] &= ~src.bits[i]; |
|
| 126 | 128 | } |
|
| 127 | 129 | } |
|
| 128 | 130 | ||
| 129 | 131 | /// Check if two bitsets are equal. |
|
| 130 | - | export fn eq(a: *Bitset, b: *Bitset) -> bool { |
|
| 132 | + | export unsafe fn eq(a: &Bitset, b: &Bitset) -> bool { |
|
| 131 | 133 | let numWordsA = a.bits.len; |
|
| 132 | 134 | let numWordsB = b.bits.len; |
|
| 133 | 135 | let minWords = min(numWordsA, numWordsB); |
|
| 134 | 136 | ||
| 135 | 137 | for i in 0..minWords { |
| 149 | 151 | } |
|
| 150 | 152 | return true; |
|
| 151 | 153 | } |
|
| 152 | 154 | ||
| 153 | 155 | /// Copy bits from source to destination. |
|
| 154 | - | export fn copy(dst: *mut Bitset, src: *Bitset) { |
|
| 156 | + | export unsafe fn copy(dst: &mut Bitset, src: &Bitset) { |
|
| 155 | 157 | let numWords = dst.bits.len; |
|
| 156 | 158 | let srcWords = src.bits.len; |
|
| 157 | 159 | let minWords = min(numWords, srcWords); |
|
| 158 | 160 | ||
| 159 | 161 | for i in 0..minWords { |
| 164 | 166 | set dst.bits[i] = 0; |
|
| 165 | 167 | } |
|
| 166 | 168 | } |
|
| 167 | 169 | ||
| 168 | 170 | /// Clear all bits. |
|
| 169 | - | export fn clearAll(bs: *mut Bitset) { |
|
| 171 | + | export unsafe fn clearAll(bs: &mut Bitset) { |
|
| 170 | 172 | let numWords = bs.bits.len; |
|
| 171 | 173 | for i in 0..numWords { |
|
| 172 | 174 | set bs.bits[i] = 0; |
|
| 173 | 175 | } |
|
| 174 | 176 | } |
|
| 175 | 177 | ||
| 176 | 178 | /// Iterator state for iterating set bits. |
|
| 177 | 179 | export record BitIter: Copy { |
|
| 178 | - | /// Bitset being iterated. |
|
| 179 | - | bs: *Bitset, |
|
| 180 | + | /// Bitset being iterated. It must outlive this iterator. |
|
| 181 | + | bs: *unsafe Bitset, |
|
| 180 | 182 | /// Current word index. |
|
| 181 | 183 | wordIdx: u32, |
|
| 182 | 184 | /// Remaining bits in the current word (visited bits cleared). |
|
| 183 | 185 | remaining: u32, |
|
| 184 | 186 | } |
|
| 185 | 187 | ||
| 186 | 188 | /// Create an iterator over set bits. |
|
| 187 | - | export fn iter(bs: *Bitset) -> BitIter { |
|
| 189 | + | /// The bitset and its backing words must outlive the iterator. |
|
| 190 | + | export unsafe fn iter(bs: &Bitset) -> BitIter { |
|
| 188 | 191 | let remaining = bs.bits[0] if bs.len > 0 else 0; |
|
| 189 | - | return BitIter { bs, wordIdx: 0, remaining }; |
|
| 192 | + | return BitIter { bs: bs as *unsafe Bitset, wordIdx: 0, remaining }; |
|
| 190 | 193 | } |
|
| 191 | 194 | ||
| 192 | 195 | /// Get the next set bit, or nil if none remain. |
|
| 193 | - | export fn iterNext(it: *mut BitIter) -> ?u32 { |
|
| 196 | + | export unsafe fn iterNext(it: &mut BitIter) -> ?u32 { |
|
| 194 | 197 | let numWords = it.bs.bits.len; |
|
| 195 | 198 | // Skip to next non-zero word. |
|
| 196 | 199 | while it.remaining == 0 { |
|
| 197 | 200 | set it.wordIdx += 1; |
|
| 198 | 201 | if it.wordIdx >= numWords { |
lib/std/lang/gen/bitset/tests.rad
+11 -11
| 1 | 1 | //! Tests for the bitset module. |
|
| 2 | 2 | ||
| 3 | 3 | use std::testing; |
|
| 4 | 4 | ||
| 5 | 5 | /// Test [`super::init`] and basic set/contains operations. |
|
| 6 | - | @test fn testInit() throws (testing::TestError) { |
|
| 6 | + | @test unsafe fn testInit() throws (testing::TestError) { |
|
| 7 | 7 | let mut bits: [u32; 4] = undefined; |
|
| 8 | 8 | let mut bs = super::init(&mut bits[..]); |
|
| 9 | 9 | ||
| 10 | 10 | // `init` should zero-initialize, so all bits start unset. |
|
| 11 | 11 | try testing::expect(not super::contains(&bs, 0)); |
| 30 | 30 | try testing::expect(not super::contains(&bs, 33)); |
|
| 31 | 31 | try testing::expect(not super::contains(&bs, 126)); |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | /// Test clear operation. |
|
| 35 | - | @test fn testClear() throws (testing::TestError) { |
|
| 35 | + | @test unsafe fn testClear() throws (testing::TestError) { |
|
| 36 | 36 | let mut bits: [u32; 2] = [0; 2]; |
|
| 37 | 37 | let mut bs = super::new(&mut bits[..]); |
|
| 38 | 38 | ||
| 39 | 39 | super::put(&mut bs, 0); |
|
| 40 | 40 | super::put(&mut bs, 31); |
| 50 | 50 | try testing::expect(not super::contains(&bs, 31)); |
|
| 51 | 51 | try testing::expect(super::contains(&bs, 32)); |
|
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | 54 | /// Test population count. |
|
| 55 | - | @test fn testCount() throws (testing::TestError) { |
|
| 55 | + | @test unsafe fn testCount() throws (testing::TestError) { |
|
| 56 | 56 | let mut bits: [u32; 2] = [0; 2]; |
|
| 57 | 57 | let mut bs = super::new(&mut bits[..]); |
|
| 58 | 58 | ||
| 59 | 59 | try testing::expect(super::count(&bs) == 0); |
|
| 60 | 60 |
| 69 | 69 | super::clear(&mut bs, 31); |
|
| 70 | 70 | try testing::expect(super::count(&bs) == 3); |
|
| 71 | 71 | } |
|
| 72 | 72 | ||
| 73 | 73 | /// Test union operation. |
|
| 74 | - | @test fn testUnion() throws (testing::TestError) { |
|
| 74 | + | @test unsafe fn testUnion() throws (testing::TestError) { |
|
| 75 | 75 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 76 | 76 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 77 | 77 | let mut a = super::new(&mut bits_a[..]); |
|
| 78 | 78 | let mut b = super::new(&mut bits_b[..]); |
|
| 79 | 79 |
| 90 | 90 | try testing::expect(super::contains(&a, 20)); |
|
| 91 | 91 | try testing::expect(super::count(&a) == 3); |
|
| 92 | 92 | } |
|
| 93 | 93 | ||
| 94 | 94 | /// Test subtract operation. |
|
| 95 | - | @test fn testSubtract() throws (testing::TestError) { |
|
| 95 | + | @test unsafe fn testSubtract() throws (testing::TestError) { |
|
| 96 | 96 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 97 | 97 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 98 | 98 | let mut a = super::new(&mut bits_a[..]); |
|
| 99 | 99 | let mut b = super::new(&mut bits_b[..]); |
|
| 100 | 100 |
| 112 | 112 | try testing::expect(super::contains(&a, 20)); |
|
| 113 | 113 | try testing::expect(super::count(&a) == 2); |
|
| 114 | 114 | } |
|
| 115 | 115 | ||
| 116 | 116 | /// Test equality check. |
|
| 117 | - | @test fn testEq() throws (testing::TestError) { |
|
| 117 | + | @test unsafe fn testEq() throws (testing::TestError) { |
|
| 118 | 118 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 119 | 119 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 120 | 120 | let mut a = super::new(&mut bits_a[..]); |
|
| 121 | 121 | let mut b = super::new(&mut bits_b[..]); |
|
| 122 | 122 |
| 136 | 136 | super::put(&mut b, 33); |
|
| 137 | 137 | try testing::expect(not super::eq(&a, &b)); |
|
| 138 | 138 | } |
|
| 139 | 139 | ||
| 140 | 140 | /// Test copy operation. |
|
| 141 | - | @test fn testCopy() throws (testing::TestError) { |
|
| 141 | + | @test unsafe fn testCopy() throws (testing::TestError) { |
|
| 142 | 142 | let mut bits_a: [u32; 2] = [0; 2]; |
|
| 143 | 143 | let mut bits_b: [u32; 2] = [0; 2]; |
|
| 144 | 144 | let mut a = super::new(&mut bits_a[..]); |
|
| 145 | 145 | let mut b = super::new(&mut bits_b[..]); |
|
| 146 | 146 |
| 155 | 155 | try testing::expect(super::contains(&b, 31)); |
|
| 156 | 156 | try testing::expect(super::contains(&b, 63)); |
|
| 157 | 157 | } |
|
| 158 | 158 | ||
| 159 | 159 | /// Test clearAll operation. |
|
| 160 | - | @test fn testClearAll() throws (testing::TestError) { |
|
| 160 | + | @test unsafe fn testClearAll() throws (testing::TestError) { |
|
| 161 | 161 | let mut bits: [u32; 2] = [0; 2]; |
|
| 162 | 162 | let mut bs = super::new(&mut bits[..]); |
|
| 163 | 163 | ||
| 164 | 164 | super::put(&mut bs, 0); |
|
| 165 | 165 | super::put(&mut bs, 31); |
| 172 | 172 | try testing::expect(not super::contains(&bs, 0)); |
|
| 173 | 173 | try testing::expect(not super::contains(&bs, 31)); |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Test iteration over set bits. |
|
| 177 | - | @test fn testIter() throws (testing::TestError) { |
|
| 177 | + | @test unsafe fn testIter() throws (testing::TestError) { |
|
| 178 | 178 | let mut bits: [u32; 2] = [0; 2]; |
|
| 179 | 179 | let mut bs = super::new(&mut bits[..]); |
|
| 180 | 180 | ||
| 181 | 181 | super::put(&mut bs, 3); |
|
| 182 | 182 | super::put(&mut bs, 31); |
| 195 | 195 | try testing::expect(count == 4); |
|
| 196 | 196 | try testing::expect(sum == 3 + 31 + 32 + 50); |
|
| 197 | 197 | } |
|
| 198 | 198 | ||
| 199 | 199 | /// Test iteration on empty bitset. |
|
| 200 | - | @test fn testIterEmpty() throws (testing::TestError) { |
|
| 200 | + | @test unsafe fn testIterEmpty() throws (testing::TestError) { |
|
| 201 | 201 | let mut bits: [u32; 2] = [0; 2]; |
|
| 202 | 202 | let mut bs = super::new(&mut bits[..]); |
|
| 203 | 203 | let mut it = super::iter(&bs); |
|
| 204 | 204 | let result = super::iterNext(&mut it); |
|
| 205 | 205 |
| 216 | 216 | try testing::expect(super::wordsFor(64) == 2); |
|
| 217 | 217 | try testing::expect(super::wordsFor(65) == 3); |
|
| 218 | 218 | } |
|
| 219 | 219 | ||
| 220 | 220 | /// Test out-of-bounds access is safe. |
|
| 221 | - | @test fn testOutOfBounds() throws (testing::TestError) { |
|
| 221 | + | @test unsafe fn testOutOfBounds() throws (testing::TestError) { |
|
| 222 | 222 | let mut bits: [u32; 1] = [0; 1]; |
|
| 223 | 223 | let mut bs = super::new(&mut bits[..]); |
|
| 224 | 224 | ||
| 225 | 225 | // Setting beyond length should be ignored. |
|
| 226 | 226 | super::put(&mut bs, 100); |
lib/std/lang/gen/data.rad
+10 -15
| 36 | 36 | /// so that only meaningful bytes need to be written to the output file. |
|
| 37 | 37 | /// Returns the updated offset past all placed symbols. |
|
| 38 | 38 | export fn layoutSection( |
|
| 39 | 39 | items: *[il::Data], |
|
| 40 | 40 | syms: *mut [DataSym], |
|
| 41 | - | count: *mut u32, |
|
| 41 | + | count: &mut u32, |
|
| 42 | 42 | base: u32, |
|
| 43 | 43 | readOnly: bool |
|
| 44 | 44 | ) -> u32 { |
|
| 45 | 45 | return layoutSectionAtOffset(items, syms, count, base, 0, readOnly); |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | 48 | /// Lay out data symbols for a single section starting at [`startOffset`]. |
|
| 49 | 49 | export fn layoutSectionAtOffset( |
|
| 50 | 50 | items: *[il::Data], |
|
| 51 | 51 | syms: *mut [DataSym], |
|
| 52 | - | count: *mut u32, |
|
| 52 | + | count: &mut u32, |
|
| 53 | 53 | base: u32, |
|
| 54 | 54 | startOffset: u32, |
|
| 55 | 55 | readOnly: bool |
|
| 56 | 56 | ) -> u32 { |
|
| 57 | 57 | let mut offset: u32 = startOffset; |
| 82 | 82 | /// Emit data bytes for a single section (read-only or read-write) into `buf`. |
|
| 83 | 83 | /// Iterates data requiring sidecar image bytes, serializing each data item. |
|
| 84 | 84 | /// Returns the total number of bytes written. |
|
| 85 | 85 | export fn emitSection( |
|
| 86 | 86 | items: *[il::Data], |
|
| 87 | - | dataSymMap: *DataSymMap, |
|
| 88 | - | fnLabels: *labels::Labels, |
|
| 87 | + | dataSymMap: &DataSymMap, |
|
| 88 | + | fnLabels: &labels::Labels, |
|
| 89 | 89 | codeBase: u32, |
|
| 90 | 90 | buf: *mut [u8], |
|
| 91 | 91 | readOnly: bool |
|
| 92 | 92 | ) -> u32 { |
|
| 93 | 93 | return emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0); |
|
| 94 | 94 | } |
|
| 95 | 95 | ||
| 96 | 96 | /// Emit data bytes for a single section starting at `startOffset`. |
|
| 97 | 97 | export fn emitSectionAtOffset( |
|
| 98 | 98 | items: *[il::Data], |
|
| 99 | - | dataSymMap: *DataSymMap, |
|
| 100 | - | fnLabels: *labels::Labels, |
|
| 99 | + | dataSymMap: &DataSymMap, |
|
| 100 | + | fnLabels: &labels::Labels, |
|
| 101 | 101 | codeBase: u32, |
|
| 102 | 102 | buf: *mut [u8], |
|
| 103 | 103 | readOnly: bool, |
|
| 104 | 104 | startOffset: u32 |
|
| 105 | 105 | ) -> u32 { |
| 114 | 114 | let v = &data.values[j]; |
|
| 115 | 115 | for _ in 0..v.count { |
|
| 116 | 116 | match v.item { |
|
| 117 | 117 | case il::DataItem::Val { typ, val } => { |
|
| 118 | 118 | let size = il::typeSize(typ); |
|
| 119 | - | let valPtr = &val as *u8; |
|
| 120 | - | try! mem::copy(&mut buf[offset..], @sliceOf(valPtr, size)); |
|
| 119 | + | try! mem::copy(&mut buf[offset..], @sliceOf(&val as &u8, size)); |
|
| 121 | 120 | ||
| 122 | 121 | set offset += size; |
|
| 123 | 122 | }, |
|
| 124 | 123 | case il::DataItem::Sym(name) => { |
|
| 125 | 124 | let addr = lookupAddr(dataSymMap, name) else { |
|
| 126 | 125 | panic "emitSectionAtOffset: data symbol not found"; |
|
| 127 | 126 | }; |
|
| 128 | 127 | let addr64: u64 = addr as u64; |
|
| 129 | - | let addrPtr = &addr64 as *u8; |
|
| 130 | - | ||
| 131 | - | try! mem::copy(&mut buf[offset..], @sliceOf(addrPtr, 8)); |
|
| 128 | + | try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8)); |
|
| 132 | 129 | ||
| 133 | 130 | set offset += @sizeOf(u64); |
|
| 134 | 131 | }, |
|
| 135 | 132 | case il::DataItem::Fn(name) => { |
|
| 136 | 133 | let addr = codeBase + labels::funcOffset(fnLabels, name) as u32; |
|
| 137 | 134 | let addr64: u64 = addr as u64; |
|
| 138 | - | let addrPtr = &addr64 as *u8; |
|
| 139 | - | ||
| 140 | - | try! mem::copy(&mut buf[offset..], @sliceOf(addrPtr, 8)); |
|
| 135 | + | try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8)); |
|
| 141 | 136 | ||
| 142 | 137 | set offset += @sizeOf(*u8); |
|
| 143 | 138 | }, |
|
| 144 | 139 | case il::DataItem::Str(s) => { |
|
| 145 | 140 | try! mem::copy(&mut buf[offset..], s); |
| 166 | 161 | } |
|
| 167 | 162 | return DataSymMap { dict: d, syms }; |
|
| 168 | 163 | } |
|
| 169 | 164 | ||
| 170 | 165 | /// Resolve a data symbol to its final absolute address using the hash map. |
|
| 171 | - | export fn lookupAddr(m: *DataSymMap, name: *[u8]) -> ?u32 { |
|
| 166 | + | export fn lookupAddr(m: &DataSymMap, name: *[u8]) -> ?u32 { |
|
| 172 | 167 | if let v = dict::get(&m.dict, name) { |
|
| 173 | 168 | return v as u32; |
|
| 174 | 169 | } |
|
| 175 | 170 | return nil; |
|
| 176 | 171 | } |
lib/std/lang/gen/labels.rad
+6 -6
| 30 | 30 | funcs: dict::init(funcEntries), |
|
| 31 | 31 | }; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | /// Reset block count for a new function. |
|
| 35 | - | export fn resetBlocks(l: *mut Labels) { |
|
| 35 | + | export fn resetBlocks(l: &mut Labels) { |
|
| 36 | 36 | set l.blockCount = 0; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Record a block's code offset by its index. O(1). |
|
| 40 | - | export fn recordBlock(l: *mut Labels, blockIdx: u32, offset: i32) { |
|
| 40 | + | export fn recordBlock(l: &mut Labels, blockIdx: u32, offset: i32) { |
|
| 41 | 41 | assert blockIdx < l.blockOffsets.len, "recordBlock: block index out of range"; |
|
| 42 | 42 | set l.blockOffsets[blockIdx] = offset; |
|
| 43 | 43 | set l.blockCount += 1; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | /// Look up a block's byte offset by index. O(1). |
|
| 47 | - | export fn blockOffset(l: *Labels, blockIdx: u32) -> i32 { |
|
| 47 | + | export fn blockOffset(l: &Labels, blockIdx: u32) -> i32 { |
|
| 48 | 48 | assert blockIdx < l.blockCount, "blockOffset: block not recorded"; |
|
| 49 | 49 | return l.blockOffsets[blockIdx]; |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | 52 | /// Look up a function's byte offset by name. |
|
| 53 | - | export fn funcOffset(l: *Labels, name: *[u8]) -> i32 { |
|
| 53 | + | export fn funcOffset(l: &Labels, name: *[u8]) -> i32 { |
|
| 54 | 54 | if let offset = dict::get(&l.funcs, name) { |
|
| 55 | 55 | return offset; |
|
| 56 | 56 | } |
|
| 57 | 57 | panic "funcOffset: unknown function"; |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | /// Compute branch offset to a block given source instruction index. |
|
| 61 | - | export fn branchToBlock(l: *Labels, srcIndex: u32, blockIdx: u32, instrSize: i32) -> i32 { |
|
| 61 | + | export fn branchToBlock(l: &Labels, srcIndex: u32, blockIdx: u32, instrSize: i32) -> i32 { |
|
| 62 | 62 | let targetOffset = blockOffset(l, blockIdx); |
|
| 63 | 63 | let srcOffset = srcIndex as i32 * instrSize; |
|
| 64 | 64 | ||
| 65 | 65 | return targetOffset - srcOffset; |
|
| 66 | 66 | } |
|
| 67 | 67 | ||
| 68 | 68 | /// Compute branch offset to a function given source instruction index. |
|
| 69 | - | export fn branchToFunc(l: *Labels, srcIndex: u32, name: *[u8], instrSize: i32) -> i32 { |
|
| 69 | + | export fn branchToFunc(l: &Labels, srcIndex: u32, name: *[u8], instrSize: i32) -> i32 { |
|
| 70 | 70 | let targetOffset = funcOffset(l, name); |
|
| 71 | 71 | let srcOffset = srcIndex as i32 * instrSize; |
|
| 72 | 72 | ||
| 73 | 73 | return targetOffset - srcOffset; |
|
| 74 | 74 | } |
lib/std/lang/gen/regalloc.rad
+3 -3
| 46 | 46 | ||
| 47 | 47 | /// Run register allocation on a function. |
|
| 48 | 48 | /// |
|
| 49 | 49 | /// Returns a mapping from SSA registers to physical registers, plus |
|
| 50 | 50 | /// spill information. |
|
| 51 | - | export fn allocate( |
|
| 51 | + | export unsafe fn allocate( |
|
| 52 | 52 | func: *il::Fn, |
|
| 53 | - | config: *TargetConfig, |
|
| 54 | - | arena: *mut alloc::Arena |
|
| 53 | + | config: &TargetConfig, |
|
| 54 | + | arena: &mut alloc::Arena |
|
| 55 | 55 | ) -> AllocResult throws (alloc::AllocError) { |
|
| 56 | 56 | // Phase 1: Liveness analysis. |
|
| 57 | 57 | let live = try liveness::analyze(func, arena); |
|
| 58 | 58 | // Phase 2: Spill analysis (determine which values need stack slots). |
|
| 59 | 59 | let spillInfo = try spill::analyze(func, &live, config.allocatable.len, config.calleeSaved.len, config.slotSize, arena); |
lib/std/lang/gen/regalloc/assign.rad
+41 -31
| 35 | 35 | usedCalleeSaved: u32, |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Per-instruction context for freeing and allocating register uses. |
|
| 39 | 39 | record InstrCtx: Copy { |
|
| 40 | - | current: *mut RegMap, |
|
| 41 | - | usedRegs: *mut bitset::Bitset, |
|
| 40 | + | current: *unsafe mut RegMap, |
|
| 41 | + | usedRegs: *unsafe mut bitset::Bitset, |
|
| 42 | 42 | /// Last operand-use index for each register used in the current block. |
|
| 43 | 43 | lastUse: *[u32], |
|
| 44 | - | live: *liveness::LiveInfo, |
|
| 44 | + | live: *unsafe liveness::LiveInfo, |
|
| 45 | 45 | blockIdx: u32, |
|
| 46 | 46 | instrIdx: u32, |
|
| 47 | 47 | allocatable: *[gen::Reg], |
|
| 48 | 48 | calleeSaved: *[gen::Reg], |
|
| 49 | 49 | assignments: *mut [?gen::Reg], |
|
| 50 | - | spillInfo: *spill::SpillInfo, |
|
| 50 | + | spillInfo: *unsafe spill::SpillInfo, |
|
| 51 | 51 | } |
|
| 52 | 52 | ||
| 53 | 53 | /// Context for recording the last operand-use index in a block. |
|
| 54 | 54 | record LastUseCtx: Copy { |
|
| 55 | 55 | /// Per-register indices, shared by all blocks in the function. |
| 57 | 57 | /// Index of the instruction whose operands are being recorded. |
|
| 58 | 58 | index: u32, |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Compute register assignment. |
|
| 62 | - | export fn assign( |
|
| 62 | + | export unsafe fn assign( |
|
| 63 | 63 | func: *il::Fn, |
|
| 64 | - | live: *liveness::LiveInfo, |
|
| 65 | - | spillInfo: *spill::SpillInfo, |
|
| 66 | - | config: *super::TargetConfig, |
|
| 67 | - | arena: *mut alloc::Arena |
|
| 64 | + | live: &liveness::LiveInfo, |
|
| 65 | + | spillInfo: &spill::SpillInfo, |
|
| 66 | + | config: &super::TargetConfig, |
|
| 67 | + | arena: &mut alloc::Arena |
|
| 68 | 68 | ) -> AssignInfo throws (alloc::AllocError) { |
|
| 69 | 69 | let maxReg = live.maxReg; |
|
| 70 | 70 | let blockCount = func.blocks.len; |
|
| 71 | 71 | let allocatable = config.allocatable; |
|
| 72 | 72 |
| 109 | 109 | // Record every operand before allocation. Only current-block operands |
|
| 110 | 110 | // query this table, so every read is initialized by this scan. |
|
| 111 | 111 | // Entries for other registers need not be cleared between blocks. |
|
| 112 | 112 | for instr, i in block.instrs { |
|
| 113 | 113 | let mut ctx = LastUseCtx { lastUse, index: i }; |
|
| 114 | - | il::forEachReg(instr, recordLastUseCb, &mut ctx as *mut opaque); |
|
| 114 | + | il::forEachReg(instr, recordLastUseCb, &mut ctx as &mut opaque); |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | 117 | // Reset for new block. |
|
| 118 | 118 | set current.n = 0; |
|
| 119 | 119 | bitset::clearAll(&mut usedRegs); |
| 148 | 148 | for instr, i in block.instrs { |
|
| 149 | 149 | let mut ctx = InstrCtx { |
|
| 150 | 150 | current: &mut current, |
|
| 151 | 151 | usedRegs: &mut usedRegs, |
|
| 152 | 152 | lastUse, |
|
| 153 | - | live, |
|
| 153 | + | live: live as *unsafe liveness::LiveInfo, |
|
| 154 | 154 | blockIdx: b, |
|
| 155 | 155 | instrIdx: i, |
|
| 156 | 156 | allocatable, |
|
| 157 | 157 | calleeSaved: config.calleeSaved, |
|
| 158 | 158 | assignments, |
|
| 159 | - | spillInfo, |
|
| 159 | + | spillInfo: spillInfo as *unsafe spill::SpillInfo, |
|
| 160 | 160 | }; |
|
| 161 | - | il::forEachReg(instr, processInstrRegCb, &mut ctx as *mut opaque); |
|
| 161 | + | il::forEachReg(instr, processInstrRegCb, &mut ctx as &mut opaque); |
|
| 162 | 162 | ||
| 163 | 163 | // Allocate destination. |
|
| 164 | 164 | if let dst = il::instrDst(instr) { |
|
| 165 | 165 | if dst.n < maxReg and not spill::isSpilled(spillInfo, dst) { |
|
| 166 | 166 | set assignments[dst.n] = rallocReg(&mut current, &mut usedRegs, dst.n, allocatable, config.calleeSaved, spillInfo); |
| 185 | 185 | usedCalleeSaved, |
|
| 186 | 186 | }; |
|
| 187 | 187 | } |
|
| 188 | 188 | ||
| 189 | 189 | /// Create an empty register map. |
|
| 190 | - | fn createRegMap(arena: *mut alloc::Arena) -> RegMap throws (alloc::AllocError) { |
|
| 190 | + | fn createRegMap(arena: &mut alloc::Arena) -> RegMap throws (alloc::AllocError) { |
|
| 191 | 191 | let virtRegs = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_ACTIVE) as *mut [u32]; |
|
| 192 | 192 | let physRegs = try alloc::allocSlice(arena, @sizeOf(gen::Reg), @alignOf(gen::Reg), MAX_ACTIVE) as *mut [gen::Reg]; |
|
| 193 | 193 | ||
| 194 | 194 | return RegMap { virtRegs, physRegs, n: 0 }; |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// Find physical register for a virtual register in RegMap. |
|
| 198 | - | fn rmapFind(rmap: *RegMap, virtReg: u32) -> ?gen::Reg { |
|
| 198 | + | fn rmapFind(rmap: &RegMap, virtReg: u32) -> ?gen::Reg { |
|
| 199 | 199 | for i in 0..rmap.n { |
|
| 200 | 200 | if rmap.virtRegs[i] == virtReg { |
|
| 201 | 201 | return rmap.physRegs[i]; |
|
| 202 | 202 | } |
|
| 203 | 203 | } |
|
| 204 | 204 | return nil; |
|
| 205 | 205 | } |
|
| 206 | 206 | ||
| 207 | 207 | /// Add a mapping to the register map. |
|
| 208 | - | fn rmapSet(rmap: *mut RegMap, virtReg: u32, physReg: gen::Reg) { |
|
| 208 | + | fn rmapSet(rmap: &mut RegMap, virtReg: u32, physReg: gen::Reg) { |
|
| 209 | 209 | assert rmap.n < MAX_ACTIVE, "rmapSet: register map overflow"; |
|
| 210 | 210 | set rmap.virtRegs[rmap.n] = virtReg; |
|
| 211 | 211 | set rmap.physRegs[rmap.n] = physReg; |
|
| 212 | 212 | set rmap.n += 1; |
|
| 213 | 213 | } |
|
| 214 | 214 | ||
| 215 | 215 | /// Remove a mapping from the register map and return its physical register. |
|
| 216 | - | fn rmapRemove(rmap: *mut RegMap, virtReg: u32) -> ?gen::Reg { |
|
| 216 | + | fn rmapRemove(rmap: &mut RegMap, virtReg: u32) -> ?gen::Reg { |
|
| 217 | 217 | for i in 0..rmap.n { |
|
| 218 | 218 | if rmap.virtRegs[i] == virtReg { |
|
| 219 | 219 | let phys = rmap.physRegs[i]; |
|
| 220 | 220 | // Swap with last and decrement. |
|
| 221 | 221 | set rmap.n -= 1; |
| 228 | 228 | } |
|
| 229 | 229 | return nil; |
|
| 230 | 230 | } |
|
| 231 | 231 | ||
| 232 | 232 | /// Find first free register in pool, allocate it, return it. |
|
| 233 | - | fn findFreeInPool(usedRegs: *mut bitset::Bitset, current: *mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg { |
|
| 233 | + | unsafe fn findFreeInPool(usedRegs: &mut bitset::Bitset, current: &mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg { |
|
| 234 | 234 | for i in 0..pool.len { |
|
| 235 | 235 | let r = pool[i]; |
|
| 236 | 236 | if not bitset::contains(usedRegs, *r as u32) { |
|
| 237 | 237 | bitset::put(usedRegs, *r as u32); |
|
| 238 | 238 | rmapSet(current, ssaReg, r); |
| 242 | 242 | return nil; |
|
| 243 | 243 | } |
|
| 244 | 244 | ||
| 245 | 245 | /// Allocate a physical register for an SSA register. |
|
| 246 | 246 | /// Cross-call values are steered to callee-saved registers. |
|
| 247 | - | fn rallocReg( |
|
| 248 | - | current: *mut RegMap, |
|
| 249 | - | usedRegs: *mut bitset::Bitset, |
|
| 247 | + | unsafe fn rallocReg( |
|
| 248 | + | current: &mut RegMap, |
|
| 249 | + | usedRegs: &mut bitset::Bitset, |
|
| 250 | 250 | ssaReg: u32, |
|
| 251 | 251 | allocatable: *[gen::Reg], |
|
| 252 | 252 | calleeSaved: *[gen::Reg], |
|
| 253 | - | spillInfo: *spill::SpillInfo |
|
| 253 | + | spillInfo: &spill::SpillInfo |
|
| 254 | 254 | ) -> gen::Reg { |
|
| 255 | 255 | // Check if already assigned. |
|
| 256 | 256 | if let phys = rmapFind(current, ssaReg) { |
|
| 257 | 257 | return phys; |
|
| 258 | 258 | } |
| 270 | 270 | } |
|
| 271 | 271 | panic "rallocReg: no free register, spilling fault"; |
|
| 272 | 272 | } |
|
| 273 | 273 | ||
| 274 | 274 | /// Record the current index; forward traversal leaves the last operand use. |
|
| 275 | - | fn recordLastUseCb(reg: il::Reg, ctxPtr: *mut opaque) { |
|
| 276 | - | let ctx = ctxPtr as *mut LastUseCtx; |
|
| 275 | + | fn recordLastUseCb(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 276 | + | recordLastUse(reg, ctxPtr as &mut LastUseCtx); |
|
| 277 | + | } |
|
| 278 | + | ||
| 279 | + | /// Record the last instruction that uses the register. |
|
| 280 | + | fn recordLastUse(reg: il::Reg, ctx: &mut LastUseCtx) { |
|
| 277 | 281 | set ctx.lastUse[reg.n] = ctx.index; |
|
| 278 | 282 | } |
|
| 279 | 283 | ||
| 280 | 284 | /// Free operands with no later block use or live-out use, then allocate missing uses. |
|
| 281 | - | fn processInstrRegCb(reg: il::Reg, ctxPtr: *mut opaque) { |
|
| 282 | - | let ctx = ctxPtr as *mut InstrCtx; |
|
| 285 | + | unsafe fn processInstrRegCb(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 286 | + | processInstrReg(reg, ctxPtr as &mut InstrCtx); |
|
| 287 | + | } |
|
| 288 | + | ||
| 289 | + | /// Release expired registers and assign a register for this operand. |
|
| 290 | + | unsafe fn processInstrReg(reg: il::Reg, ctx: &mut InstrCtx) { |
|
| 283 | 291 | if not (bitset::contains(&ctx.live.liveOut[ctx.blockIdx], reg.n) or ctx.lastUse[reg.n] > ctx.instrIdx) { |
|
| 284 | - | if let phys = rmapRemove(ctx.current, reg.n) { |
|
| 285 | - | bitset::clear(ctx.usedRegs, *phys as u32); |
|
| 292 | + | if let phys = rmapRemove(&mut *ctx.current, reg.n) { |
|
| 293 | + | bitset::clear(&mut *ctx.usedRegs, *phys as u32); |
|
| 286 | 294 | } |
|
| 287 | 295 | } |
|
| 288 | 296 | assert reg.n < ctx.assignments.len, "processInstrRegCb: register out of bounds"; |
|
| 289 | - | if spill::isSpilled(ctx.spillInfo, reg) { |
|
| 297 | + | if spill::isSpilled(&*ctx.spillInfo, reg) { |
|
| 290 | 298 | return; // Spilled values don't get physical registers. |
|
| 291 | 299 | } |
|
| 292 | 300 | if ctx.assignments[reg.n] == nil { |
|
| 301 | + | let current = ctx.current; |
|
| 302 | + | let usedRegs = ctx.usedRegs; |
|
| 293 | 303 | set ctx.assignments[reg.n] = rallocReg( |
|
| 294 | - | ctx.current, ctx.usedRegs, reg.n, ctx.allocatable, |
|
| 295 | - | ctx.calleeSaved, ctx.spillInfo |
|
| 304 | + | &mut *current, &mut *usedRegs, reg.n, ctx.allocatable, |
|
| 305 | + | ctx.calleeSaved, &*ctx.spillInfo |
|
| 296 | 306 | ); |
|
| 297 | 307 | } |
|
| 298 | 308 | } |
lib/std/lang/gen/regalloc/liveness.rad
+28 -16
| 59 | 59 | target: u32, |
|
| 60 | 60 | found: bool, |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Compute liveness by growing live sets until no live-in set changes. |
|
| 64 | - | export fn analyze(func: *il::Fn, arena: *mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) { |
|
| 64 | + | export unsafe fn analyze(func: *il::Fn, arena: &mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) { |
|
| 65 | 65 | let blockCount = func.blocks.len; |
|
| 66 | 66 | if blockCount == 0 { |
|
| 67 | 67 | return LiveInfo { |
|
| 68 | 68 | liveIn: &mut [], |
|
| 69 | 69 | liveOut: &mut [], |
| 83 | 83 | let block = &func.blocks[b]; |
|
| 84 | 84 | for p in block.params { |
|
| 85 | 85 | set maxReg = maxRegNum(p.value.n, maxReg); |
|
| 86 | 86 | } |
|
| 87 | 87 | for i in 0..block.instrs.len { |
|
| 88 | - | il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as *mut opaque); |
|
| 88 | + | il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as &mut opaque); |
|
| 89 | 89 | if let dst = il::instrDst(block.instrs[i]) { |
|
| 90 | 90 | set maxReg = maxRegNum(dst.n, maxReg); |
|
| 91 | 91 | } |
|
| 92 | 92 | } |
|
| 93 | 93 | } |
| 132 | 132 | return LiveInfo { liveIn, liveOut, defs, uses, blockCount, maxReg }; |
|
| 133 | 133 | } |
|
| 134 | 134 | ||
| 135 | 135 | /// Compute `liveIn = uses | (liveOut - defs)` and update `dst`. |
|
| 136 | 136 | /// Returns `true` if `dst` changed. Combined loop avoids multiple passes. |
|
| 137 | - | fn computeAndUpdateLiveIn( |
|
| 137 | + | unsafe fn computeAndUpdateLiveIn( |
|
| 138 | 138 | dst: *mut bitset::Bitset, |
|
| 139 | 139 | liveOut: *bitset::Bitset, |
|
| 140 | 140 | defs: *bitset::Bitset, |
|
| 141 | 141 | uses: *bitset::Bitset |
|
| 142 | 142 | ) -> bool { |
| 151 | 151 | } |
|
| 152 | 152 | return changed; |
|
| 153 | 153 | } |
|
| 154 | 154 | ||
| 155 | 155 | /// Compute local defs and uses for a single block. |
|
| 156 | - | fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) { |
|
| 156 | + | unsafe fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) { |
|
| 157 | 157 | for p in block.params { |
|
| 158 | 158 | bitset::put(defs, p.value.n); |
|
| 159 | 159 | } |
|
| 160 | 160 | for i in 0..block.instrs.len { |
|
| 161 | 161 | let instr = block.instrs[i]; |
|
| 162 | 162 | let mut ctx = DefsUses { defs, uses }; |
|
| 163 | - | il::forEachReg(instr, addUseCallback, &mut ctx as *mut opaque); |
|
| 163 | + | il::forEachReg(instr, addUseCallback, &mut ctx as &mut opaque); |
|
| 164 | 164 | ||
| 165 | 165 | if let dst = il::instrDst(instr) { |
|
| 166 | 166 | bitset::put(defs, dst.n); |
|
| 167 | 167 | } |
|
| 168 | 168 | } |
|
| 169 | 169 | } |
|
| 170 | 170 | ||
| 171 | 171 | /// Callback for [`il::forEachReg`]: adds register to uses if not already defined. |
|
| 172 | - | fn addUseCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 173 | - | let c = ctx as *mut DefsUses; |
|
| 172 | + | unsafe fn addUseCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 173 | + | addUse(reg, ctx as &mut DefsUses); |
|
| 174 | + | } |
|
| 175 | + | ||
| 176 | + | /// Add an undefined register to the use set. |
|
| 177 | + | unsafe fn addUse(reg: il::Reg, c: &mut DefsUses) { |
|
| 174 | 178 | if not bitset::contains(c.defs, reg.n) { |
|
| 175 | 179 | bitset::put(c.uses, reg.n); |
|
| 176 | 180 | } |
|
| 177 | 181 | } |
|
| 178 | 182 | ||
| 179 | 183 | /// Callback for [`il::forEachReg`]: updates max register number. |
|
| 180 | - | fn maxRegCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 181 | - | let max = ctx as *mut u32; |
|
| 184 | + | fn maxRegCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 185 | + | updateMaxReg(reg, ctx as &mut u32); |
|
| 186 | + | } |
|
| 187 | + | ||
| 188 | + | /// Update the largest register number. |
|
| 189 | + | fn updateMaxReg(reg: il::Reg, max: &mut u32) { |
|
| 182 | 190 | set *max = maxRegNum(reg.n, *max); |
|
| 183 | 191 | } |
|
| 184 | 192 | ||
| 185 | 193 | /// Return the larger of n+1 and current. |
|
| 186 | 194 | fn maxRegNum(n: u32, current: u32) -> u32 { |
| 189 | 197 | } |
|
| 190 | 198 | return current; |
|
| 191 | 199 | } |
|
| 192 | 200 | ||
| 193 | 201 | /// Add successor live-in sets to the block's live-out set. |
|
| 194 | - | fn addSuccessorLiveIn(func: *il::Fn, block: *il::Block, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) { |
|
| 202 | + | unsafe fn addSuccessorLiveIn(func: *il::Fn, block: *il::Block, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) { |
|
| 195 | 203 | if block.instrs.len == 0 { |
|
| 196 | 204 | return; |
|
| 197 | 205 | } |
|
| 198 | 206 | let term = block.instrs[block.instrs.len - 1]; |
|
| 199 | 207 |
| 213 | 221 | else => {}, |
|
| 214 | 222 | } |
|
| 215 | 223 | } |
|
| 216 | 224 | ||
| 217 | 225 | /// Union a target block's live-in set into the block's live-out set. |
|
| 218 | - | fn unionBlockLiveIn(target: u32, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) { |
|
| 226 | + | unsafe fn unionBlockLiveIn(target: u32, liveIn: *[bitset::Bitset], liveOut: *mut bitset::Bitset) { |
|
| 219 | 227 | bitset::union_(liveOut, &liveIn[target]); |
|
| 220 | 228 | } |
|
| 221 | 229 | ||
| 222 | 230 | /// Check if a register has any use after this instruction. |
|
| 223 | - | export fn hasLaterUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 231 | + | export unsafe fn hasLaterUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 224 | 232 | let block = &func.blocks[blockIdx]; |
|
| 225 | 233 | ||
| 226 | 234 | if bitset::contains(&info.liveOut[blockIdx], reg.n) { |
|
| 227 | 235 | return true; |
|
| 228 | 236 | } |
| 233 | 241 | } |
|
| 234 | 242 | return false; |
|
| 235 | 243 | } |
|
| 236 | 244 | ||
| 237 | 245 | /// Check if an instruction uses a specific register. |
|
| 238 | - | fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool { |
|
| 246 | + | unsafe fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool { |
|
| 239 | 247 | let mut ctx = FindCtx { target: reg.n, found: false }; |
|
| 240 | - | il::forEachReg(instr, findRegCallback, &mut ctx as *mut opaque); |
|
| 248 | + | il::forEachReg(instr, findRegCallback, &mut ctx as &mut opaque); |
|
| 241 | 249 | return ctx.found; |
|
| 242 | 250 | } |
|
| 243 | 251 | ||
| 244 | 252 | /// Callback for [`il::forEachReg`]: sets found if register matches target. |
|
| 245 | - | fn findRegCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 246 | - | let c = ctx as *mut FindCtx; |
|
| 253 | + | fn findRegCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 254 | + | findReg(reg, ctx as &mut FindCtx); |
|
| 255 | + | } |
|
| 256 | + | ||
| 257 | + | /// Set the match flag when the register equals the target. |
|
| 258 | + | fn findReg(reg: il::Reg, c: &mut FindCtx) { |
|
| 247 | 259 | if reg.n == c.target { |
|
| 248 | 260 | set c.found = true; |
|
| 249 | 261 | } |
|
| 250 | 262 | } |
lib/std/lang/gen/regalloc/spill.rad
+28 -24
| 77 | 77 | costs: *mut [SpillCost], |
|
| 78 | 78 | weight: u32, |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Analyze a function and determine which values need spill slots. |
|
| 82 | - | export fn analyze( |
|
| 82 | + | export unsafe fn analyze( |
|
| 83 | 83 | func: *il::Fn, |
|
| 84 | - | live: *liveness::LiveInfo, |
|
| 84 | + | live: &liveness::LiveInfo, |
|
| 85 | 85 | numRegs: u32, |
|
| 86 | 86 | numCalleeSaved: u32, |
|
| 87 | 87 | slotSize: u32, |
|
| 88 | - | arena: *mut alloc::Arena |
|
| 88 | + | arena: &mut alloc::Arena |
|
| 89 | 89 | ) -> SpillInfo throws (alloc::AllocError) { |
|
| 90 | 90 | let maxReg = live.maxReg; |
|
| 91 | 91 | if maxReg == 0 { |
|
| 92 | 92 | let calleeClass = try bitset::allocate(arena, 0); |
|
| 93 | 93 | return SpillInfo { |
| 140 | 140 | // Remove definition from live set. |
|
| 141 | 141 | if let dst = il::instrDst(instr) { |
|
| 142 | 142 | bitset::clear(&mut scratch, dst.n); |
|
| 143 | 143 | } |
|
| 144 | 144 | // Add uses to live set. |
|
| 145 | - | il::forEachReg(instr, addRegToSetCallback, &mut scratch as *mut opaque); |
|
| 145 | + | il::forEachReg(instr, addRegToSetCallback, &mut scratch as &mut opaque); |
|
| 146 | 146 | } |
|
| 147 | 147 | // Also limit pressure at block entry. |
|
| 148 | 148 | limitPressure(&mut scratch, &mut spilled, costs, numRegs); |
|
| 149 | 149 | } |
|
| 150 | 150 |
| 176 | 176 | } |
|
| 177 | 177 | return SpillInfo { slots, frameSize, calleeClass, maxReg }; |
|
| 178 | 178 | } |
|
| 179 | 179 | ||
| 180 | 180 | /// Calculate spill costs for all registers, weighted by loop depth. |
|
| 181 | - | fn fillCosts(func: *il::Fn, costs: *mut [SpillCost]) { |
|
| 181 | + | unsafe fn fillCosts(func: *il::Fn, costs: *mut [SpillCost]) { |
|
| 182 | 182 | for b in 0..func.blocks.len { |
|
| 183 | 183 | let block = &func.blocks[b]; |
|
| 184 | 184 | ||
| 185 | 185 | // Exponential weight for loop depth, capped to avoid overflow. |
|
| 186 | 186 | let depth = MAX_LOOP_WEIGHT if block.loopDepth > MAX_LOOP_WEIGHT else block.loopDepth; |
| 202 | 202 | set costs[dst.n].defs = costs[dst.n].defs + weight; |
|
| 203 | 203 | } |
|
| 204 | 204 | } |
|
| 205 | 205 | // Count uses. |
|
| 206 | 206 | let mut ctx = CountCtx { costs, weight }; |
|
| 207 | - | il::forEachReg(instr, countRegUseCallback, &mut ctx as *mut opaque); |
|
| 207 | + | il::forEachReg(instr, countRegUseCallback, &mut ctx as &mut opaque); |
|
| 208 | 208 | } |
|
| 209 | 209 | } |
|
| 210 | 210 | } |
|
| 211 | 211 | ||
| 212 | 212 | /// Sort candidates by cost (ascending) using insertion sort, then spill |
|
| 213 | 213 | /// the cheapest `excess` values: set in `spilled`, clear in `source`. |
|
| 214 | - | fn spillCheapest( |
|
| 215 | - | c: *mut Candidates, |
|
| 214 | + | unsafe fn spillCheapest( |
|
| 215 | + | c: &mut Candidates, |
|
| 216 | 216 | excess: u32, |
|
| 217 | - | source: *mut bitset::Bitset, |
|
| 218 | - | spilled: *mut bitset::Bitset |
|
| 217 | + | source: &mut bitset::Bitset, |
|
| 218 | + | spilled: &mut bitset::Bitset |
|
| 219 | 219 | ) { |
|
| 220 | 220 | // Insertion sort ascending by cost. |
|
| 221 | 221 | for i in 1..c.n { |
|
| 222 | 222 | let key = c.entries[i]; |
|
| 223 | 223 | let mut j: u32 = i; |
| 233 | 233 | bitset::clear(source, c.entries[i].reg); |
|
| 234 | 234 | } |
|
| 235 | 235 | } |
|
| 236 | 236 | ||
| 237 | 237 | /// Collect all values from a bitset into a candidates buffer with their costs. |
|
| 238 | - | fn collectCandidates(bs: *bitset::Bitset, costs: *[SpillCost]) -> Candidates { |
|
| 238 | + | unsafe fn collectCandidates(bs: &bitset::Bitset, costs: *[SpillCost]) -> Candidates { |
|
| 239 | 239 | let mut c = Candidates { entries: undefined, n: 0 }; |
|
| 240 | 240 | let mut it = bitset::iter(bs); |
|
| 241 | 241 | while let reg = bitset::iterNext(&mut it) { |
|
| 242 | 242 | assert c.n < MAX_CANDIDATES, "collectCandidates: too many live values"; |
|
| 243 | 243 | if reg < costs.len { |
| 247 | 247 | } |
|
| 248 | 248 | return c; |
|
| 249 | 249 | } |
|
| 250 | 250 | ||
| 251 | 251 | /// Limit register pressure by marking low-cost values as spilled. |
|
| 252 | - | fn limitPressure( |
|
| 253 | - | live: *mut bitset::Bitset, |
|
| 254 | - | spilled: *mut bitset::Bitset, |
|
| 252 | + | unsafe fn limitPressure( |
|
| 253 | + | live: &mut bitset::Bitset, |
|
| 254 | + | spilled: &mut bitset::Bitset, |
|
| 255 | 255 | costs: *[SpillCost], |
|
| 256 | 256 | numRegs: u32 |
|
| 257 | 257 | ) { |
|
| 258 | 258 | let liveCount = bitset::count(live); |
|
| 259 | 259 | if liveCount <= numRegs { |
| 274 | 274 | /// Limit cross-call pressure by spilling values that exceed callee-saved capacity. |
|
| 275 | 275 | /// |
|
| 276 | 276 | /// At a call site, every live value must survive the call in a callee-saved |
|
| 277 | 277 | /// register. If the count exceeds `numCalleeSaved`, spill the cheapest |
|
| 278 | 278 | /// crossing values. |
|
| 279 | - | fn limitCrossCallPressure( |
|
| 280 | - | live: *mut bitset::Bitset, |
|
| 281 | - | spilled: *mut bitset::Bitset, |
|
| 279 | + | unsafe fn limitCrossCallPressure( |
|
| 280 | + | live: &mut bitset::Bitset, |
|
| 281 | + | spilled: &mut bitset::Bitset, |
|
| 282 | 282 | costs: *[SpillCost], |
|
| 283 | - | calleeClass: *mut bitset::Bitset, |
|
| 283 | + | calleeClass: &mut bitset::Bitset, |
|
| 284 | 284 | numCalleeSaved: u32, |
|
| 285 | 285 | callDst: ?il::Reg |
|
| 286 | 286 | ) { |
|
| 287 | 287 | // Collect crossing candidates: live values excluding the call destination. |
|
| 288 | 288 | let mut candidates: [CostEntry; 256] = undefined; |
| 310 | 310 | } |
|
| 311 | 311 | } |
|
| 312 | 312 | } |
|
| 313 | 313 | ||
| 314 | 314 | /// Callback for [`il::forEachReg`]: increments use count for register. |
|
| 315 | - | fn countRegUseCallback(reg: il::Reg, ctxPtr: *mut opaque) { |
|
| 316 | - | let ctx = ctxPtr as *mut CountCtx; |
|
| 315 | + | fn countRegUseCallback(reg: il::Reg, ctxPtr: &mut opaque) { |
|
| 316 | + | countRegUse(reg, ctxPtr as &mut CountCtx); |
|
| 317 | + | } |
|
| 318 | + | ||
| 319 | + | /// Add the block weight to the register use count. |
|
| 320 | + | fn countRegUse(reg: il::Reg, ctx: &mut CountCtx) { |
|
| 317 | 321 | assert reg.n < ctx.costs.len, "countRegUseCallback: register out of bounds"; |
|
| 318 | 322 | set ctx.costs[reg.n].uses = ctx.costs[reg.n].uses + ctx.weight; |
|
| 319 | 323 | } |
|
| 320 | 324 | ||
| 321 | 325 | /// Callback for [`il::forEachReg`]: adds register to live set. |
|
| 322 | - | fn addRegToSetCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 323 | - | bitset::put(ctx as *mut bitset::Bitset, reg.n); |
|
| 326 | + | unsafe fn addRegToSetCallback(reg: il::Reg, ctx: &mut opaque) { |
|
| 327 | + | bitset::put(ctx as &mut bitset::Bitset, reg.n); |
|
| 324 | 328 | } |
|
| 325 | 329 | ||
| 326 | 330 | /// Check if a register is spilled. |
|
| 327 | - | export fn isSpilled(info: *SpillInfo, reg: il::Reg) -> bool { |
|
| 331 | + | export fn isSpilled(info: &SpillInfo, reg: il::Reg) -> bool { |
|
| 328 | 332 | if reg.n >= info.maxReg { |
|
| 329 | 333 | return false; |
|
| 330 | 334 | } |
|
| 331 | 335 | return info.slots[reg.n] >= 0; |
|
| 332 | 336 | } |
|
| 333 | 337 | ||
| 334 | 338 | /// Get spill slot offset for a register, or `nil` if not spilled. |
|
| 335 | - | export fn spillSlot(info: *SpillInfo, reg: il::Reg) -> ?i32 { |
|
| 339 | + | export fn spillSlot(info: &SpillInfo, reg: il::Reg) -> ?i32 { |
|
| 336 | 340 | if isSpilled(info, reg) { |
|
| 337 | 341 | return info.slots[reg.n]; |
|
| 338 | 342 | } |
|
| 339 | 343 | return nil; |
|
| 340 | 344 | } |
lib/std/lang/il.rad
+3 -3
| 80 | 80 | ||
| 81 | 81 | /// Separator for qualified symbol names. |
|
| 82 | 82 | export constant PATH_SEPARATOR: *[u8] = "::"; |
|
| 83 | 83 | ||
| 84 | 84 | /// Format a qualified symbol name: `pkg::mod::path::name`. |
|
| 85 | - | export fn formatQualifiedName(arena: *mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] { |
|
| 85 | + | export fn formatQualifiedName(arena: &mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] { |
|
| 86 | 86 | let mut totalLen: u32 = name.len; |
|
| 87 | 87 | for segment in path { |
|
| 88 | 88 | set totalLen += segment.len + PATH_SEPARATOR.len; |
|
| 89 | 89 | } |
|
| 90 | 90 | let buf = try! alloc::allocSlice(arena, 1, 1, totalLen) as *mut [u8]; |
| 425 | 425 | } |
|
| 426 | 426 | } |
|
| 427 | 427 | ||
| 428 | 428 | /// Call a function for each register used by an instruction. |
|
| 429 | 429 | /// This is called by the register allocator to analyze register usage. |
|
| 430 | - | export fn forEachReg(instr: Instr, f: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 430 | + | export unsafe fn forEachReg(instr: Instr, f: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 431 | 431 | match instr { |
|
| 432 | 432 | case Instr::Reserve { size, .. } => |
|
| 433 | 433 | withReg(size, f, ctx), |
|
| 434 | 434 | case Instr::Load { src, .. } => f(src, ctx), |
|
| 435 | 435 | case Instr::Sload { src, .. } => f(src, ctx), |
| 503 | 503 | Instr::MemoryFence => {}, |
|
| 504 | 504 | } |
|
| 505 | 505 | } |
|
| 506 | 506 | ||
| 507 | 507 | /// Call callback if value is a register. |
|
| 508 | - | fn withReg(val: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 508 | + | unsafe fn withReg(val: Val, callback: unsafe fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 509 | 509 | if let case Val::Reg(r) = val { |
|
| 510 | 510 | callback(r, ctx); |
|
| 511 | 511 | } |
|
| 512 | 512 | } |
lib/std/lang/il/printer.rad
+32 -30
| 6 | 6 | /////////////////////// |
|
| 7 | 7 | // String formatting // |
|
| 8 | 8 | /////////////////////// |
|
| 9 | 9 | ||
| 10 | 10 | /// Write a `u32` before its stack buffer leaves scope. |
|
| 11 | - | fn writeU32(out: *mut sexpr::Output, val: u32) { |
|
| 11 | + | unsafe fn writeU32(out: &mut sexpr::Output, val: u32) { |
|
| 12 | 12 | let mut digits: [u8; 10] = undefined; |
|
| 13 | - | write(out, fmt::formatU32(val, &mut digits[..])); |
|
| 13 | + | let start = fmt::formatU32(val, &mut digits[..]); |
|
| 14 | + | write(out, &digits[start..]); |
|
| 14 | 15 | } |
|
| 15 | 16 | ||
| 16 | 17 | /// Write an `i32` before its stack buffer leaves scope. |
|
| 17 | - | fn writeI32(out: *mut sexpr::Output, val: i32) { |
|
| 18 | + | unsafe fn writeI32(out: &mut sexpr::Output, val: i32) { |
|
| 18 | 19 | let mut digits: [u8; 12] = undefined; |
|
| 19 | - | write(out, fmt::formatI32(val, &mut digits[..])); |
|
| 20 | + | let start = fmt::formatI32(val, &mut digits[..]); |
|
| 21 | + | write(out, &digits[start..]); |
|
| 20 | 22 | } |
|
| 21 | 23 | ||
| 22 | 24 | /// Write an `i64` before its stack buffer leaves scope. |
|
| 23 | - | fn writeI64(out: *mut sexpr::Output, val: i64) { |
|
| 25 | + | unsafe fn writeI64(out: &mut sexpr::Output, val: i64) { |
|
| 24 | 26 | let mut digits: [u8; 20] = undefined; |
|
| 25 | - | write(out, fmt::formatI64(val, &mut digits[..])); |
|
| 27 | + | let start = fmt::formatI64(val, &mut digits[..]); |
|
| 28 | + | write(out, &digits[start..]); |
|
| 26 | 29 | } |
|
| 27 | 30 | ||
| 28 | 31 | ///////////////////// |
|
| 29 | 32 | // Output helpers // |
|
| 30 | 33 | ///////////////////// |
|
| 31 | 34 | ||
| 32 | 35 | /// Write a string to the output. |
|
| 33 | - | fn write(out: *mut sexpr::Output, s: *[u8]) { |
|
| 36 | + | unsafe fn write(out: &mut sexpr::Output, s: &[u8]) { |
|
| 34 | 37 | sexpr::write(out, s); |
|
| 35 | 38 | } |
|
| 36 | 39 | ||
| 37 | 40 | /// Emit indentation. |
|
| 38 | - | fn indent(out: *mut sexpr::Output, depth: u32) { |
|
| 41 | + | unsafe fn indent(out: &mut sexpr::Output, depth: u32) { |
|
| 39 | 42 | for _ in 0..depth { |
|
| 40 | 43 | write(out, " "); |
|
| 41 | 44 | } |
|
| 42 | 45 | } |
|
| 43 | 46 |
| 50 | 53 | } |
|
| 51 | 54 | return false; |
|
| 52 | 55 | } |
|
| 53 | 56 | ||
| 54 | 57 | /// Write a symbol name. Simple names use `$name`, qualified names use `$"name"`. |
|
| 55 | - | fn writeSymbol(out: *mut sexpr::Output, name: *[u8]) { |
|
| 58 | + | unsafe fn writeSymbol(out: &mut sexpr::Output, name: *[u8]) { |
|
| 56 | 59 | write(out, "$"); |
|
| 57 | 60 | if needsQuoting(name) { |
|
| 58 | 61 | write(out, "\""); |
|
| 59 | 62 | write(out, name); |
|
| 60 | 63 | write(out, "\""); |
| 76 | 79 | case super::Type::W64 => return "w64", |
|
| 77 | 80 | } |
|
| 78 | 81 | } |
|
| 79 | 82 | ||
| 80 | 83 | /// Write a type. |
|
| 81 | - | fn writeType(out: *mut sexpr::Output, typ: super::Type) { |
|
| 84 | + | unsafe fn writeType(out: &mut sexpr::Output, typ: super::Type) { |
|
| 82 | 85 | write(out, typeStr(typ)); |
|
| 83 | 86 | } |
|
| 84 | 87 | ||
| 85 | 88 | //////////////////////// |
|
| 86 | 89 | // Operation printing // |
| 122 | 125 | //////////////////// |
|
| 123 | 126 | // Value printing // |
|
| 124 | 127 | //////////////////// |
|
| 125 | 128 | ||
| 126 | 129 | /// Write a value (register, immediate, symbol, or undefined). |
|
| 127 | - | fn writeVal(out: *mut sexpr::Output, val: super::Val) { |
|
| 130 | + | unsafe fn writeVal(out: &mut sexpr::Output, val: super::Val) { |
|
| 128 | 131 | match val { |
|
| 129 | 132 | case super::Val::Reg(reg) => writeReg(out, reg), |
|
| 130 | 133 | case super::Val::Imm(v) => writeI64(out, v), |
|
| 131 | 134 | case super::Val::DataSym(name) => writeSymbol(out, name), |
|
| 132 | 135 | case super::Val::FnAddr(name) => writeSymbol(out, name), |
|
| 133 | 136 | case super::Val::Undef => write(out, "undefined"), |
|
| 134 | 137 | } |
|
| 135 | 138 | } |
|
| 136 | 139 | ||
| 137 | 140 | /// Write a register in one operation before its stack buffer leaves scope. |
|
| 138 | - | fn writeReg(out: *mut sexpr::Output, reg: super::Reg) { |
|
| 141 | + | unsafe fn writeReg(out: &mut sexpr::Output, reg: super::Reg) { |
|
| 139 | 142 | let mut buffer: [u8; 11] = undefined; |
|
| 140 | - | let digits = fmt::formatU32(reg.n, &mut buffer[1..]); |
|
| 141 | - | let start = buffer.len - digits.len - 1; |
|
| 143 | + | let start = fmt::formatU32(reg.n, &mut buffer[1..]); |
|
| 142 | 144 | set buffer[start] = '%'; |
|
| 143 | 145 | write(out, &buffer[start..]); |
|
| 144 | 146 | } |
|
| 145 | 147 | ||
| 146 | 148 | /// Write a comma-separated argument list in parentheses. |
|
| 147 | - | fn writeArgs(out: *mut sexpr::Output, args: *[super::Val]) { |
|
| 149 | + | unsafe fn writeArgs(out: &mut sexpr::Output, args: *[super::Val]) { |
|
| 148 | 150 | write(out, "("); |
|
| 149 | 151 | for arg, i in args { |
|
| 150 | 152 | if i > 0 { |
|
| 151 | 153 | write(out, ", "); |
|
| 152 | 154 | } |
| 154 | 156 | } |
|
| 155 | 157 | write(out, ")"); |
|
| 156 | 158 | } |
|
| 157 | 159 | ||
| 158 | 160 | /// Write a typed parameter (e.g., `w32 %0`). |
|
| 159 | - | fn writeParam(out: *mut sexpr::Output, param: super::Param) { |
|
| 161 | + | unsafe fn writeParam(out: &mut sexpr::Output, param: super::Param) { |
|
| 160 | 162 | writeType(out, param.type); |
|
| 161 | 163 | write(out, " "); |
|
| 162 | 164 | writeReg(out, param.value); |
|
| 163 | 165 | } |
|
| 164 | 166 | ||
| 165 | 167 | /// Write a comma-separated parameter list. |
|
| 166 | - | fn writeParams(out: *mut sexpr::Output, params: *[super::Param]) { |
|
| 168 | + | unsafe fn writeParams(out: &mut sexpr::Output, params: *[super::Param]) { |
|
| 167 | 169 | for param, i in params { |
|
| 168 | 170 | if i > 0 { |
|
| 169 | 171 | write(out, ", "); |
|
| 170 | 172 | } |
|
| 171 | 173 | writeParam(out, param); |
| 175 | 177 | ////////////////////////// |
|
| 176 | 178 | // Instruction printing // |
|
| 177 | 179 | ////////////////////////// |
|
| 178 | 180 | ||
| 179 | 181 | /// Write an instruction. |
|
| 180 | - | fn writeInstr(out: *mut sexpr::Output, blocks: *[super::Block], inst: super::Instr) { |
|
| 182 | + | unsafe fn writeInstr(out: &mut sexpr::Output, blocks: *[super::Block], inst: super::Instr) { |
|
| 181 | 183 | match inst { |
|
| 182 | 184 | // Memory operations. |
|
| 183 | 185 | case super::Instr::Reserve { dst, size, alignment } => { |
|
| 184 | 186 | write(out, "reserve "); |
|
| 185 | 187 | writeReg(out, dst); |
| 344 | 346 | } |
|
| 345 | 347 | } |
|
| 346 | 348 | } |
|
| 347 | 349 | ||
| 348 | 350 | /// Write a typed binary operation: `op type %dst %a %b`. |
|
| 349 | - | fn writeTypedBinOp( |
|
| 350 | - | out: *mut sexpr::Output, |
|
| 351 | + | unsafe fn writeTypedBinOp( |
|
| 352 | + | out: &mut sexpr::Output, |
|
| 351 | 353 | name: *[u8], |
|
| 352 | 354 | typ: super::Type, |
|
| 353 | 355 | dst: super::Reg, |
|
| 354 | 356 | va: super::Val, |
|
| 355 | 357 | vb: super::Val |
| 364 | 366 | write(out, " "); |
|
| 365 | 367 | writeVal(out, vb); |
|
| 366 | 368 | } |
|
| 367 | 369 | ||
| 368 | 370 | /// Write a typed unary operation: `op type %dst %val`. |
|
| 369 | - | fn writeTypedUnaryOp( |
|
| 370 | - | out: *mut sexpr::Output, |
|
| 371 | + | unsafe fn writeTypedUnaryOp( |
|
| 372 | + | out: &mut sexpr::Output, |
|
| 371 | 373 | name: *[u8], |
|
| 372 | 374 | typ: super::Type, |
|
| 373 | 375 | dst: super::Reg, |
|
| 374 | 376 | val: super::Val |
|
| 375 | 377 | ) { |
| 385 | 387 | //////////////////// |
|
| 386 | 388 | // Block printing // |
|
| 387 | 389 | //////////////////// |
|
| 388 | 390 | ||
| 389 | 391 | /// Write a basic block. |
|
| 390 | - | fn writeBlock(out: *mut sexpr::Output, blocks: *[super::Block], block: *super::Block) { |
|
| 392 | + | unsafe fn writeBlock(out: &mut sexpr::Output, blocks: *[super::Block], block: *super::Block) { |
|
| 391 | 393 | // Block label. |
|
| 392 | 394 | write(out, " @"); |
|
| 393 | 395 | write(out, block.label); |
|
| 394 | 396 | ||
| 395 | 397 | // Block parameters. |
| 411 | 413 | /////////////////////// |
|
| 412 | 414 | // Function printing // |
|
| 413 | 415 | /////////////////////// |
|
| 414 | 416 | ||
| 415 | 417 | /// Write a function. |
|
| 416 | - | fn writeFn(out: *mut sexpr::Output, f: *super::Fn) { |
|
| 418 | + | unsafe fn writeFn(out: &mut sexpr::Output, f: *super::Fn) { |
|
| 417 | 419 | // Function signature. |
|
| 418 | 420 | if f.isExtern { |
|
| 419 | 421 | write(out, "extern "); |
|
| 420 | 422 | } |
|
| 421 | 423 | write(out, "fn "); |
| 445 | 447 | /////////////////// |
|
| 446 | 448 | // Data printing // |
|
| 447 | 449 | /////////////////// |
|
| 448 | 450 | ||
| 449 | 451 | /// Write a data item. |
|
| 450 | - | fn writeDataItem(out: *mut sexpr::Output, item: super::DataItem) { |
|
| 452 | + | unsafe fn writeDataItem(out: &mut sexpr::Output, item: super::DataItem) { |
|
| 451 | 453 | match item { |
|
| 452 | 454 | case super::DataItem::Val { typ, val } => { |
|
| 453 | 455 | writeType(out, typ); |
|
| 454 | 456 | write(out, " "); |
|
| 455 | 457 | writeI64(out, val); |
| 471 | 473 | } |
|
| 472 | 474 | } |
|
| 473 | 475 | } |
|
| 474 | 476 | ||
| 475 | 477 | /// Write a data value (item with optional repeat count). |
|
| 476 | - | fn writeDataValue(out: *mut sexpr::Output, value: super::DataValue) { |
|
| 478 | + | unsafe fn writeDataValue(out: &mut sexpr::Output, value: super::DataValue) { |
|
| 477 | 479 | writeDataItem(out, value.item); |
|
| 478 | 480 | if value.count > 1 { |
|
| 479 | 481 | write(out, " * "); |
|
| 480 | 482 | writeU32(out, value.count); |
|
| 481 | 483 | } |
|
| 482 | 484 | } |
|
| 483 | 485 | ||
| 484 | 486 | /// Write global data. |
|
| 485 | - | fn writeData(out: *mut sexpr::Output, d: super::Data) { |
|
| 487 | + | unsafe fn writeData(out: &mut sexpr::Output, d: super::Data) { |
|
| 486 | 488 | write(out, "data "); |
|
| 487 | 489 | if not d.readOnly { |
|
| 488 | 490 | write(out, "mut "); |
|
| 489 | 491 | } |
|
| 490 | 492 | writeSymbol(out, d.name); |
| 503 | 505 | ////////////////////// |
|
| 504 | 506 | // Program printing // |
|
| 505 | 507 | ////////////////////// |
|
| 506 | 508 | ||
| 507 | 509 | /// Print a program. |
|
| 508 | - | export fn printProgram(out: *mut sexpr::Output, program: *super::Program) { |
|
| 510 | + | export unsafe fn printProgram(out: &mut sexpr::Output, program: &super::Program) { |
|
| 509 | 511 | // Data declarations. |
|
| 510 | 512 | for data, i in program.data { |
|
| 511 | 513 | writeData(out, data); |
|
| 512 | 514 | if i < program.data.len - 1 or program.fns.len > 0 { |
|
| 513 | 515 | write(out, "\n"); |
| 521 | 523 | } |
|
| 522 | 524 | } |
|
| 523 | 525 | } |
|
| 524 | 526 | ||
| 525 | 527 | /// Print a program to a buffer, returning the written slice. |
|
| 526 | - | export fn printProgramToBuffer( |
|
| 527 | - | program: *super::Program, |
|
| 528 | + | export unsafe fn printProgramToBuffer( |
|
| 529 | + | program: &super::Program, |
|
| 528 | 530 | buf: *mut [u8] |
|
| 529 | 531 | ) -> *[u8] { |
|
| 530 | 532 | let mut pos: u32 = 0; |
|
| 531 | 533 | let mut out = sexpr::Output::Buffer { buf, pos: &mut pos }; |
|
| 532 | 534 | printProgram(&mut out, program); |
lib/std/lang/lower.rad
+489 -481
| 135 | 135 | /// Invalid variable use. |
|
| 136 | 136 | InvalidUse, |
|
| 137 | 137 | /// Unexpected node value. |
|
| 138 | 138 | UnexpectedNodeValue(*ast::Node), |
|
| 139 | 139 | /// Unexpected type. |
|
| 140 | - | UnexpectedType(*resolver::Type), |
|
| 140 | + | UnexpectedType(resolver::Type), |
|
| 141 | 141 | ||
| 142 | 142 | /// Missing control flow target. |
|
| 143 | 143 | MissingTarget, |
|
| 144 | 144 | /// Missing metadata that should have been set by resolver. |
|
| 145 | 145 | MissingMetadata, |
| 264 | 264 | Default, |
|
| 265 | 265 | } |
|
| 266 | 266 | ||
| 267 | 267 | /// Function sink used by lowerers that consume functions as they are produced. |
|
| 268 | 268 | export record FnSink: Copy { |
|
| 269 | - | /// Opaque context passed to the sink callback. |
|
| 270 | - | ctx: *mut opaque, |
|
| 269 | + | /// Opaque context. It must remain valid for every sink callback. |
|
| 270 | + | ctx: *unsafe mut opaque, |
|
| 271 | 271 | /// Callback invoked for each lowered function. |
|
| 272 | - | emitFn: fn(*mut opaque, *il::Fn, FnRole), |
|
| 272 | + | emitFn: unsafe fn(*unsafe mut opaque, *il::Fn, FnRole), |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// Destination for functions produced by the lowerer. |
|
| 276 | 276 | export union FnOutput: Copy { |
|
| 277 | 277 | /// Store lowered functions in the provided slice. |
| 283 | 283 | /// Module-level lowering context. Shared across all function lowerings. |
|
| 284 | 284 | /// Holds global state like the data section (strings, constants) and provides |
|
| 285 | 285 | /// access to the resolver for type queries. |
|
| 286 | 286 | export record Lowerer: Copy { |
|
| 287 | 287 | /// Arena for persistent lowering state, including data and symbol names. |
|
| 288 | - | arena: *mut alloc::Arena, |
|
| 288 | + | arena: *unsafe mut alloc::Arena, |
|
| 289 | 289 | /// Arena for allocations owned by the function currently being lowered. |
|
| 290 | - | fnArena: *mut alloc::Arena, |
|
| 290 | + | fnArena: *unsafe mut alloc::Arena, |
|
| 291 | 291 | /// Allocator backed by the arena. |
|
| 292 | 292 | allocator: alloc::Allocator, |
|
| 293 | 293 | /// Resolver for type information. Used to query types, symbols, and |
|
| 294 | 294 | /// compile-time constant values during lowering. |
|
| 295 | - | resolver: *resolver::Resolver, |
|
| 295 | + | resolver: *unsafe resolver::Resolver, |
|
| 296 | 296 | /// Module graph for cross-module symbol resolution. |
|
| 297 | - | moduleGraph: ?*module::ModuleGraph, |
|
| 297 | + | moduleGraph: ?*unsafe module::ModuleGraph, |
|
| 298 | 298 | /// Package name for qualified symbol names. |
|
| 299 | 299 | pkgName: *[u8], |
|
| 300 | 300 | /// Current module being lowered. |
|
| 301 | 301 | currentMod: ?u16, |
|
| 302 | 302 | /// Global data items (string literals, constants, static arrays). |
| 340 | 340 | return maxSize; |
|
| 341 | 341 | } |
|
| 342 | 342 | ||
| 343 | 343 | /// Get or assign a globally unique error tag for the given error type. |
|
| 344 | 344 | /// Tag `0` is reserved for success; error tags start at `1`. |
|
| 345 | - | fn getOrAssignErrorTag(self: *mut Lowerer, errType: resolver::Type) -> u32 { |
|
| 345 | + | fn getOrAssignErrorTag(self: &mut Lowerer, errType: resolver::Type) -> u32 { |
|
| 346 | 346 | for entry in self.errTags { |
|
| 347 | 347 | if entry.ty == errType { |
|
| 348 | 348 | return entry.tag; |
|
| 349 | 349 | } |
|
| 350 | 350 | } |
| 355 | 355 | ||
| 356 | 356 | return tag; |
|
| 357 | 357 | } |
|
| 358 | 358 | ||
| 359 | 359 | /// Emit one function to the active output. |
|
| 360 | - | fn emitFunction(self: *mut Lowerer, func: *il::Fn, role: FnRole) { |
|
| 360 | + | unsafe fn emitFunction(self: &mut Lowerer, func: *il::Fn, role: FnRole) { |
|
| 361 | 361 | match self.output { |
|
| 362 | 362 | case FnOutput::Accumulate(accumulated) => { |
|
| 363 | 363 | let mut fns = accumulated; |
|
| 364 | 364 | fns.append(func, self.allocator); |
|
| 365 | 365 | set self.output = FnOutput::Accumulate(fns); |
| 415 | 415 | } |
|
| 416 | 416 | return true; |
|
| 417 | 417 | } |
|
| 418 | 418 | ||
| 419 | 419 | /// Append a data value to the builder. |
|
| 420 | - | fn dataBuilderPush(b: *mut DataValueBuilder, value: il::DataValue) { |
|
| 420 | + | fn dataBuilderPush(b: &mut DataValueBuilder, value: il::DataValue) { |
|
| 421 | 421 | b.values.append(value, b.allocator); |
|
| 422 | 422 | ||
| 423 | 423 | if value.count > 0 and not dataIsZero(value.item) { |
|
| 424 | 424 | set b.zeroInit = false; |
|
| 425 | 425 | } |
|
| 426 | 426 | } |
|
| 427 | 427 | ||
| 428 | 428 | /// Return the accumulated values. |
|
| 429 | - | fn dataBuilderFinish(b: *DataValueBuilder) -> ConstDataResult { |
|
| 429 | + | fn dataBuilderFinish(b: &DataValueBuilder) -> ConstDataResult { |
|
| 430 | 430 | return ConstDataResult { |
|
| 431 | 431 | values: &b.values[..], |
|
| 432 | 432 | zeroInit: b.zeroInit, |
|
| 433 | 433 | }; |
|
| 434 | 434 | } |
| 672 | 672 | ||
| 673 | 673 | /// Per-function lowering state. Created fresh for each function and contains |
|
| 674 | 674 | /// all the mutable state needed during function body lowering. |
|
| 675 | 675 | record FnLowerer: Copy { |
|
| 676 | 676 | /// Reference to the module-level lowerer. |
|
| 677 | - | low: *mut Lowerer, |
|
| 677 | + | low: *unsafe mut Lowerer, |
|
| 678 | 678 | /// Allocator for IL allocations. |
|
| 679 | 679 | allocator: alloc::Allocator, |
|
| 680 | 680 | /// Type signature of the function being lowered. |
|
| 681 | 681 | fnType: *resolver::FnType, |
|
| 682 | 682 | /// Function name, used as prefix for generated data symbols. |
| 740 | 740 | /// 2. Iterates over top-level declarations, lowering each. |
|
| 741 | 741 | /// 3. Returns the complete IL program with functions and data section. |
|
| 742 | 742 | /// |
|
| 743 | 743 | /// The resolver must have already processed the AST -- we rely on its type |
|
| 744 | 744 | /// annotations, symbol table, and constant evaluations. |
|
| 745 | - | export fn lower( |
|
| 746 | - | res: *resolver::Resolver, |
|
| 745 | + | export unsafe fn lower( |
|
| 746 | + | res: &resolver::Resolver, |
|
| 747 | 747 | root: *ast::Node, |
|
| 748 | 748 | pkgName: *[u8], |
|
| 749 | - | arena: *mut alloc::Arena |
|
| 749 | + | arena: &mut alloc::Arena |
|
| 750 | 750 | ) -> il::Program throws (LowerError) { |
|
| 751 | 751 | let mut low = Lowerer { |
|
| 752 | - | arena: arena, |
|
| 753 | - | fnArena: arena, |
|
| 752 | + | arena: arena as *unsafe mut alloc::Arena, |
|
| 753 | + | fnArena: arena as *unsafe mut alloc::Arena, |
|
| 754 | 754 | allocator: alloc::arenaAllocator(arena), |
|
| 755 | - | resolver: res, |
|
| 755 | + | resolver: res as *unsafe resolver::Resolver, |
|
| 756 | 756 | moduleGraph: nil, |
|
| 757 | 757 | pkgName, |
|
| 758 | 758 | currentMod: nil, |
|
| 759 | 759 | data: &mut [], |
|
| 760 | 760 | output: FnOutput::Accumulate(&mut []), |
| 771 | 771 | ///////////////////////////////// |
|
| 772 | 772 | // Multi-Module Lowering API // |
|
| 773 | 773 | ///////////////////////////////// |
|
| 774 | 774 | ||
| 775 | 775 | /// Create a lowerer for multi-module compilation. |
|
| 776 | - | export fn lowerer( |
|
| 777 | - | res: *resolver::Resolver, |
|
| 778 | - | graph: *module::ModuleGraph, |
|
| 776 | + | /// The resolver, graph, and both arenas must outlive the returned lowerer. |
|
| 777 | + | export unsafe fn lowerer( |
|
| 778 | + | res: *unsafe resolver::Resolver, |
|
| 779 | + | graph: &module::ModuleGraph, |
|
| 779 | 780 | pkgName: *[u8], |
|
| 780 | - | arena: *mut alloc::Arena, |
|
| 781 | - | fnArena: *mut alloc::Arena, |
|
| 781 | + | arena: *unsafe mut alloc::Arena, |
|
| 782 | + | fnArena: *unsafe mut alloc::Arena, |
|
| 782 | 783 | options: LowerOptions |
|
| 783 | 784 | ) -> Lowerer { |
|
| 784 | 785 | return Lowerer { |
|
| 785 | - | arena, |
|
| 786 | - | fnArena, |
|
| 787 | - | allocator: alloc::arenaAllocator(arena), |
|
| 788 | - | resolver: res, |
|
| 789 | - | moduleGraph: graph, |
|
| 786 | + | arena: arena as *unsafe mut alloc::Arena, |
|
| 787 | + | fnArena: fnArena as *unsafe mut alloc::Arena, |
|
| 788 | + | allocator: alloc::arenaAllocator(&mut *arena), |
|
| 789 | + | resolver: res as *unsafe resolver::Resolver, |
|
| 790 | + | moduleGraph: graph as *unsafe module::ModuleGraph, |
|
| 790 | 791 | pkgName, |
|
| 791 | 792 | currentMod: nil, |
|
| 792 | 793 | data: &mut [], |
|
| 793 | 794 | output: FnOutput::Accumulate(&mut []), |
|
| 794 | 795 | fnSyms: &mut [], |
| 798 | 799 | }; |
|
| 799 | 800 | } |
|
| 800 | 801 | ||
| 801 | 802 | /// Lower a module's AST into the lowerer. |
|
| 802 | 803 | /// Call this for each module in the package, then use `finalize` to get the program. |
|
| 803 | - | export fn lowerModule( |
|
| 804 | - | low: *mut Lowerer, |
|
| 804 | + | export unsafe fn lowerModule( |
|
| 805 | + | low: &mut Lowerer, |
|
| 805 | 806 | moduleId: u16, |
|
| 806 | 807 | root: *ast::Node, |
|
| 807 | 808 | isRoot: bool |
|
| 808 | 809 | ) throws (LowerError) { |
|
| 809 | 810 | set low.currentMod = moduleId; |
|
| 810 | 811 | try lowerDecls(low, root, isRoot); |
|
| 811 | 812 | } |
|
| 812 | 813 | ||
| 813 | 814 | /// Lower all top-level declarations in a block. |
|
| 814 | - | fn lowerDecls(low: *mut Lowerer, root: *ast::Node, isRoot: bool) throws (LowerError) { |
|
| 815 | + | unsafe fn lowerDecls(low: &mut Lowerer, root: *ast::Node, isRoot: bool) throws (LowerError) { |
|
| 815 | 816 | let case ast::NodeValue::Block(block) = root.value else { |
|
| 816 | 817 | throw LowerError::ExpectedBlock(root); |
|
| 817 | 818 | }; |
|
| 818 | 819 | let stmtsList = block.statements; |
|
| 819 | 820 |
| 843 | 844 | } |
|
| 844 | 845 | } |
|
| 845 | 846 | } |
|
| 846 | 847 | ||
| 847 | 848 | /// Finalize lowering and return the unified IL program. |
|
| 848 | - | export fn finalize(low: *Lowerer) -> il::Program { |
|
| 849 | + | export fn finalize(low: &Lowerer) -> il::Program { |
|
| 849 | 850 | let mut fns: *mut [*il::Fn] = undefined; |
|
| 850 | 851 | match low.output { |
|
| 851 | 852 | case FnOutput::Accumulate(accumulated) => { |
|
| 852 | 853 | set fns = accumulated; |
|
| 853 | 854 | } |
| 865 | 866 | // Qualified Name Construction // |
|
| 866 | 867 | ///////////////////////////////// |
|
| 867 | 868 | ||
| 868 | 869 | /// Get module path segments for the current or specified module. |
|
| 869 | 870 | /// Returns empty slice if no module graph or module not found. |
|
| 870 | - | fn getModulePath(self: *mut Lowerer, modId: ?u16) -> *[*[u8]] { |
|
| 871 | + | unsafe fn getModulePath(self: &mut Lowerer, modId: ?u16) -> *[*[u8]] { |
|
| 871 | 872 | let graph = self.moduleGraph else { |
|
| 872 | 873 | return &[]; |
|
| 873 | 874 | }; |
|
| 874 | 875 | let mut id = modId; |
|
| 875 | 876 | if id == nil { |
|
| 876 | 877 | set id = self.currentMod; |
|
| 877 | 878 | } |
|
| 878 | 879 | let actualId = id else { |
|
| 879 | 880 | return &[]; |
|
| 880 | 881 | }; |
|
| 881 | - | let entry = module::get(graph, actualId) else { |
|
| 882 | + | let entry = module::get(&*graph, actualId) else { |
|
| 882 | 883 | return &[]; |
|
| 883 | 884 | }; |
|
| 884 | 885 | return module::moduleQualifiedPath(entry); |
|
| 885 | 886 | } |
|
| 886 | 887 | ||
| 887 | 888 | /// Build a qualified name string for a symbol. |
|
| 888 | 889 | /// If `modId` is nil, uses current module. |
|
| 889 | - | fn qualifyName(self: *mut Lowerer, modId: ?u16, name: *[u8]) -> *[u8] { |
|
| 890 | + | unsafe fn qualifyName(self: &mut Lowerer, modId: ?u16, name: *[u8]) -> *[u8] { |
|
| 890 | 891 | let path = getModulePath(self, modId); |
|
| 891 | 892 | if path.len == 0 { |
|
| 892 | 893 | return name; |
|
| 893 | 894 | } |
|
| 894 | - | return il::formatQualifiedName(self.arena, path, name); |
|
| 895 | + | return il::formatQualifiedName(&mut *self.arena, path, name); |
|
| 895 | 896 | } |
|
| 896 | 897 | ||
| 897 | 898 | /// Register a function symbol with its qualified name. |
|
| 898 | 899 | /// Called when lowering function declarations, so cross-package calls can find |
|
| 899 | 900 | /// the function by name. |
|
| 900 | - | fn registerFnSym(self: *mut Lowerer, sym: *resolver::Symbol, qualName: *[u8]) { |
|
| 901 | + | fn registerFnSym(self: &mut Lowerer, sym: *resolver::Symbol, qualName: *[u8]) { |
|
| 901 | 902 | self.fnSyms.append(FnSymEntry { sym, qualName }, self.allocator); |
|
| 902 | 903 | } |
|
| 903 | 904 | ||
| 904 | 905 | /// Look up a function's qualified name by its symbol. |
|
| 905 | 906 | /// Returns `nil` if the symbol wasn't registered (e.g. callee's module is not yet lowered). |
|
| 906 | 907 | // TODO: This is kind of dubious as an optimization, if it depends on the order |
|
| 907 | 908 | // in which modules are lowered. |
|
| 908 | 909 | // TODO: Use a hash table here? |
|
| 909 | - | fn lookupFnSym(self: *Lowerer, sym: *resolver::Symbol) -> ?*[u8] { |
|
| 910 | + | fn lookupFnSym(self: &Lowerer, sym: *resolver::Symbol) -> ?*[u8] { |
|
| 910 | 911 | for entry in self.fnSyms { |
|
| 911 | 912 | if entry.sym == sym { |
|
| 912 | 913 | return entry.qualName; |
|
| 913 | 914 | } |
|
| 914 | 915 | } |
|
| 915 | 916 | return nil; |
|
| 916 | 917 | } |
|
| 917 | 918 | ||
| 918 | 919 | /// Set the package context for lowering. |
|
| 919 | 920 | /// Called before lowering each package. |
|
| 920 | - | export fn setPackage(self: *mut Lowerer, graph: *module::ModuleGraph, pkgName: *[u8]) { |
|
| 921 | - | set self.moduleGraph = graph; |
|
| 921 | + | /// The graph must outlive later uses of the lowerer. |
|
| 922 | + | export unsafe fn setPackage(self: &mut Lowerer, graph: &module::ModuleGraph, pkgName: *[u8]) { |
|
| 923 | + | set self.moduleGraph = graph as *unsafe module::ModuleGraph; |
|
| 922 | 924 | set self.pkgName = pkgName; |
|
| 923 | 925 | set self.currentMod = nil; |
|
| 924 | 926 | } |
|
| 925 | 927 | ||
| 926 | 928 | /// Create a new function lowerer for a given function type and name. |
|
| 927 | - | fn fnLowerer( |
|
| 928 | - | self: *mut Lowerer, |
|
| 929 | + | unsafe fn fnLowerer( |
|
| 930 | + | self: &mut Lowerer, |
|
| 929 | 931 | node: *ast::Node, |
|
| 930 | 932 | fnType: *resolver::FnType, |
|
| 931 | 933 | qualName: *[u8] |
|
| 932 | 934 | ) -> FnLowerer { |
|
| 933 | - | let loopStack = try! alloc::allocSlice(self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *mut [LoopCtx]; |
|
| 935 | + | let loopStack = try! alloc::allocSlice(&mut *self.fnArena, @sizeOf(LoopCtx), @alignOf(LoopCtx), MAX_LOOP_DEPTH) as *mut [LoopCtx]; |
|
| 934 | 936 | ||
| 935 | 937 | let mut fnLow = FnLowerer { |
|
| 936 | - | low: self, |
|
| 937 | - | allocator: alloc::arenaAllocator(self.fnArena), |
|
| 938 | + | low: self as *unsafe mut Lowerer, |
|
| 939 | + | allocator: alloc::arenaAllocator(&mut *self.fnArena), |
|
| 938 | 940 | fnType: fnType, |
|
| 939 | 941 | fnName: qualName, |
|
| 940 | 942 | vars: &mut [], |
|
| 941 | 943 | params: &mut [], |
|
| 942 | 944 | blockData: &mut [], |
| 968 | 970 | /// This sets up the per-function lowering state, processes parameters, |
|
| 969 | 971 | /// then lowers the function body into a CFG of basic blocks. |
|
| 970 | 972 | /// |
|
| 971 | 973 | /// For throwing functions, the return type is a result aggregate |
|
| 972 | 974 | /// rather than the declared return type. |
|
| 973 | - | fn lowerFnDecl(self: *mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*il::Fn throws (LowerError) { |
|
| 975 | + | unsafe fn lowerFnDecl(self: &mut Lowerer, node: *ast::Node, decl: ast::FnDecl) -> ?*il::Fn throws (LowerError) { |
|
| 974 | 976 | if not shouldLowerFn(&decl, self.options.buildTest) { |
|
| 975 | 977 | return nil; |
|
| 976 | 978 | } |
|
| 977 | 979 | let case ast::NodeValue::Ident(name) = decl.name.value else { |
|
| 978 | 980 | throw LowerError::ExpectedIdentifier; |
|
| 979 | 981 | }; |
|
| 980 | - | let data = resolver::nodeData(self.resolver, node); |
|
| 982 | + | let data = resolver::nodeData(&*self.resolver, node); |
|
| 981 | 983 | let case resolver::Type::Fn(fnType) = data.ty else { |
|
| 982 | 984 | throw LowerError::ExpectedFunction; |
|
| 983 | 985 | }; |
|
| 984 | 986 | let isExtern = checkAttr(decl.attrs, ast::Attribute::Extern); |
|
| 985 | 987 |
| 997 | 999 | // as the first argument; the callee writes the return value into it. |
|
| 998 | 1000 | if requiresReturnParam(fnType) and not isExtern { |
|
| 999 | 1001 | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1000 | 1002 | } |
|
| 1001 | 1003 | let lowParams = try lowerParams(&mut fnLow, *fnType, decl.sig.params, nil); |
|
| 1002 | - | let func = try! alloc::alloc(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn; |
|
| 1004 | + | let func = try! alloc::alloc(&mut *self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn; |
|
| 1003 | 1005 | ||
| 1004 | 1006 | set *func = il::Fn { |
|
| 1005 | 1007 | name: qualName, |
|
| 1006 | 1008 | params: lowParams, |
|
| 1007 | 1009 | returnType: undefined, |
| 1028 | 1030 | ||
| 1029 | 1031 | return func; |
|
| 1030 | 1032 | } |
|
| 1031 | 1033 | ||
| 1032 | 1034 | /// Build a qualified name of the form "Type::method". |
|
| 1033 | - | fn instanceMethodName(self: *mut Lowerer, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] { |
|
| 1035 | + | unsafe fn instanceMethodName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], methodName: *[u8]) -> *[u8] { |
|
| 1034 | 1036 | let sepLen: u32 = 2; // "::" |
|
| 1035 | 1037 | let totalLen = typeName.len + sepLen + methodName.len; |
|
| 1036 | - | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1038 | + | let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1037 | 1039 | let mut pos: u32 = 0; |
|
| 1038 | 1040 | ||
| 1039 | 1041 | set pos += try! mem::copy(&mut buf[pos..], typeName); |
|
| 1040 | 1042 | set pos += try! mem::copy(&mut buf[pos..], "::"); |
|
| 1041 | 1043 | set pos += try! mem::copy(&mut buf[pos..], methodName); |
| 1043 | 1045 | ||
| 1044 | 1046 | return qualifyName(self, modId, &buf[..totalLen]); |
|
| 1045 | 1047 | } |
|
| 1046 | 1048 | ||
| 1047 | 1049 | /// Build a v-table data name of the form "vtable::Type::Trait". |
|
| 1048 | - | fn vtableName(self: *mut Lowerer, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] { |
|
| 1050 | + | unsafe fn vtableName(self: &mut Lowerer, modId: ?u16, typeName: *[u8], traitName: *[u8]) -> *[u8] { |
|
| 1049 | 1051 | let prefix = "vtable::"; |
|
| 1050 | 1052 | let sepLen: u32 = 2; // "::" |
|
| 1051 | 1053 | let totalLen = prefix.len + typeName.len + sepLen + traitName.len; |
|
| 1052 | - | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1054 | + | let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1053 | 1055 | let mut pos: u32 = 0; |
|
| 1054 | 1056 | ||
| 1055 | 1057 | set pos += try! mem::copy(&mut buf[pos..], prefix); |
|
| 1056 | 1058 | set pos += try! mem::copy(&mut buf[pos..], typeName); |
|
| 1057 | 1059 | set pos += try! mem::copy(&mut buf[pos..], "::"); |
| 1066 | 1068 | /// Each method in the instance block is lowered as a standalone function |
|
| 1067 | 1069 | /// with a qualified name of the form `Type::method`. A read-only v-table |
|
| 1068 | 1070 | /// data record is emitted containing pointers to these functions, ordered |
|
| 1069 | 1071 | /// by the trait's method indices. The v-table is later referenced when |
|
| 1070 | 1072 | /// constructing trait objects for dynamic dispatch. |
|
| 1071 | - | fn lowerInstanceDecl( |
|
| 1072 | - | self: *mut Lowerer, |
|
| 1073 | + | unsafe fn lowerInstanceDecl( |
|
| 1074 | + | self: &mut Lowerer, |
|
| 1073 | 1075 | node: *ast::Node, |
|
| 1074 | 1076 | traitNameNode: *ast::Node, |
|
| 1075 | 1077 | targetTypeNode: *ast::Node, |
|
| 1076 | 1078 | methods: *mut [*ast::Node] |
|
| 1077 | 1079 | ) throws (LowerError) { |
|
| 1078 | 1080 | // Look up the trait and type from the resolver. |
|
| 1079 | - | let traitSym = resolver::nodeData(self.resolver, traitNameNode).sym |
|
| 1081 | + | let traitSym = resolver::nodeData(&*self.resolver, traitNameNode).sym |
|
| 1080 | 1082 | else throw LowerError::MissingSymbol(traitNameNode); |
|
| 1081 | 1083 | let case resolver::SymbolData::Trait(traitInfo) = traitSym.data |
|
| 1082 | 1084 | else throw LowerError::MissingMetadata; |
|
| 1083 | - | let typeSym = resolver::nodeData(self.resolver, targetTypeNode).sym |
|
| 1085 | + | let typeSym = resolver::nodeData(&*self.resolver, targetTypeNode).sym |
|
| 1084 | 1086 | else throw LowerError::MissingSymbol(targetTypeNode); |
|
| 1085 | 1087 | ||
| 1086 | 1088 | let tName = traitSym.name; |
|
| 1087 | 1089 | let typeName = typeSym.name; |
|
| 1088 | 1090 |
| 1122 | 1124 | } |
|
| 1123 | 1125 | ||
| 1124 | 1126 | // Create v-table in data section, used for dynamic dispatch. |
|
| 1125 | 1127 | let vName = vtableName(self, nil, typeName, tName); |
|
| 1126 | 1128 | let values = try! alloc::allocSlice( |
|
| 1127 | - | self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32 |
|
| 1129 | + | &mut *self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), traitInfo.methods.len as u32 |
|
| 1128 | 1130 | ) as *mut [il::DataValue]; |
|
| 1129 | 1131 | ||
| 1130 | 1132 | for i in 0..traitInfo.methods.len { |
|
| 1131 | 1133 | set values[i] = il::DataValue { |
|
| 1132 | 1134 | item: il::DataItem::Fn(methodNames[i]), |
| 1143 | 1145 | }, self.allocator); |
|
| 1144 | 1146 | } |
|
| 1145 | 1147 | ||
| 1146 | 1148 | /// Lower a method node into an IL function with the given qualified name. |
|
| 1147 | 1149 | /// Shared by both instance methods and standalone methods. |
|
| 1148 | - | fn lowerMethod( |
|
| 1149 | - | self: *mut Lowerer, |
|
| 1150 | + | unsafe fn lowerMethod( |
|
| 1151 | + | self: &mut Lowerer, |
|
| 1150 | 1152 | node: *ast::Node, |
|
| 1151 | 1153 | qualName: *[u8], |
|
| 1152 | 1154 | receiverName: *ast::Node, |
|
| 1153 | 1155 | sig: ast::FnSig, |
|
| 1154 | 1156 | body: *ast::Node, |
|
| 1155 | 1157 | ) -> ?*il::Fn throws (LowerError) { |
|
| 1156 | - | let data = resolver::nodeData(self.resolver, node); |
|
| 1158 | + | let data = resolver::nodeData(&*self.resolver, node); |
|
| 1157 | 1159 | let case resolver::Type::Fn(fnType) = data.ty else { |
|
| 1158 | 1160 | throw LowerError::ExpectedFunction; |
|
| 1159 | 1161 | }; |
|
| 1160 | 1162 | let sym = data.sym else throw LowerError::MissingSymbol(node); |
|
| 1161 | 1163 | registerFnSym(self, sym, qualName); |
| 1163 | 1165 | let mut fnLow = fnLowerer(self, node, fnType, qualName); |
|
| 1164 | 1166 | if requiresReturnParam(fnType) { |
|
| 1165 | 1167 | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1166 | 1168 | } |
|
| 1167 | 1169 | let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName); |
|
| 1168 | - | let func = try! alloc::alloc(self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn; |
|
| 1170 | + | let func = try! alloc::alloc(&mut *self.fnArena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn; |
|
| 1169 | 1171 | ||
| 1170 | 1172 | set *func = il::Fn { |
|
| 1171 | 1173 | name: qualName, |
|
| 1172 | 1174 | params: lowParams, |
|
| 1173 | 1175 | returnType: ilType(self, *fnType.returnType), |
| 1184 | 1186 | return func; |
|
| 1185 | 1187 | } |
|
| 1186 | 1188 | ||
| 1187 | 1189 | /// Lower a standalone method declaration. |
|
| 1188 | 1190 | /// Produces a function with qualified name `Type::method`. |
|
| 1189 | - | fn lowerMethodDecl( |
|
| 1190 | - | self: *mut Lowerer, |
|
| 1191 | + | unsafe fn lowerMethodDecl( |
|
| 1192 | + | self: &mut Lowerer, |
|
| 1191 | 1193 | node: *ast::Node, |
|
| 1192 | 1194 | name: *ast::Node, |
|
| 1193 | 1195 | receiverName: *ast::Node, |
|
| 1194 | 1196 | sig: ast::FnSig, |
|
| 1195 | 1197 | body: *ast::Node, |
|
| 1196 | 1198 | ) -> ?*il::Fn throws (LowerError) { |
|
| 1197 | - | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1199 | + | let sym = resolver::nodeData(&*self.resolver, node).sym |
|
| 1198 | 1200 | else throw LowerError::MissingSymbol(node); |
|
| 1199 | 1201 | let case ast::NodeValue::Ident(mName) = name.value |
|
| 1200 | 1202 | else throw LowerError::ExpectedIdentifier; |
|
| 1201 | - | let me = resolver::findMethodBySymbol(self.resolver, sym) |
|
| 1203 | + | let me = resolver::findMethodBySymbol(&*self.resolver, sym) |
|
| 1202 | 1204 | else throw LowerError::MissingMetadata; |
|
| 1203 | 1205 | let qualName = instanceMethodName(self, nil, me.concreteTypeName, mName); |
|
| 1204 | 1206 | ||
| 1205 | 1207 | return try lowerMethod(self, node, qualName, receiverName, sig, body); |
|
| 1206 | 1208 | } |
|
| 1207 | 1209 | ||
| 1208 | 1210 | /// Check if a function should be lowered. |
|
| 1209 | - | fn shouldLowerFn(decl: *ast::FnDecl, buildTest: bool) -> bool { |
|
| 1211 | + | fn shouldLowerFn(decl: &ast::FnDecl, buildTest: bool) -> bool { |
|
| 1210 | 1212 | if checkAttr(decl.attrs, ast::Attribute::Test) { |
|
| 1211 | 1213 | return buildTest; |
|
| 1212 | 1214 | } |
|
| 1213 | 1215 | return true; |
|
| 1214 | 1216 | } |
| 1221 | 1223 | return false; |
|
| 1222 | 1224 | } |
|
| 1223 | 1225 | ||
| 1224 | 1226 | /// Create a label with a numeric suffix, eg. `@base0`. |
|
| 1225 | 1227 | /// This ensures unique labels like `@then0`, `@then1`, etc. |
|
| 1226 | - | fn labelWithSuffix(self: *mut FnLowerer, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) { |
|
| 1228 | + | unsafe fn labelWithSuffix(self: &mut FnLowerer, base: *[u8], suffix: u32) -> *[u8] throws (LowerError) { |
|
| 1227 | 1229 | let mut digits: [u8; fmt::U32_STR_LEN] = undefined; |
|
| 1228 | - | let suffixText = fmt::formatU32(suffix, &mut digits[..]); |
|
| 1229 | - | let totalLen = base.len + suffixText.len; |
|
| 1230 | - | let buf = try! alloc::allocSlice(self.low.fnArena, 1, 1, totalLen) as *mut [u8]; |
|
| 1230 | + | let start = fmt::formatU32(suffix, &mut digits[..]); |
|
| 1231 | + | let totalLen = base.len + digits.len - start; |
|
| 1232 | + | let buf = try! alloc::allocSlice(&mut *self.low.fnArena, 1, 1, totalLen) as *mut [u8]; |
|
| 1231 | 1233 | ||
| 1232 | 1234 | try! mem::copy(&mut buf[..base.len], base); |
|
| 1233 | - | try! mem::copy(&mut buf[base.len..totalLen], suffixText); |
|
| 1235 | + | try! mem::copy(&mut buf[base.len..totalLen], &digits[start..]); |
|
| 1234 | 1236 | ||
| 1235 | 1237 | return &buf[..totalLen]; |
|
| 1236 | 1238 | } |
|
| 1237 | 1239 | ||
| 1238 | 1240 | /// Generate a unique label by appending the global counter to the base. |
|
| 1239 | - | fn nextLabel(self: *mut FnLowerer, base: *[u8]) -> *[u8] throws (LowerError) { |
|
| 1241 | + | unsafe fn nextLabel(self: &mut FnLowerer, base: *[u8]) -> *[u8] throws (LowerError) { |
|
| 1240 | 1242 | let idx = self.labelCounter; |
|
| 1241 | 1243 | set self.labelCounter += 1; |
|
| 1242 | 1244 | ||
| 1243 | 1245 | return try labelWithSuffix(self, base, idx); |
|
| 1244 | 1246 | } |
| 1266 | 1268 | else => panic, |
|
| 1267 | 1269 | } |
|
| 1268 | 1270 | } |
|
| 1269 | 1271 | ||
| 1270 | 1272 | /// Convert a constant value to an IL value. |
|
| 1271 | - | fn constValueToVal(self: *mut FnLowerer, val: resolver::ConstValue, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 1273 | + | unsafe fn constValueToVal(self: &mut FnLowerer, val: resolver::ConstValue, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 1272 | 1274 | if let case resolver::ConstValue::String(s) = val { |
|
| 1273 | 1275 | return try lowerStringLit(self, node, s); |
|
| 1274 | 1276 | } |
|
| 1275 | 1277 | return il::Val::Imm(constToScalar(val)); |
|
| 1276 | 1278 | } |
|
| 1277 | 1279 | ||
| 1278 | 1280 | /// Convert a resolver constant value to an IL data initializer item. |
|
| 1279 | - | fn constValueToDataItem(self: *mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem { |
|
| 1281 | + | fn constValueToDataItem(self: &mut Lowerer, val: resolver::ConstValue, typ: resolver::Type) -> il::DataItem { |
|
| 1280 | 1282 | if let case resolver::ConstValue::String(s) = val { |
|
| 1281 | 1283 | return il::DataItem::Str(s); |
|
| 1282 | 1284 | } |
|
| 1283 | 1285 | // Bool and char are byte-sized; integer uses the declared type. |
|
| 1284 | 1286 | let mut irTyp = il::Type::W8; |
| 1288 | 1290 | return il::DataItem::Val { typ: irTyp, val: constToScalar(val) }; |
|
| 1289 | 1291 | } |
|
| 1290 | 1292 | ||
| 1291 | 1293 | /// Lower scalar-like constant nodes into data values, including fallback handling |
|
| 1292 | 1294 | /// for void-variant tags and slice string initializers. |
|
| 1293 | - | fn lowerConstScalarDataInto( |
|
| 1294 | - | self: *mut Lowerer, |
|
| 1295 | + | unsafe fn lowerConstScalarDataInto( |
|
| 1296 | + | self: &mut Lowerer, |
|
| 1295 | 1297 | node: *ast::Node, |
|
| 1296 | 1298 | ty: resolver::Type, |
|
| 1297 | 1299 | dataPrefix: *[u8], |
|
| 1298 | - | b: *mut DataValueBuilder |
|
| 1300 | + | b: &mut DataValueBuilder |
|
| 1299 | 1301 | ) throws (LowerError) { |
|
| 1300 | - | let val = resolver::constValueEntry(self.resolver, node) else { |
|
| 1301 | - | if let idx = voidVariantIndex(self.resolver, node) { |
|
| 1302 | + | let val = resolver::constValueEntry(&*self.resolver, node) else { |
|
| 1303 | + | if let idx = voidVariantIndex(&*self.resolver, node) { |
|
| 1302 | 1304 | dataBuilderPush(b, il::DataValue { |
|
| 1303 | 1305 | item: il::DataItem::Val { typ: il::Type::W8, val: idx }, |
|
| 1304 | 1306 | count: 1 |
|
| 1305 | 1307 | }); |
|
| 1306 | 1308 | return; |
| 1320 | 1322 | count: 1 |
|
| 1321 | 1323 | }); |
|
| 1322 | 1324 | } |
|
| 1323 | 1325 | ||
| 1324 | 1326 | /// Lower a constant or static declaration to the data section. |
|
| 1325 | - | fn lowerDataDecl( |
|
| 1326 | - | self: *mut Lowerer, |
|
| 1327 | + | unsafe fn lowerDataDecl( |
|
| 1328 | + | self: &mut Lowerer, |
|
| 1327 | 1329 | node: *ast::Node, |
|
| 1328 | 1330 | value: *ast::Node, |
|
| 1329 | 1331 | readOnly: bool |
|
| 1330 | 1332 | ) throws (LowerError) { |
|
| 1331 | - | let data = resolver::nodeData(self.resolver, node); |
|
| 1333 | + | let data = resolver::nodeData(&*self.resolver, node); |
|
| 1332 | 1334 | let sym = data.sym else { |
|
| 1333 | 1335 | throw LowerError::MissingSymbol(node); |
|
| 1334 | 1336 | }; |
|
| 1335 | 1337 | if data.ty == resolver::Type::Unknown { |
|
| 1336 | 1338 | throw LowerError::MissingType(node); |
| 1350 | 1352 | values: result.values, |
|
| 1351 | 1353 | }, self.allocator); |
|
| 1352 | 1354 | } |
|
| 1353 | 1355 | ||
| 1354 | 1356 | /// Emit the in-memory representation of a slice header: `{ ptr, len, cap }`. |
|
| 1355 | - | fn dataSliceHeader(b: *mut DataValueBuilder, dataSym: *[u8], len: u32) { |
|
| 1357 | + | fn dataSliceHeader(b: &mut DataValueBuilder, dataSym: *[u8], len: u32) { |
|
| 1356 | 1358 | dataBuilderPush(b, il::DataValue { |
|
| 1357 | 1359 | item: il::DataItem::Sym(dataSym), |
|
| 1358 | 1360 | count: 1 |
|
| 1359 | 1361 | }); |
|
| 1360 | 1362 | dataBuilderPush(b, il::DataValue { |
| 1372 | 1374 | count: 1 |
|
| 1373 | 1375 | }); |
|
| 1374 | 1376 | } |
|
| 1375 | 1377 | ||
| 1376 | 1378 | /// Lower a compile-time `&[...]` expression to a concrete slice header. |
|
| 1377 | - | fn lowerConstAddressSliceInto( |
|
| 1378 | - | self: *mut Lowerer, |
|
| 1379 | + | unsafe fn lowerConstAddressSliceInto( |
|
| 1380 | + | self: &mut Lowerer, |
|
| 1379 | 1381 | addr: ast::AddressOf, |
|
| 1380 | 1382 | ty: resolver::Type, |
|
| 1381 | 1383 | dataPrefix: *[u8], |
|
| 1382 | - | b: *mut DataValueBuilder |
|
| 1384 | + | b: &mut DataValueBuilder |
|
| 1383 | 1385 | ) throws (LowerError) { |
|
| 1384 | 1386 | let case resolver::Type::Slice { mutable, .. } = ty |
|
| 1385 | 1387 | else throw LowerError::ExpectedSliceOrArray; |
|
| 1386 | - | let targetTy = resolver::typeFor(self.resolver, addr.target) |
|
| 1388 | + | let targetTy = resolver::typeFor(&*self.resolver, addr.target) |
|
| 1387 | 1389 | else throw LowerError::MissingType(addr.target); |
|
| 1388 | 1390 | let case resolver::Type::Array(arrInfo) = targetTy |
|
| 1389 | 1391 | else throw LowerError::ExpectedArray; |
|
| 1390 | 1392 | ||
| 1391 | 1393 | let mut nested = dataBuilder(self.allocator); |
| 1407 | 1409 | dataSliceHeader(b, dataName, arrInfo.length); |
|
| 1408 | 1410 | } |
|
| 1409 | 1411 | ||
| 1410 | 1412 | /// Lower a constant expression payload into a builder without slot padding. |
|
| 1411 | 1413 | /// Compute the type layout only when undefined data needs a byte count. |
|
| 1412 | - | fn lowerConstDataPayloadInto( |
|
| 1413 | - | self: *mut Lowerer, |
|
| 1414 | + | unsafe fn lowerConstDataPayloadInto( |
|
| 1415 | + | self: &mut Lowerer, |
|
| 1414 | 1416 | node: *ast::Node, |
|
| 1415 | 1417 | ty: resolver::Type, |
|
| 1416 | 1418 | dataPrefix: *[u8], |
|
| 1417 | - | b: *mut DataValueBuilder |
|
| 1419 | + | b: &mut DataValueBuilder |
|
| 1418 | 1420 | ) throws (LowerError) { |
|
| 1419 | 1421 | // Function pointer references in constant data. |
|
| 1420 | 1422 | if let case resolver::Type::Fn(_) = ty { |
|
| 1421 | - | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1423 | + | let sym = resolver::nodeData(&*self.resolver, node).sym |
|
| 1422 | 1424 | else throw LowerError::MissingSymbol(node); |
|
| 1423 | - | let modId = resolver::moduleIdForSymbol(self.resolver, sym); |
|
| 1425 | + | let modId = resolver::moduleIdForSymbol(&*self.resolver, sym); |
|
| 1424 | 1426 | let qualName = qualifyName(self, modId, sym.name); |
|
| 1425 | 1427 | dataBuilderPush(b, il::DataValue { |
|
| 1426 | 1428 | item: il::DataItem::Fn(qualName), count: 1, |
|
| 1427 | 1429 | }); |
|
| 1428 | 1430 | return; |
|
| 1429 | 1431 | } |
|
| 1430 | 1432 | // In constant data, a void variant of a mixed union still occupies the |
|
| 1431 | 1433 | // full tagged-union slot. Emitting only the tag corrupts following fields. |
|
| 1432 | - | if let sym = resolver::nodeData(self.resolver, node).sym { |
|
| 1434 | + | if let sym = resolver::nodeData(&*self.resolver, node).sym { |
|
| 1433 | 1435 | if let case resolver::SymbolData::Variant { type: resolver::Type::Void, .. } = sym.data { |
|
| 1434 | 1436 | if let case resolver::Type::Nominal(resolver::NominalType::Union(_)) = ty { |
|
| 1435 | 1437 | try lowerConstUnionVariantInto(self, node, sym, ty, &mut [], dataPrefix, b); |
|
| 1436 | 1438 | return; |
|
| 1437 | 1439 | } |
| 1450 | 1452 | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 1451 | 1453 | try lowerConstArrayRepeatInto(self, repeat, ty, dataPrefix, b), |
|
| 1452 | 1454 | case ast::NodeValue::RecordLit(recLit) => |
|
| 1453 | 1455 | try lowerConstRecordLitInto(self, node, recLit, ty, dataPrefix, b), |
|
| 1454 | 1456 | case ast::NodeValue::Call(call) => { |
|
| 1455 | - | let calleeSym = resolver::nodeData(self.resolver, call.callee).sym |
|
| 1457 | + | let calleeSym = resolver::nodeData(&*self.resolver, call.callee).sym |
|
| 1456 | 1458 | else throw LowerError::MissingSymbol(call.callee); |
|
| 1457 | 1459 | match calleeSym.data { |
|
| 1458 | 1460 | case resolver::SymbolData::Variant { .. } => |
|
| 1459 | 1461 | try lowerConstUnionVariantInto(self, node, calleeSym, ty, call.args, dataPrefix, b), |
|
| 1460 | 1462 | case resolver::SymbolData::Type(resolver::NominalType::Record(recInfo)) => { |
| 1466 | 1468 | case ast::NodeValue::AddressOf(addr) => { |
|
| 1467 | 1469 | try lowerConstAddressSliceInto(self, addr, ty, dataPrefix, b); |
|
| 1468 | 1470 | } |
|
| 1469 | 1471 | case ast::NodeValue::Ident(_) => { |
|
| 1470 | 1472 | // Identifier referencing a constant. |
|
| 1471 | - | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1473 | + | let sym = resolver::nodeData(&*self.resolver, node).sym |
|
| 1472 | 1474 | else throw LowerError::MissingSymbol(node); |
|
| 1473 | 1475 | let case ast::NodeValue::ConstDecl(decl) = sym.node.value |
|
| 1474 | 1476 | else throw LowerError::MissingConst(node); |
|
| 1475 | 1477 | ||
| 1476 | 1478 | try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b); |
|
| 1477 | 1479 | }, |
|
| 1478 | 1480 | case ast::NodeValue::ScopeAccess(_) => { |
|
| 1479 | - | let sym = resolver::nodeData(self.resolver, node).sym |
|
| 1481 | + | let sym = resolver::nodeData(&*self.resolver, node).sym |
|
| 1480 | 1482 | else throw LowerError::MissingSymbol(node); |
|
| 1481 | 1483 | if let case ast::NodeValue::ConstDecl(decl) = sym.node.value { |
|
| 1482 | 1484 | try lowerConstDataPayloadInto(self, decl.value, ty, dataPrefix, b); |
|
| 1483 | 1485 | } else { |
|
| 1484 | 1486 | try lowerConstScalarDataInto(self, node, ty, dataPrefix, b); |
| 1490 | 1492 | } |
|
| 1491 | 1493 | } |
|
| 1492 | 1494 | } |
|
| 1493 | 1495 | ||
| 1494 | 1496 | /// Lower a constant expression into a builder, padding to the given slot size. |
|
| 1495 | - | fn lowerConstDataInto( |
|
| 1496 | - | self: *mut Lowerer, |
|
| 1497 | + | unsafe fn lowerConstDataInto( |
|
| 1498 | + | self: &mut Lowerer, |
|
| 1497 | 1499 | node: *ast::Node, |
|
| 1498 | 1500 | ty: resolver::Type, |
|
| 1499 | 1501 | slotSize: u32, |
|
| 1500 | 1502 | dataPrefix: *[u8], |
|
| 1501 | - | b: *mut DataValueBuilder |
|
| 1503 | + | b: &mut DataValueBuilder |
|
| 1502 | 1504 | ) throws (LowerError) { |
|
| 1503 | 1505 | let layout = resolver::getTypeLayout(ty); |
|
| 1504 | 1506 | try lowerConstDataPayloadInto(self, node, ty, dataPrefix, b); |
|
| 1505 | 1507 | // Pad to fill the enclosing slot. |
|
| 1506 | 1508 | let padding = slotSize - layout.size; |
| 1509 | 1511 | } |
|
| 1510 | 1512 | } |
|
| 1511 | 1513 | ||
| 1512 | 1514 | /// Flatten a constant array literal `[a, b, c]` into a builder. |
|
| 1513 | 1515 | /// Each element payload fills its type size; no extra slot padding is needed. |
|
| 1514 | - | fn lowerConstArrayLitInto( |
|
| 1515 | - | self: *mut Lowerer, |
|
| 1516 | + | unsafe fn lowerConstArrayLitInto( |
|
| 1517 | + | self: &mut Lowerer, |
|
| 1516 | 1518 | elems: *mut [*ast::Node], |
|
| 1517 | 1519 | ty: resolver::Type, |
|
| 1518 | 1520 | dataPrefix: *[u8], |
|
| 1519 | - | b: *mut DataValueBuilder |
|
| 1521 | + | b: &mut DataValueBuilder |
|
| 1520 | 1522 | ) throws (LowerError) { |
|
| 1521 | 1523 | let case resolver::Type::Array(arrInfo) = ty |
|
| 1522 | 1524 | else throw LowerError::ExpectedArray; |
|
| 1523 | 1525 | let elemTy = *arrInfo.item; |
|
| 1524 | 1526 |
| 1528 | 1530 | } |
|
| 1529 | 1531 | ||
| 1530 | 1532 | /// Build data values for a constant array repeat literal `[item; count]`. |
|
| 1531 | 1533 | /// Repeat element payloads without extra slot padding. Undefined data uses |
|
| 1532 | 1534 | /// the element layout to compute the total byte count. |
|
| 1533 | - | fn lowerConstArrayRepeatInto( |
|
| 1534 | - | self: *mut Lowerer, |
|
| 1535 | + | unsafe fn lowerConstArrayRepeatInto( |
|
| 1536 | + | self: &mut Lowerer, |
|
| 1535 | 1537 | repeat: ast::ArrayRepeatLit, |
|
| 1536 | 1538 | ty: resolver::Type, |
|
| 1537 | 1539 | dataPrefix: *[u8], |
|
| 1538 | - | b: *mut DataValueBuilder |
|
| 1540 | + | b: &mut DataValueBuilder |
|
| 1539 | 1541 | ) throws (LowerError) { |
|
| 1540 | 1542 | let case resolver::Type::Array(arrInfo) = ty |
|
| 1541 | 1543 | else throw LowerError::ExpectedArray; |
|
| 1542 | 1544 | let length = arrInfo.length; |
|
| 1543 | 1545 | let elemTy = *arrInfo.item; |
| 1546 | 1548 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 1547 | 1549 | dataBuilderPush(b, il::DataValue { |
|
| 1548 | 1550 | item: il::DataItem::Undef, |
|
| 1549 | 1551 | count: elemLayout.size * length |
|
| 1550 | 1552 | }); |
|
| 1551 | - | } else if let val = resolver::constValueEntry(self.resolver, repeat.item) { |
|
| 1553 | + | } else if let val = resolver::constValueEntry(&*self.resolver, repeat.item) { |
|
| 1552 | 1554 | if let case resolver::ConstValue::String(_) = val { |
|
| 1553 | 1555 | // A string used as a slice is represented by a three-word slice |
|
| 1554 | 1556 | // header, not by the bytes of the string itself. |
|
| 1555 | 1557 | for _ in 0..length { |
|
| 1556 | 1558 | try lowerConstDataPayloadInto(self, repeat.item, elemTy, dataPrefix, b); |
| 1568 | 1570 | } |
|
| 1569 | 1571 | } |
|
| 1570 | 1572 | ||
| 1571 | 1573 | /// Build data values for a constant record literal. |
|
| 1572 | 1574 | /// Each field is lowered with a slot size that includes trailing padding. |
|
| 1573 | - | fn lowerConstRecordLitInto( |
|
| 1574 | - | self: *mut Lowerer, |
|
| 1575 | + | unsafe fn lowerConstRecordLitInto( |
|
| 1576 | + | self: &mut Lowerer, |
|
| 1575 | 1577 | node: *ast::Node, |
|
| 1576 | 1578 | recLit: ast::RecordLit, |
|
| 1577 | 1579 | ty: resolver::Type, |
|
| 1578 | 1580 | dataPrefix: *[u8], |
|
| 1579 | - | b: *mut DataValueBuilder |
|
| 1581 | + | b: &mut DataValueBuilder |
|
| 1580 | 1582 | ) throws (LowerError) { |
|
| 1581 | 1583 | match ty { |
|
| 1582 | 1584 | case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => { |
|
| 1583 | 1585 | try lowerConstRecordCtorInto(self, recLit.fields, recInfo, dataPrefix, b); |
|
| 1584 | 1586 | } |
|
| 1585 | 1587 | case resolver::Type::Nominal(resolver::NominalType::Union(_)) => { |
|
| 1586 | 1588 | let typeName = recLit.typeName else { |
|
| 1587 | 1589 | throw LowerError::ExpectedVariant; |
|
| 1588 | 1590 | }; |
|
| 1589 | - | let sym = resolver::nodeData(self.resolver, typeName).sym else { |
|
| 1591 | + | let sym = resolver::nodeData(&*self.resolver, typeName).sym else { |
|
| 1590 | 1592 | throw LowerError::MissingSymbol(typeName); |
|
| 1591 | 1593 | }; |
|
| 1592 | 1594 | try lowerConstUnionVariantInto(self, node, sym, ty, recLit.fields, dataPrefix, b); |
|
| 1593 | 1595 | } |
|
| 1594 | 1596 | else => throw LowerError::ExpectedRecord, |
|
| 1595 | 1597 | } |
|
| 1596 | 1598 | } |
|
| 1597 | 1599 | ||
| 1598 | 1600 | /// Build data values for record constants. |
|
| 1599 | - | fn lowerConstRecordCtorInto( |
|
| 1600 | - | self: *mut Lowerer, |
|
| 1601 | + | unsafe fn lowerConstRecordCtorInto( |
|
| 1602 | + | self: &mut Lowerer, |
|
| 1601 | 1603 | args: *mut [*ast::Node], |
|
| 1602 | 1604 | recInfo: resolver::RecordType, |
|
| 1603 | 1605 | dataPrefix: *[u8], |
|
| 1604 | - | b: *mut DataValueBuilder |
|
| 1606 | + | b: &mut DataValueBuilder |
|
| 1605 | 1607 | ) throws (LowerError) { |
|
| 1606 | 1608 | let layout = recInfo.layout; |
|
| 1607 | 1609 | for argNode, i in args { |
|
| 1608 | 1610 | let mut valueNode = argNode; |
|
| 1609 | 1611 | if let case ast::NodeValue::RecordLitField(fieldLit) = argNode.value { |
| 1620 | 1622 | try lowerConstDataInto(self, valueNode, fieldInfo.fieldType, slotSize, dataPrefix, b); |
|
| 1621 | 1623 | } |
|
| 1622 | 1624 | } |
|
| 1623 | 1625 | ||
| 1624 | 1626 | /// Build data values for a constant union variant value from payload fields/args. |
|
| 1625 | - | fn lowerConstUnionVariantInto( |
|
| 1626 | - | self: *mut Lowerer, |
|
| 1627 | + | unsafe fn lowerConstUnionVariantInto( |
|
| 1628 | + | self: &mut Lowerer, |
|
| 1627 | 1629 | node: *ast::Node, |
|
| 1628 | 1630 | variantSym: *mut resolver::Symbol, |
|
| 1629 | 1631 | ty: resolver::Type, |
|
| 1630 | 1632 | payloadArgs: *mut [*ast::Node], |
|
| 1631 | 1633 | dataPrefix: *[u8], |
|
| 1632 | - | b: *mut DataValueBuilder |
|
| 1634 | + | b: &mut DataValueBuilder |
|
| 1633 | 1635 | ) throws (LowerError) { |
|
| 1634 | 1636 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = variantSym.data |
|
| 1635 | 1637 | else throw LowerError::UnexpectedNodeValue(node); |
|
| 1636 | 1638 | ||
| 1637 | 1639 | let unionInfo = unionInfoFromType(ty) else { |
| 1680 | 1682 | } |
|
| 1681 | 1683 | } |
|
| 1682 | 1684 | ||
| 1683 | 1685 | /// Find an existing string data entry with matching content. |
|
| 1684 | 1686 | // TODO: Optimize with hash table or remove? |
|
| 1685 | - | fn findStringData(self: *Lowerer, s: *[u8]) -> ?*[u8] { |
|
| 1687 | + | fn findStringData(self: &Lowerer, s: *[u8]) -> ?*[u8] { |
|
| 1686 | 1688 | for d in self.data { |
|
| 1687 | 1689 | if d.values.len == 1 { |
|
| 1688 | 1690 | if let case il::DataItem::Str(existing) = d.values[0].item { |
|
| 1689 | 1691 | if mem::eq(existing, s) { |
|
| 1690 | 1692 | return d.name; |
| 1695 | 1697 | return nil; |
|
| 1696 | 1698 | } |
|
| 1697 | 1699 | ||
| 1698 | 1700 | /// Compose a segmented symbol name from a list of path segments. |
|
| 1699 | 1701 | /// Example: `["func", "nominal", "VALUE"]` -> `func$nominal$VALUE`. |
|
| 1700 | - | fn buildSegmentedName( |
|
| 1701 | - | self: *mut Lowerer, |
|
| 1702 | - | segments: *[*[u8]] |
|
| 1702 | + | unsafe fn buildSegmentedName( |
|
| 1703 | + | self: &mut Lowerer, |
|
| 1704 | + | segments: &[*[u8]] |
|
| 1703 | 1705 | ) -> *[u8] throws (LowerError) { |
|
| 1704 | 1706 | assert segments.len > 0; |
|
| 1705 | 1707 | ||
| 1706 | 1708 | let mut totalLen: u32 = 0; |
|
| 1707 | 1709 | for segment in segments { |
|
| 1708 | 1710 | set totalLen += segment.len; |
|
| 1709 | 1711 | } |
|
| 1710 | 1712 | if segments.len > 1 { |
|
| 1711 | 1713 | set totalLen += segments.len - 1; |
|
| 1712 | 1714 | } |
|
| 1713 | - | let buf = try! alloc::allocSlice(self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1715 | + | let buf = try! alloc::allocSlice(&mut *self.arena, 1, 1, totalLen) as *mut [u8]; |
|
| 1714 | 1716 | let mut pos: u32 = 0; |
|
| 1715 | 1717 | ||
| 1716 | 1718 | for segment, i in segments { |
|
| 1717 | 1719 | set pos += try! mem::copy(&mut buf[pos..], segment); |
|
| 1718 | 1720 | if i + 1 <> segments.len { |
| 1724 | 1726 | ||
| 1725 | 1727 | return &buf[..totalLen]; |
|
| 1726 | 1728 | } |
|
| 1727 | 1729 | ||
| 1728 | 1730 | /// Generate a unique name for declaration-local backing data entries. |
|
| 1729 | - | fn nextDeclDataName( |
|
| 1730 | - | self: *mut Lowerer, |
|
| 1731 | + | unsafe fn nextDeclDataName( |
|
| 1732 | + | self: &mut Lowerer, |
|
| 1731 | 1733 | prefix: *[u8], |
|
| 1732 | 1734 | count: u32, |
|
| 1733 | 1735 | namespace: *[u8] |
|
| 1734 | 1736 | ) -> *[u8] throws (LowerError) { |
|
| 1735 | 1737 | let mut digits: [u8; fmt::U32_STR_LEN] = undefined; |
|
| 1736 | - | let suffix = fmt::formatU32(count, &mut digits[..]); |
|
| 1737 | - | let segments: *mut [*[u8]] = &mut [prefix, namespace, suffix]; |
|
| 1738 | + | let start = fmt::formatU32(count, &mut digits[..]); |
|
| 1739 | + | let suffix = try! alloc::allocSlice(&mut *self.arena, 1, 1, digits.len - start) as *mut [u8]; |
|
| 1740 | + | try! mem::copy(suffix, &digits[start..]); |
|
| 1741 | + | let segments = [prefix, namespace, suffix]; |
|
| 1738 | 1742 | ||
| 1739 | - | return try buildSegmentedName(self, segments); |
|
| 1743 | + | return try buildSegmentedName(self, &segments[..]); |
|
| 1740 | 1744 | } |
|
| 1741 | 1745 | ||
| 1742 | 1746 | /// Append a data entry using a function-local literal namespace (`prefix$literal$N`). |
|
| 1743 | - | fn pushDeclData( |
|
| 1744 | - | self: *mut Lowerer, |
|
| 1747 | + | unsafe fn pushDeclData( |
|
| 1748 | + | self: &mut Lowerer, |
|
| 1745 | 1749 | size: u32, |
|
| 1746 | 1750 | alignment: u32, |
|
| 1747 | 1751 | readOnly: bool, |
|
| 1748 | 1752 | values: *[il::DataValue], |
|
| 1749 | 1753 | dataPrefix: *[u8] |
|
| 1750 | 1754 | ) -> *[u8] throws (LowerError) { |
|
| 1751 | - | let name = try nextDeclDataName(self, dataPrefix, self.data.len, "literal"); |
|
| 1755 | + | let dataCount = self.data.len; |
|
| 1756 | + | let name = try nextDeclDataName(self, dataPrefix, dataCount, "literal"); |
|
| 1752 | 1757 | self.data.append(il::Data { |
|
| 1753 | 1758 | name, |
|
| 1754 | 1759 | size, |
|
| 1755 | 1760 | alignment, |
|
| 1756 | 1761 | readOnly, |
| 1760 | 1765 | ||
| 1761 | 1766 | return name; |
|
| 1762 | 1767 | } |
|
| 1763 | 1768 | ||
| 1764 | 1769 | /// Find or create read-only string data and return its symbol name. |
|
| 1765 | - | fn getOrCreateStringData( |
|
| 1766 | - | self: *mut Lowerer, |
|
| 1770 | + | unsafe fn getOrCreateStringData( |
|
| 1771 | + | self: &mut Lowerer, |
|
| 1767 | 1772 | s: *[u8], |
|
| 1768 | 1773 | dataPrefix: *[u8] |
|
| 1769 | 1774 | ) -> *[u8] throws (LowerError) { |
|
| 1770 | 1775 | if let existing = findStringData(self, s) { |
|
| 1771 | 1776 | return existing; |
|
| 1772 | 1777 | } |
|
| 1773 | 1778 | let values = try! alloc::allocSlice( |
|
| 1774 | - | self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1 |
|
| 1779 | + | &mut *self.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), 1 |
|
| 1775 | 1780 | ) as *mut [il::DataValue]; |
|
| 1776 | 1781 | ||
| 1777 | 1782 | set values[0] = il::DataValue { |
|
| 1778 | 1783 | item: il::DataItem::Str(s), |
|
| 1779 | 1784 | count: 1 |
| 1817 | 1822 | }, |
|
| 1818 | 1823 | } |
|
| 1819 | 1824 | } |
|
| 1820 | 1825 | ||
| 1821 | 1826 | /// Compare two data value slices for structural equality. |
|
| 1822 | - | fn dataValuesEq(a: *[il::DataValue], b: *[il::DataValue]) -> bool { |
|
| 1827 | + | fn dataValuesEq(a: &[il::DataValue], b: &[il::DataValue]) -> bool { |
|
| 1823 | 1828 | if a.len <> b.len { |
|
| 1824 | 1829 | return false; |
|
| 1825 | 1830 | } |
|
| 1826 | 1831 | for i in 0..a.len { |
|
| 1827 | 1832 | if a[i].count <> b[i].count { |
| 1834 | 1839 | return true; |
|
| 1835 | 1840 | } |
|
| 1836 | 1841 | ||
| 1837 | 1842 | /// Find an existing read-only slice data entry with matching values. |
|
| 1838 | 1843 | // TODO: Optimize with hash table or remove? |
|
| 1839 | - | fn findSliceData(self: *Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] { |
|
| 1844 | + | fn findSliceData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] { |
|
| 1840 | 1845 | for d in self.data { |
|
| 1841 | 1846 | if d.alignment == alignment and d.readOnly and dataValuesEq(d.values, values) { |
|
| 1842 | 1847 | return d.name; |
|
| 1843 | 1848 | } |
|
| 1844 | 1849 | } |
|
| 1845 | 1850 | return nil; |
|
| 1846 | 1851 | } |
|
| 1847 | 1852 | ||
| 1848 | 1853 | /// Find existing constant data entry with matching content. |
|
| 1849 | 1854 | /// Handles both string data and slice data. |
|
| 1850 | - | fn findConstData(self: *Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] { |
|
| 1855 | + | fn findConstData(self: &Lowerer, values: *[il::DataValue], alignment: u32) -> ?*[u8] { |
|
| 1851 | 1856 | // Fast path for strings. |
|
| 1852 | 1857 | if values.len == 1 and alignment == 1 { |
|
| 1853 | 1858 | if let case il::DataItem::Str(s) = values[0].item { |
|
| 1854 | 1859 | return findStringData(self, s); |
|
| 1855 | 1860 | } |
| 1858 | 1863 | return findSliceData(self, values, alignment); |
|
| 1859 | 1864 | } |
|
| 1860 | 1865 | ||
| 1861 | 1866 | /// Lower constant data to a slice value. |
|
| 1862 | 1867 | /// Creates or reuses a data section entry, then builds a slice header on the stack. |
|
| 1863 | - | fn lowerConstDataAsSlice( |
|
| 1864 | - | self: *mut FnLowerer, |
|
| 1865 | - | result: *ConstDataResult, |
|
| 1868 | + | unsafe fn lowerConstDataAsSlice( |
|
| 1869 | + | self: &mut FnLowerer, |
|
| 1870 | + | result: &ConstDataResult, |
|
| 1866 | 1871 | alignment: u32, |
|
| 1867 | 1872 | readOnly: bool, |
|
| 1868 | 1873 | elemTy: *resolver::Type, |
|
| 1869 | 1874 | mutable: bool, |
|
| 1870 | 1875 | length: u32 |
| 1873 | 1878 | let elemLayout = resolver::getTypeLayout(*elemTy); |
|
| 1874 | 1879 | let size = elemLayout.size * length; |
|
| 1875 | 1880 | let mut dataName: *[u8] = undefined; |
|
| 1876 | 1881 | let mut found: ?*[u8] = nil; |
|
| 1877 | 1882 | if readOnly { |
|
| 1878 | - | set found = findConstData(self.low, values, alignment); |
|
| 1883 | + | set found = findConstData(&mut *self.low, values, alignment); |
|
| 1879 | 1884 | } |
|
| 1880 | 1885 | if let name = found { |
|
| 1881 | 1886 | set dataName = name; |
|
| 1882 | 1887 | } else { |
|
| 1883 | 1888 | set dataName = try nextDataName(self); |
| 1899 | 1904 | self, elemTy, mutable, il::Val::Reg(ptrReg), il::Val::Imm(length as i64), il::Val::Imm(length as i64) |
|
| 1900 | 1905 | ); |
|
| 1901 | 1906 | } |
|
| 1902 | 1907 | ||
| 1903 | 1908 | /// Generate a unique data name for inline literals, eg. `fnName$literal$N`. |
|
| 1904 | - | fn nextDataName(self: *mut FnLowerer) -> *[u8] throws (LowerError) { |
|
| 1909 | + | unsafe fn nextDataName(self: &mut FnLowerer) -> *[u8] throws (LowerError) { |
|
| 1905 | 1910 | let counter = self.dataCounter; |
|
| 1906 | 1911 | set self.dataCounter += 1; |
|
| 1907 | - | return try nextDeclDataName(self.low, self.fnName, counter, "literal"); |
|
| 1912 | + | let fnName = self.fnName; |
|
| 1913 | + | return try nextDeclDataName(&mut *self.low, fnName, counter, "literal"); |
|
| 1908 | 1914 | } |
|
| 1909 | 1915 | ||
| 1910 | 1916 | /// Assign a unique function-local data symbol name. |
|
| 1911 | - | fn registerLocalDataDeclName(self: *mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 1912 | - | let sym = resolver::nodeData(self.low.resolver, node).sym |
|
| 1917 | + | unsafe fn registerLocalDataDeclName(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 1918 | + | let sym = resolver::nodeData(&*self.low.resolver, node).sym |
|
| 1913 | 1919 | else throw LowerError::MissingSymbol(node); |
|
| 1914 | 1920 | ||
| 1915 | 1921 | let prefix = self.fnName; |
|
| 1916 | - | let segments: *mut [*[u8]] = &mut [prefix, "nominal", sym.name]; |
|
| 1917 | - | let name = try buildSegmentedName(self.low, segments); |
|
| 1922 | + | let segments = [prefix, "nominal", sym.name]; |
|
| 1923 | + | let name = try buildSegmentedName(&mut *self.low, &segments[..]); |
|
| 1918 | 1924 | ||
| 1919 | 1925 | set sym.name = name; |
|
| 1920 | 1926 | } |
|
| 1921 | 1927 | ||
| 1922 | 1928 | /// Get the next available SSA register. |
|
| 1923 | - | fn nextReg(self: *mut FnLowerer) -> il::Reg { |
|
| 1929 | + | fn nextReg(self: &mut FnLowerer) -> il::Reg { |
|
| 1924 | 1930 | let reg = il::Reg { n: self.regCounter }; |
|
| 1925 | 1931 | set self.regCounter += 1; |
|
| 1926 | 1932 | return reg; |
|
| 1927 | 1933 | } |
|
| 1928 | 1934 | ||
| 1929 | 1935 | /// Look up the resolved type of an AST node, or throw `MissingType`. |
|
| 1930 | - | fn typeOf(self: *mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) { |
|
| 1931 | - | let ty = resolver::typeFor(self.low.resolver, node) |
|
| 1936 | + | unsafe fn typeOf(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) { |
|
| 1937 | + | let ty = resolver::typeFor(&*self.low.resolver, node) |
|
| 1932 | 1938 | else throw LowerError::MissingType(node); |
|
| 1933 | 1939 | return ty; |
|
| 1934 | 1940 | } |
|
| 1935 | 1941 | ||
| 1936 | 1942 | /// Look up the symbol for an AST node, or throw `MissingSymbol`. |
|
| 1937 | - | fn symOf(self: *mut FnLowerer, node: *ast::Node) -> *mut resolver::Symbol throws (LowerError) { |
|
| 1938 | - | let sym = resolver::nodeData(self.low.resolver, node).sym |
|
| 1943 | + | unsafe fn symOf(self: &mut FnLowerer, node: *ast::Node) -> *mut resolver::Symbol throws (LowerError) { |
|
| 1944 | + | let sym = resolver::nodeData(&*self.low.resolver, node).sym |
|
| 1939 | 1945 | else throw LowerError::MissingSymbol(node); |
|
| 1940 | 1946 | return sym; |
|
| 1941 | 1947 | } |
|
| 1942 | 1948 | ||
| 1943 | 1949 | /// Remove the last block parameter and its associated variable. |
|
| 1944 | 1950 | /// Used when detecting a trivial phi that can be eliminated. |
|
| 1945 | - | fn removeLastBlockParam(self: *mut FnLowerer, block: BlockId) { |
|
| 1951 | + | fn removeLastBlockParam(self: &mut FnLowerer, block: BlockId) { |
|
| 1946 | 1952 | let blk = getBlockMut(self, block); |
|
| 1947 | 1953 | if blk.params.len > 0 { |
|
| 1948 | 1954 | // TODO: Use `pop`? |
|
| 1949 | 1955 | set blk.params = @sliceOf(blk.params.ptr, blk.params.len - 1, blk.params.cap); |
|
| 1950 | 1956 | } |
| 1957 | 1963 | /// Rewrite cached SSA values for a variable across all blocks, and also |
|
| 1958 | 1964 | /// rewrite any terminator arguments that reference the provisional register. |
|
| 1959 | 1965 | /// The latter is necessary because recursive SSA resolution may have already |
|
| 1960 | 1966 | /// patched terminator arguments with the provisional value before it was |
|
| 1961 | 1967 | /// found to be trivial. |
|
| 1962 | - | fn rewriteCachedVarValue(self: *mut FnLowerer, v: Var, from: il::Val, to: il::Val) { |
|
| 1968 | + | fn rewriteCachedVarValue(self: &mut FnLowerer, v: Var, from: il::Val, to: il::Val) { |
|
| 1963 | 1969 | for i in 0..self.blockData.len { |
|
| 1964 | 1970 | let blk = getBlockMut(self, BlockId(i)); |
|
| 1965 | 1971 | if blk.vars[*v] == from { |
|
| 1966 | 1972 | set blk.vars[*v] = to; |
|
| 1967 | 1973 | } |
| 2014 | 2020 | /// Create a new basic block with the given label base. |
|
| 2015 | 2021 | /// |
|
| 2016 | 2022 | /// The block is initially unsealed (predecessors may be added later) and empty. |
|
| 2017 | 2023 | /// Returns a [`BlockId`] that can be used for jumps and branches. The block must |
|
| 2018 | 2024 | /// be switched to via [`switchToBlock`] before instructions can be emitted. |
|
| 2019 | - | fn createBlock(self: *mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) { |
|
| 2025 | + | unsafe fn createBlock(self: &mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) { |
|
| 2020 | 2026 | let label = try nextLabel(self, labelBase); |
|
| 2021 | 2027 | let id = BlockId(self.blockData.len); |
|
| 2022 | 2028 | let varCount = self.fnType.localCount; |
|
| 2023 | - | let vars = try! alloc::allocSlice(self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val]; |
|
| 2029 | + | let vars = try! alloc::allocSlice(&mut *self.low.fnArena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val]; |
|
| 2024 | 2030 | ||
| 2025 | 2031 | for i in 0..varCount { |
|
| 2026 | 2032 | set vars[i] = nil; |
|
| 2027 | 2033 | } |
|
| 2028 | 2034 | self.blockData.append(BlockData { |
| 2039 | 2045 | ||
| 2040 | 2046 | return id; |
|
| 2041 | 2047 | } |
|
| 2042 | 2048 | ||
| 2043 | 2049 | /// Create a new block with a single parameter. |
|
| 2044 | - | fn createBlockWithParam( |
|
| 2045 | - | self: *mut FnLowerer, |
|
| 2050 | + | unsafe fn createBlockWithParam( |
|
| 2051 | + | self: &mut FnLowerer, |
|
| 2046 | 2052 | labelBase: *[u8], |
|
| 2047 | 2053 | param: il::Param |
|
| 2048 | 2054 | ) -> BlockId throws (LowerError) { |
|
| 2049 | 2055 | let block = try createBlock(self, labelBase); |
|
| 2050 | 2056 | let blk = getBlockMut(self, block); |
| 2053 | 2059 | return block; |
|
| 2054 | 2060 | } |
|
| 2055 | 2061 | ||
| 2056 | 2062 | /// Switch to building a different block. |
|
| 2057 | 2063 | /// All subsequent `emit` calls will add instructions to this block. |
|
| 2058 | - | fn switchToBlock(self: *mut FnLowerer, block: BlockId) { |
|
| 2064 | + | fn switchToBlock(self: &mut FnLowerer, block: BlockId) { |
|
| 2059 | 2065 | set self.currentBlock = block; |
|
| 2060 | 2066 | } |
|
| 2061 | 2067 | ||
| 2062 | 2068 | /// Seal a block, indicating all predecessor edges are now known. |
|
| 2063 | 2069 | /// |
|
| 2064 | 2070 | /// Sealing enables SSA construction to resolve variable uses by looking up |
|
| 2065 | 2071 | /// values from predecessors and inserting block parameters as needed. It |
|
| 2066 | 2072 | /// does not prevent instructions from being added to the block. |
|
| 2067 | - | fn sealBlock(self: *mut FnLowerer, block: BlockId) throws (LowerError) { |
|
| 2073 | + | unsafe fn sealBlock(self: &mut FnLowerer, block: BlockId) throws (LowerError) { |
|
| 2068 | 2074 | let blk = getBlockMut(self, block); |
|
| 2069 | 2075 | let case Sealed::No = blk.sealState else { |
|
| 2070 | 2076 | return; // Already sealed. |
|
| 2071 | 2077 | }; |
|
| 2072 | 2078 | // Keep the current parameter list. Resolution can add more parameters. |
| 2078 | 2084 | try resolveBlockArgs(self, block, Var(varId), paramIdx); |
|
| 2079 | 2085 | } |
|
| 2080 | 2086 | } |
|
| 2081 | 2087 | ||
| 2082 | 2088 | /// Seal a block and switch to it. |
|
| 2083 | - | fn switchToAndSeal(self: *mut FnLowerer, block: BlockId) throws (LowerError) { |
|
| 2089 | + | unsafe fn switchToAndSeal(self: &mut FnLowerer, block: BlockId) throws (LowerError) { |
|
| 2084 | 2090 | try sealBlock(self, block); |
|
| 2085 | 2091 | switchToBlock(self, block); |
|
| 2086 | 2092 | } |
|
| 2087 | 2093 | ||
| 2088 | 2094 | /// Get block data by block id. |
|
| 2089 | - | fn getBlock(self: *FnLowerer, block: BlockId) -> *BlockData { |
|
| 2095 | + | fn getBlock(self: &FnLowerer, block: BlockId) -> *BlockData { |
|
| 2090 | 2096 | return &self.blockData[*block]; |
|
| 2091 | 2097 | } |
|
| 2092 | 2098 | ||
| 2093 | 2099 | /// Get mutable block data by block id. |
|
| 2094 | - | fn getBlockMut(self: *mut FnLowerer, block: BlockId) -> *mut BlockData { |
|
| 2100 | + | fn getBlockMut(self: &mut FnLowerer, block: BlockId) -> *mut BlockData { |
|
| 2095 | 2101 | return &mut self.blockData[*block]; |
|
| 2096 | 2102 | } |
|
| 2097 | 2103 | ||
| 2098 | 2104 | /// Get the current block being built. |
|
| 2099 | - | fn currentBlock(self: *FnLowerer) -> BlockId { |
|
| 2105 | + | fn currentBlock(self: &FnLowerer) -> BlockId { |
|
| 2100 | 2106 | let block = self.currentBlock else { |
|
| 2101 | 2107 | panic "currentBlock: no current block"; |
|
| 2102 | 2108 | }; |
|
| 2103 | 2109 | return block; |
|
| 2104 | 2110 | } |
| 2106 | 2112 | ////////////////////////// |
|
| 2107 | 2113 | // Instruction Emission // |
|
| 2108 | 2114 | ////////////////////////// |
|
| 2109 | 2115 | ||
| 2110 | 2116 | /// Emit an instruction to the current block. |
|
| 2111 | - | fn emit(self: *mut FnLowerer, instr: il::Instr) { |
|
| 2117 | + | unsafe fn emit(self: &mut FnLowerer, instr: il::Instr) { |
|
| 2112 | 2118 | let blk = self.currentBlock else panic; |
|
| 2113 | 2119 | let mut block = getBlockMut(self, blk); |
|
| 2114 | 2120 | ||
| 2115 | 2121 | // Track whether this function is a leaf. |
|
| 2116 | 2122 | if self.isLeaf { |
| 2126 | 2132 | } |
|
| 2127 | 2133 | block.instrs.append(instr, self.allocator); |
|
| 2128 | 2134 | } |
|
| 2129 | 2135 | ||
| 2130 | 2136 | /// Emit an unconditional jump to `target`. |
|
| 2131 | - | fn emitJmp(self: *mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2137 | + | unsafe fn emitJmp(self: &mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2132 | 2138 | emit(self, il::Instr::Jmp { target: *target, args: &mut [] }); |
|
| 2133 | 2139 | addPredecessor(self, target, currentBlock(self)); |
|
| 2134 | 2140 | } |
|
| 2135 | 2141 | ||
| 2136 | 2142 | /// Emit an unconditional jump to `target` with a single argument. |
|
| 2137 | - | fn emitJmpWithArg(self: *mut FnLowerer, target: BlockId, arg: il::Val) throws (LowerError) { |
|
| 2143 | + | unsafe fn emitJmpWithArg(self: &mut FnLowerer, target: BlockId, arg: il::Val) throws (LowerError) { |
|
| 2138 | 2144 | let args = try allocVal(self, arg); |
|
| 2139 | 2145 | emit(self, il::Instr::Jmp { target: *target, args }); |
|
| 2140 | 2146 | addPredecessor(self, target, currentBlock(self)); |
|
| 2141 | 2147 | } |
|
| 2142 | 2148 | ||
| 2143 | 2149 | /// Emit an unconditional jump to `target` and switch to it. |
|
| 2144 | - | fn switchAndJumpTo(self: *mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2150 | + | unsafe fn switchAndJumpTo(self: &mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2145 | 2151 | try emitJmp(self, target); |
|
| 2146 | 2152 | switchToBlock(self, target); |
|
| 2147 | 2153 | } |
|
| 2148 | 2154 | ||
| 2149 | 2155 | /// Emit a conditional branch based on `cond`. |
|
| 2150 | - | fn emitBr(self: *mut FnLowerer, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) { |
|
| 2156 | + | unsafe fn emitBr(self: &mut FnLowerer, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) { |
|
| 2151 | 2157 | assert thenBlock <> elseBlock; |
|
| 2152 | 2158 | emit(self, il::Instr::Br { |
|
| 2153 | 2159 | op: il::CmpOp::Ne, |
|
| 2154 | 2160 | typ: il::Type::W32, |
|
| 2155 | 2161 | a: il::Val::Reg(cond), |
| 2162 | 2168 | addPredecessor(self, thenBlock, currentBlock(self)); |
|
| 2163 | 2169 | addPredecessor(self, elseBlock, currentBlock(self)); |
|
| 2164 | 2170 | } |
|
| 2165 | 2171 | ||
| 2166 | 2172 | /// Emit a compare-and-branch instruction with the given comparison op. |
|
| 2167 | - | fn emitBrCmp( |
|
| 2168 | - | self: *mut FnLowerer, |
|
| 2173 | + | unsafe fn emitBrCmp( |
|
| 2174 | + | self: &mut FnLowerer, |
|
| 2169 | 2175 | op: il::CmpOp, |
|
| 2170 | 2176 | typ: il::Type, |
|
| 2171 | 2177 | a: il::Val, |
|
| 2172 | 2178 | b: il::Val, |
|
| 2173 | 2179 | thenBlock: BlockId, |
| 2182 | 2188 | addPredecessor(self, thenBlock, currentBlock(self)); |
|
| 2183 | 2189 | addPredecessor(self, elseBlock, currentBlock(self)); |
|
| 2184 | 2190 | } |
|
| 2185 | 2191 | ||
| 2186 | 2192 | /// Emit a guard that traps with `ebreak` when a comparison is false. |
|
| 2187 | - | fn emitTrapUnlessCmp( |
|
| 2188 | - | self: *mut FnLowerer, |
|
| 2193 | + | unsafe fn emitTrapUnlessCmp( |
|
| 2194 | + | self: &mut FnLowerer, |
|
| 2189 | 2195 | op: il::CmpOp, |
|
| 2190 | 2196 | typ: il::Type, |
|
| 2191 | 2197 | a: il::Val, |
|
| 2192 | 2198 | b: il::Val |
|
| 2193 | 2199 | ) throws (LowerError) { |
| 2222 | 2228 | } |
|
| 2223 | 2229 | return aa <= bb; |
|
| 2224 | 2230 | } |
|
| 2225 | 2231 | ||
| 2226 | 2232 | /// Emit an `ebreak` when `a < b` holds. |
|
| 2227 | - | fn emitTrapIfLt( |
|
| 2228 | - | self: *mut FnLowerer, |
|
| 2233 | + | unsafe fn emitTrapIfLt( |
|
| 2234 | + | self: &mut FnLowerer, |
|
| 2229 | 2235 | typ: il::Type, |
|
| 2230 | 2236 | a: il::Val, |
|
| 2231 | 2237 | b: il::Val |
|
| 2232 | 2238 | ) throws (LowerError) { |
|
| 2233 | 2239 | let trapBlock = try createBlock(self, "guard#trap"); |
| 2242 | 2248 | try switchToAndSeal(self, passBlock); |
|
| 2243 | 2249 | } |
|
| 2244 | 2250 | ||
| 2245 | 2251 | /// Emit a conditional branch. Uses fused compare-and-branch for simple scalar |
|
| 2246 | 2252 | /// comparisons, falls back to separate comparison plus branch otherwise. |
|
| 2247 | - | fn emitCondBranch( |
|
| 2248 | - | self: *mut FnLowerer, |
|
| 2253 | + | unsafe fn emitCondBranch( |
|
| 2254 | + | self: &mut FnLowerer, |
|
| 2249 | 2255 | cond: *ast::Node, |
|
| 2250 | 2256 | thenBlock: BlockId, |
|
| 2251 | 2257 | elseBlock: BlockId |
|
| 2252 | 2258 | ) throws (LowerError) { |
|
| 2253 | 2259 | // Try fused compare-and-branch for simple scalar comparisons. |
| 2258 | 2264 | let operandTy = scalarComparisonType(leftTy, rightTy); |
|
| 2259 | 2265 | let unsigned = isUnsignedType(operandTy); |
|
| 2260 | 2266 | if let op = cmpOpFrom(binop.op, unsigned) { |
|
| 2261 | 2267 | let a = try lowerExpr(self, binop.left); |
|
| 2262 | 2268 | let b = try lowerExpr(self, binop.right); |
|
| 2263 | - | let typ = ilType(self.low, operandTy); |
|
| 2269 | + | let typ = ilType(&mut *self.low, operandTy); |
|
| 2264 | 2270 | ||
| 2265 | 2271 | // Swap operands if needed. |
|
| 2266 | 2272 | match binop.op { |
|
| 2267 | 2273 | case ast::BinaryOp::Gt => // `a > b` = `b < a` |
|
| 2268 | 2274 | try emitBrCmp(self, op, typ, b, a, thenBlock, elseBlock), |
| 2283 | 2289 | ||
| 2284 | 2290 | try emitBr(self, condReg, thenBlock, elseBlock); |
|
| 2285 | 2291 | } |
|
| 2286 | 2292 | ||
| 2287 | 2293 | /// Emit a 32-bit store instruction at the given offset. |
|
| 2288 | - | fn emitStoreW32At(self: *mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2294 | + | unsafe fn emitStoreW32At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2289 | 2295 | emit(self, il::Instr::Store { typ: il::Type::W32, src, dst, offset }); |
|
| 2290 | 2296 | } |
|
| 2291 | 2297 | ||
| 2292 | 2298 | /// Emit a 32-bit load instruction at the given offset. |
|
| 2293 | - | fn emitLoadW32At(self: *mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2299 | + | unsafe fn emitLoadW32At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2294 | 2300 | emit(self, il::Instr::Load { typ: il::Type::W32, dst, src, offset }); |
|
| 2295 | 2301 | } |
|
| 2296 | 2302 | ||
| 2297 | 2303 | /// Emit an 8-bit store instruction at the given offset. |
|
| 2298 | - | fn emitStoreW8At(self: *mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2304 | + | unsafe fn emitStoreW8At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2299 | 2305 | emit(self, il::Instr::Store { typ: il::Type::W8, src, dst, offset }); |
|
| 2300 | 2306 | } |
|
| 2301 | 2307 | ||
| 2302 | 2308 | /// Emit an 8-bit load instruction at the given offset. |
|
| 2303 | - | fn emitLoadW8At(self: *mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2309 | + | unsafe fn emitLoadW8At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2304 | 2310 | emit(self, il::Instr::Load { typ: il::Type::W8, dst, src, offset }); |
|
| 2305 | 2311 | } |
|
| 2306 | 2312 | ||
| 2307 | 2313 | /// Emit a 64-bit store instruction at the given offset. |
|
| 2308 | - | fn emitStoreW64At(self: *mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2314 | + | unsafe fn emitStoreW64At(self: &mut FnLowerer, src: il::Val, dst: il::Reg, offset: i32) { |
|
| 2309 | 2315 | emit(self, il::Instr::Store { typ: il::Type::W64, src, dst, offset }); |
|
| 2310 | 2316 | } |
|
| 2311 | 2317 | ||
| 2312 | 2318 | /// Emit a 64-bit load instruction at the given offset. |
|
| 2313 | - | fn emitLoadW64At(self: *mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2319 | + | unsafe fn emitLoadW64At(self: &mut FnLowerer, dst: il::Reg, src: il::Reg, offset: i32) { |
|
| 2314 | 2320 | emit(self, il::Instr::Load { typ: il::Type::W64, dst, src, offset }); |
|
| 2315 | 2321 | } |
|
| 2316 | 2322 | ||
| 2317 | 2323 | /// Load a tag from memory at `src` plus `offset` with the given IL type. |
|
| 2318 | - | fn loadTag(self: *mut FnLowerer, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val { |
|
| 2324 | + | unsafe fn loadTag(self: &mut FnLowerer, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val { |
|
| 2319 | 2325 | let dst = nextReg(self); |
|
| 2320 | 2326 | emit(self, il::Instr::Load { typ: tagType, dst, src, offset }); |
|
| 2321 | 2327 | return il::Val::Reg(dst); |
|
| 2322 | 2328 | } |
|
| 2323 | 2329 | ||
| 2324 | 2330 | /// Load the data pointer from a slice value. |
|
| 2325 | - | fn loadSlicePtr(self: *mut FnLowerer, sliceReg: il::Reg) -> il::Reg { |
|
| 2331 | + | unsafe fn loadSlicePtr(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Reg { |
|
| 2326 | 2332 | let ptrReg = nextReg(self); |
|
| 2327 | 2333 | emitLoadW64At(self, ptrReg, sliceReg, SLICE_PTR_OFFSET); |
|
| 2328 | 2334 | return ptrReg; |
|
| 2329 | 2335 | } |
|
| 2330 | 2336 | ||
| 2331 | 2337 | /// Load the length from a slice value. |
|
| 2332 | - | fn loadSliceLen(self: *mut FnLowerer, sliceReg: il::Reg) -> il::Val { |
|
| 2338 | + | unsafe fn loadSliceLen(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Val { |
|
| 2333 | 2339 | let lenReg = nextReg(self); |
|
| 2334 | 2340 | emitLoadW32At(self, lenReg, sliceReg, SLICE_LEN_OFFSET); |
|
| 2335 | 2341 | return il::Val::Reg(lenReg); |
|
| 2336 | 2342 | } |
|
| 2337 | 2343 | ||
| 2338 | 2344 | /// Load the capacity from a slice value. |
|
| 2339 | - | fn loadSliceCap(self: *mut FnLowerer, sliceReg: il::Reg) -> il::Val { |
|
| 2345 | + | unsafe fn loadSliceCap(self: &mut FnLowerer, sliceReg: il::Reg) -> il::Val { |
|
| 2340 | 2346 | let capReg = nextReg(self); |
|
| 2341 | 2347 | emitLoadW32At(self, capReg, sliceReg, SLICE_CAP_OFFSET); |
|
| 2342 | 2348 | return il::Val::Reg(capReg); |
|
| 2343 | 2349 | } |
|
| 2344 | 2350 | ||
| 2345 | 2351 | /// Emit a load instruction for a scalar value at `src` plus `offset`. |
|
| 2346 | 2352 | /// For reading values that may be aggregates, use `emitRead` instead. |
|
| 2347 | - | fn emitLoad(self: *mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val { |
|
| 2353 | + | unsafe fn emitLoad(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val { |
|
| 2348 | 2354 | let dst = nextReg(self); |
|
| 2349 | - | let ilTyp = ilType(self.low, typ); |
|
| 2355 | + | let ilTyp = ilType(&mut *self.low, typ); |
|
| 2350 | 2356 | ||
| 2351 | 2357 | if isSignedType(typ) { |
|
| 2352 | 2358 | emit(self, il::Instr::Sload { typ: ilTyp, dst, src, offset }); |
|
| 2353 | 2359 | } else { |
|
| 2354 | 2360 | emit(self, il::Instr::Load { typ: ilTyp, dst, src, offset }); |
| 2356 | 2362 | return il::Val::Reg(dst); |
|
| 2357 | 2363 | } |
|
| 2358 | 2364 | ||
| 2359 | 2365 | /// Read a value from memory at `src` plus `offset`. Aggregates are represented |
|
| 2360 | 2366 | /// as pointers, so we return the address directly. Scalars are loaded via [`emitLoad`]. |
|
| 2361 | - | fn emitRead(self: *mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val { |
|
| 2367 | + | unsafe fn emitRead(self: &mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val { |
|
| 2362 | 2368 | if isAggregateType(typ) { |
|
| 2363 | 2369 | let ptr = emitPtrOffset(self, src, offset); |
|
| 2364 | 2370 | return il::Val::Reg(ptr); |
|
| 2365 | 2371 | } |
|
| 2366 | 2372 | return emitLoad(self, src, offset, typ); |
|
| 2367 | 2373 | } |
|
| 2368 | 2374 | ||
| 2369 | 2375 | /// Emit a copy instruction that loads a data symbol's address into a register. |
|
| 2370 | - | fn emitDataAddr(self: *mut FnLowerer, sym: *resolver::Symbol) -> il::Reg { |
|
| 2376 | + | unsafe fn emitDataAddr(self: &mut FnLowerer, sym: *resolver::Symbol) -> il::Reg { |
|
| 2371 | 2377 | let dst = nextReg(self); |
|
| 2372 | - | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym); |
|
| 2373 | - | let qualName = qualifyName(self.low, modId, sym.name); |
|
| 2378 | + | let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym); |
|
| 2379 | + | let qualName = qualifyName(&mut *self.low, modId, sym.name); |
|
| 2374 | 2380 | ||
| 2375 | 2381 | emit(self, il::Instr::Copy { dst, val: il::Val::DataSym(qualName) }); |
|
| 2376 | 2382 | ||
| 2377 | 2383 | return dst; |
|
| 2378 | 2384 | } |
|
| 2379 | 2385 | ||
| 2380 | 2386 | /// Emit a copy instruction that loads a function's address into a register. |
|
| 2381 | - | fn emitFnAddr(self: *mut FnLowerer, sym: *resolver::Symbol) -> il::Reg { |
|
| 2387 | + | unsafe fn emitFnAddr(self: &mut FnLowerer, sym: *resolver::Symbol) -> il::Reg { |
|
| 2382 | 2388 | let dst = nextReg(self); |
|
| 2383 | - | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym); |
|
| 2384 | - | let qualName = qualifyName(self.low, modId, sym.name); |
|
| 2389 | + | let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym); |
|
| 2390 | + | let qualName = qualifyName(&mut *self.low, modId, sym.name); |
|
| 2385 | 2391 | ||
| 2386 | 2392 | emit(self, il::Instr::Copy { dst, val: il::Val::FnAddr(qualName) }); |
|
| 2387 | 2393 | ||
| 2388 | 2394 | return dst; |
|
| 2389 | 2395 | } |
|
| 2390 | 2396 | ||
| 2391 | 2397 | /// Emit pattern tests for a single pattern. |
|
| 2392 | - | fn emitPatternMatch( |
|
| 2393 | - | self: *mut FnLowerer, |
|
| 2394 | - | subject: *MatchSubject, |
|
| 2398 | + | unsafe fn emitPatternMatch( |
|
| 2399 | + | self: &mut FnLowerer, |
|
| 2400 | + | subject: &MatchSubject, |
|
| 2395 | 2401 | pattern: *ast::Node, |
|
| 2396 | 2402 | matchBlock: BlockId, |
|
| 2397 | 2403 | fallthrough: BlockId |
|
| 2398 | 2404 | ) throws (LowerError) { |
|
| 2399 | 2405 | // Wildcards always match; array patterns are tested element-by-element |
| 2433 | 2439 | } |
|
| 2434 | 2440 | case MatchSubjectKind::Union(unionInfo) => { |
|
| 2435 | 2441 | assert not isNil; |
|
| 2436 | 2442 | ||
| 2437 | 2443 | let case resolver::NodeExtra::UnionVariant { tag: variantTag, .. } = |
|
| 2438 | - | resolver::nodeData(self.low.resolver, pattern).extra |
|
| 2444 | + | resolver::nodeData(&*self.low.resolver, pattern).extra |
|
| 2439 | 2445 | else { |
|
| 2440 | 2446 | throw LowerError::ExpectedVariant; |
|
| 2441 | 2447 | }; |
|
| 2442 | 2448 | // Void unions are passed by value (the tag itself). |
|
| 2443 | 2449 | // Non-void unions are passed by reference (need to load tag). |
| 2479 | 2485 | } |
|
| 2480 | 2486 | ||
| 2481 | 2487 | /// Emit branches for multiple patterns. The first pattern that matches |
|
| 2482 | 2488 | /// causes a jump to the match block. If no patterns match, we jump to the |
|
| 2483 | 2489 | /// fallthrough block. |
|
| 2484 | - | fn emitPatternMatches( |
|
| 2485 | - | self: *mut FnLowerer, |
|
| 2486 | - | subject: *MatchSubject, |
|
| 2487 | - | patterns: *mut [*ast::Node], |
|
| 2490 | + | unsafe fn emitPatternMatches( |
|
| 2491 | + | self: &mut FnLowerer, |
|
| 2492 | + | subject: &MatchSubject, |
|
| 2493 | + | patterns: &[*ast::Node], |
|
| 2488 | 2494 | matchBlock: BlockId, |
|
| 2489 | 2495 | fallthrough: BlockId |
|
| 2490 | 2496 | ) throws (LowerError) { |
|
| 2491 | 2497 | assert patterns.len > 0; |
|
| 2492 | 2498 |
| 2508 | 2514 | ||
| 2509 | 2515 | /// Emit a match binding pattern. |
|
| 2510 | 2516 | /// Binding patterns always match for regular values, but for optionals they |
|
| 2511 | 2517 | /// check for the presence of a value. Jumps to `valuePresent` on success, |
|
| 2512 | 2518 | /// `valueAbsent` on failure. |
|
| 2513 | - | fn emitBindingTest( |
|
| 2514 | - | self: *mut FnLowerer, |
|
| 2515 | - | subject: *MatchSubject, |
|
| 2519 | + | unsafe fn emitBindingTest( |
|
| 2520 | + | self: &mut FnLowerer, |
|
| 2521 | + | subject: &MatchSubject, |
|
| 2516 | 2522 | valuePresent: BlockId, |
|
| 2517 | 2523 | valueAbsent: BlockId |
|
| 2518 | 2524 | ) throws (LowerError) { |
|
| 2519 | 2525 | match subject.kind { |
|
| 2520 | 2526 | case MatchSubjectKind::OptionalPtr, MatchSubjectKind::OptionalAggregate => { |
| 2527 | 2533 | } |
|
| 2528 | 2534 | } |
|
| 2529 | 2535 | } |
|
| 2530 | 2536 | ||
| 2531 | 2537 | /// Emit a jump to target if the current block hasn't terminated, then seal the target block. |
|
| 2532 | - | fn emitJmpAndSeal(self: *mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2538 | + | unsafe fn emitJmpAndSeal(self: &mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 2533 | 2539 | if not blockHasTerminator(self) { |
|
| 2534 | 2540 | try emitJmp(self, target); |
|
| 2535 | 2541 | } |
|
| 2536 | 2542 | try sealBlock(self, target); |
|
| 2537 | 2543 | } |
|
| 2538 | 2544 | ||
| 2539 | 2545 | /// Check if the current block already has a terminator instruction. |
|
| 2540 | - | fn blockHasTerminator(self: *FnLowerer) -> bool { |
|
| 2546 | + | fn blockHasTerminator(self: &FnLowerer) -> bool { |
|
| 2541 | 2547 | let blk = getBlock(self, currentBlock(self)); |
|
| 2542 | 2548 | if blk.instrs.len == 0 { |
|
| 2543 | 2549 | return false; |
|
| 2544 | 2550 | } |
|
| 2545 | 2551 | match blk.instrs[blk.instrs.len - 1] { |
| 2567 | 2573 | /// return 0; // @else diverges, no jump to merge. |
|
| 2568 | 2574 | /// } |
|
| 2569 | 2575 | /// |
|
| 2570 | 2576 | /// In the above example, the merge block stays `nil`, and no code is generated |
|
| 2571 | 2577 | /// after the `if`. The merge block is created on first use. |
|
| 2572 | - | fn emitMergeIfUnterminated(self: *mut FnLowerer, mergeBlock: *mut ?BlockId) throws (LowerError) { |
|
| 2578 | + | unsafe fn emitMergeIfUnterminated(self: &mut FnLowerer, mergeBlock: &mut ?BlockId) throws (LowerError) { |
|
| 2573 | 2579 | if not blockHasTerminator(self) { |
|
| 2574 | 2580 | if *mergeBlock == nil { |
|
| 2575 | 2581 | set *mergeBlock = try createBlock(self, "merge"); |
|
| 2576 | 2582 | } |
|
| 2577 | 2583 | let target = *mergeBlock else { throw LowerError::MissingTarget; }; |
| 2583 | 2589 | // Control Flow Edge Management // |
|
| 2584 | 2590 | ////////////////////////////////// |
|
| 2585 | 2591 | ||
| 2586 | 2592 | /// Add a predecessor edge from `pred` to `target`. |
|
| 2587 | 2593 | /// Must be called before the target block is sealed. Duplicates are ignored. |
|
| 2588 | - | fn addPredecessor(self: *mut FnLowerer, target: BlockId, pred: BlockId) { |
|
| 2594 | + | fn addPredecessor(self: &mut FnLowerer, target: BlockId, pred: BlockId) { |
|
| 2589 | 2595 | let blk = getBlockMut(self, target); |
|
| 2590 | 2596 | assert blk.sealState <> Sealed::Yes, "addPredecessor: adding predecessor to sealed block"; |
|
| 2591 | 2597 | let preds = &mut blk.preds; |
|
| 2592 | 2598 | for i in 0..preds.len { |
|
| 2593 | 2599 | if preds[i] == *pred { // Avoid duplicate predecessor entries. |
| 2596 | 2602 | } |
|
| 2597 | 2603 | preds.append(*pred, self.allocator); |
|
| 2598 | 2604 | } |
|
| 2599 | 2605 | ||
| 2600 | 2606 | /// Finalize all blocks and return the block array. |
|
| 2601 | - | fn finalizeBlocks(self: *mut FnLowerer) -> *[il::Block] throws (LowerError) { |
|
| 2607 | + | unsafe fn finalizeBlocks(self: &mut FnLowerer) -> *[il::Block] throws (LowerError) { |
|
| 2608 | + | let blockCount = self.blockData.len; |
|
| 2602 | 2609 | let blocks = try! alloc::allocSlice( |
|
| 2603 | - | self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), self.blockData.len |
|
| 2610 | + | &mut *self.low.fnArena, @sizeOf(il::Block), @alignOf(il::Block), blockCount |
|
| 2604 | 2611 | ) as *mut [il::Block]; |
|
| 2605 | 2612 | ||
| 2606 | 2613 | for i in 0..self.blockData.len { |
|
| 2607 | 2614 | let data = &self.blockData[i]; |
|
| 2608 | 2615 |
| 2622 | 2629 | // Loop Management // |
|
| 2623 | 2630 | ///////////////////// |
|
| 2624 | 2631 | ||
| 2625 | 2632 | /// Enter a loop context for break/continue handling. |
|
| 2626 | 2633 | /// `continueBlock` is `nil` when the continue target is created lazily. |
|
| 2627 | - | fn enterLoop(self: *mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) { |
|
| 2634 | + | fn enterLoop(self: &mut FnLowerer, breakBlock: BlockId, continueBlock: ?BlockId) { |
|
| 2628 | 2635 | assert self.loopDepth < self.loopStack.len, "enterLoop: loop depth overflow"; |
|
| 2629 | 2636 | let slot = &mut self.loopStack[self.loopDepth]; |
|
| 2630 | 2637 | ||
| 2631 | 2638 | set slot.breakTarget = breakBlock; |
|
| 2632 | 2639 | set slot.continueTarget = continueBlock; |
|
| 2633 | 2640 | set self.loopDepth += 1; |
|
| 2634 | 2641 | } |
|
| 2635 | 2642 | ||
| 2636 | 2643 | /// Exit the current loop context. |
|
| 2637 | - | fn exitLoop(self: *mut FnLowerer) { |
|
| 2644 | + | fn exitLoop(self: &mut FnLowerer) { |
|
| 2638 | 2645 | assert self.loopDepth <> 0, "exitLoop: loopDepth is zero"; |
|
| 2639 | 2646 | set self.loopDepth -= 1; |
|
| 2640 | 2647 | } |
|
| 2641 | 2648 | ||
| 2642 | 2649 | /// Get the current loop context. |
|
| 2643 | - | fn currentLoop(self: *mut FnLowerer) -> ?*mut LoopCtx { |
|
| 2650 | + | fn currentLoop(self: &mut FnLowerer) -> ?*mut LoopCtx { |
|
| 2644 | 2651 | if self.loopDepth == 0 { |
|
| 2645 | 2652 | return nil; |
|
| 2646 | 2653 | } |
|
| 2647 | 2654 | return &mut self.loopStack[self.loopDepth - 1]; |
|
| 2648 | 2655 | } |
|
| 2649 | 2656 | ||
| 2650 | 2657 | /// Get or lazily create the continue target block for the current loop. |
|
| 2651 | - | fn getOrCreateContinueBlock(self: *mut FnLowerer) -> BlockId throws (LowerError) { |
|
| 2658 | + | unsafe fn getOrCreateContinueBlock(self: &mut FnLowerer) -> BlockId throws (LowerError) { |
|
| 2652 | 2659 | let ctx = currentLoop(self) else { |
|
| 2653 | 2660 | throw LowerError::OutsideOfLoop; |
|
| 2654 | 2661 | }; |
|
| 2655 | 2662 | if let block = ctx.continueTarget { |
|
| 2656 | 2663 | return block; |
| 2659 | 2666 | set ctx.continueTarget = block; |
|
| 2660 | 2667 | return block; |
|
| 2661 | 2668 | } |
|
| 2662 | 2669 | ||
| 2663 | 2670 | /// Allocate a slice of values in the lowering arena. |
|
| 2664 | - | fn allocVals(self: *mut FnLowerer, len: u32) -> *mut [il::Val] throws (LowerError) { |
|
| 2665 | - | return try! alloc::allocSlice(self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *mut [il::Val]; |
|
| 2671 | + | unsafe fn allocVals(self: &mut FnLowerer, len: u32) -> *mut [il::Val] throws (LowerError) { |
|
| 2672 | + | return try! alloc::allocSlice(&mut *self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), len) as *mut [il::Val]; |
|
| 2666 | 2673 | } |
|
| 2667 | 2674 | ||
| 2668 | 2675 | /// Allocate a single-value slice in the lowering arena. |
|
| 2669 | - | fn allocVal(self: *mut FnLowerer, val: il::Val) -> *mut [il::Val] throws (LowerError) { |
|
| 2676 | + | unsafe fn allocVal(self: &mut FnLowerer, val: il::Val) -> *mut [il::Val] throws (LowerError) { |
|
| 2670 | 2677 | let args = try allocVals(self, 1); |
|
| 2671 | 2678 | set args[0] = val; |
|
| 2672 | 2679 | return args; |
|
| 2673 | 2680 | } |
|
| 2674 | 2681 |
| 2761 | 2768 | // block is later sealed via [`sealBlock`], all incomplete block params are resolved. |
|
| 2762 | 2769 | ||
| 2763 | 2770 | /// Declare a new source-level variable and define its initial value. |
|
| 2764 | 2771 | /// If called before any block exists (e.g., for parameters), the definition is skipped. |
|
| 2765 | 2772 | fn newVar( |
|
| 2766 | - | self: *mut FnLowerer, |
|
| 2773 | + | self: &mut FnLowerer, |
|
| 2767 | 2774 | name: ?*[u8], |
|
| 2768 | 2775 | type: il::Type, |
|
| 2769 | 2776 | mutable: bool, |
|
| 2770 | 2777 | val: il::Val |
|
| 2771 | 2778 | ) -> Var { |
| 2781 | 2788 | ||
| 2782 | 2789 | /// Define (write) a variable. Record the SSA value of a variable in the |
|
| 2783 | 2790 | /// current block. Called when a variable is assigned or initialized (`let` |
|
| 2784 | 2791 | /// bindings, assignments, loop updates). When [`useVar`] is later called, |
|
| 2785 | 2792 | /// it will retrieve this value. |
|
| 2786 | - | fn defVar(self: *mut FnLowerer, v: Var, val: il::Val) { |
|
| 2793 | + | fn defVar(self: &mut FnLowerer, v: Var, val: il::Val) { |
|
| 2787 | 2794 | assert *v < self.vars.len; |
|
| 2788 | 2795 | set getBlockMut(self, currentBlock(self)).vars[*v] = val; |
|
| 2789 | 2796 | } |
|
| 2790 | 2797 | ||
| 2791 | 2798 | /// Use (read) the current value of a variable in the current block. |
|
| 2792 | 2799 | /// May insert block parameters if the value must come from predecessors. |
|
| 2793 | - | fn useVar(self: *mut FnLowerer, v: Var) -> il::Val throws (LowerError) { |
|
| 2800 | + | unsafe fn useVar(self: &mut FnLowerer, v: Var) -> il::Val throws (LowerError) { |
|
| 2794 | 2801 | return try useVarInBlock(self, currentBlock(self), v); |
|
| 2795 | 2802 | } |
|
| 2796 | 2803 | ||
| 2797 | 2804 | /// Resolve which SSA definition of a variable reaches a use point in a given block. |
|
| 2798 | 2805 | /// |
|
| 2799 | 2806 | /// Given a variable and a block where it's used, this function finds the |
|
| 2800 | 2807 | /// correct [`il::Val`] that holds the variable's value at that program point. |
|
| 2801 | 2808 | /// When control flow merges from multiple predecessors with different |
|
| 2802 | 2809 | /// definitions, it creates a block parameter to unify them. |
|
| 2803 | - | fn useVarInBlock(self: *mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) { |
|
| 2810 | + | unsafe fn useVarInBlock(self: &mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) { |
|
| 2804 | 2811 | assert *v < self.vars.len; |
|
| 2805 | 2812 | ||
| 2806 | 2813 | let blk = getBlockMut(self, block); |
|
| 2807 | 2814 | if let val = blk.vars[*v] { |
|
| 2808 | 2815 | return val; |
| 2833 | 2840 | return try createBlockParam(self, block, v); |
|
| 2834 | 2841 | } |
|
| 2835 | 2842 | ||
| 2836 | 2843 | /// Look up a variable by name in the current scope. |
|
| 2837 | 2844 | /// Searches from most recently declared to first, enabling shadowing. |
|
| 2838 | - | fn lookupVarByName(self: *FnLowerer, name: *[u8]) -> ?Var { |
|
| 2845 | + | fn lookupVarByName(self: &FnLowerer, name: *[u8]) -> ?Var { |
|
| 2839 | 2846 | let mut id = self.vars.len; |
|
| 2840 | 2847 | while id > 0 { |
|
| 2841 | 2848 | set id -= 1; |
|
| 2842 | 2849 | if let varName = self.vars[id].name { |
|
| 2843 | 2850 | // Names are interned strings, so pointer comparison suffices. |
| 2848 | 2855 | } |
|
| 2849 | 2856 | return nil; |
|
| 2850 | 2857 | } |
|
| 2851 | 2858 | ||
| 2852 | 2859 | /// Look up a local variable bound to an identifier node. |
|
| 2853 | - | fn lookupLocalVar(self: *FnLowerer, node: *ast::Node) -> ?Var { |
|
| 2860 | + | fn lookupLocalVar(self: &FnLowerer, node: *ast::Node) -> ?Var { |
|
| 2854 | 2861 | let case ast::NodeValue::Ident(name) = node.value else { |
|
| 2855 | 2862 | return nil; |
|
| 2856 | 2863 | }; |
|
| 2857 | 2864 | return lookupVarByName(self, name); |
|
| 2858 | 2865 | } |
|
| 2859 | 2866 | ||
| 2860 | 2867 | /// Save current lexical variable scope depth. |
|
| 2861 | - | fn enterVarScope(self: *FnLowerer) -> u32 { |
|
| 2868 | + | fn enterVarScope(self: &FnLowerer) -> u32 { |
|
| 2862 | 2869 | return self.vars.len; |
|
| 2863 | 2870 | } |
|
| 2864 | 2871 | ||
| 2865 | 2872 | /// Restore lexical variable scope depth. |
|
| 2866 | - | fn exitVarScope(self: *mut FnLowerer, savedVarsLen: u32) { |
|
| 2873 | + | fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) { |
|
| 2867 | 2874 | set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap); |
|
| 2868 | 2875 | } |
|
| 2869 | 2876 | ||
| 2870 | 2877 | /// Get the metadata for a variable. |
|
| 2871 | - | fn getVar(self: *FnLowerer, v: Var) -> *VarData { |
|
| 2878 | + | fn getVar(self: &FnLowerer, v: Var) -> *VarData { |
|
| 2872 | 2879 | assert *v < self.vars.len; |
|
| 2873 | 2880 | return &self.vars[*v]; |
|
| 2874 | 2881 | } |
|
| 2875 | 2882 | ||
| 2876 | 2883 | /// Create a block parameter to merge a variable's value from multiple |
| 2887 | 2894 | /// @end(w32 %1) // x = %1, merged from predecessors |
|
| 2888 | 2895 | /// ret %1; |
|
| 2889 | 2896 | /// |
|
| 2890 | 2897 | /// Create a register `%1` as a block parameter. In a sealed block, patch each |
|
| 2891 | 2898 | /// predecessor's jump to pass its value of `x`. Otherwise, defer until sealing. |
|
| 2892 | - | fn createBlockParam(self: *mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) { |
|
| 2899 | + | unsafe fn createBlockParam(self: &mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) { |
|
| 2893 | 2900 | // Entry block must not have block parameters. |
|
| 2894 | 2901 | assert block <> self.entryBlock, "createBlockParam: entry block must not have block parameters"; |
|
| 2895 | 2902 | // Allocate a register to hold the merged value. |
|
| 2896 | 2903 | let reg = nextReg(self); |
|
| 2897 | 2904 | let type = getVar(self, v).type; |
| 2933 | 2940 | /// x3 = phi(x1, x2) |
|
| 2934 | 2941 | /// |
|
| 2935 | 2942 | /// This representation avoids the need for phi nodes to reference their |
|
| 2936 | 2943 | /// predecessor blocks explicitly, since the control flow edges already encode |
|
| 2937 | 2944 | /// that information. |
|
| 2938 | - | fn resolveBlockArgs(self: *mut FnLowerer, block: BlockId, v: Var, paramIdx: u32) throws (LowerError) { |
|
| 2945 | + | unsafe fn resolveBlockArgs(self: &mut FnLowerer, block: BlockId, v: Var, paramIdx: u32) throws (LowerError) { |
|
| 2939 | 2946 | let blk = getBlock(self, block); |
|
| 2940 | 2947 | ||
| 2941 | 2948 | // For each predecessor, recursively look up the variable's reaching definition |
|
| 2942 | 2949 | // in that block, then patch the predecessor's terminator to pass that value |
|
| 2943 | 2950 | // as an argument to this block's parameter. |
| 2951 | 2958 | } |
|
| 2952 | 2959 | } |
|
| 2953 | 2960 | ||
| 2954 | 2961 | /// Check if a block parameter is trivial, i.e. all predecessors provide |
|
| 2955 | 2962 | /// the same value. Returns the trivial value if so. |
|
| 2956 | - | fn getTrivialPhiVal(self: *mut FnLowerer, block: BlockId, v: Var) -> ?il::Val throws (LowerError) { |
|
| 2963 | + | unsafe fn getTrivialPhiVal(self: &mut FnLowerer, block: BlockId, v: Var) -> ?il::Val throws (LowerError) { |
|
| 2957 | 2964 | let blk = getBlock(self, block); |
|
| 2958 | 2965 | // Get the block parameter register. |
|
| 2959 | 2966 | let paramReg = blk.vars[*v]; |
|
| 2960 | 2967 | // Check if all predecessors provide the same value. |
|
| 2961 | 2968 | let mut sameVal: ?il::Val = nil; |
| 2985 | 2992 | return sameVal; |
|
| 2986 | 2993 | } |
|
| 2987 | 2994 | ||
| 2988 | 2995 | /// Patch a single terminator argument for a specific edge. This is used during |
|
| 2989 | 2996 | /// SSA construction to pass variable values along control flow edges. |
|
| 2990 | - | fn patchTerminatorArg( |
|
| 2991 | - | self: *mut FnLowerer, |
|
| 2997 | + | unsafe fn patchTerminatorArg( |
|
| 2998 | + | self: &mut FnLowerer, |
|
| 2992 | 2999 | from: BlockId, // The predecessor block containing the terminator to patch. |
|
| 2993 | 3000 | target: u32, // The index of the target block we're passing the value to. |
|
| 2994 | 3001 | paramIdx: u32, // The index of the block parameter to set. |
|
| 2995 | 3002 | val: il::Val // The value to pass as the argument. |
|
| 2996 | 3003 | ) { |
| 3033 | 3040 | } |
|
| 3034 | 3041 | } |
|
| 3035 | 3042 | } |
|
| 3036 | 3043 | ||
| 3037 | 3044 | /// Grow an args array to hold at least the given capacity. |
|
| 3038 | - | fn growArgs(self: *mut FnLowerer, args: *mut [il::Val], capacity: u32) -> *mut [il::Val] { |
|
| 3045 | + | unsafe fn growArgs(self: &mut FnLowerer, args: *mut [il::Val], capacity: u32) -> *mut [il::Val] { |
|
| 3039 | 3046 | if args.len >= capacity { |
|
| 3040 | 3047 | return args; |
|
| 3041 | 3048 | } |
|
| 3042 | 3049 | let newArgs = try! alloc::allocSlice( |
|
| 3043 | - | self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity |
|
| 3050 | + | &mut *self.low.fnArena, @sizeOf(il::Val), @alignOf(il::Val), capacity |
|
| 3044 | 3051 | ) as *mut [il::Val]; |
|
| 3045 | 3052 | ||
| 3046 | 3053 | for arg, i in args { |
|
| 3047 | 3054 | set newArgs[i] = arg; |
|
| 3048 | 3055 | } |
| 3063 | 3070 | return name; |
|
| 3064 | 3071 | } |
|
| 3065 | 3072 | ||
| 3066 | 3073 | /// Lower function parameters. Declares variables for each parameter. |
|
| 3067 | 3074 | /// When a receiver name is passed, we're handling a trait method. |
|
| 3068 | - | fn lowerParams( |
|
| 3069 | - | self: *mut FnLowerer, |
|
| 3075 | + | unsafe fn lowerParams( |
|
| 3076 | + | self: &mut FnLowerer, |
|
| 3070 | 3077 | fnType: resolver::FnType, |
|
| 3071 | 3078 | astParams: *mut [*ast::Node], |
|
| 3072 | 3079 | receiverName: ?*ast::Node |
|
| 3073 | 3080 | ) -> *[il::Param] throws (LowerError) { |
|
| 3074 | 3081 | let offset: u32 = 1 if self.returnReg <> nil else 0; |
| 3077 | 3084 | return &[]; |
|
| 3078 | 3085 | } |
|
| 3079 | 3086 | assert fnType.paramTypes.len as u32 <= resolver::MAX_FN_PARAMS; |
|
| 3080 | 3087 | ||
| 3081 | 3088 | let params = try! alloc::allocSlice( |
|
| 3082 | - | self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen |
|
| 3089 | + | &mut *self.low.fnArena, @sizeOf(il::Param), @alignOf(il::Param), totalLen |
|
| 3083 | 3090 | ) as *mut [il::Param]; |
|
| 3084 | 3091 | ||
| 3085 | 3092 | if let reg = self.returnReg { |
|
| 3086 | 3093 | set params[0] = il::Param { value: reg, type: il::Type::W64 }; |
|
| 3087 | 3094 | } |
|
| 3088 | 3095 | for i in 0..fnType.paramTypes.len as u32 { |
|
| 3089 | - | let type = ilType(self.low, *fnType.paramTypes[i]); |
|
| 3096 | + | let type = ilType(&mut *self.low, *fnType.paramTypes[i]); |
|
| 3090 | 3097 | let reg = nextReg(self); |
|
| 3091 | 3098 | ||
| 3092 | 3099 | set params[i + offset] = il::Param { value: reg, type }; |
|
| 3093 | 3100 | ||
| 3094 | 3101 | // Declare the parameter variable. For the receiver, the name comes |
| 3113 | 3120 | } |
|
| 3114 | 3121 | return params; |
|
| 3115 | 3122 | } |
|
| 3116 | 3123 | ||
| 3117 | 3124 | /// Resolve match subject. |
|
| 3118 | - | fn lowerMatchSubject(self: *mut FnLowerer, subject: *ast::Node) -> MatchSubject throws (LowerError) { |
|
| 3125 | + | unsafe fn lowerMatchSubject(self: &mut FnLowerer, subject: *ast::Node) -> MatchSubject throws (LowerError) { |
|
| 3119 | 3126 | let mut val = try lowerExpr(self, subject); |
|
| 3120 | 3127 | let subjectType = try typeOf(self, subject); |
|
| 3121 | 3128 | let unwrapped = resolver::unwrapMatchSubject(subjectType); |
|
| 3122 | 3129 | ||
| 3123 | 3130 | // When matching an aggregate by value, copy it to a fresh stack slot so |
| 3130 | 3137 | ||
| 3131 | 3138 | let mut bindType = unwrapped.effectiveTy; |
|
| 3132 | 3139 | if let case resolver::Type::Optional(inner) = unwrapped.effectiveTy { |
|
| 3133 | 3140 | set bindType = *inner; |
|
| 3134 | 3141 | } |
|
| 3135 | - | let ilType = ilType(self.low, unwrapped.effectiveTy); |
|
| 3142 | + | let ilType = ilType(&mut *self.low, unwrapped.effectiveTy); |
|
| 3136 | 3143 | let kind = matchSubjectKind(unwrapped.effectiveTy); |
|
| 3137 | 3144 | ||
| 3138 | 3145 | return MatchSubject { val, type: unwrapped.effectiveTy, ilType, bindType, kind, by: unwrapped.by }; |
|
| 3139 | 3146 | } |
|
| 3140 | 3147 |
| 3153 | 3160 | else => return false, |
|
| 3154 | 3161 | } |
|
| 3155 | 3162 | } |
|
| 3156 | 3163 | ||
| 3157 | 3164 | /// Load the tag byte from a tagged value aggregate (optionals and unions). |
|
| 3158 | - | fn tvalTagReg(self: *mut FnLowerer, base: il::Reg) -> il::Reg { |
|
| 3165 | + | unsafe fn tvalTagReg(self: &mut FnLowerer, base: il::Reg) -> il::Reg { |
|
| 3159 | 3166 | let tagReg = nextReg(self); |
|
| 3160 | 3167 | emitLoadW8At(self, tagReg, base, TVAL_TAG_OFFSET); |
|
| 3161 | 3168 | return tagReg; |
|
| 3162 | 3169 | } |
|
| 3163 | 3170 | ||
| 3164 | 3171 | /// Load the tag word from a result aggregate. |
|
| 3165 | - | fn resultTagReg(self: *mut FnLowerer, base: il::Reg) -> il::Reg { |
|
| 3172 | + | unsafe fn resultTagReg(self: &mut FnLowerer, base: il::Reg) -> il::Reg { |
|
| 3166 | 3173 | let tagReg = nextReg(self); |
|
| 3167 | 3174 | emitLoadW64At(self, tagReg, base, TVAL_TAG_OFFSET); |
|
| 3168 | 3175 | return tagReg; |
|
| 3169 | 3176 | } |
|
| 3170 | 3177 | ||
| 3171 | 3178 | /// Get the register to compare against `0` for optional `nil` checking. |
|
| 3172 | 3179 | /// For null-ptr-optimized types, loads the data pointer, or returns it |
|
| 3173 | 3180 | /// directly for scalar pointers. For aggregates, returns the tag register. |
|
| 3174 | - | fn optionalNilReg(self: *mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) { |
|
| 3181 | + | unsafe fn optionalNilReg(self: &mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) { |
|
| 3175 | 3182 | let reg = emitValToReg(self, val); |
|
| 3176 | 3183 | ||
| 3177 | 3184 | match typ { |
|
| 3178 | 3185 | case resolver::Type::Optional(resolver::Type::Slice { .. }) => { |
|
| 3179 | 3186 | let ptrReg = nextReg(self); |
| 3185 | 3192 | else => return reg, |
|
| 3186 | 3193 | } |
|
| 3187 | 3194 | } |
|
| 3188 | 3195 | ||
| 3189 | 3196 | /// Lower an optional nil check (`opt == nil` or `opt <> nil`). |
|
| 3190 | - | fn lowerNilCheck(self: *mut FnLowerer, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) { |
|
| 3197 | + | unsafe fn lowerNilCheck(self: &mut FnLowerer, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) { |
|
| 3191 | 3198 | let optTy = try typeOf(self, opt); |
|
| 3192 | 3199 | // Handle `nil == nil` or `nil <> nil`. |
|
| 3193 | 3200 | if optTy == resolver::Type::Nil { |
|
| 3194 | 3201 | return il::Val::Imm(1) if isEq else il::Val::Imm(0); |
|
| 3195 | 3202 | } |
| 3203 | 3210 | let op = il::BinOp::Eq if isEq else il::BinOp::Ne; |
|
| 3204 | 3211 | return emitTypedBinOp(self, op, cmpType, il::Val::Reg(cmpReg), il::Val::Imm(0)); |
|
| 3205 | 3212 | } |
|
| 3206 | 3213 | ||
| 3207 | 3214 | /// Load the payload value from a tagged value aggregate at the given offset. |
|
| 3208 | - | fn tvalPayloadVal(self: *mut FnLowerer, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val { |
|
| 3215 | + | unsafe fn tvalPayloadVal(self: &mut FnLowerer, base: il::Reg, payload: resolver::Type, valOffset: i32) -> il::Val { |
|
| 3209 | 3216 | if payload == resolver::Type::Void { |
|
| 3210 | 3217 | return il::Val::Undef; |
|
| 3211 | 3218 | } |
|
| 3212 | 3219 | return emitRead(self, base, valOffset, payload); |
|
| 3213 | 3220 | } |
|
| 3214 | 3221 | ||
| 3215 | 3222 | /// Compute the address of the payload in a tagged value aggregate. |
|
| 3216 | - | fn tvalPayloadAddr(self: *mut FnLowerer, base: il::Reg, valOffset: i32) -> il::Val { |
|
| 3223 | + | unsafe fn tvalPayloadAddr(self: &mut FnLowerer, base: il::Reg, valOffset: i32) -> il::Val { |
|
| 3217 | 3224 | return il::Val::Reg(emitPtrOffset(self, base, valOffset)); |
|
| 3218 | 3225 | } |
|
| 3219 | 3226 | ||
| 3220 | 3227 | /// Bind a variable to a tagged value's payload. |
|
| 3221 | - | fn bindPayloadVariable( |
|
| 3222 | - | self: *mut FnLowerer, |
|
| 3228 | + | unsafe fn bindPayloadVariable( |
|
| 3229 | + | self: &mut FnLowerer, |
|
| 3223 | 3230 | name: *[u8], |
|
| 3224 | 3231 | subjectVal: il::Val, |
|
| 3225 | 3232 | bindType: resolver::Type, |
|
| 3226 | 3233 | matchBy: resolver::MatchBy, |
|
| 3227 | 3234 | valOffset: i32, |
| 3234 | 3241 | case resolver::MatchBy::Value => |
|
| 3235 | 3242 | set payload = tvalPayloadVal(self, base, bindType, valOffset), |
|
| 3236 | 3243 | case resolver::MatchBy::Ref, resolver::MatchBy::MutRef => |
|
| 3237 | 3244 | set payload = tvalPayloadAddr(self, base, valOffset), |
|
| 3238 | 3245 | }; |
|
| 3239 | - | return newVar(self, name, ilType(self.low, bindType), mutable, payload); |
|
| 3246 | + | return newVar(self, name, ilType(&mut *self.low, bindType), mutable, payload); |
|
| 3240 | 3247 | } |
|
| 3241 | 3248 | ||
| 3242 | 3249 | /// Bind an identifier from a matched subject. |
|
| 3243 | - | fn bindMatchVariable( |
|
| 3244 | - | self: *mut FnLowerer, |
|
| 3245 | - | subject: *MatchSubject, |
|
| 3250 | + | unsafe fn bindMatchVariable( |
|
| 3251 | + | self: &mut FnLowerer, |
|
| 3252 | + | subject: &MatchSubject, |
|
| 3246 | 3253 | binding: *ast::Node, |
|
| 3247 | 3254 | mutable: bool |
|
| 3248 | 3255 | ) -> ?Var throws (LowerError) { |
|
| 3249 | 3256 | // Only bind if the pattern is an identifier. |
|
| 3250 | 3257 | let case ast::NodeValue::Ident(name) = binding.value else { |
| 3255 | 3262 | if let case MatchSubjectKind::OptionalAggregate = subject.kind { |
|
| 3256 | 3263 | let valOffset = resolver::getOptionalValOffset(subject.bindType) as i32; |
|
| 3257 | 3264 | return try bindPayloadVariable(self, name, subject.val, subject.bindType, subject.by, valOffset, mutable); |
|
| 3258 | 3265 | } |
|
| 3259 | 3266 | // Declare the variable in the current block's scope. |
|
| 3260 | - | return newVar(self, name, ilType(self.low, subject.bindType), mutable, subject.val); |
|
| 3267 | + | return newVar(self, name, ilType(&mut *self.low, subject.bindType), mutable, subject.val); |
|
| 3261 | 3268 | } |
|
| 3262 | 3269 | ||
| 3263 | 3270 | /// Bind variables from inside case patterns (union variants, records, slices). |
|
| 3264 | 3271 | /// `failBlock` is passed when nested patterns may require additional tests |
|
| 3265 | 3272 | /// that branch on mismatch (e.g. nested union variant tests). |
|
| 3266 | - | fn bindPatternVariables(self: *mut FnLowerer, subject: *MatchSubject, patterns: *mut [*ast::Node], failBlock: BlockId) throws (LowerError) { |
|
| 3273 | + | unsafe fn bindPatternVariables(self: &mut FnLowerer, subject: &MatchSubject, patterns: &[*ast::Node], failBlock: BlockId) throws (LowerError) { |
|
| 3267 | 3274 | for pattern in patterns { |
|
| 3268 | 3275 | ||
| 3269 | 3276 | // Handle simple variant patterns like `Variant(x)`. |
|
| 3270 | - | if let arg = resolver::variantPatternBinding(self.low.resolver, pattern) { |
|
| 3277 | + | if let arg = resolver::variantPatternBinding(&*self.low.resolver, pattern) { |
|
| 3271 | 3278 | let case MatchSubjectKind::Union(unionInfo) = subject.kind |
|
| 3272 | 3279 | else panic "bindPatternVariables: expected union subject"; |
|
| 3273 | 3280 | let valOffset = unionInfo.valOffset as i32; |
|
| 3274 | 3281 | ||
| 3275 | 3282 | // Get the actual field type from the variant's record info. |
|
| 3276 | 3283 | // This preserves the original data layout type (e.g. `*T`) even when |
|
| 3277 | 3284 | // the resolver resolved the pattern against a dereferenced type (`T`). |
|
| 3278 | - | let variantExtra = resolver::nodeData(self.low.resolver, pattern).extra; |
|
| 3285 | + | let variantExtra = resolver::nodeData(&*self.low.resolver, pattern).extra; |
|
| 3279 | 3286 | let case resolver::NodeExtra::UnionVariant { ordinal, .. } = variantExtra |
|
| 3280 | 3287 | else panic "bindPatternVariables: expected variant extra"; |
|
| 3281 | 3288 | let payloadType = unionInfo.variants[ordinal].valueType; |
|
| 3282 | 3289 | let payloadRec = resolver::getRecord(payloadType) |
|
| 3283 | 3290 | else panic "bindPatternVariables: expected record payload"; |
| 3315 | 3322 | } |
|
| 3316 | 3323 | ||
| 3317 | 3324 | /// Bind variables from an array literal pattern (e.g., `[a, 1, c]`). |
|
| 3318 | 3325 | /// Each element is either bound as a variable, skipped (placeholder), or |
|
| 3319 | 3326 | /// tested against the subject element, branching to `failBlock` on mismatch. |
|
| 3320 | - | fn bindArrayPatternElements( |
|
| 3321 | - | self: *mut FnLowerer, |
|
| 3322 | - | subject: *MatchSubject, |
|
| 3327 | + | unsafe fn bindArrayPatternElements( |
|
| 3328 | + | self: &mut FnLowerer, |
|
| 3329 | + | subject: &MatchSubject, |
|
| 3323 | 3330 | items: *mut [*ast::Node], |
|
| 3324 | 3331 | failBlock: BlockId |
|
| 3325 | 3332 | ) throws (LowerError) { |
|
| 3326 | 3333 | let case resolver::Type::Array(arrInfo) = subject.type |
|
| 3327 | 3334 | else throw LowerError::ExpectedSliceOrArray; |
| 3340 | 3347 | try bindFieldVariable(self, elem, base, fieldInfo, subject.by, failBlock); |
|
| 3341 | 3348 | } |
|
| 3342 | 3349 | } |
|
| 3343 | 3350 | ||
| 3344 | 3351 | /// Bind fields from a record pattern. |
|
| 3345 | - | fn bindRecordPatternFields(self: *mut FnLowerer, subject: *MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) { |
|
| 3352 | + | unsafe fn bindRecordPatternFields(self: &mut FnLowerer, subject: &MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) { |
|
| 3346 | 3353 | // No fields to bind (e.g., `{ .. }`). |
|
| 3347 | 3354 | if lit.fields.len == 0 { |
|
| 3348 | 3355 | return; |
|
| 3349 | 3356 | } |
|
| 3350 | 3357 | // Optional value patterns were already compared structurally by |
| 3357 | 3364 | let case MatchSubjectKind::Union(unionInfo) = subject.kind |
|
| 3358 | 3365 | else panic "bindRecordPatternFields: expected union subject"; |
|
| 3359 | 3366 | ||
| 3360 | 3367 | // Get the variant index from the pattern node. |
|
| 3361 | 3368 | let case resolver::NodeExtra::UnionVariant { ordinal: variantOrdinal, .. } = |
|
| 3362 | - | resolver::nodeData(self.low.resolver, pattern).extra |
|
| 3369 | + | resolver::nodeData(&*self.low.resolver, pattern).extra |
|
| 3363 | 3370 | else throw LowerError::MissingMetadata; |
|
| 3364 | 3371 | ||
| 3365 | 3372 | // Get the record type from the variant's payload type. |
|
| 3366 | 3373 | let payloadType = unionInfo.variants[variantOrdinal].valueType; |
|
| 3367 | 3374 | let recInfo = resolver::getRecord(payloadType) |
| 3375 | 3382 | try bindNestedRecordFields(self, payloadBase, lit, recInfo, subject.by, failBlock); |
|
| 3376 | 3383 | } |
|
| 3377 | 3384 | ||
| 3378 | 3385 | /// Bind a single record field to a pattern variable, with support for nested |
|
| 3379 | 3386 | /// pattern tests that branch to `failBlock` on mismatch. |
|
| 3380 | - | fn bindFieldVariable( |
|
| 3381 | - | self: *mut FnLowerer, |
|
| 3387 | + | unsafe fn bindFieldVariable( |
|
| 3388 | + | self: &mut FnLowerer, |
|
| 3382 | 3389 | binding: *ast::Node, |
|
| 3383 | 3390 | base: il::Reg, |
|
| 3384 | 3391 | fieldInfo: resolver::RecordField, |
|
| 3385 | 3392 | matchBy: resolver::MatchBy, |
|
| 3386 | 3393 | failBlock: BlockId |
| 3388 | 3395 | match binding.value { |
|
| 3389 | 3396 | case ast::NodeValue::Ident(name) => { |
|
| 3390 | 3397 | let val = emitRead(self, base, fieldInfo.offset, fieldInfo.fieldType) |
|
| 3391 | 3398 | if matchBy == resolver::MatchBy::Value |
|
| 3392 | 3399 | else il::Val::Reg(emitPtrOffset(self, base, fieldInfo.offset)); |
|
| 3393 | - | newVar(self, name, ilType(self.low, fieldInfo.fieldType), false, val); |
|
| 3400 | + | newVar(self, name, ilType(&mut *self.low, fieldInfo.fieldType), false, val); |
|
| 3394 | 3401 | } |
|
| 3395 | 3402 | case ast::NodeValue::Placeholder => {} |
|
| 3396 | 3403 | case ast::NodeValue::RecordLit(lit) => { |
|
| 3397 | 3404 | // Check if this record literal is a union variant pattern. |
|
| 3398 | 3405 | if let keyNode = resolver::patternVariantKeyNode(binding) { |
|
| 3399 | - | if let case resolver::NodeExtra::UnionVariant { .. } = resolver::nodeData(self.low.resolver, keyNode).extra { |
|
| 3406 | + | if let case resolver::NodeExtra::UnionVariant { .. } = resolver::nodeData(&*self.low.resolver, keyNode).extra { |
|
| 3400 | 3407 | try emitNestedFieldTest(self, binding, base, fieldInfo, matchBy, failBlock); |
|
| 3401 | 3408 | return; |
|
| 3402 | 3409 | } |
|
| 3403 | 3410 | } |
|
| 3404 | 3411 | // Plain nested record destructuring pattern. |
| 3424 | 3431 | } |
|
| 3425 | 3432 | ||
| 3426 | 3433 | /// Emit a nested pattern test for a record field value, branching to |
|
| 3427 | 3434 | /// `failBlock` if the pattern does not match. On success, continues in |
|
| 3428 | 3435 | /// a fresh block and binds any nested variables. |
|
| 3429 | - | fn emitNestedFieldTest( |
|
| 3430 | - | self: *mut FnLowerer, |
|
| 3436 | + | unsafe fn emitNestedFieldTest( |
|
| 3437 | + | self: &mut FnLowerer, |
|
| 3431 | 3438 | pattern: *ast::Node, |
|
| 3432 | 3439 | base: il::Reg, |
|
| 3433 | 3440 | fieldInfo: resolver::RecordField, |
|
| 3434 | 3441 | matchBy: resolver::MatchBy, |
|
| 3435 | 3442 | failBlock: BlockId |
| 3448 | 3455 | set derefBase = ptrReg; |
|
| 3449 | 3456 | set fieldType = *target; |
|
| 3450 | 3457 | } |
|
| 3451 | 3458 | } |
|
| 3452 | 3459 | // Build a MatchSubject for the nested field. |
|
| 3453 | - | let ilTy = ilType(self.low, fieldType); |
|
| 3460 | + | let ilTy = ilType(&mut *self.low, fieldType); |
|
| 3454 | 3461 | let kind = matchSubjectKind(fieldType); |
|
| 3455 | 3462 | ||
| 3456 | 3463 | // Determine the subject value. |
|
| 3457 | 3464 | let mut val: il::Val = undefined; |
|
| 3458 | 3465 | if let reg = derefBase { |
| 3478 | 3485 | let continueBlock = try createBlock(self, "nest"); |
|
| 3479 | 3486 | try emitPatternMatch(self, &nestedSubject, pattern, continueBlock, failBlock); |
|
| 3480 | 3487 | try switchToAndSeal(self, continueBlock); |
|
| 3481 | 3488 | ||
| 3482 | 3489 | // After the test succeeds, bind any nested variables. |
|
| 3483 | - | let patterns: *mut [*ast::Node] = &mut [pattern]; |
|
| 3484 | - | try bindPatternVariables(self, &nestedSubject, patterns, failBlock); |
|
| 3490 | + | let patterns = [pattern]; |
|
| 3491 | + | try bindPatternVariables(self, &nestedSubject, &patterns[..], failBlock); |
|
| 3485 | 3492 | } |
|
| 3486 | 3493 | ||
| 3487 | 3494 | /// Bind variables from a nested record literal pattern. |
|
| 3488 | - | fn bindNestedRecordFields( |
|
| 3489 | - | self: *mut FnLowerer, |
|
| 3495 | + | unsafe fn bindNestedRecordFields( |
|
| 3496 | + | self: &mut FnLowerer, |
|
| 3490 | 3497 | base: il::Reg, |
|
| 3491 | 3498 | lit: ast::RecordLit, |
|
| 3492 | 3499 | recInfo: resolver::RecordType, |
|
| 3493 | 3500 | matchBy: resolver::MatchBy, |
|
| 3494 | 3501 | failBlock: BlockId |
|
| 3495 | 3502 | ) throws (LowerError) { |
|
| 3496 | 3503 | for fieldNode in lit.fields { |
|
| 3497 | 3504 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value else { |
|
| 3498 | 3505 | throw LowerError::UnexpectedNodeValue(fieldNode); |
|
| 3499 | 3506 | }; |
|
| 3500 | - | let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode) |
|
| 3507 | + | let fieldIdx = resolver::recordFieldIndexFor(&*self.low.resolver, fieldNode) |
|
| 3501 | 3508 | else throw LowerError::MissingMetadata; |
|
| 3502 | 3509 | if fieldIdx >= recInfo.fields.len { |
|
| 3503 | 3510 | throw LowerError::MissingMetadata; |
|
| 3504 | 3511 | } |
|
| 3505 | 3512 | let fieldInfo = recInfo.fields[fieldIdx]; |
| 3507 | 3514 | try bindFieldVariable(self, field.value, base, fieldInfo, matchBy, failBlock); |
|
| 3508 | 3515 | } |
|
| 3509 | 3516 | } |
|
| 3510 | 3517 | ||
| 3511 | 3518 | /// Lower function body to a list of basic blocks. |
|
| 3512 | - | fn lowerFnBody(self: *mut FnLowerer, body: *ast::Node) -> *[il::Block] throws (LowerError) { |
|
| 3519 | + | unsafe fn lowerFnBody(self: &mut FnLowerer, body: *ast::Node) -> *[il::Block] throws (LowerError) { |
|
| 3513 | 3520 | // Create and switch to entry block. |
|
| 3514 | 3521 | let entry = try createBlock(self, "entry"); |
|
| 3515 | 3522 | set self.entryBlock = entry; |
|
| 3516 | 3523 | switchToBlock(self, entry); |
|
| 3517 | 3524 |
| 3540 | 3547 | } |
|
| 3541 | 3548 | return try finalizeBlocks(self); |
|
| 3542 | 3549 | } |
|
| 3543 | 3550 | ||
| 3544 | 3551 | /// Lower a scalar match as a switch instruction. |
|
| 3545 | - | fn lowerMatchSwitch(self: *mut FnLowerer, prongs: *mut [*ast::Node], subject: *MatchSubject, mergeBlock: *mut ?BlockId) throws (LowerError) { |
|
| 3552 | + | unsafe fn lowerMatchSwitch(self: &mut FnLowerer, prongs: *mut [*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) { |
|
| 3546 | 3553 | let mut blocks: [BlockId; 32] = undefined; |
|
| 3547 | 3554 | let mut cases: *mut [il::SwitchCase] = &mut []; |
|
| 3548 | 3555 | let mut defaultIdx: u32 = 0; |
|
| 3549 | 3556 | let entry = currentBlock(self); |
|
| 3550 | 3557 |
| 3558 | 3565 | set defaultIdx = i; |
|
| 3559 | 3566 | } |
|
| 3560 | 3567 | case ast::ProngArm::Case(pats) => { |
|
| 3561 | 3568 | set blocks[i] = try createBlock(self, "case"); |
|
| 3562 | 3569 | for pat in pats { |
|
| 3563 | - | let cv = resolver::constValueEntry(self.low.resolver, pat) |
|
| 3570 | + | let cv = resolver::constValueEntry(&*self.low.resolver, pat) |
|
| 3564 | 3571 | else throw LowerError::MissingConst(pat); |
|
| 3565 | 3572 | ||
| 3566 | 3573 | cases.append(il::SwitchCase { |
|
| 3567 | 3574 | value: constToScalar(cv), |
|
| 3568 | 3575 | target: *blocks[i], |
| 3643 | 3650 | /// arm#1: |
|
| 3644 | 3651 | /// jmp else#0; // guard failed, fallthrough to `else` |
|
| 3645 | 3652 | /// else#0: |
|
| 3646 | 3653 | /// ret 0; // `else` body |
|
| 3647 | 3654 | /// |
|
| 3648 | - | fn lowerMatch(self: *mut FnLowerer, node: *ast::Node, m: ast::Match) throws (LowerError) { |
|
| 3655 | + | unsafe fn lowerMatch(self: &mut FnLowerer, node: *ast::Node, m: ast::Match) throws (LowerError) { |
|
| 3649 | 3656 | assert m.prongs.len > 0; |
|
| 3650 | 3657 | ||
| 3651 | 3658 | let prongs = m.prongs; |
|
| 3652 | 3659 | // Lower the subject expression once; reused across all arms. |
|
| 3653 | 3660 | let subject = try lowerMatchSubject(self, m.subject); |
|
| 3654 | 3661 | // Merge block created lazily if any arm needs it (i.e., doesn't diverge). |
|
| 3655 | 3662 | let mut mergeBlock: ?BlockId = nil; |
|
| 3656 | 3663 | ||
| 3657 | 3664 | // Use `switch` instruction for matches with constant patterns. |
|
| 3658 | - | if resolver::isMatchConst(self.low.resolver, node) { |
|
| 3665 | + | if resolver::isMatchConst(&*self.low.resolver, node) { |
|
| 3659 | 3666 | try lowerMatchSwitch(self, prongs, &subject, &mut mergeBlock); |
|
| 3660 | 3667 | return; |
|
| 3661 | 3668 | } |
|
| 3662 | 3669 | // Fallback: chained branches. |
|
| 3663 | 3670 | let firstArm = try createBlock(self, "arm"); |
| 3669 | 3676 | let case ast::NodeValue::MatchProng(prong) = prongNode.value |
|
| 3670 | 3677 | else panic "lowerMatch: expected match prong"; |
|
| 3671 | 3678 | ||
| 3672 | 3679 | let isLastArm = i + 1 == prongs.len; |
|
| 3673 | 3680 | let hasGuard = prong.guard <> nil; |
|
| 3674 | - | let catchAll = resolver::isProngCatchAll(self.low.resolver, prongNode); |
|
| 3681 | + | let catchAll = resolver::isProngCatchAll(&*self.low.resolver, prongNode); |
|
| 3675 | 3682 | ||
| 3676 | 3683 | // Entry block: guard block if present, otherwise the body block. |
|
| 3677 | 3684 | // The guard block must be created before the body block so that |
|
| 3678 | 3685 | // block indices are in reverse post-order (RPO), which the register |
|
| 3679 | 3686 | // allocator requires. |
| 3696 | 3703 | // Emit pattern test: branch to entry block on match, next arm on fail. |
|
| 3697 | 3704 | match prong.arm { |
|
| 3698 | 3705 | case ast::ProngArm::Binding(_) if not catchAll => |
|
| 3699 | 3706 | try emitBindingTest(self, &subject, entryBlock, nextArm), |
|
| 3700 | 3707 | case ast::ProngArm::Case(patterns) if not catchAll => |
|
| 3701 | - | try emitPatternMatches(self, &subject, patterns, entryBlock, nextArm), |
|
| 3708 | + | try emitPatternMatches(self, &subject, &patterns[..], entryBlock, nextArm), |
|
| 3702 | 3709 | else => |
|
| 3703 | 3710 | try emitJmp(self, entryBlock), |
|
| 3704 | 3711 | } |
|
| 3705 | 3712 | // Switch to entry block, where any variable bindings need to be created. |
|
| 3706 | 3713 | try switchToAndSeal(self, entryBlock); |
| 3711 | 3718 | // block. |
|
| 3712 | 3719 | match prong.arm { |
|
| 3713 | 3720 | case ast::ProngArm::Binding(pat) => |
|
| 3714 | 3721 | try bindMatchVariable(self, &subject, pat, false), |
|
| 3715 | 3722 | case ast::ProngArm::Case(patterns) => |
|
| 3716 | - | try bindPatternVariables(self, &subject, patterns, nextArm), |
|
| 3723 | + | try bindPatternVariables(self, &subject, &patterns[..], nextArm), |
|
| 3717 | 3724 | else => {}, |
|
| 3718 | 3725 | } |
|
| 3719 | 3726 | ||
| 3720 | 3727 | // Evaluate guard if present; can still fail to next arm. |
|
| 3721 | 3728 | if let g = prong.guard { |
| 3747 | 3754 | try switchToAndSeal(self, blk); |
|
| 3748 | 3755 | } |
|
| 3749 | 3756 | } |
|
| 3750 | 3757 | ||
| 3751 | 3758 | /// Lower an `if let` statement. |
|
| 3752 | - | fn lowerIfLet(self: *mut FnLowerer, cond: ast::IfLet) throws (LowerError) { |
|
| 3759 | + | unsafe fn lowerIfLet(self: &mut FnLowerer, cond: ast::IfLet) throws (LowerError) { |
|
| 3753 | 3760 | let savedVarsLen = enterVarScope(self); |
|
| 3754 | 3761 | let subject = try lowerMatchSubject(self, cond.pattern.scrutinee); |
|
| 3755 | 3762 | let mut thenBlock: BlockId = undefined; |
|
| 3756 | 3763 | if cond.pattern.guard == nil { |
|
| 3757 | 3764 | set thenBlock = try createBlock(self, "then"); |
| 3785 | 3792 | /// Emit pattern match branch with optional guard, and bind variables. |
|
| 3786 | 3793 | /// Used by `if-let`, `let-else`, and `while-let` lowering. |
|
| 3787 | 3794 | /// |
|
| 3788 | 3795 | /// When a guard is present, the guard block is created before `successBlock` |
|
| 3789 | 3796 | /// to ensure block indices are in RPO. |
|
| 3790 | - | fn lowerPatternMatch( |
|
| 3791 | - | self: *mut FnLowerer, |
|
| 3792 | - | subject: *MatchSubject, |
|
| 3793 | - | pat: *ast::PatternMatch, |
|
| 3794 | - | successBlock: *mut BlockId, |
|
| 3797 | + | unsafe fn lowerPatternMatch( |
|
| 3798 | + | self: &mut FnLowerer, |
|
| 3799 | + | subject: &MatchSubject, |
|
| 3800 | + | pat: &ast::PatternMatch, |
|
| 3801 | + | successBlock: &mut BlockId, |
|
| 3795 | 3802 | successLabel: *[u8], |
|
| 3796 | 3803 | failBlock: BlockId |
|
| 3797 | 3804 | ) throws (LowerError) { |
|
| 3798 | 3805 | // If guard present, pattern match jumps to @guard, then guard evaluation |
|
| 3799 | 3806 | // jumps to `successBlock` or `failBlock`. Otherwise, jump directly to |
| 3805 | 3812 | } else { |
|
| 3806 | 3813 | set targetBlock = *successBlock; |
|
| 3807 | 3814 | } |
|
| 3808 | 3815 | match pat.kind { |
|
| 3809 | 3816 | case ast::PatternKind::Case => { |
|
| 3810 | - | let patterns: *mut [*ast::Node] = &mut [pat.pattern]; |
|
| 3817 | + | let patterns = [pat.pattern]; |
|
| 3811 | 3818 | // Jump to `targetBlock` if the pattern matches, `failBlock` otherwise. |
|
| 3812 | - | try emitPatternMatches(self, subject, patterns, targetBlock, failBlock); |
|
| 3819 | + | try emitPatternMatches(self, subject, &patterns[..], targetBlock, failBlock); |
|
| 3813 | 3820 | try switchToAndSeal(self, targetBlock); |
|
| 3814 | 3821 | // Bind any variables inside the pattern. Nested patterns may |
|
| 3815 | 3822 | // emit additional tests that branch to `failBlock`, switching |
|
| 3816 | 3823 | // the current block. |
|
| 3817 | - | try bindPatternVariables(self, subject, patterns, failBlock); |
|
| 3824 | + | try bindPatternVariables(self, subject, &patterns[..], failBlock); |
|
| 3818 | 3825 | } |
|
| 3819 | 3826 | case ast::PatternKind::Binding => { |
|
| 3820 | 3827 | // Jump to `targetBlock` if there is a value present, `failBlock` otherwise. |
|
| 3821 | 3828 | try emitBindingTest(self, subject, targetBlock, failBlock); |
|
| 3822 | 3829 | try switchToAndSeal(self, targetBlock); |
| 3837 | 3844 | try switchToAndSeal(self, *successBlock); |
|
| 3838 | 3845 | } |
|
| 3839 | 3846 | } |
|
| 3840 | 3847 | ||
| 3841 | 3848 | /// Lower a `let-else` statement. |
|
| 3842 | - | fn lowerLetElse(self: *mut FnLowerer, letElse: ast::LetElse) throws (LowerError) { |
|
| 3849 | + | unsafe fn lowerLetElse(self: &mut FnLowerer, letElse: ast::LetElse) throws (LowerError) { |
|
| 3843 | 3850 | let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee); |
|
| 3844 | 3851 | let mut successBlock: BlockId = undefined; |
|
| 3845 | 3852 | if letElse.pattern.guard == nil { |
|
| 3846 | 3853 | set successBlock = try createBlock(self, "success"); |
|
| 3847 | 3854 | } |
| 3878 | 3885 | // Continue at @merge after a successful match or value-producing fallback. |
|
| 3879 | 3886 | try switchToAndSeal(self, mergeBlock); |
|
| 3880 | 3887 | } |
|
| 3881 | 3888 | ||
| 3882 | 3889 | /// Lower a `while let` loop as a match-driven loop. |
|
| 3883 | - | fn lowerWhileLet(self: *mut FnLowerer, w: ast::WhileLet) throws (LowerError) { |
|
| 3890 | + | unsafe fn lowerWhileLet(self: &mut FnLowerer, w: ast::WhileLet) throws (LowerError) { |
|
| 3884 | 3891 | let savedVarsLen = enterVarScope(self); |
|
| 3885 | 3892 | // Create control flow blocks: loop header, body (created lazily when |
|
| 3886 | 3893 | // there's a guard), and exit. |
|
| 3887 | 3894 | let whileBlock = try createBlock(self, "while"); |
|
| 3888 | 3895 | let mut bodyBlock: BlockId = undefined; |
| 3911 | 3918 | /////////////////// |
|
| 3912 | 3919 | // Node Lowering // |
|
| 3913 | 3920 | /////////////////// |
|
| 3914 | 3921 | ||
| 3915 | 3922 | /// Lower an AST node. |
|
| 3916 | - | fn lowerNode(self: *mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 3923 | + | unsafe fn lowerNode(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 3917 | 3924 | if self.low.options.debug { |
|
| 3918 | 3925 | set self.srcLoc.offset = node.span.offset; |
|
| 3919 | 3926 | } |
|
| 3920 | 3927 | match node.value { |
|
| 3921 | 3928 | case ast::NodeValue::Block(_) => { |
| 3931 | 3938 | try lowerLet(self, node, l); |
|
| 3932 | 3939 | } |
|
| 3933 | 3940 | case ast::NodeValue::ConstDecl(decl) => { |
|
| 3934 | 3941 | // Local constants lower to data declarations and emit no runtime code. |
|
| 3935 | 3942 | try registerLocalDataDeclName(self, node); |
|
| 3936 | - | try lowerDataDecl(self.low, node, decl.value, true); |
|
| 3943 | + | try lowerDataDecl(&mut *self.low, node, decl.value, true); |
|
| 3937 | 3944 | } |
|
| 3938 | 3945 | case ast::NodeValue::StaticDecl(decl) => { |
|
| 3939 | 3946 | // Local statics lower to data declarations and emit no runtime code. |
|
| 3940 | 3947 | try registerLocalDataDeclName(self, node); |
|
| 3941 | - | try lowerDataDecl(self.low, node, decl.value, false); |
|
| 3948 | + | try lowerDataDecl(&mut *self.low, node, decl.value, false); |
|
| 3942 | 3949 | } |
|
| 3943 | 3950 | case ast::NodeValue::If(i) => { |
|
| 3944 | 3951 | try lowerIf(self, i); |
|
| 3945 | 3952 | } |
|
| 3946 | 3953 | case ast::NodeValue::IfLet(i) => { |
| 4001 | 4008 | } |
|
| 4002 | 4009 | } |
|
| 4003 | 4010 | } |
|
| 4004 | 4011 | ||
| 4005 | 4012 | /// Lower a code block. |
|
| 4006 | - | fn lowerBlock(self: *mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 4013 | + | unsafe fn lowerBlock(self: &mut FnLowerer, node: *ast::Node) throws (LowerError) { |
|
| 4007 | 4014 | let case ast::NodeValue::Block(blk) = node.value else { |
|
| 4008 | 4015 | throw LowerError::ExpectedBlock(node); |
|
| 4009 | 4016 | }; |
|
| 4010 | 4017 | let savedVarsLen = enterVarScope(self); |
|
| 4011 | 4018 | for stmt in blk.statements { |
| 4042 | 4049 | ||
| 4043 | 4050 | /// Return the effective type of a node after any coercion applied by |
|
| 4044 | 4051 | /// the resolver. `lowerExpr` already materializes the coercion in the |
|
| 4045 | 4052 | /// IL value, so the lowerer must use the post-coercion type when |
|
| 4046 | 4053 | /// choosing how to compare or store that value. |
|
| 4047 | - | fn effectiveType(self: *mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) { |
|
| 4054 | + | unsafe fn effectiveType(self: &mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) { |
|
| 4048 | 4055 | let ty = try typeOf(self, node); |
|
| 4049 | - | if let coerce = resolver::coercionFor(self.low.resolver, node) { |
|
| 4056 | + | if let coerce = resolver::coercionFor(&*self.low.resolver, node) { |
|
| 4050 | 4057 | if let case resolver::Coercion::OptionalLift(optTy) = coerce { |
|
| 4051 | 4058 | return optTy; |
|
| 4052 | 4059 | } |
|
| 4053 | 4060 | } |
|
| 4054 | 4061 | return ty; |
| 4104 | 4111 | } |
|
| 4105 | 4112 | ||
| 4106 | 4113 | /// Check if a node is a void union variant literal (e.g. `Color::Red`). |
|
| 4107 | 4114 | /// If so, returns the variant's tag index. This enables optimized comparisons |
|
| 4108 | 4115 | /// that only check the tag instead of doing full aggregate comparison. |
|
| 4109 | - | fn voidVariantIndex(res: *resolver::Resolver, node: *ast::Node) -> ?i64 { |
|
| 4116 | + | fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 { |
|
| 4110 | 4117 | let data = resolver::nodeData(res, node); |
|
| 4111 | 4118 | let sym = data.sym else { |
|
| 4112 | 4119 | return nil; |
|
| 4113 | 4120 | }; |
|
| 4114 | 4121 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else { |
| 4120 | 4127 | } |
|
| 4121 | 4128 | return index as i64; |
|
| 4122 | 4129 | } |
|
| 4123 | 4130 | ||
| 4124 | 4131 | /// Reserve stack storage for a value of the given type. |
|
| 4125 | - | fn emitReserve(self: *mut FnLowerer, typ: resolver::Type) -> il::Reg throws (LowerError) { |
|
| 4132 | + | unsafe fn emitReserve(self: &mut FnLowerer, typ: resolver::Type) -> il::Reg throws (LowerError) { |
|
| 4126 | 4133 | let layout = resolver::getTypeLayout(typ); |
|
| 4127 | 4134 | return emitReserveLayout(self, layout); |
|
| 4128 | 4135 | } |
|
| 4129 | 4136 | ||
| 4130 | 4137 | /// Reserve stack storage with an explicit layout. |
|
| 4131 | - | fn emitReserveLayout(self: *mut FnLowerer, layout: resolver::Layout) -> il::Reg { |
|
| 4138 | + | unsafe fn emitReserveLayout(self: &mut FnLowerer, layout: resolver::Layout) -> il::Reg { |
|
| 4132 | 4139 | let dst = nextReg(self); |
|
| 4133 | 4140 | ||
| 4134 | 4141 | emit(self, il::Instr::Reserve { |
|
| 4135 | 4142 | dst, |
|
| 4136 | 4143 | size: il::Val::Imm(layout.size as i64), |
| 4138 | 4145 | }); |
|
| 4139 | 4146 | return dst; |
|
| 4140 | 4147 | } |
|
| 4141 | 4148 | ||
| 4142 | 4149 | /// Store a value into an address. |
|
| 4143 | - | fn emitStore(self: *mut FnLowerer, base: il::Reg, offset: i32, typ: resolver::Type, src: il::Val) throws (LowerError) { |
|
| 4150 | + | unsafe fn emitStore(self: &mut FnLowerer, base: il::Reg, offset: i32, typ: resolver::Type, src: il::Val) throws (LowerError) { |
|
| 4144 | 4151 | // `undefined` values need no store. |
|
| 4145 | 4152 | if let case il::Val::Undef = src { |
|
| 4146 | 4153 | return; |
|
| 4147 | 4154 | } |
|
| 4148 | 4155 | if isAggregateType(typ) { |
| 4151 | 4158 | let layout = resolver::getTypeLayout(typ); |
|
| 4152 | 4159 | ||
| 4153 | 4160 | emit(self, il::Instr::Blit { dst, src, size: il::Val::Imm(layout.size as i64) }); |
|
| 4154 | 4161 | } else { |
|
| 4155 | 4162 | emit(self, il::Instr::Store { |
|
| 4156 | - | typ: ilType(self.low, typ), |
|
| 4163 | + | typ: ilType(&mut *self.low, typ), |
|
| 4157 | 4164 | src, |
|
| 4158 | 4165 | dst: base, |
|
| 4159 | 4166 | offset, |
|
| 4160 | 4167 | }); |
|
| 4161 | 4168 | } |
|
| 4162 | 4169 | } |
|
| 4163 | 4170 | ||
| 4164 | 4171 | /// Allocate stack space for a value and store it. Returns a pointer to the value. |
|
| 4165 | - | fn emitStackVal(self: *mut FnLowerer, typ: resolver::Type, val: il::Val) -> il::Val throws (LowerError) { |
|
| 4172 | + | unsafe fn emitStackVal(self: &mut FnLowerer, typ: resolver::Type, val: il::Val) -> il::Val throws (LowerError) { |
|
| 4166 | 4173 | let ptr = try emitReserve(self, typ); |
|
| 4167 | 4174 | try emitStore(self, ptr, 0, typ, val); |
|
| 4168 | 4175 | return il::Val::Reg(ptr); |
|
| 4169 | 4176 | } |
|
| 4170 | 4177 | ||
| 4171 | 4178 | /// Generic helper to build any tagged aggregate. |
|
| 4172 | 4179 | /// Reserves space based on the provided layout, stores the tag, and optionally |
|
| 4173 | 4180 | /// stores the payload value at `valOffset`. |
|
| 4174 | - | fn buildTagged( |
|
| 4175 | - | self: *mut FnLowerer, |
|
| 4181 | + | unsafe fn buildTagged( |
|
| 4182 | + | self: &mut FnLowerer, |
|
| 4176 | 4183 | layout: resolver::Layout, |
|
| 4177 | 4184 | tag: i64, |
|
| 4178 | 4185 | payload: ?il::Val, |
|
| 4179 | 4186 | payloadType: resolver::Type, |
|
| 4180 | 4187 | tagSize: u32, |
| 4203 | 4210 | /// Wrap a value in an optional type. |
|
| 4204 | 4211 | /// |
|
| 4205 | 4212 | /// For optional pointers (`?*T`), the value is returned as-is since pointers |
|
| 4206 | 4213 | /// use zero to represent `nil`. For other optionals, builds a tagged aggregate. |
|
| 4207 | 4214 | /// with the tag set to `1`, and the value as payload. |
|
| 4208 | - | fn wrapInOptional(self: *mut FnLowerer, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4215 | + | unsafe fn wrapInOptional(self: &mut FnLowerer, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4209 | 4216 | let case resolver::Type::Optional(inner) = optType else { |
|
| 4210 | 4217 | throw LowerError::ExpectedOptional; |
|
| 4211 | 4218 | }; |
|
| 4212 | 4219 | // Null-pointer-optimized (NPO) types are used as-is -- valid values are never null. |
|
| 4213 | 4220 | if resolver::isNullableType(*inner) { |
| 4221 | 4228 | ||
| 4222 | 4229 | /// Build a `nil` value for an optional type. |
|
| 4223 | 4230 | /// |
|
| 4224 | 4231 | /// For optional pointers (`?*T`), returns an immediate `0` (null pointer). |
|
| 4225 | 4232 | /// For other optionals, builds a tagged aggregate with tag set to `0` (absent). |
|
| 4226 | - | fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4233 | + | unsafe fn buildNilOptional(self: &mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4227 | 4234 | let case resolver::Type::Optional(inner) = optType |
|
| 4228 | 4235 | else throw LowerError::ExpectedOptional; |
|
| 4229 | 4236 | if let case resolver::Type::Pointer { .. } = *inner { |
|
| 4230 | 4237 | return il::Val::Imm(0); |
|
| 4231 | 4238 | } |
| 4237 | 4244 | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4238 | 4245 | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4239 | 4246 | } |
|
| 4240 | 4247 | ||
| 4241 | 4248 | /// Build a result value for throwing functions. |
|
| 4242 | - | fn buildResult( |
|
| 4243 | - | self: *mut FnLowerer, |
|
| 4249 | + | unsafe fn buildResult( |
|
| 4250 | + | self: &mut FnLowerer, |
|
| 4244 | 4251 | tag: i64, |
|
| 4245 | 4252 | payload: ?il::Val, |
|
| 4246 | 4253 | payloadType: resolver::Type |
|
| 4247 | 4254 | ) -> il::Val throws (LowerError) { |
|
| 4248 | 4255 | let successType = *self.fnType.returnType; |
| 4251 | 4258 | ); |
|
| 4252 | 4259 | return try buildTagged(self, layout, tag, payload, payloadType, resolver::PTR_SIZE as i32, RESULT_VAL_OFFSET); |
|
| 4253 | 4260 | } |
|
| 4254 | 4261 | ||
| 4255 | 4262 | /// Build a slice aggregate from a data pointer, length and capacity. |
|
| 4256 | - | fn buildSliceValue( |
|
| 4257 | - | self: *mut FnLowerer, |
|
| 4263 | + | unsafe fn buildSliceValue( |
|
| 4264 | + | self: &mut FnLowerer, |
|
| 4258 | 4265 | elemTy: *resolver::Type, |
|
| 4259 | 4266 | mutable: bool, |
|
| 4260 | 4267 | ptrVal: il::Val, |
|
| 4261 | 4268 | lenVal: il::Val, |
|
| 4262 | 4269 | capVal: il::Val |
| 4279 | 4286 | ||
| 4280 | 4287 | return il::Val::Reg(dst); |
|
| 4281 | 4288 | } |
|
| 4282 | 4289 | ||
| 4283 | 4290 | /// Build a trait object fat pointer from a data pointer and a v-table. |
|
| 4284 | - | fn buildTraitObject( |
|
| 4285 | - | self: *mut FnLowerer, |
|
| 4291 | + | unsafe fn buildTraitObject( |
|
| 4292 | + | self: &mut FnLowerer, |
|
| 4286 | 4293 | dataVal: il::Val, |
|
| 4287 | 4294 | traitInfo: *resolver::TraitType, |
|
| 4288 | - | inst: *resolver::InstanceEntry |
|
| 4295 | + | inst: &resolver::InstanceEntry |
|
| 4289 | 4296 | ) -> il::Val throws (LowerError) { |
|
| 4290 | - | let vName = vtableName(self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name); |
|
| 4297 | + | let vName = vtableName(&mut *self.low, inst.moduleId, inst.concreteTypeName, traitInfo.name); |
|
| 4291 | 4298 | ||
| 4292 | 4299 | // Reserve space for the trait object on the stack. |
|
| 4293 | 4300 | let slot = emitReserveLayout(self, resolver::Layout { |
|
| 4294 | 4301 | size: resolver::PTR_SIZE * 2, |
|
| 4295 | 4302 | alignment: resolver::PTR_SIZE, |
| 4312 | 4319 | }); |
|
| 4313 | 4320 | return il::Val::Reg(slot); |
|
| 4314 | 4321 | } |
|
| 4315 | 4322 | ||
| 4316 | 4323 | /// Compute a field pointer by adding a byte offset to a base address. |
|
| 4317 | - | fn emitPtrOffset(self: *mut FnLowerer, base: il::Reg, offset: i32) -> il::Reg { |
|
| 4324 | + | unsafe fn emitPtrOffset(self: &mut FnLowerer, base: il::Reg, offset: i32) -> il::Reg { |
|
| 4318 | 4325 | if offset == 0 { |
|
| 4319 | 4326 | return base; |
|
| 4320 | 4327 | } |
|
| 4321 | 4328 | let dst = nextReg(self); |
|
| 4322 | 4329 |
| 4330 | 4337 | return dst; |
|
| 4331 | 4338 | } |
|
| 4332 | 4339 | ||
| 4333 | 4340 | /// Emit an element address computation for array/slice indexing. |
|
| 4334 | 4341 | /// Computes: `base + idx * stride`. |
|
| 4335 | - | fn emitElem(self: *mut FnLowerer, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg { |
|
| 4342 | + | unsafe fn emitElem(self: &mut FnLowerer, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg { |
|
| 4336 | 4343 | // If index is zero, return base directly. |
|
| 4337 | 4344 | if idx == il::Val::Imm(0) { |
|
| 4338 | 4345 | return base; |
|
| 4339 | 4346 | } |
|
| 4340 | 4347 | // If stride is `1`, skip the multiply. |
| 4371 | 4378 | }); |
|
| 4372 | 4379 | return dst; |
|
| 4373 | 4380 | } |
|
| 4374 | 4381 | ||
| 4375 | 4382 | /// Emit a typed binary operation, returning the result as a value. |
|
| 4376 | - | fn emitTypedBinOp(self: *mut FnLowerer, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val { |
|
| 4383 | + | unsafe fn emitTypedBinOp(self: &mut FnLowerer, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val { |
|
| 4377 | 4384 | let dst = nextReg(self); |
|
| 4378 | 4385 | emit(self, il::Instr::BinOp { op, typ, dst, a, b }); |
|
| 4379 | 4386 | return il::Val::Reg(dst); |
|
| 4380 | 4387 | } |
|
| 4381 | 4388 | ||
| 4382 | 4389 | /// Emit a tag comparison for void variant equality/inequality. |
|
| 4383 | - | fn emitTagCmp(self: *mut FnLowerer, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val |
|
| 4390 | + | unsafe fn emitTagCmp(self: &mut FnLowerer, op: ast::BinaryOp, val: il::Val, tagIdx: i64, valType: resolver::Type) -> il::Val |
|
| 4384 | 4391 | throws (LowerError) |
|
| 4385 | 4392 | { |
|
| 4386 | 4393 | let reg = emitValToReg(self, val); |
|
| 4387 | 4394 | ||
| 4388 | 4395 | // For all-void unions, the value *is* the tag, not a pointer. |
| 4395 | 4402 | let binOp = il::BinOp::Eq if op == ast::BinaryOp::Eq else il::BinOp::Ne; |
|
| 4396 | 4403 | return emitTypedBinOp(self, binOp, il::Type::W8, tag, il::Val::Imm(tagIdx)); |
|
| 4397 | 4404 | } |
|
| 4398 | 4405 | ||
| 4399 | 4406 | /// Logical "and" between two values. Returns the result in a register. |
|
| 4400 | - | fn emitLogicalAnd(self: *mut FnLowerer, left: ?il::Val, right: il::Val) -> il::Val { |
|
| 4407 | + | unsafe fn emitLogicalAnd(self: &mut FnLowerer, left: ?il::Val, right: il::Val) -> il::Val { |
|
| 4401 | 4408 | let prev = left else { |
|
| 4402 | 4409 | return right; |
|
| 4403 | 4410 | }; |
|
| 4404 | 4411 | return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, prev, right); |
|
| 4405 | 4412 | } |
| 4407 | 4414 | ////////////////////////// |
|
| 4408 | 4415 | // Aggregate Comparison // |
|
| 4409 | 4416 | ////////////////////////// |
|
| 4410 | 4417 | ||
| 4411 | 4418 | /// Emit an equality test for values at an offset of the given base registers. |
|
| 4412 | - | fn emitEqAtOffset( |
|
| 4413 | - | self: *mut FnLowerer, |
|
| 4419 | + | unsafe fn emitEqAtOffset( |
|
| 4420 | + | self: &mut FnLowerer, |
|
| 4414 | 4421 | left: il::Reg, |
|
| 4415 | 4422 | right: il::Reg, |
|
| 4416 | 4423 | offset: i32, |
|
| 4417 | 4424 | fieldType: resolver::Type |
|
| 4418 | 4425 | ) -> il::Val throws (LowerError) { |
| 4422 | 4429 | } |
|
| 4423 | 4430 | // For scalar types, load and compare directly. |
|
| 4424 | 4431 | let a = emitLoad(self, left, offset, fieldType); |
|
| 4425 | 4432 | let b = emitLoad(self, right, offset, fieldType); |
|
| 4426 | 4433 | let dst = nextReg(self); |
|
| 4427 | - | emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ: ilType(self.low, fieldType), dst, a, b }); |
|
| 4434 | + | emit(self, il::Instr::BinOp { op: il::BinOp::Eq, typ: ilType(&mut *self.low, fieldType), dst, a, b }); |
|
| 4428 | 4435 | ||
| 4429 | 4436 | return il::Val::Reg(dst); |
|
| 4430 | 4437 | } |
|
| 4431 | 4438 | ||
| 4432 | 4439 | /// Compare two record values for equality. |
|
| 4433 | - | fn lowerRecordEq( |
|
| 4434 | - | self: *mut FnLowerer, |
|
| 4440 | + | unsafe fn lowerRecordEq( |
|
| 4441 | + | self: &mut FnLowerer, |
|
| 4435 | 4442 | recInfo: resolver::RecordType, |
|
| 4436 | 4443 | a: il::Reg, |
|
| 4437 | 4444 | b: il::Reg, |
|
| 4438 | 4445 | offset: i32 |
|
| 4439 | 4446 | ) -> il::Val throws (LowerError) { |
| 4449 | 4456 | } |
|
| 4450 | 4457 | return il::Val::Imm(1); |
|
| 4451 | 4458 | } |
|
| 4452 | 4459 | ||
| 4453 | 4460 | /// Compare two slice values for equality. |
|
| 4454 | - | fn lowerSliceEq( |
|
| 4455 | - | self: *mut FnLowerer, |
|
| 4461 | + | unsafe fn lowerSliceEq( |
|
| 4462 | + | self: &mut FnLowerer, |
|
| 4456 | 4463 | elemTy: *resolver::Type, |
|
| 4457 | 4464 | mutable: bool, |
|
| 4458 | 4465 | a: il::Reg, |
|
| 4459 | 4466 | b: il::Reg, |
|
| 4460 | 4467 | offset: i32 |
| 4479 | 4486 | /// branchless formulation: `tagEq AND (tagNil OR payloadEq)` |
|
| 4480 | 4487 | /// |
|
| 4481 | 4488 | /// For inner types that may contain uninitialized data when `nil` (unions, |
|
| 4482 | 4489 | /// nested optionals), the payload comparison is guarded behind a branch |
|
| 4483 | 4490 | /// so that `nil` payloads are never inspected. |
|
| 4484 | - | fn lowerOptionalEq( |
|
| 4485 | - | self: *mut FnLowerer, |
|
| 4491 | + | unsafe fn lowerOptionalEq( |
|
| 4492 | + | self: &mut FnLowerer, |
|
| 4486 | 4493 | inner: resolver::Type, |
|
| 4487 | 4494 | a: il::Reg, |
|
| 4488 | 4495 | b: il::Reg, |
|
| 4489 | 4496 | offset: i32 |
|
| 4490 | 4497 | ) -> il::Val throws (LowerError) { |
| 4571 | 4578 | /// For all-void unions, we skip the control flow entirely and just compare |
|
| 4572 | 4579 | /// the tags directly. |
|
| 4573 | 4580 | /// |
|
| 4574 | 4581 | /// TODO: Could be optimized to branchless when all non-void variants share |
|
| 4575 | 4582 | /// the same payload type: `tagEq AND (isVoidVariant OR payloadEq)`. |
|
| 4576 | - | fn lowerUnionEq( |
|
| 4577 | - | self: *mut FnLowerer, |
|
| 4583 | + | unsafe fn lowerUnionEq( |
|
| 4584 | + | self: &mut FnLowerer, |
|
| 4578 | 4585 | unionInfo: resolver::UnionType, |
|
| 4579 | 4586 | a: il::Reg, |
|
| 4580 | 4587 | b: il::Reg, |
|
| 4581 | 4588 | offset: i32 |
|
| 4582 | 4589 | ) -> il::Val throws (LowerError) { |
| 4615 | 4622 | ||
| 4616 | 4623 | // Create comparison blocks for each non-void variant and build switch cases. |
|
| 4617 | 4624 | // Void variants jump directly to merge with `true`. |
|
| 4618 | 4625 | let trueArgs = try allocVal(self, il::Val::Imm(1)); |
|
| 4619 | 4626 | let cases = try! alloc::allocSlice( |
|
| 4620 | - | self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32 |
|
| 4627 | + | &mut *self.low.fnArena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), unionInfo.variants.len as u32 |
|
| 4621 | 4628 | ) as *mut [il::SwitchCase]; |
|
| 4622 | 4629 | ||
| 4623 | 4630 | let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined; |
|
| 4624 | 4631 | for variant, i in unionInfo.variants { |
|
| 4625 | 4632 | if variant.valueType == resolver::Type::Void { |
| 4678 | 4685 | try switchToAndSeal(self, mergeBlock); |
|
| 4679 | 4686 | return il::Val::Reg(resultReg); |
|
| 4680 | 4687 | } |
|
| 4681 | 4688 | ||
| 4682 | 4689 | /// Compare two array values for equality, element by element. |
|
| 4683 | - | fn lowerArrayEq( |
|
| 4684 | - | self: *mut FnLowerer, |
|
| 4690 | + | unsafe fn lowerArrayEq( |
|
| 4691 | + | self: &mut FnLowerer, |
|
| 4685 | 4692 | arr: resolver::ArrayType, |
|
| 4686 | 4693 | a: il::Reg, |
|
| 4687 | 4694 | b: il::Reg, |
|
| 4688 | 4695 | offset: i32 |
|
| 4689 | 4696 | ) -> il::Val throws (LowerError) { |
| 4702 | 4709 | // Empty arrays are always equal. |
|
| 4703 | 4710 | return il::Val::Imm(1); |
|
| 4704 | 4711 | } |
|
| 4705 | 4712 | ||
| 4706 | 4713 | /// Compare two aggregate values for equality. |
|
| 4707 | - | fn lowerAggregateEq( |
|
| 4708 | - | self: *mut FnLowerer, |
|
| 4714 | + | unsafe fn lowerAggregateEq( |
|
| 4715 | + | self: &mut FnLowerer, |
|
| 4709 | 4716 | typ: resolver::Type, |
|
| 4710 | 4717 | a: il::Reg, |
|
| 4711 | 4718 | b: il::Reg, |
|
| 4712 | 4719 | offset: i32 |
|
| 4713 | 4720 | ) -> il::Val throws (LowerError) { |
| 4736 | 4743 | } |
|
| 4737 | 4744 | } |
|
| 4738 | 4745 | ||
| 4739 | 4746 | /// Lower a record literal expression. Handles both plain records and union variant |
|
| 4740 | 4747 | /// record literals like `Union::Variant { field: value }`. |
|
| 4741 | - | fn lowerRecordLit(self: *mut FnLowerer, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) { |
|
| 4748 | + | unsafe fn lowerRecordLit(self: &mut FnLowerer, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) { |
|
| 4742 | 4749 | let typ = try typeOf(self, node); |
|
| 4743 | 4750 | match typ { |
|
| 4744 | 4751 | case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => { |
|
| 4745 | 4752 | let dst = try emitReserve(self, typ); |
|
| 4746 | 4753 | try lowerRecordFields(self, dst, &recInfo, lit.fields, 0); |
| 4767 | 4774 | emitStoreW8At(self, il::Val::Imm(index as i64), dst, TVAL_TAG_OFFSET); |
|
| 4768 | 4775 | try lowerRecordFields(self, dst, &recInfo, lit.fields, valOffset); |
|
| 4769 | 4776 | ||
| 4770 | 4777 | return il::Val::Reg(dst); |
|
| 4771 | 4778 | } |
|
| 4772 | - | else => throw LowerError::UnexpectedType(&typ), |
|
| 4779 | + | else => throw LowerError::UnexpectedType(typ), |
|
| 4773 | 4780 | } |
|
| 4774 | 4781 | } |
|
| 4775 | 4782 | ||
| 4776 | 4783 | /// Lower fields of a record literal into a destination register. |
|
| 4777 | 4784 | /// The `offset` is added to each field's offset when storing. |
|
| 4778 | - | fn lowerRecordFields( |
|
| 4779 | - | self: *mut FnLowerer, |
|
| 4785 | + | unsafe fn lowerRecordFields( |
|
| 4786 | + | self: &mut FnLowerer, |
|
| 4780 | 4787 | dst: il::Reg, |
|
| 4781 | - | recInfo: *resolver::RecordType, |
|
| 4788 | + | recInfo: &resolver::RecordType, |
|
| 4782 | 4789 | fields: *mut [*ast::Node], |
|
| 4783 | 4790 | offset: i32 |
|
| 4784 | 4791 | ) throws (LowerError) { |
|
| 4785 | 4792 | for fieldNode, i in fields { |
|
| 4786 | 4793 | let case ast::NodeValue::RecordLitField(field) = fieldNode.value else { |
|
| 4787 | 4794 | throw LowerError::UnexpectedNodeValue(fieldNode); |
|
| 4788 | 4795 | }; |
|
| 4789 | 4796 | let mut fieldIdx: u32 = i; |
|
| 4790 | 4797 | if recInfo.labeled { |
|
| 4791 | - | let idx = resolver::recordFieldIndexFor(self.low.resolver, fieldNode) else { |
|
| 4798 | + | let idx = resolver::recordFieldIndexFor(&*self.low.resolver, fieldNode) else { |
|
| 4792 | 4799 | throw LowerError::MissingMetadata; |
|
| 4793 | 4800 | }; |
|
| 4794 | 4801 | set fieldIdx = idx; |
|
| 4795 | 4802 | } |
|
| 4796 | 4803 | // Skip `undefined` fields, they need no initialization. |
| 4803 | 4810 | } |
|
| 4804 | 4811 | } |
|
| 4805 | 4812 | } |
|
| 4806 | 4813 | ||
| 4807 | 4814 | /// Lower an unlabeled record constructor call. |
|
| 4808 | - | fn lowerRecordCtor(self: *mut FnLowerer, nominal: *resolver::NominalType, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 4815 | + | unsafe fn lowerRecordCtor(self: &mut FnLowerer, nominal: *resolver::NominalType, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 4809 | 4816 | let case resolver::NominalType::Record(recInfo) = *nominal else { |
|
| 4810 | 4817 | throw LowerError::ExpectedRecord; |
|
| 4811 | 4818 | }; |
|
| 4812 | 4819 | let typ = resolver::Type::Nominal(nominal); |
|
| 4813 | 4820 | let dst = try emitReserve(self, typ); |
| 4822 | 4829 | } |
|
| 4823 | 4830 | return il::Val::Reg(dst); |
|
| 4824 | 4831 | } |
|
| 4825 | 4832 | ||
| 4826 | 4833 | /// Lower an array literal expression like `[1, 2, 3]`. |
|
| 4827 | - | fn lowerArrayLit(self: *mut FnLowerer, node: *ast::Node, elements: *mut [*ast::Node]) -> il::Val |
|
| 4834 | + | unsafe fn lowerArrayLit(self: &mut FnLowerer, node: *ast::Node, elements: *mut [*ast::Node]) -> il::Val |
|
| 4828 | 4835 | throws (LowerError) |
|
| 4829 | 4836 | { |
|
| 4830 | 4837 | let typ = try typeOf(self, node); |
|
| 4831 | 4838 | let case resolver::Type::Array(arrInfo) = typ else { |
|
| 4832 | 4839 | throw LowerError::ExpectedArray; |
| 4845 | 4852 | } |
|
| 4846 | 4853 | ||
| 4847 | 4854 | /// Lower an array repeat literal expression like `[42; 3]`. |
|
| 4848 | 4855 | /// Unrolls the initialization at compile time. |
|
| 4849 | 4856 | // TODO: Beyond a certain length, lower this to a loop. |
|
| 4850 | - | fn lowerArrayRepeatLit(self: *mut FnLowerer, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val |
|
| 4857 | + | unsafe fn lowerArrayRepeatLit(self: &mut FnLowerer, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val |
|
| 4851 | 4858 | throws (LowerError) |
|
| 4852 | 4859 | { |
|
| 4853 | 4860 | let typ = try typeOf(self, node); |
|
| 4854 | 4861 | let case resolver::Type::Array(arrInfo) = typ else { |
|
| 4855 | 4862 | throw LowerError::ExpectedArray; |
| 4869 | 4876 | } |
|
| 4870 | 4877 | return il::Val::Reg(dst); |
|
| 4871 | 4878 | } |
|
| 4872 | 4879 | ||
| 4873 | 4880 | /// Lower a union constructor call like `Union::Variant(...)`. |
|
| 4874 | - | fn lowerUnionCtor(self: *mut FnLowerer, node: *ast::Node, sym: *mut resolver::Symbol, call: ast::Call) -> il::Val |
|
| 4881 | + | unsafe fn lowerUnionCtor(self: &mut FnLowerer, node: *ast::Node, sym: *mut resolver::Symbol, call: ast::Call) -> il::Val |
|
| 4875 | 4882 | throws (LowerError) |
|
| 4876 | 4883 | { |
|
| 4877 | 4884 | let unionTy = try typeOf(self, node); |
|
| 4878 | 4885 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else { |
|
| 4879 | 4886 | throw LowerError::ExpectedVariant; |
| 4891 | 4898 | } |
|
| 4892 | 4899 | return try buildTagged(self, resolver::getTypeLayout(unionTy), index as i64, payloadVal, payloadType, 1, valOffset); |
|
| 4893 | 4900 | } |
|
| 4894 | 4901 | ||
| 4895 | 4902 | /// Lower a field access into a pointer to the field. |
|
| 4896 | - | fn lowerFieldRef(self: *mut FnLowerer, access: ast::Access) -> FieldRef throws (LowerError) { |
|
| 4903 | + | unsafe fn lowerFieldRef(self: &mut FnLowerer, access: ast::Access) -> FieldRef throws (LowerError) { |
|
| 4897 | 4904 | let parentTy = try typeOf(self, access.parent); |
|
| 4898 | 4905 | let subjectTy = resolver::autoDeref(parentTy); |
|
| 4899 | - | let fieldIdx = resolver::recordFieldIndexFor(self.low.resolver, access.child) else { |
|
| 4906 | + | let fieldIdx = resolver::recordFieldIndexFor(&*self.low.resolver, access.child) else { |
|
| 4900 | 4907 | throw LowerError::MissingMetadata; |
|
| 4901 | 4908 | }; |
|
| 4902 | 4909 | let fieldInfo = resolver::getRecordField(subjectTy, fieldIdx) else { |
|
| 4903 | 4910 | throw LowerError::FieldNotFound; |
|
| 4904 | 4911 | }; |
| 4911 | 4918 | fieldType: fieldInfo.fieldType, |
|
| 4912 | 4919 | }; |
|
| 4913 | 4920 | } |
|
| 4914 | 4921 | ||
| 4915 | 4922 | /// Lower a field access expression. |
|
| 4916 | - | fn lowerFieldAccess(self: *mut FnLowerer, access: ast::Access) -> il::Val throws (LowerError) { |
|
| 4923 | + | unsafe fn lowerFieldAccess(self: &mut FnLowerer, access: ast::Access) -> il::Val throws (LowerError) { |
|
| 4917 | 4924 | let fieldRef = try lowerFieldRef(self, access); |
|
| 4918 | 4925 | return emitRead(self, fieldRef.base, fieldRef.offset, fieldRef.fieldType); |
|
| 4919 | 4926 | } |
|
| 4920 | 4927 | ||
| 4921 | 4928 | /// Compute data pointer and element count for a range into a container. |
|
| 4922 | 4929 | /// Used by both slice range expressions (`&a[start..end]`) and slice |
|
| 4923 | 4930 | /// assignments (`a[start..end] = value`). |
|
| 4924 | - | fn resolveSliceRangePtr( |
|
| 4925 | - | self: *mut FnLowerer, |
|
| 4931 | + | unsafe fn resolveSliceRangePtr( |
|
| 4932 | + | self: &mut FnLowerer, |
|
| 4926 | 4933 | container: *ast::Node, |
|
| 4927 | 4934 | range: ast::Range, |
|
| 4928 | 4935 | info: resolver::SliceRangeInfo |
|
| 4929 | 4936 | ) -> SliceRangeResult throws (LowerError) { |
|
| 4930 | 4937 | let baseVal = try lowerExpr(self, container); |
| 4985 | 4992 | } |
|
| 4986 | 4993 | return SliceRangeResult { dataReg, count }; |
|
| 4987 | 4994 | } |
|
| 4988 | 4995 | ||
| 4989 | 4996 | /// Lower a slice range expression into a slice header value. |
|
| 4990 | - | fn lowerSliceRange( |
|
| 4991 | - | self: *mut FnLowerer, |
|
| 4997 | + | unsafe fn lowerSliceRange( |
|
| 4998 | + | self: &mut FnLowerer, |
|
| 4992 | 4999 | container: *ast::Node, |
|
| 4993 | 5000 | range: ast::Range, |
|
| 4994 | 5001 | sliceNode: *ast::Node |
|
| 4995 | 5002 | ) -> il::Val throws (LowerError) { |
|
| 4996 | - | let info = resolver::sliceRangeInfoFor(self.low.resolver, sliceNode) else { |
|
| 5003 | + | let info = resolver::sliceRangeInfoFor(&*self.low.resolver, sliceNode) else { |
|
| 4997 | 5004 | throw LowerError::MissingMetadata; |
|
| 4998 | 5005 | }; |
|
| 4999 | 5006 | let r = try resolveSliceRangePtr(self, container, range, info); |
|
| 5000 | 5007 | return try buildSliceValue( |
|
| 5001 | 5008 | self, info.itemType, info.mutable, il::Val::Reg(r.dataReg), r.count, r.count |
|
| 5002 | 5009 | ); |
|
| 5003 | 5010 | } |
|
| 5004 | 5011 | ||
| 5005 | 5012 | /// Lower an address-of (`&x`) expression. |
|
| 5006 | - | fn lowerAddressOf(self: *mut FnLowerer, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) { |
|
| 5013 | + | unsafe fn lowerAddressOf(self: &mut FnLowerer, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) { |
|
| 5007 | 5014 | // Handle subscript: `&ary[i]` or `&ary[start..end]`. |
|
| 5008 | 5015 | if let case ast::NodeValue::Subscript { container, index } = addr.target.value { |
|
| 5009 | 5016 | if let case ast::NodeValue::Range(range) = index.value { |
|
| 5010 | 5017 | return try lowerSliceRange(self, container, range, node); |
|
| 5011 | 5018 | } |
| 5035 | 5042 | // Already address-taken; return existing stack pointer. |
|
| 5036 | 5043 | return val; |
|
| 5037 | 5044 | } |
|
| 5038 | 5045 | // Materialize a stack slot using the declaration's resolved |
|
| 5039 | 5046 | // layout so `align(N)` on locals is honored. |
|
| 5040 | - | let layout = resolver::getLayout(self.low.resolver, addr.target, typ); |
|
| 5047 | + | let layout = resolver::getLayout(&*self.low.resolver, addr.target, typ); |
|
| 5041 | 5048 | let slot = emitReserveLayout(self, layout); |
|
| 5042 | 5049 | try emitStore(self, slot, 0, typ, val); |
|
| 5043 | 5050 | let stackVal = il::Val::Reg(slot); |
|
| 5044 | 5051 | ||
| 5045 | 5052 | set self.vars[*v].addressTaken = true; |
|
| 5046 | 5053 | defVar(self, v, stackVal); |
|
| 5047 | 5054 | ||
| 5048 | 5055 | return stackVal; |
|
| 5049 | 5056 | } |
|
| 5050 | 5057 | // Fall back to symbol lookup for constants/statics. |
|
| 5051 | - | if let sym = resolver::nodeData(self.low.resolver, addr.target).sym { |
|
| 5058 | + | if let sym = resolver::nodeData(&*self.low.resolver, addr.target).sym { |
|
| 5052 | 5059 | return il::Val::Reg(emitDataAddr(self, sym)); |
|
| 5053 | 5060 | } else { |
|
| 5054 | 5061 | throw LowerError::MissingSymbol(node); |
|
| 5055 | 5062 | } |
|
| 5056 | 5063 | } |
| 5069 | 5076 | } |
|
| 5070 | 5077 | throw LowerError::UnexpectedNodeValue(addr.target); |
|
| 5071 | 5078 | } |
|
| 5072 | 5079 | ||
| 5073 | 5080 | /// Lower an addressed array literal as a slice. |
|
| 5074 | - | fn lowerArrayLiteralSlice( |
|
| 5075 | - | self: *mut FnLowerer, |
|
| 5081 | + | unsafe fn lowerArrayLiteralSlice( |
|
| 5082 | + | self: &mut FnLowerer, |
|
| 5076 | 5083 | sliceNode: *ast::Node, |
|
| 5077 | 5084 | arrayNode: *ast::Node |
|
| 5078 | 5085 | ) -> il::Val throws (LowerError) { |
|
| 5079 | 5086 | let sliceTy = try typeOf(self, sliceNode); |
|
| 5080 | 5087 | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 5081 | - | throw LowerError::UnexpectedType(&sliceTy); |
|
| 5088 | + | throw LowerError::UnexpectedType(sliceTy); |
|
| 5082 | 5089 | }; |
|
| 5083 | 5090 | let arrayTy = try typeOf(self, arrayNode); |
|
| 5084 | 5091 | let case resolver::Type::Array(arrayInfo) = arrayTy else { |
|
| 5085 | 5092 | throw LowerError::ExpectedArray; |
|
| 5086 | 5093 | }; |
| 5088 | 5095 | if length == 0 { |
|
| 5089 | 5096 | return try buildSliceValue( |
|
| 5090 | 5097 | self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0) |
|
| 5091 | 5098 | ); |
|
| 5092 | 5099 | } |
|
| 5093 | - | if resolver::isConstExpr(self.low.resolver, arrayNode) { |
|
| 5100 | + | if resolver::isConstExpr(&*self.low.resolver, arrayNode) { |
|
| 5101 | + | let fnName = self.fnName; |
|
| 5094 | 5102 | let mut b = dataBuilder(self.low.allocator); |
|
| 5095 | 5103 | match arrayNode.value { |
|
| 5096 | 5104 | case ast::NodeValue::ArrayLit(elements) => |
|
| 5097 | - | try lowerConstArrayLitInto(self.low, elements, arrayTy, self.fnName, &mut b), |
|
| 5105 | + | try lowerConstArrayLitInto(&mut *self.low, elements, arrayTy, fnName, &mut b), |
|
| 5098 | 5106 | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 5099 | - | try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, self.fnName, &mut b), |
|
| 5107 | + | try lowerConstArrayRepeatInto(&mut *self.low, repeat, arrayTy, fnName, &mut b), |
|
| 5100 | 5108 | else => throw LowerError::UnexpectedNodeValue(arrayNode), |
|
| 5101 | 5109 | } |
|
| 5102 | 5110 | let result = dataBuilderFinish(&b); |
|
| 5103 | 5111 | let alignment = resolver::getTypeLayout(*item).alignment; |
|
| 5104 | 5112 | return try lowerConstDataAsSlice( |
| 5113 | 5121 | ||
| 5114 | 5122 | /// Lower the common element pointer computation for subscript operations. |
|
| 5115 | 5123 | /// Handles both arrays and slices by resolving the container type, extracting |
|
| 5116 | 5124 | /// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute |
|
| 5117 | 5125 | /// the element address. |
|
| 5118 | - | fn lowerElemPtr( |
|
| 5119 | - | self: *mut FnLowerer, container: *ast::Node, index: *ast::Node |
|
| 5126 | + | unsafe fn lowerElemPtr( |
|
| 5127 | + | self: &mut FnLowerer, container: *ast::Node, index: *ast::Node |
|
| 5120 | 5128 | ) -> ElemPtrResult throws (LowerError) { |
|
| 5121 | 5129 | let containerTy = try typeOf(self, container); |
|
| 5122 | 5130 | let subjectTy = resolver::autoDeref(containerTy); |
|
| 5123 | 5131 | let baseVal = try lowerExpr(self, container); |
|
| 5124 | 5132 | let indexVal = try lowerExpr(self, index); |
| 5139 | 5147 | case resolver::Type::Array(arrInfo) => { |
|
| 5140 | 5148 | set elemType = *arrInfo.item; |
|
| 5141 | 5149 | // Runtime safety check: index must be strictly less than array length. |
|
| 5142 | 5150 | // Skip when the index is a compile-time constant, since we check |
|
| 5143 | 5151 | // that in the resolver. |
|
| 5144 | - | if not resolver::isConstExpr(self.low.resolver, index) { |
|
| 5152 | + | if not resolver::isConstExpr(&*self.low.resolver, index) { |
|
| 5145 | 5153 | let arrLen = il::Val::Imm(arrInfo.length as i64); |
|
| 5146 | 5154 | try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, arrLen); |
|
| 5147 | 5155 | } |
|
| 5148 | 5156 | } |
|
| 5149 | 5157 | else => throw LowerError::ExpectedSliceOrArray, |
| 5155 | 5163 | } |
|
| 5156 | 5164 | ||
| 5157 | 5165 | /// Lower a dereference expression. |
|
| 5158 | 5166 | /// Handles both pointer deref (`*ptr`) and record deref (`*r` on single-field |
|
| 5159 | 5167 | /// unlabeled record). Both read at offset 0 using the resolver-assigned type. |
|
| 5160 | - | fn lowerDeref(self: *mut FnLowerer, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) { |
|
| 5168 | + | unsafe fn lowerDeref(self: &mut FnLowerer, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) { |
|
| 5161 | 5169 | let type = try typeOf(self, node); |
|
| 5162 | 5170 | let ptrVal = try lowerExpr(self, target); |
|
| 5163 | 5171 | let ptrReg = emitValToReg(self, ptrVal); |
|
| 5164 | 5172 | ||
| 5165 | 5173 | return emitRead(self, ptrReg, 0, type); |
|
| 5166 | 5174 | } |
|
| 5167 | 5175 | ||
| 5168 | 5176 | /// Lower a subscript expression. |
|
| 5169 | - | fn lowerSubscript(self: *mut FnLowerer, node: *ast::Node, container: *ast::Node, index: *ast::Node) -> il::Val |
|
| 5177 | + | unsafe fn lowerSubscript(self: &mut FnLowerer, node: *ast::Node, container: *ast::Node, index: *ast::Node) -> il::Val |
|
| 5170 | 5178 | throws (LowerError) |
|
| 5171 | 5179 | { |
|
| 5172 | 5180 | if let case ast::NodeValue::Range(_) = index.value { |
|
| 5173 | 5181 | panic "lowerSubscript: range subscript must use address-of (&)"; |
|
| 5174 | 5182 | } |
| 5176 | 5184 | ||
| 5177 | 5185 | return emitRead(self, result.elemReg, 0, result.elemType); |
|
| 5178 | 5186 | } |
|
| 5179 | 5187 | ||
| 5180 | 5188 | /// Lower a let binding. |
|
| 5181 | - | fn lowerLet(self: *mut FnLowerer, node: *ast::Node, l: ast::Let) throws (LowerError) { |
|
| 5189 | + | unsafe fn lowerLet(self: &mut FnLowerer, node: *ast::Node, l: ast::Let) throws (LowerError) { |
|
| 5182 | 5190 | // Evaluate value. |
|
| 5183 | 5191 | let val = try lowerExpr(self, l.value); |
|
| 5184 | 5192 | // Handle placeholder pattern: `let _ = expr;` |
|
| 5185 | 5193 | if let case ast::NodeValue::Placeholder = l.ident.value { |
|
| 5186 | 5194 | return; |
|
| 5187 | 5195 | } |
|
| 5188 | 5196 | let case ast::NodeValue::Ident(name) = l.ident.value else { |
|
| 5189 | 5197 | throw LowerError::ExpectedIdentifier; |
|
| 5190 | 5198 | }; |
|
| 5191 | 5199 | let typ = try typeOf(self, l.value); |
|
| 5192 | - | let ilType = ilType(self.low, typ); |
|
| 5200 | + | let ilType = ilType(&mut *self.low, typ); |
|
| 5193 | 5201 | let mut varVal = val; |
|
| 5194 | 5202 | ||
| 5195 | 5203 | // Aggregates with persistent storage need a local copy to avoid aliasing. |
|
| 5196 | 5204 | // Temporaries such as literals or call results can be adopted directly. |
|
| 5197 | 5205 | // This is because aggregates are represented as memory addresses |
| 5201 | 5209 | // Void variant literals (e.g. `Option::None`) use scope access syntax and |
|
| 5202 | 5210 | // are flagged as place expressions, but they are freshly constructed |
|
| 5203 | 5211 | // temporaries with no persistent storage. |
|
| 5204 | 5212 | if isAggregateType(typ) and |
|
| 5205 | 5213 | ast::isPlaceExpr(l.value) and |
|
| 5206 | - | voidVariantIndex(self.low.resolver, l.value) == nil { |
|
| 5214 | + | voidVariantIndex(&*self.low.resolver, l.value) == nil { |
|
| 5207 | 5215 | set varVal = try emitStackVal(self, typ, val); |
|
| 5208 | 5216 | } |
|
| 5209 | 5217 | ||
| 5210 | 5218 | // If the resolver determined that this variable's address is taken |
|
| 5211 | 5219 | // anywhere in the function, allocate a stack slot immediately so the |
|
| 5212 | 5220 | // SSA value is always a pointer. This avoids mixing integer and pointer |
|
| 5213 | 5221 | // values in loop phis when `&var` or `&mut var` appears inside a loop. |
|
| 5214 | 5222 | if not isAggregateType(typ) { |
|
| 5215 | - | if let sym = resolver::nodeData(self.low.resolver, node).sym { |
|
| 5223 | + | if let sym = resolver::nodeData(&*self.low.resolver, node).sym { |
|
| 5216 | 5224 | if let case resolver::SymbolData::Value { addressTaken, .. } = sym.data; addressTaken { |
|
| 5217 | - | let layout = resolver::getLayout(self.low.resolver, node, typ); |
|
| 5225 | + | let layout = resolver::getLayout(&*self.low.resolver, node, typ); |
|
| 5218 | 5226 | let slot = emitReserveLayout(self, layout); |
|
| 5219 | 5227 | try emitStore(self, slot, 0, typ, varVal); |
|
| 5220 | 5228 | ||
| 5221 | 5229 | let v = newVar(self, name, ilType, l.mutable, il::Val::Reg(slot)); |
|
| 5222 | 5230 | set self.vars[*v].addressTaken = true; |
| 5240 | 5248 | /// |
|
| 5241 | 5249 | /// @entry -> (true) @then ---> @end <--. |
|
| 5242 | 5250 | /// | ) |
|
| 5243 | 5251 | /// `---- (false) -------------------' |
|
| 5244 | 5252 | /// |
|
| 5245 | - | fn lowerIf(self: *mut FnLowerer, i: ast::If) throws (LowerError) { |
|
| 5253 | + | unsafe fn lowerIf(self: &mut FnLowerer, i: ast::If) throws (LowerError) { |
|
| 5246 | 5254 | let thenBlock = try createBlock(self, "then"); |
|
| 5247 | 5255 | ||
| 5248 | 5256 | if let elseNode = i.elseBranch { // If-else case. |
|
| 5249 | 5257 | let elseBlock = try createBlock(self, "else"); |
|
| 5250 | 5258 | try emitCondBranch(self, i.condition, thenBlock, elseBlock); |
| 5304 | 5312 | } |
|
| 5305 | 5313 | ||
| 5306 | 5314 | /// Lower an assignment target that designates a memory location, and return |
|
| 5307 | 5315 | /// the address to store through. Returns `nil` without emitting anything for |
|
| 5308 | 5316 | /// targets that aren't memory-backed, such as locals tracked in SSA. |
|
| 5309 | - | fn lowerPlace(self: *mut FnLowerer, target: *ast::Node) -> ?FieldRef throws (LowerError) { |
|
| 5317 | + | unsafe fn lowerPlace(self: &mut FnLowerer, target: *ast::Node) -> ?FieldRef throws (LowerError) { |
|
| 5310 | 5318 | match target.value { |
|
| 5311 | 5319 | case ast::NodeValue::FieldAccess(access) => { |
|
| 5312 | 5320 | return try lowerFieldRef(self, access); |
|
| 5313 | 5321 | } |
|
| 5314 | 5322 | case ast::NodeValue::Deref(pointer) => { |
| 5330 | 5338 | /// whether it was handled. Nothing is emitted when it isn't. |
|
| 5331 | 5339 | /// |
|
| 5332 | 5340 | /// Compound assignments share their target node with the left operand of the |
|
| 5333 | 5341 | /// desugared binary expression. Resolve that place once so side effects in a |
|
| 5334 | 5342 | /// dereference, field parent, or subscript index are not repeated for the store. |
|
| 5335 | - | fn lowerCompoundAssign( |
|
| 5336 | - | self: *mut FnLowerer, expr: *ast::Node, binop: ast::BinOp |
|
| 5343 | + | unsafe fn lowerCompoundAssign( |
|
| 5344 | + | self: &mut FnLowerer, expr: *ast::Node, binop: ast::BinOp |
|
| 5337 | 5345 | ) -> bool throws (LowerError) { |
|
| 5338 | 5346 | let place = try lowerPlace(self, binop.left) else return false; |
|
| 5339 | 5347 | let current = emitRead(self, place.base, place.offset, place.fieldType); |
|
| 5340 | 5348 | let left = try applyCoercion(self, binop.left, current); |
|
| 5341 | 5349 | let right = try lowerExpr(self, binop.right); |
|
| 5342 | 5350 | let exprType = try typeOf(self, expr); |
|
| 5343 | 5351 | let result = emitScalarBinOp( |
|
| 5344 | - | self, binop.op, ilType(self.low, exprType), left, right, isUnsignedType(exprType) |
|
| 5352 | + | self, binop.op, ilType(&mut *self.low, exprType), left, right, isUnsignedType(exprType) |
|
| 5345 | 5353 | ); |
|
| 5346 | 5354 | let assigned = try applyCoercion(self, expr, result); |
|
| 5347 | 5355 | try emitStore(self, place.base, place.offset, place.fieldType, assigned); |
|
| 5348 | 5356 | ||
| 5349 | 5357 | return true; |
|
| 5350 | 5358 | } |
|
| 5351 | 5359 | ||
| 5352 | 5360 | /// Lower an assignment statement. |
|
| 5353 | - | fn lowerAssign(self: *mut FnLowerer, node: *ast::Node, a: ast::Assign) throws (LowerError) { |
|
| 5361 | + | unsafe fn lowerAssign(self: &mut FnLowerer, node: *ast::Node, a: ast::Assign) throws (LowerError) { |
|
| 5354 | 5362 | // Slice assignment: `slice[range] = value`. |
|
| 5355 | - | if let info = resolver::sliceRangeInfoFor(self.low.resolver, node) { |
|
| 5363 | + | if let info = resolver::sliceRangeInfoFor(&*self.low.resolver, node) { |
|
| 5356 | 5364 | let case ast::NodeValue::Subscript { container, index } = a.left.value |
|
| 5357 | 5365 | else panic "lowerAssign: slice assign without subscript"; |
|
| 5358 | 5366 | let case ast::NodeValue::Range(range) = index.value |
|
| 5359 | 5367 | else panic "lowerAssign: slice assign without range"; |
|
| 5360 | 5368 | try lowerSliceAssign(self, a.right, container, range, info); |
| 5405 | 5413 | } |
|
| 5406 | 5414 | } |
|
| 5407 | 5415 | } |
|
| 5408 | 5416 | ||
| 5409 | 5417 | /// Lower `slice[range] = value`. |
|
| 5410 | - | fn lowerSliceAssign( |
|
| 5411 | - | self: *mut FnLowerer, |
|
| 5418 | + | unsafe fn lowerSliceAssign( |
|
| 5419 | + | self: &mut FnLowerer, |
|
| 5412 | 5420 | rhs: *ast::Node, |
|
| 5413 | 5421 | container: *ast::Node, |
|
| 5414 | 5422 | range: ast::Range, |
|
| 5415 | 5423 | info: resolver::SliceRangeInfo |
|
| 5416 | 5424 | ) throws (LowerError) { |
| 5437 | 5445 | try emitFillLoop(self, r.dataReg, fillVal, r.count, *info.itemType, elemSize); |
|
| 5438 | 5446 | } |
|
| 5439 | 5447 | } |
|
| 5440 | 5448 | ||
| 5441 | 5449 | /// Emit a typed fill loop: `for i in 0..count { dst[i * stride] = value; }`. |
|
| 5442 | - | fn emitFillLoop( |
|
| 5443 | - | self: *mut FnLowerer, |
|
| 5450 | + | unsafe fn emitFillLoop( |
|
| 5451 | + | self: &mut FnLowerer, |
|
| 5444 | 5452 | dst: il::Reg, |
|
| 5445 | 5453 | value: il::Val, |
|
| 5446 | 5454 | count: il::Val, |
|
| 5447 | 5455 | elemType: resolver::Type, |
|
| 5448 | 5456 | elemSize: u32 |
| 5487 | 5495 | /// |
|
| 5488 | 5496 | /// @entry -> @loop -> @loop |
|
| 5489 | 5497 | /// | |
|
| 5490 | 5498 | /// `----> @end |
|
| 5491 | 5499 | /// |
|
| 5492 | - | fn lowerLoop(self: *mut FnLowerer, body: *ast::Node) throws (LowerError) { |
|
| 5500 | + | unsafe fn lowerLoop(self: &mut FnLowerer, body: *ast::Node) throws (LowerError) { |
|
| 5493 | 5501 | let loopBlock = try createBlock(self, "loop"); |
|
| 5494 | 5502 | let endBlock = try createBlock(self, "merge"); |
|
| 5495 | 5503 | ||
| 5496 | 5504 | // Enter the loop with the given break and continue targets. |
|
| 5497 | 5505 | // `break` jumps to `endBlock`, |
| 5520 | 5528 | /// |
|
| 5521 | 5529 | /// @entry -> @loop -> (true) @body -> @loop |
|
| 5522 | 5530 | /// | |
|
| 5523 | 5531 | /// `----> (false) @end |
|
| 5524 | 5532 | /// |
|
| 5525 | - | fn lowerWhile(self: *mut FnLowerer, w: ast::While) throws (LowerError) { |
|
| 5533 | + | unsafe fn lowerWhile(self: &mut FnLowerer, w: ast::While) throws (LowerError) { |
|
| 5526 | 5534 | let whileBlock = try createBlock(self, "while"); |
|
| 5527 | 5535 | let bodyBlock = try createBlock(self, "body"); |
|
| 5528 | 5536 | let endBlock = try createBlock(self, "merge"); |
|
| 5529 | 5537 | ||
| 5530 | 5538 | enterLoop(self, endBlock, whileBlock); |
| 5544 | 5552 | try switchToAndSeal(self, endBlock); |
|
| 5545 | 5553 | exitLoop(self); |
|
| 5546 | 5554 | } |
|
| 5547 | 5555 | ||
| 5548 | 5556 | /// Emit an increment of a variable by `1`. |
|
| 5549 | - | fn emitIncrement(self: *mut FnLowerer, v: Var, typ: il::Type) throws (LowerError) { |
|
| 5557 | + | unsafe fn emitIncrement(self: &mut FnLowerer, v: Var, typ: il::Type) throws (LowerError) { |
|
| 5550 | 5558 | let cur = try useVar(self, v); |
|
| 5551 | 5559 | let next = nextReg(self); |
|
| 5552 | 5560 | ||
| 5553 | 5561 | emit(self, il::Instr::BinOp { op: il::BinOp::Add, typ, dst: next, a: cur, b: il::Val::Imm(1) }); |
|
| 5554 | 5562 | defVar(self, v, il::Val::Reg(next)); |
| 5559 | 5567 | /// The step block is created lazily (after the loop body) so that it gets |
|
| 5560 | 5568 | /// a block index higher than all body blocks. This ensures the register |
|
| 5561 | 5569 | /// allocator processes definitions before uses in forward block order, |
|
| 5562 | 5570 | /// avoiding stale assignments when a value defined deep in the body flows |
|
| 5563 | 5571 | /// through the step block as a block argument. |
|
| 5564 | - | fn lowerForLoop(self: *mut FnLowerer, iter: *ForIter, body: *ast::Node) throws (LowerError) { |
|
| 5572 | + | unsafe fn lowerForLoop(self: &mut FnLowerer, iter: &ForIter, body: *ast::Node) throws (LowerError) { |
|
| 5565 | 5573 | let loopBlock = try createBlock(self, "loop"); |
|
| 5566 | 5574 | let bodyBlock = try createBlock(self, "body"); |
|
| 5567 | 5575 | let endBlock = try createBlock(self, "merge"); |
|
| 5568 | 5576 | ||
| 5569 | 5577 | enterLoop(self, endBlock, nil); |
| 5628 | 5636 | ||
| 5629 | 5637 | switchToBlock(self, endBlock); |
|
| 5630 | 5638 | } |
|
| 5631 | 5639 | ||
| 5632 | 5640 | /// Lower a `for` loop over a range, array, or slice. |
|
| 5633 | - | fn lowerFor(self: *mut FnLowerer, node: *ast::Node, f: ast::For) throws (LowerError) { |
|
| 5641 | + | unsafe fn lowerFor(self: &mut FnLowerer, node: *ast::Node, f: ast::For) throws (LowerError) { |
|
| 5634 | 5642 | let savedVarsLen = enterVarScope(self); |
|
| 5635 | - | let info = resolver::forLoopInfoFor(self.low.resolver, node) else { |
|
| 5643 | + | let info = resolver::forLoopInfoFor(&*self.low.resolver, node) else { |
|
| 5636 | 5644 | throw LowerError::MissingMetadata; |
|
| 5637 | 5645 | }; |
|
| 5638 | 5646 | match info { |
|
| 5639 | 5647 | case resolver::ForLoopInfo::Range { valType, range, bindingName, indexName } => { |
|
| 5640 | 5648 | let endExpr = range.end else { |
| 5643 | 5651 | let mut startVal = il::Val::Imm(0); |
|
| 5644 | 5652 | if let start = range.start { |
|
| 5645 | 5653 | set startVal = try lowerExpr(self, start); |
|
| 5646 | 5654 | } |
|
| 5647 | 5655 | let endVal = try lowerExpr(self, endExpr); |
|
| 5648 | - | let iterType = ilType(self.low, *valType); |
|
| 5656 | + | let iterType = ilType(&mut *self.low, *valType); |
|
| 5649 | 5657 | let valVar = newVar(self, bindingName, iterType, false, startVal); |
|
| 5650 | 5658 | ||
| 5651 | 5659 | let mut indexVar: ?Var = nil; |
|
| 5652 | 5660 | if indexName <> nil { // Optional index always starts at zero. |
|
| 5653 | 5661 | set indexVar = newVar(self, indexName, il::Type::W32, false, il::Val::Imm(0)); |
| 5678 | 5686 | let mut valVar: ?Var = nil; |
|
| 5679 | 5687 | if bindingName <> nil { |
|
| 5680 | 5688 | set valVar = newVar( |
|
| 5681 | 5689 | self, |
|
| 5682 | 5690 | bindingName, |
|
| 5683 | - | ilType(self.low, *elemType), |
|
| 5691 | + | ilType(&mut *self.low, *elemType), |
|
| 5684 | 5692 | false, |
|
| 5685 | 5693 | il::Val::Undef |
|
| 5686 | 5694 | ); |
|
| 5687 | 5695 | } |
|
| 5688 | 5696 | let iter = ForIter::Collection { valVar, idxVar, dataReg, lengthVal, elemType }; |
| 5692 | 5700 | } |
|
| 5693 | 5701 | exitVarScope(self, savedVarsLen); |
|
| 5694 | 5702 | } |
|
| 5695 | 5703 | ||
| 5696 | 5704 | /// Lower a break statement. |
|
| 5697 | - | fn lowerBreak(self: *mut FnLowerer) throws (LowerError) { |
|
| 5705 | + | unsafe fn lowerBreak(self: &mut FnLowerer) throws (LowerError) { |
|
| 5698 | 5706 | let ctx = currentLoop(self) else { |
|
| 5699 | 5707 | throw LowerError::OutsideOfLoop; |
|
| 5700 | 5708 | }; |
|
| 5701 | 5709 | try emitJmp(self, ctx.breakTarget); |
|
| 5702 | 5710 | } |
|
| 5703 | 5711 | ||
| 5704 | 5712 | /// Lower a continue statement. |
|
| 5705 | - | fn lowerContinue(self: *mut FnLowerer) throws (LowerError) { |
|
| 5713 | + | unsafe fn lowerContinue(self: &mut FnLowerer) throws (LowerError) { |
|
| 5706 | 5714 | let block = try getOrCreateContinueBlock(self); |
|
| 5707 | 5715 | try emitJmp(self, block); |
|
| 5708 | 5716 | } |
|
| 5709 | 5717 | ||
| 5710 | 5718 | /// Emit a return, blitting into the caller's return buffer if needed. |
|
| 5711 | 5719 | /// |
|
| 5712 | 5720 | /// When the function has a return buffer parameter, the value is blitted |
|
| 5713 | 5721 | /// into the buffer and the buffer pointer is returned. Otherwise, the value is |
|
| 5714 | 5722 | /// returned directly. |
|
| 5715 | - | fn emitRetVal(self: *mut FnLowerer, val: il::Val) throws (LowerError) { |
|
| 5723 | + | unsafe fn emitRetVal(self: &mut FnLowerer, val: il::Val) throws (LowerError) { |
|
| 5716 | 5724 | if let retReg = self.returnReg { |
|
| 5717 | 5725 | let src = emitValToReg(self, val); |
|
| 5718 | 5726 | let size = resolver::getResultLayout(*self.fnType.returnType, self.fnType.throwList).size |
|
| 5719 | 5727 | if self.fnType.throwList.len > 0 |
|
| 5720 | 5728 | else resolver::getTypeLayout(*self.fnType.returnType).size; |
| 5731 | 5739 | emit(self, il::Instr::Ret { val }); |
|
| 5732 | 5740 | } |
|
| 5733 | 5741 | } |
|
| 5734 | 5742 | ||
| 5735 | 5743 | /// Lower a return statement. |
|
| 5736 | - | fn lowerReturnStmt(self: *mut FnLowerer, node: *ast::Node, value: ?*ast::Node) throws (LowerError) { |
|
| 5744 | + | unsafe fn lowerReturnStmt(self: &mut FnLowerer, node: *ast::Node, value: ?*ast::Node) throws (LowerError) { |
|
| 5737 | 5745 | let mut val = il::Val::Undef; |
|
| 5738 | 5746 | if let expr = value { |
|
| 5739 | 5747 | set val = try lowerExpr(self, expr); |
|
| 5740 | 5748 | } |
|
| 5741 | 5749 | set val = try applyCoercion(self, node, val); |
|
| 5742 | 5750 | try emitRetVal(self, val); |
|
| 5743 | 5751 | } |
|
| 5744 | 5752 | ||
| 5745 | 5753 | /// Lower a throw statement. |
|
| 5746 | - | fn lowerThrowStmt(self: *mut FnLowerer, expr: *ast::Node) throws (LowerError) { |
|
| 5754 | + | unsafe fn lowerThrowStmt(self: &mut FnLowerer, expr: *ast::Node) throws (LowerError) { |
|
| 5747 | 5755 | assert self.fnType.throwList.len > 0; |
|
| 5748 | 5756 | ||
| 5749 | 5757 | let errType = *self.fnType.throwList[0] if self.fnType.throwList.len == 1 |
|
| 5750 | 5758 | else try typeOf(self, expr); |
|
| 5751 | - | let tag = getOrAssignErrorTag(self.low, errType) as i64; |
|
| 5759 | + | let tag = getOrAssignErrorTag(&mut *self.low, errType) as i64; |
|
| 5752 | 5760 | let errVal = try lowerExpr(self, expr); |
|
| 5753 | 5761 | let resultVal = try buildResult(self, tag, errVal, errType); |
|
| 5754 | 5762 | ||
| 5755 | 5763 | try emitRetVal(self, resultVal); |
|
| 5756 | 5764 | } |
|
| 5757 | 5765 | ||
| 5758 | 5766 | /// Ensure a value is in a register (eg. for branch conditions). |
|
| 5759 | - | fn emitValToReg(self: *mut FnLowerer, val: il::Val) -> il::Reg { |
|
| 5767 | + | unsafe fn emitValToReg(self: &mut FnLowerer, val: il::Val) -> il::Reg { |
|
| 5760 | 5768 | match val { |
|
| 5761 | 5769 | case il::Val::Reg(r) => return r, |
|
| 5762 | 5770 | case il::Val::Imm(_), il::Val::DataSym(_), il::Val::FnAddr(_) => { |
|
| 5763 | 5771 | let dst = nextReg(self); |
|
| 5764 | 5772 | emit(self, il::Instr::Copy { dst, val }); |
| 5806 | 5814 | /// // ... |
|
| 5807 | 5815 | /// jmp @end(%b); |
|
| 5808 | 5816 | /// @end(w8 %result) |
|
| 5809 | 5817 | /// ret %result; |
|
| 5810 | 5818 | /// |
|
| 5811 | - | fn lowerLogicalOp( |
|
| 5812 | - | self: *mut FnLowerer, |
|
| 5819 | + | unsafe fn lowerLogicalOp( |
|
| 5820 | + | self: &mut FnLowerer, |
|
| 5813 | 5821 | binop: ast::BinOp, |
|
| 5814 | 5822 | thenLabel: *[u8], |
|
| 5815 | 5823 | elseLabel: *[u8], |
|
| 5816 | 5824 | mergeLabel: *[u8], |
|
| 5817 | 5825 | op: LogicalOp |
| 5856 | 5864 | try switchToAndSeal(self, mergeBlock); |
|
| 5857 | 5865 | return il::Val::Reg(resultReg); |
|
| 5858 | 5866 | } |
|
| 5859 | 5867 | ||
| 5860 | 5868 | /// Lower a conditional expression (`thenExpr if condition else elseExpr`). |
|
| 5861 | - | fn lowerCondExpr(self: *mut FnLowerer, node: *ast::Node, cond: ast::CondExpr) -> il::Val |
|
| 5869 | + | unsafe fn lowerCondExpr(self: &mut FnLowerer, node: *ast::Node, cond: ast::CondExpr) -> il::Val |
|
| 5862 | 5870 | throws (LowerError) |
|
| 5863 | 5871 | { |
|
| 5864 | 5872 | let typ = try typeOf(self, node); |
|
| 5865 | 5873 | let thenBlock = try createBlock(self, "cond#then"); |
|
| 5866 | 5874 | let elseBlock = try createBlock(self, "cond#else"); |
| 5887 | 5895 | ||
| 5888 | 5896 | return il::Val::Reg(dst); |
|
| 5889 | 5897 | } else { |
|
| 5890 | 5898 | try emitCondBranch(self, cond.condition, thenBlock, elseBlock); |
|
| 5891 | 5899 | ||
| 5892 | - | let resultType = ilType(self.low, typ); |
|
| 5900 | + | let resultType = ilType(&mut *self.low, typ); |
|
| 5893 | 5901 | let resultReg = nextReg(self); |
|
| 5894 | 5902 | let mergeBlock = try createBlockWithParam( |
|
| 5895 | 5903 | self, "cond#merge", il::Param { value: resultReg, type: resultType } |
|
| 5896 | 5904 | ); |
|
| 5897 | 5905 | try switchToAndSeal(self, thenBlock); |
| 5923 | 5931 | else => return nil, |
|
| 5924 | 5932 | } |
|
| 5925 | 5933 | } |
|
| 5926 | 5934 | ||
| 5927 | 5935 | /// Lower a binary operation. |
|
| 5928 | - | fn lowerBinOp(self: *mut FnLowerer, node: *ast::Node, binop: ast::BinOp) -> il::Val throws (LowerError) { |
|
| 5936 | + | unsafe fn lowerBinOp(self: &mut FnLowerer, node: *ast::Node, binop: ast::BinOp) -> il::Val throws (LowerError) { |
|
| 5929 | 5937 | // Short-circuit logical operators don't evaluate both operands eagerly. |
|
| 5930 | 5938 | if binop.op == ast::BinaryOp::And { |
|
| 5931 | 5939 | return try lowerLogicalOp(self, binop, "and#then", "and#else", "and#end", LogicalOp::And); |
|
| 5932 | 5940 | } else if binop.op == ast::BinaryOp::Or { |
|
| 5933 | 5941 | return try lowerLogicalOp(self, binop, "or#then", "or#else", "or#end", LogicalOp::Or); |
| 5958 | 5966 | ||
| 5959 | 5967 | if isComparison { |
|
| 5960 | 5968 | let leftTy = try effectiveType(self, binop.left); |
|
| 5961 | 5969 | let rightTy = try effectiveType(self, binop.right); |
|
| 5962 | 5970 | // Optimize: comparing with a void variant just needs tag comparison. |
|
| 5963 | - | if let idx = voidVariantIndex(self.low.resolver, binop.left) { |
|
| 5971 | + | if let idx = voidVariantIndex(&*self.low.resolver, binop.left) { |
|
| 5964 | 5972 | return try emitTagCmp(self, binop.op, b, idx, rightTy); |
|
| 5965 | - | } else if let idx = voidVariantIndex(self.low.resolver, binop.right) { |
|
| 5973 | + | } else if let idx = voidVariantIndex(&*self.low.resolver, binop.right) { |
|
| 5966 | 5974 | return try emitTagCmp(self, binop.op, a, idx, leftTy); |
|
| 5967 | 5975 | } |
|
| 5968 | 5976 | // Aggregate types require element-wise comparison. |
|
| 5969 | 5977 | // When comparing `?T` with `T`, wrap the scalar side. |
|
| 5970 | 5978 | if isAggregateType(leftTy) { |
| 5978 | 5986 | let lhs = try wrapInOptional(self, a, rightTy); |
|
| 5979 | 5987 | return try emitAggregateEqOp(self, binop.op, rightTy, lhs, b); |
|
| 5980 | 5988 | } |
|
| 5981 | 5989 | set resultTy = scalarComparisonType(leftTy, rightTy); |
|
| 5982 | 5990 | } |
|
| 5983 | - | return emitScalarBinOp(self, binop.op, ilType(self.low, resultTy), a, b, isUnsignedType(resultTy)); |
|
| 5991 | + | return emitScalarBinOp(self, binop.op, ilType(&mut *self.low, resultTy), a, b, isUnsignedType(resultTy)); |
|
| 5984 | 5992 | } |
|
| 5985 | 5993 | ||
| 5986 | 5994 | /// Emit an aggregate equality or inequality comparison. |
|
| 5987 | - | fn emitAggregateEqOp( |
|
| 5988 | - | self: *mut FnLowerer, |
|
| 5995 | + | unsafe fn emitAggregateEqOp( |
|
| 5996 | + | self: &mut FnLowerer, |
|
| 5989 | 5997 | op: ast::BinaryOp, |
|
| 5990 | 5998 | typ: resolver::Type, |
|
| 5991 | 5999 | a: il::Val, |
|
| 5992 | 6000 | b: il::Val |
|
| 5993 | 6001 | ) -> il::Val throws (LowerError) { |
| 6000 | 6008 | } |
|
| 6001 | 6009 | return result; |
|
| 6002 | 6010 | } |
|
| 6003 | 6011 | ||
| 6004 | 6012 | /// Emit a scalar binary operation instruction. |
|
| 6005 | - | fn emitScalarBinOp( |
|
| 6006 | - | self: *mut FnLowerer, |
|
| 6013 | + | unsafe fn emitScalarBinOp( |
|
| 6014 | + | self: &mut FnLowerer, |
|
| 6007 | 6015 | op: ast::BinaryOp, |
|
| 6008 | 6016 | typ: il::Type, |
|
| 6009 | 6017 | a: il::Val, |
|
| 6010 | 6018 | b: il::Val, |
|
| 6011 | 6019 | unsigned: bool |
| 6077 | 6085 | } |
|
| 6078 | 6086 | return il::Val::Reg(dst); |
|
| 6079 | 6087 | } |
|
| 6080 | 6088 | ||
| 6081 | 6089 | /// Normalize sub-word values to well-defined high bits. |
|
| 6082 | - | fn normalizeSubword(self: *mut FnLowerer, typ: il::Type, unsigned: bool, val: il::Val) -> il::Val { |
|
| 6090 | + | unsafe fn normalizeSubword(self: &mut FnLowerer, typ: il::Type, unsigned: bool, val: il::Val) -> il::Val { |
|
| 6083 | 6091 | if typ == il::Type::W8 or typ == il::Type::W16 { |
|
| 6084 | 6092 | let extDst: il::Reg = nextReg(self); |
|
| 6085 | 6093 | if unsigned { |
|
| 6086 | 6094 | emit(self, il::Instr::Zext { typ, dst: extDst, val }); |
|
| 6087 | 6095 | } else { |
| 6091 | 6099 | } |
|
| 6092 | 6100 | return val; |
|
| 6093 | 6101 | } |
|
| 6094 | 6102 | ||
| 6095 | 6103 | /// Lower a unary operation. |
|
| 6096 | - | fn lowerUnOp(self: *mut FnLowerer, node: *ast::Node, unop: ast::UnOp) -> il::Val throws (LowerError) { |
|
| 6104 | + | unsafe fn lowerUnOp(self: &mut FnLowerer, node: *ast::Node, unop: ast::UnOp) -> il::Val throws (LowerError) { |
|
| 6097 | 6105 | if unop.op == ast::UnaryOp::Neg { |
|
| 6098 | 6106 | if let case ast::NodeValue::Number(lit) = unop.value.value { |
|
| 6099 | 6107 | return il::Val::Imm((0 - lit.magnitude) as i64); |
|
| 6100 | 6108 | } |
|
| 6101 | 6109 | } |
|
| 6102 | 6110 | let val = try lowerExpr(self, unop.value); |
|
| 6103 | 6111 | let t = try typeOf(self, node); |
|
| 6104 | - | let typ = ilType(self.low, t); |
|
| 6112 | + | let typ = ilType(&mut *self.low, t); |
|
| 6105 | 6113 | let dst = nextReg(self); |
|
| 6106 | 6114 | let mut needsExt: bool = false; |
|
| 6107 | 6115 | ||
| 6108 | 6116 | match unop.op { |
|
| 6109 | 6117 | case ast::UnaryOp::Not => { |
| 6123 | 6131 | } |
|
| 6124 | 6132 | return il::Val::Reg(dst); |
|
| 6125 | 6133 | } |
|
| 6126 | 6134 | ||
| 6127 | 6135 | /// Lower a cast expression (`x as T`). |
|
| 6128 | - | fn lowerCast(self: *mut FnLowerer, node: *ast::Node, cast: ast::As) -> il::Val throws (LowerError) { |
|
| 6136 | + | unsafe fn lowerCast(self: &mut FnLowerer, node: *ast::Node, cast: ast::As) -> il::Val throws (LowerError) { |
|
| 6129 | 6137 | let val = try lowerExpr(self, cast.value); |
|
| 6130 | 6138 | ||
| 6131 | 6139 | let srcType = try typeOf(self, cast.value); |
|
| 6132 | 6140 | let dstType = try typeOf(self, node); |
|
| 6133 | 6141 | if resolver::typesEqual(srcType, dstType) { |
| 6155 | 6163 | ||
| 6156 | 6164 | /// Lower a string literal to a slice value. |
|
| 6157 | 6165 | /// |
|
| 6158 | 6166 | /// String literals are stored as global data and the result is a slice |
|
| 6159 | 6167 | /// pointing to the data with the appropriate length. |
|
| 6160 | - | fn lowerStringLit(self: *mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) { |
|
| 6168 | + | unsafe fn lowerStringLit(self: &mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) { |
|
| 6161 | 6169 | // Get the slice type from the node. |
|
| 6162 | 6170 | let sliceTy = try typeOf(self, node); |
|
| 6163 | 6171 | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
|
| 6164 | 6172 | else throw LowerError::ExpectedSliceOrArray; |
|
| 6165 | 6173 | // Build the string data value. |
|
| 6166 | 6174 | let ptr = try! alloc::alloc( |
|
| 6167 | - | self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue) |
|
| 6175 | + | &mut *self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue) |
|
| 6168 | 6176 | ) as *mut il::DataValue; |
|
| 6169 | 6177 | ||
| 6170 | 6178 | set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 }; |
|
| 6171 | 6179 | let result = ConstDataResult { values: @sliceOf(ptr, 1), zeroInit: false }; |
|
| 6172 | 6180 |
| 6174 | 6182 | self, &result, 1, true, item, mutable, s.len |
|
| 6175 | 6183 | ); |
|
| 6176 | 6184 | } |
|
| 6177 | 6185 | ||
| 6178 | 6186 | /// Lower a builtin call expression. |
|
| 6179 | - | fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6187 | + | unsafe fn lowerBuiltinCall(self: &mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6180 | 6188 | match kind { |
|
| 6181 | 6189 | case ast::Builtin::SliceOf => return try lowerSliceOf(self, node, args), |
|
| 6182 | 6190 | case ast::Builtin::SizeOf, ast::Builtin::AlignOf => { |
|
| 6183 | - | let constVal = resolver::constValueEntry(self.low.resolver, node) else { |
|
| 6191 | + | let constVal = resolver::constValueEntry(&*self.low.resolver, node) else { |
|
| 6184 | 6192 | throw LowerError::MissingConst(node); |
|
| 6185 | 6193 | }; |
|
| 6186 | 6194 | return try constValueToVal(self, constVal, node); |
|
| 6187 | 6195 | } |
|
| 6188 | 6196 | } |
|
| 6189 | 6197 | } |
|
| 6190 | 6198 | ||
| 6191 | 6199 | /// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call. |
|
| 6192 | - | fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6200 | + | unsafe fn lowerSliceOf(self: &mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6193 | 6201 | if args.len <> 2 and args.len <> 3 { |
|
| 6194 | 6202 | throw LowerError::InvalidArgCount; |
|
| 6195 | 6203 | } |
|
| 6196 | 6204 | let sliceTy = try typeOf(self, node); |
|
| 6197 | 6205 | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
| 6207 | 6215 | } |
|
| 6208 | 6216 | return try buildSliceValue(self, item, mutable, ptrVal, lenVal, capVal); |
|
| 6209 | 6217 | } |
|
| 6210 | 6218 | ||
| 6211 | 6219 | /// Lower a `try` expression. |
|
| 6212 | - | fn lowerTry(self: *mut FnLowerer, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) { |
|
| 6220 | + | unsafe fn lowerTry(self: &mut FnLowerer, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) { |
|
| 6213 | 6221 | let case ast::NodeValue::Call(callExpr) = t.expr.value else { |
|
| 6214 | 6222 | throw LowerError::ExpectedCall; |
|
| 6215 | 6223 | }; |
|
| 6216 | 6224 | let calleeTy = try typeOf(self, callExpr.callee); |
|
| 6217 | 6225 | let case resolver::Type::Fn(calleeInfo) = calleeTy else { |
| 6222 | 6230 | // Type of the try expression, which is either the return type of the function |
|
| 6223 | 6231 | // if successful, or an optional of it, if using `try?`. |
|
| 6224 | 6232 | let tryExprTy = try typeOf(self, node); |
|
| 6225 | 6233 | // Check for trait method dispatch or standalone method call. |
|
| 6226 | 6234 | let mut resVal: il::Val = undefined; |
|
| 6227 | - | let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra; |
|
| 6235 | + | let callNodeExtra = resolver::nodeData(&*self.low.resolver, t.expr).extra; |
|
| 6228 | 6236 | if let case resolver::NodeExtra::TraitMethodCall { |
|
| 6229 | 6237 | traitInfo, methodIndex |
|
| 6230 | 6238 | } = callNodeExtra { |
|
| 6231 | 6239 | set resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex); |
|
| 6232 | 6240 | } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra { |
|
| 6233 | - | set resVal = try lowerMethodCall(self, t.expr, callExpr, method); |
|
| 6241 | + | set resVal = try lowerMethodCall(self, t.expr, callExpr, &*method); |
|
| 6234 | 6242 | } else { |
|
| 6235 | 6243 | set resVal = try lowerCall(self, t.expr, callExpr); |
|
| 6236 | 6244 | } |
|
| 6237 | 6245 | let base = emitValToReg(self, resVal); // The result value. |
|
| 6238 | 6246 | let tagReg = resultTagReg(self, base); // The result tag. |
| 6300 | 6308 | let case ast::NodeValue::Ident(name) = binding.value else { |
|
| 6301 | 6309 | throw LowerError::ExpectedIdentifier; |
|
| 6302 | 6310 | }; |
|
| 6303 | 6311 | let errTy = *calleeInfo.throwList[0]; |
|
| 6304 | 6312 | let errVal = tvalPayloadVal(self, base, errTy, RESULT_VAL_OFFSET); |
|
| 6305 | - | let _ = newVar(self, name, ilType(self.low, errTy), false, errVal); |
|
| 6313 | + | let _ = newVar(self, name, ilType(&mut *self.low, errTy), false, errVal); |
|
| 6306 | 6314 | } |
|
| 6307 | 6315 | try lowerBlock(self, first.body); |
|
| 6308 | 6316 | try emitMergeIfUnterminated(self, &mut mergeBlock); |
|
| 6309 | 6317 | exitVarScope(self, savedVarsLen); |
|
| 6310 | 6318 | } |
| 6351 | 6359 | /// Lower typed multi-catch clauses. |
|
| 6352 | 6360 | /// |
|
| 6353 | 6361 | /// Emits a switch on the global error tag to dispatch to the correct catch |
|
| 6354 | 6362 | /// clause. Each typed clause extracts the error payload for its specific type |
|
| 6355 | 6363 | /// and binds it to the clause's identifier. |
|
| 6356 | - | fn lowerMultiCatch( |
|
| 6357 | - | self: *mut FnLowerer, |
|
| 6364 | + | unsafe fn lowerMultiCatch( |
|
| 6365 | + | self: &mut FnLowerer, |
|
| 6358 | 6366 | catches: *mut [*ast::Node], |
|
| 6359 | 6367 | calleeInfo: *resolver::FnType, |
|
| 6360 | 6368 | base: il::Reg, |
|
| 6361 | 6369 | tagReg: il::Reg, |
|
| 6362 | - | mergeBlock: *mut ?BlockId |
|
| 6370 | + | mergeBlock: &mut ?BlockId |
|
| 6363 | 6371 | ) throws (LowerError) { |
|
| 6364 | 6372 | let entry = currentBlock(self); |
|
| 6365 | 6373 | ||
| 6366 | 6374 | // First pass: create blocks, resolve error types, and build switch cases. |
|
| 6367 | 6375 | let mut blocks: [BlockId; MAX_CATCH_CLAUSES] = undefined; |
| 6379 | 6387 | if let typeNode = clause.typeNode { |
|
| 6380 | 6388 | let errTy = try typeOf(self, typeNode); |
|
| 6381 | 6389 | set errTypes[i] = errTy; |
|
| 6382 | 6390 | ||
| 6383 | 6391 | cases.append(il::SwitchCase { |
|
| 6384 | - | value: getOrAssignErrorTag(self.low, errTy) as i64, |
|
| 6392 | + | value: getOrAssignErrorTag(&mut *self.low, errTy) as i64, |
|
| 6385 | 6393 | target: *blocks[i], |
|
| 6386 | 6394 | args: &mut [] |
|
| 6387 | 6395 | }, self.allocator); |
|
| 6388 | 6396 | } else { |
|
| 6389 | 6397 | set errTypes[i] = nil; |
| 6419 | 6427 | throw LowerError::ExpectedIdentifier; |
|
| 6420 | 6428 | }; |
|
| 6421 | 6429 | let errTy = errTypes[i] else panic "lowerMultiCatch: catch-all with binding"; |
|
| 6422 | 6430 | let errVal = tvalPayloadVal(self, base, errTy, RESULT_VAL_OFFSET); |
|
| 6423 | 6431 | ||
| 6424 | - | newVar(self, name, ilType(self.low, errTy), false, errVal); |
|
| 6432 | + | newVar(self, name, ilType(&mut *self.low, errTy), false, errVal); |
|
| 6425 | 6433 | } |
|
| 6426 | 6434 | try lowerBlock(self, clause.body); |
|
| 6427 | 6435 | try emitMergeIfUnterminated(self, mergeBlock); |
|
| 6428 | 6436 | ||
| 6429 | 6437 | exitVarScope(self, savedVarsLen); |
| 6439 | 6447 | /// Emit a byte-copy loop: `for i in 0..size { dst[i] = src[i]; }`. |
|
| 6440 | 6448 | /// |
|
| 6441 | 6449 | /// Used when `blit` cannot be used because the copy size is dynamic. |
|
| 6442 | 6450 | /// Terminates the current block and leaves the builder positioned |
|
| 6443 | 6451 | /// after the loop. |
|
| 6444 | - | fn emitByteCopyLoop( |
|
| 6445 | - | self: *mut FnLowerer, |
|
| 6452 | + | unsafe fn emitByteCopyLoop( |
|
| 6453 | + | self: &mut FnLowerer, |
|
| 6446 | 6454 | dst: il::Reg, |
|
| 6447 | 6455 | src: il::Reg, |
|
| 6448 | 6456 | size: il::Val, |
|
| 6449 | 6457 | label: *[u8] |
|
| 6450 | 6458 | ) throws (LowerError) { |
| 6498 | 6506 | /// |
|
| 6499 | 6507 | /// @store: |
|
| 6500 | 6508 | /// store element at ptr + len * stride |
|
| 6501 | 6509 | /// increment len |
|
| 6502 | 6510 | /// |
|
| 6503 | - | fn lowerSliceAppend(self: *mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) { |
|
| 6511 | + | unsafe fn lowerSliceAppend(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) -> il::Val throws (LowerError) { |
|
| 6504 | 6512 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6505 | 6513 | else throw LowerError::MissingMetadata; |
|
| 6506 | 6514 | ||
| 6507 | 6515 | // Get the address of the slice header. |
|
| 6508 | 6516 | let sliceVal = try lowerExpr(self, access.parent); |
| 6584 | 6592 | ||
| 6585 | 6593 | /// Lower `slice.delete(index)`. |
|
| 6586 | 6594 | /// |
|
| 6587 | 6595 | /// Bounds-check the index, shift elements after it by one stride |
|
| 6588 | 6596 | /// via a byte-copy loop, and decrement `len`. |
|
| 6589 | - | fn lowerSliceDelete(self: *mut FnLowerer, call: ast::Call, elemType: *resolver::Type) throws (LowerError) { |
|
| 6597 | + | unsafe fn lowerSliceDelete(self: &mut FnLowerer, call: ast::Call, elemType: *resolver::Type) throws (LowerError) { |
|
| 6590 | 6598 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6591 | 6599 | else throw LowerError::MissingMetadata; |
|
| 6592 | 6600 | ||
| 6593 | 6601 | let elemLayout = resolver::getTypeLayout(*elemType); |
|
| 6594 | 6602 | let stride = elemLayout.size; |
| 6624 | 6632 | ||
| 6625 | 6633 | emitStoreW32At(self, newLen, sliceReg, SLICE_LEN_OFFSET); |
|
| 6626 | 6634 | } |
|
| 6627 | 6635 | ||
| 6628 | 6636 | /// Lower a call expression, which may be a function call or type constructor. |
|
| 6629 | - | fn lowerCallOrCtor(self: *mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6630 | - | let nodeData = resolver::nodeData(self.low.resolver, node).extra; |
|
| 6637 | + | unsafe fn lowerCallOrCtor(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6638 | + | let nodeData = resolver::nodeData(&*self.low.resolver, node).extra; |
|
| 6631 | 6639 | ||
| 6632 | 6640 | // Check for slice method dispatch. |
|
| 6633 | 6641 | if let case resolver::NodeExtra::SliceAppend { elemType } = nodeData { |
|
| 6634 | 6642 | return try lowerSliceAppend(self, call, elemType); |
|
| 6635 | 6643 | } |
| 6641 | 6649 | if let case resolver::NodeExtra::TraitMethodCall { traitInfo, methodIndex } = nodeData { |
|
| 6642 | 6650 | return try lowerTraitMethodCall(self, node, call, traitInfo, methodIndex); |
|
| 6643 | 6651 | } |
|
| 6644 | 6652 | // Check for standalone method call. |
|
| 6645 | 6653 | if let case resolver::NodeExtra::MethodCall { method } = nodeData { |
|
| 6646 | - | return try lowerMethodCall(self, node, call, method); |
|
| 6654 | + | return try lowerMethodCall(self, node, call, &*method); |
|
| 6647 | 6655 | } |
|
| 6648 | - | if let sym = resolver::nodeData(self.low.resolver, call.callee).sym { |
|
| 6656 | + | if let sym = resolver::nodeData(&*self.low.resolver, call.callee).sym { |
|
| 6649 | 6657 | if let case resolver::SymbolData::Type(nominal) = sym.data { |
|
| 6650 | 6658 | let case resolver::NominalType::Record(_) = *nominal else { |
|
| 6651 | 6659 | throw LowerError::ExpectedRecord; |
|
| 6652 | 6660 | }; |
|
| 6653 | 6661 | return try lowerRecordCtor(self, nominal, call.args); |
| 6666 | 6674 | /// load w64 %data %obj 0 // data pointer |
|
| 6667 | 6675 | /// load w64 %vtable %obj 8 // v-table pointer |
|
| 6668 | 6676 | /// load w64 %fn %vtable <slot> // function pointer |
|
| 6669 | 6677 | /// call <retTy> %ret %fn(%data, args...) |
|
| 6670 | 6678 | /// |
|
| 6671 | - | fn lowerTraitMethodCall( |
|
| 6672 | - | self: *mut FnLowerer, |
|
| 6679 | + | unsafe fn lowerTraitMethodCall( |
|
| 6680 | + | self: &mut FnLowerer, |
|
| 6673 | 6681 | node: *ast::Node, |
|
| 6674 | 6682 | call: ast::Call, |
|
| 6675 | 6683 | traitInfo: *resolver::TraitType, |
|
| 6676 | 6684 | methodIndex: u32 |
|
| 6677 | 6685 | ) -> il::Val throws (LowerError) { |
| 6728 | 6736 | ||
| 6729 | 6737 | /// Lower a call argument, snapshotting aggregate place expressions before |
|
| 6730 | 6738 | /// evaluating later arguments. Aggregates are represented by addresses in the |
|
| 6731 | 6739 | /// IL, so retaining the original address would let a later argument mutation |
|
| 6732 | 6740 | /// change the value already supplied for this argument. |
|
| 6733 | - | fn lowerCallArg(self: *mut FnLowerer, arg: *ast::Node, hasLater: bool) -> il::Val |
|
| 6741 | + | unsafe fn lowerCallArg(self: &mut FnLowerer, arg: *ast::Node, hasLater: bool) -> il::Val |
|
| 6734 | 6742 | throws (LowerError) |
|
| 6735 | 6743 | { |
|
| 6736 | 6744 | let val = try lowerExpr(self, arg); |
|
| 6737 | 6745 | ||
| 6738 | 6746 | if hasLater and ast::isPlaceExpr(arg) { |
| 6748 | 6756 | /// |
|
| 6749 | 6757 | /// All call lowering paths (regular, trait method, standalone method) converge |
|
| 6750 | 6758 | /// here after preparing the callee value, function type, and argument array. |
|
| 6751 | 6759 | /// The `args` slice must already include a slot at index zero for the hidden |
|
| 6752 | 6760 | /// return parameter; that slot is filled by this function. |
|
| 6753 | - | fn emitCallValue( |
|
| 6754 | - | self: *mut FnLowerer, |
|
| 6761 | + | unsafe fn emitCallValue( |
|
| 6762 | + | self: &mut FnLowerer, |
|
| 6755 | 6763 | callee: il::Val, |
|
| 6756 | 6764 | fnInfo: *resolver::FnType, |
|
| 6757 | 6765 | args: *mut [il::Val], |
|
| 6758 | 6766 | ) -> il::Val throws (LowerError) { |
|
| 6759 | 6767 | let retTy = *fnInfo.returnType; |
| 6778 | 6786 | let mut dst: ?il::Reg = nil; |
|
| 6779 | 6787 | if retTy <> resolver::Type::Void { |
|
| 6780 | 6788 | set dst = nextReg(self); |
|
| 6781 | 6789 | } |
|
| 6782 | 6790 | emit(self, il::Instr::Call { |
|
| 6783 | - | retTy: ilType(self.low, retTy), |
|
| 6791 | + | retTy: ilType(&mut *self.low, retTy), |
|
| 6784 | 6792 | dst, |
|
| 6785 | 6793 | func: callee, |
|
| 6786 | 6794 | args, |
|
| 6787 | 6795 | }); |
|
| 6788 | 6796 |
| 6807 | 6815 | ||
| 6808 | 6816 | /// Lower a method receiver expression to a pointer value. |
|
| 6809 | 6817 | /// |
|
| 6810 | 6818 | /// If the parent is already a pointer type, the value is used directly. |
|
| 6811 | 6819 | /// If the parent is a value type (eg. a local record), its address is taken. |
|
| 6812 | - | fn lowerReceiver(self: *mut FnLowerer, parent: *ast::Node, parentTy: resolver::Type) -> il::Val |
|
| 6820 | + | unsafe fn lowerReceiver(self: &mut FnLowerer, parent: *ast::Node, parentTy: resolver::Type) -> il::Val |
|
| 6813 | 6821 | throws (LowerError) |
|
| 6814 | 6822 | { |
|
| 6815 | 6823 | if let case resolver::Type::Pointer { .. } = parentTy { |
|
| 6816 | 6824 | // Already a pointer: lower and use directly. |
|
| 6817 | 6825 | return try lowerExpr(self, parent); |
| 6821 | 6829 | let val = try lowerExpr(self, parent); |
|
| 6822 | 6830 | if isAggregateType(parentTy) { |
|
| 6823 | 6831 | return val; |
|
| 6824 | 6832 | } |
|
| 6825 | 6833 | // Scalar value: store to a stack slot and return the slot pointer. |
|
| 6826 | - | let layout = resolver::getLayout(self.low.resolver, parent, parentTy); |
|
| 6834 | + | let layout = resolver::getLayout(&*self.low.resolver, parent, parentTy); |
|
| 6827 | 6835 | let slot = emitReserveLayout(self, layout); |
|
| 6828 | 6836 | try emitStore(self, slot, 0, parentTy, val); |
|
| 6829 | 6837 | ||
| 6830 | 6838 | return il::Val::Reg(slot); |
|
| 6831 | 6839 | } |
| 6835 | 6843 | /// Given `obj.method(args)` where `method` is a standalone method on a concrete type, |
|
| 6836 | 6844 | /// emits a direct call with the receiver address as the first argument: |
|
| 6837 | 6845 | /// |
|
| 6838 | 6846 | /// call <retTy> %ret @Type::method(&obj, args...) |
|
| 6839 | 6847 | /// |
|
| 6840 | - | fn lowerMethodCall( |
|
| 6841 | - | self: *mut FnLowerer, |
|
| 6848 | + | unsafe fn lowerMethodCall( |
|
| 6849 | + | self: &mut FnLowerer, |
|
| 6842 | 6850 | node: *ast::Node, |
|
| 6843 | 6851 | call: ast::Call, |
|
| 6844 | - | method: *resolver::MethodEntry, |
|
| 6852 | + | method: &resolver::MethodEntry, |
|
| 6845 | 6853 | ) -> il::Val throws (LowerError) { |
|
| 6846 | 6854 | let case ast::NodeValue::FieldAccess(access) = call.callee.value |
|
| 6847 | 6855 | else throw LowerError::MissingMetadata; |
|
| 6848 | 6856 | ||
| 6849 | 6857 | // Get the receiver as a pointer. |
|
| 6850 | 6858 | let parentTy = try typeOf(self, access.parent); |
|
| 6851 | 6859 | let receiverVal = try lowerReceiver(self, access.parent, parentTy); |
|
| 6852 | 6860 | ||
| 6853 | - | let qualName = instanceMethodName(self.low, nil, method.concreteTypeName, method.name); |
|
| 6861 | + | let qualName = instanceMethodName(&mut *self.low, nil, method.concreteTypeName, method.name); |
|
| 6854 | 6862 | let case resolver::SymbolData::Value { type: resolver::Type::Fn(fnInfo), .. } = method.symbol.data |
|
| 6855 | 6863 | else panic "lowerMethodCall: expected Fn type on method symbol"; |
|
| 6856 | 6864 | ||
| 6857 | 6865 | // Build args: optional return param slot + receiver + user args. |
|
| 6858 | 6866 | let argOffset: u32 = 1 if requiresReturnParam(fnInfo) else 0; |
| 6865 | 6873 | } |
|
| 6866 | 6874 | return try emitCallValue(self, il::Val::FnAddr(qualName), fnInfo, args); |
|
| 6867 | 6875 | } |
|
| 6868 | 6876 | ||
| 6869 | 6877 | /// Check if a call is to a compiler intrinsic and lower it directly. |
|
| 6870 | - | fn lowerIntrinsicCall(self: *mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) { |
|
| 6878 | + | unsafe fn lowerIntrinsicCall(self: &mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) { |
|
| 6871 | 6879 | // Get the callee symbol and check if it's marked as an intrinsic. |
|
| 6872 | - | let sym = resolver::nodeData(self.low.resolver, call.callee).sym else { |
|
| 6880 | + | let sym = resolver::nodeData(&*self.low.resolver, call.callee).sym else { |
|
| 6873 | 6881 | // Expressions or function pointers may not have an associated symbol. |
|
| 6874 | 6882 | return nil; |
|
| 6875 | 6883 | }; |
|
| 6876 | 6884 | if not ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic) { |
|
| 6877 | 6885 | return nil; |
| 6887 | 6895 | throw LowerError::UnknownIntrinsic; |
|
| 6888 | 6896 | } |
|
| 6889 | 6897 | } |
|
| 6890 | 6898 | ||
| 6891 | 6899 | /// Lower an ecall intrinsic: `ecall(num, a0, a1, a2, a3) -> i32`. |
|
| 6892 | - | fn lowerEcall(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6900 | + | unsafe fn lowerEcall(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6893 | 6901 | if call.args.len <> 5 { |
|
| 6894 | 6902 | throw LowerError::InvalidArgCount; |
|
| 6895 | 6903 | } |
|
| 6896 | 6904 | let num = try lowerExpr(self, call.args[0]); |
|
| 6897 | 6905 | let a0 = try lowerExpr(self, call.args[1]); |
| 6904 | 6912 | ||
| 6905 | 6913 | return il::Val::Reg(dst); |
|
| 6906 | 6914 | } |
|
| 6907 | 6915 | ||
| 6908 | 6916 | /// Lower an ebreak intrinsic: `ebreak()`. |
|
| 6909 | - | fn lowerEbreak(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6917 | + | unsafe fn lowerEbreak(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6910 | 6918 | if call.args.len <> 0 { |
|
| 6911 | 6919 | throw LowerError::InvalidArgCount; |
|
| 6912 | 6920 | } |
|
| 6913 | 6921 | emit(self, il::Instr::Ebreak); |
|
| 6914 | 6922 | ||
| 6915 | 6923 | return il::Val::Undef; |
|
| 6916 | 6924 | } |
|
| 6917 | 6925 | ||
| 6918 | 6926 | /// Lower `memoryFence()`. |
|
| 6919 | - | fn lowerMemoryFence(self: *mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6927 | + | unsafe fn lowerMemoryFence(self: &mut FnLowerer, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6920 | 6928 | if call.args.len <> 0 { |
|
| 6921 | 6929 | throw LowerError::InvalidArgCount; |
|
| 6922 | 6930 | } |
|
| 6923 | 6931 | emit(self, il::Instr::MemoryFence); |
|
| 6924 | 6932 | return il::Val::Undef; |
|
| 6925 | 6933 | } |
|
| 6926 | 6934 | ||
| 6927 | 6935 | /// Resolve callee to an IL value. For direct function calls, use the symbol name. |
|
| 6928 | 6936 | /// For variables holding function pointers or complex expressions (eg. `array[i]()`), |
|
| 6929 | 6937 | /// lower the callee expression. |
|
| 6930 | - | fn lowerCallee(self: *mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) { |
|
| 6931 | - | if let sym = resolver::nodeData(self.low.resolver, callee).sym { |
|
| 6938 | + | unsafe fn lowerCallee(self: &mut FnLowerer, callee: *ast::Node) -> il::Val throws (LowerError) { |
|
| 6939 | + | if let sym = resolver::nodeData(&*self.low.resolver, callee).sym { |
|
| 6932 | 6940 | if let case ast::NodeValue::FnDecl(_) = sym.node.value { |
|
| 6933 | 6941 | // First try to look up the symbol in our registered functions. |
|
| 6934 | 6942 | // This handles cross-package calls correctly, since packages are |
|
| 6935 | 6943 | // lowered in dependency order. |
|
| 6936 | - | if let qualName = lookupFnSym(self.low, sym) { |
|
| 6944 | + | if let qualName = lookupFnSym(&mut *self.low, sym) { |
|
| 6937 | 6945 | return il::Val::FnAddr(qualName); |
|
| 6938 | 6946 | } |
|
| 6939 | 6947 | // Fall back to computing the qualified name from the module graph. |
|
| 6940 | 6948 | // This works for functions in the current package. |
|
| 6941 | - | let modId = resolver::moduleIdForSymbol(self.low.resolver, sym) else { |
|
| 6949 | + | let modId = resolver::moduleIdForSymbol(&*self.low.resolver, sym) else { |
|
| 6942 | 6950 | throw LowerError::MissingMetadata; |
|
| 6943 | 6951 | }; |
|
| 6944 | - | return il::Val::FnAddr(qualifyName(self.low, modId, sym.name)); |
|
| 6952 | + | return il::Val::FnAddr(qualifyName(&mut *self.low, modId, sym.name)); |
|
| 6945 | 6953 | } |
|
| 6946 | 6954 | } |
|
| 6947 | 6955 | return try lowerExpr(self, callee); |
|
| 6948 | 6956 | } |
|
| 6949 | 6957 | ||
| 6950 | 6958 | /// Lower a function call expression. |
|
| 6951 | - | fn lowerCall(self: *mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6959 | + | unsafe fn lowerCall(self: &mut FnLowerer, node: *ast::Node, call: ast::Call) -> il::Val throws (LowerError) { |
|
| 6952 | 6960 | // Check for intrinsic calls before normal call lowering. |
|
| 6953 | 6961 | if let intrinsicVal = try lowerIntrinsicCall(self, call) { |
|
| 6954 | 6962 | return intrinsicVal; |
|
| 6955 | 6963 | } |
|
| 6956 | 6964 | let calleeTy = try typeOf(self, call.callee); |
| 6968 | 6976 | ||
| 6969 | 6977 | return try emitCallValue(self, callee, fnInfo, args); |
|
| 6970 | 6978 | } |
|
| 6971 | 6979 | ||
| 6972 | 6980 | /// Apply coercions requested by the resolver. |
|
| 6973 | - | fn applyCoercion(self: *mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) { |
|
| 6974 | - | let coerce = resolver::coercionFor(self.low.resolver, node) else { |
|
| 6981 | + | unsafe fn applyCoercion(self: &mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) { |
|
| 6982 | + | let coerce = resolver::coercionFor(&*self.low.resolver, node) else { |
|
| 6975 | 6983 | return val; |
|
| 6976 | 6984 | }; |
|
| 6977 | 6985 | match coerce { |
|
| 6978 | 6986 | case resolver::Coercion::OptionalLift(optType) => { |
|
| 6979 | 6987 | if let case ast::NodeValue::Nil = node.value { |
| 6987 | 6995 | case resolver::Coercion::ResultWrap => { |
|
| 6988 | 6996 | let payloadType = *self.fnType.returnType; |
|
| 6989 | 6997 | return try buildResult(self, 0, val, payloadType); |
|
| 6990 | 6998 | } |
|
| 6991 | 6999 | case resolver::Coercion::TraitObject { traitInfo, inst } => { |
|
| 6992 | - | return try buildTraitObject(self, val, traitInfo, inst); |
|
| 7000 | + | return try buildTraitObject(self, val, traitInfo, &*inst); |
|
| 6993 | 7001 | } |
|
| 6994 | 7002 | case resolver::Coercion::Identity => return val, |
|
| 6995 | 7003 | } |
|
| 6996 | 7004 | } |
|
| 6997 | 7005 | ||
| 6998 | 7006 | /// Lower an implicit numeric cast coercion. |
|
| 6999 | 7007 | /// |
|
| 7000 | 7008 | /// Handles widening conversions between integer types. Uses sign-extension |
|
| 7001 | 7009 | /// for signed source types and zero-extension for unsigned source types. |
|
| 7002 | - | fn lowerNumericCast(self: *mut FnLowerer, val: il::Val, srcType: resolver::Type, dstType: resolver::Type) -> il::Val { |
|
| 7010 | + | unsafe fn lowerNumericCast(self: &mut FnLowerer, val: il::Val, srcType: resolver::Type, dstType: resolver::Type) -> il::Val { |
|
| 7003 | 7011 | let srcLayout = resolver::getTypeLayout(srcType); |
|
| 7004 | 7012 | let dstLayout = resolver::getTypeLayout(dstType); |
|
| 7005 | 7013 | ||
| 7006 | 7014 | if srcLayout.size == dstLayout.size { |
|
| 7007 | 7015 | // Same size: bit pattern is unchanged, value is returned as-is. |
|
| 7008 | 7016 | return val; |
|
| 7009 | 7017 | } |
|
| 7010 | 7018 | // Widening: extend based on source signedness. |
|
| 7011 | 7019 | // Narrowing: truncate and normalize to destination width. |
|
| 7012 | 7020 | let widening = srcLayout.size < dstLayout.size; |
|
| 7013 | - | let extType = ilType(self.low, srcType) if widening else ilType(self.low, dstType); |
|
| 7021 | + | let extType = ilType(&mut *self.low, srcType) if widening else ilType(&mut *self.low, dstType); |
|
| 7014 | 7022 | let signed = isSignedType(srcType) if widening else isSignedType(dstType); |
|
| 7015 | 7023 | let dst = nextReg(self); |
|
| 7016 | 7024 | ||
| 7017 | 7025 | if signed { |
|
| 7018 | 7026 | emit(self, il::Instr::Sext { typ: extType, dst, val }); |
| 7021 | 7029 | } |
|
| 7022 | 7030 | return il::Val::Reg(dst); |
|
| 7023 | 7031 | } |
|
| 7024 | 7032 | ||
| 7025 | 7033 | /// Lower a global value symbol. |
|
| 7026 | - | fn lowerGlobalValue(self: *mut FnLowerer, sym: *resolver::Symbol, ty: resolver::Type) -> il::Val { |
|
| 7034 | + | unsafe fn lowerGlobalValue(self: &mut FnLowerer, sym: *resolver::Symbol, ty: resolver::Type) -> il::Val { |
|
| 7027 | 7035 | // Function pointer reference: return the function's address directly. |
|
| 7028 | 7036 | // Functions have no separate storage cell in the data section. |
|
| 7029 | 7037 | if let case resolver::Type::Fn(_) = ty { |
|
| 7030 | 7038 | return il::Val::Reg(emitFnAddr(self, sym)); |
|
| 7031 | 7039 | } |
| 7033 | 7041 | ||
| 7034 | 7042 | return emitRead(self, src, 0, ty); |
|
| 7035 | 7043 | } |
|
| 7036 | 7044 | ||
| 7037 | 7045 | /// Lower an identifier that refers to a global symbol. |
|
| 7038 | - | fn lowerGlobalSymbol(self: *mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7046 | + | unsafe fn lowerGlobalSymbol(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7039 | 7047 | // First try to get a compile-time constant value. |
|
| 7040 | - | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7048 | + | if let constVal = resolver::constValueEntry(&*self.low.resolver, node) { |
|
| 7041 | 7049 | return try constValueToVal(self, constVal, node); |
|
| 7042 | 7050 | } |
|
| 7043 | 7051 | // Otherwise get the symbol. |
|
| 7044 | 7052 | let sym = try symOf(self, node); |
|
| 7045 | 7053 |
| 7054 | 7062 | else => throw LowerError::UnexpectedNodeValue(node), |
|
| 7055 | 7063 | } |
|
| 7056 | 7064 | } |
|
| 7057 | 7065 | ||
| 7058 | 7066 | /// Lower an assignment to a static variable. |
|
| 7059 | - | fn lowerStaticAssign(self: *mut FnLowerer, target: *ast::Node, val: il::Val) throws (LowerError) { |
|
| 7067 | + | unsafe fn lowerStaticAssign(self: &mut FnLowerer, target: *ast::Node, val: il::Val) throws (LowerError) { |
|
| 7060 | 7068 | let sym = try symOf(self, target); |
|
| 7061 | 7069 | let case resolver::SymbolData::Value { type, .. } = sym.data else { |
|
| 7062 | 7070 | throw LowerError::ImmutableAssignment; |
|
| 7063 | 7071 | }; |
|
| 7064 | 7072 | let dst = emitDataAddr(self, sym); |
| 7066 | 7074 | try emitStore(self, dst, 0, type, val); |
|
| 7067 | 7075 | } |
|
| 7068 | 7076 | ||
| 7069 | 7077 | /// Lower a scope access expression like `Module::Const` or `Union::Variant`. |
|
| 7070 | 7078 | /// This doesn't handle record literal variants. |
|
| 7071 | - | fn lowerScopeAccess(self: *mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7079 | + | unsafe fn lowerScopeAccess(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7072 | 7080 | // First try to get a compile-time constant value. |
|
| 7073 | - | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7081 | + | if let constVal = resolver::constValueEntry(&*self.low.resolver, node) { |
|
| 7074 | 7082 | return try constValueToVal(self, constVal, node); |
|
| 7075 | 7083 | } |
|
| 7076 | 7084 | // Otherwise get the associated symbol. |
|
| 7077 | - | let data = resolver::nodeData(self.low.resolver, node); |
|
| 7085 | + | let data = resolver::nodeData(&*self.low.resolver, node); |
|
| 7078 | 7086 | let sym = data.sym else { |
|
| 7079 | 7087 | throw LowerError::MissingSymbol(node); |
|
| 7080 | 7088 | }; |
|
| 7081 | 7089 | match sym.data { |
|
| 7082 | 7090 | case resolver::SymbolData::Variant { index, .. } => { |
|
| 7083 | 7091 | let mut indexValue = index as i64; |
|
| 7084 | - | if let idx = voidVariantIndex(self.low.resolver, node) { |
|
| 7092 | + | if let idx = voidVariantIndex(&*self.low.resolver, node) { |
|
| 7085 | 7093 | set indexValue = idx; |
|
| 7086 | 7094 | } |
|
| 7087 | 7095 | // Void union variant like `Option::None`. |
|
| 7088 | 7096 | if data.ty == resolver::Type::Unknown { |
|
| 7089 | 7097 | throw LowerError::MissingType(node); |
| 7123 | 7131 | } |
|
| 7124 | 7132 | } |
|
| 7125 | 7133 | ||
| 7126 | 7134 | /// Lower an expression AST node to an IL value. |
|
| 7127 | 7135 | /// This is the main expression dispatch, all expression nodes go through here. |
|
| 7128 | - | fn lowerExpr(self: *mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7136 | + | unsafe fn lowerExpr(self: &mut FnLowerer, node: *ast::Node) -> il::Val throws (LowerError) { |
|
| 7129 | 7137 | if self.low.options.debug { |
|
| 7130 | 7138 | set self.srcLoc.offset = node.span.offset; |
|
| 7131 | 7139 | } |
|
| 7132 | 7140 | let mut val: il::Val = undefined; |
|
| 7133 | 7141 |
| 7197 | 7205 | case ast::NodeValue::Try(t) => { |
|
| 7198 | 7206 | set val = try lowerTry(self, node, t); |
|
| 7199 | 7207 | } |
|
| 7200 | 7208 | case ast::NodeValue::FieldAccess(access) => { |
|
| 7201 | 7209 | // Check for compile-time constant (e.g., `arr.len` on fixed-size arrays). |
|
| 7202 | - | if let constVal = resolver::constValueEntry(self.low.resolver, node) { |
|
| 7210 | + | if let constVal = resolver::constValueEntry(&*self.low.resolver, node) { |
|
| 7203 | 7211 | match constVal { |
|
| 7204 | 7212 | // TODO: Handle `u32` values that don't fit in an `i32`. |
|
| 7205 | 7213 | // Perhaps just store the `ConstInt`. |
|
| 7206 | 7214 | case resolver::ConstValue::Int(i) => set val = il::Val::Imm(constIntToI64(i)), |
|
| 7207 | 7215 | else => set val = try lowerFieldAccess(self, access), |
| 7256 | 7264 | set val = il::Val::Undef; |
|
| 7257 | 7265 | } |
|
| 7258 | 7266 | // Lower these as statements. |
|
| 7259 | 7267 | case ast::NodeValue::ConstDecl(decl) => { |
|
| 7260 | 7268 | try registerLocalDataDeclName(self, node); |
|
| 7261 | - | try lowerDataDecl(self.low, node, decl.value, true); |
|
| 7269 | + | try lowerDataDecl(&mut *self.low, node, decl.value, true); |
|
| 7262 | 7270 | set val = il::Val::Undef; |
|
| 7263 | 7271 | } |
|
| 7264 | 7272 | case ast::NodeValue::StaticDecl(decl) => { |
|
| 7265 | 7273 | try registerLocalDataDeclName(self, node); |
|
| 7266 | - | try lowerDataDecl(self.low, node, decl.value, false); |
|
| 7274 | + | try lowerDataDecl(&mut *self.low, node, decl.value, false); |
|
| 7267 | 7275 | set val = il::Val::Undef; |
|
| 7268 | 7276 | } |
|
| 7269 | 7277 | case ast::NodeValue::Throw { .. }, |
|
| 7270 | 7278 | ast::NodeValue::Return { .. }, |
|
| 7271 | 7279 | ast::NodeValue::Continue, |
| 7286 | 7294 | /// are used. If a Radiance type doesn't fit in a machine word, it is passed |
|
| 7287 | 7295 | /// by reference. |
|
| 7288 | 7296 | /// |
|
| 7289 | 7297 | /// The IL doesn't track signedness - that's encoded in the instructions |
|
| 7290 | 7298 | /// (e.g., Slt vs Ult). |
|
| 7291 | - | fn ilType(self: *mut Lowerer, typ: resolver::Type) -> il::Type { |
|
| 7299 | + | fn ilType(self: &mut Lowerer, typ: resolver::Type) -> il::Type { |
|
| 7292 | 7300 | match typ { |
|
| 7293 | 7301 | case resolver::Type::Bool, |
|
| 7294 | 7302 | resolver::Type::I8, |
|
| 7295 | 7303 | resolver::Type::U8 => return il::Type::W8, |
|
| 7296 | 7304 | case resolver::Type::I16, |
lib/std/lang/module.rad
+30 -26
| 84 | 84 | source: ?*[u8], |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Dense storage for all modules referenced by the compilation unit. |
|
| 88 | 88 | export record ModuleGraph: Copy { |
|
| 89 | + | /// Permanent storage for module entries. |
|
| 89 | 90 | entries: *mut [ModuleEntry], |
|
| 91 | + | /// Number of initialized entries. |
|
| 90 | 92 | entriesLen: u32, |
|
| 93 | + | /// Permanent storage for interned names. |
|
| 91 | 94 | pool: *mut strings::Pool, |
|
| 92 | - | /// Arena used for all AST node allocations. |
|
| 93 | - | arena: ?*ast::NodeArena, |
|
| 95 | + | /// AST arena. It must outlive the graph. |
|
| 96 | + | arena: ?*unsafe ast::NodeArena, |
|
| 94 | 97 | } |
|
| 95 | 98 | ||
| 96 | 99 | /// Initialize an empty module graph backed by the provided storage. |
|
| 97 | - | export fn moduleGraph( |
|
| 100 | + | /// The AST arena must outlive the returned graph. |
|
| 101 | + | export unsafe fn moduleGraph( |
|
| 98 | 102 | storage: *mut [ModuleEntry], |
|
| 99 | 103 | pool: *mut strings::Pool, |
|
| 100 | - | arena: *mut ast::NodeArena |
|
| 104 | + | arena: &mut ast::NodeArena |
|
| 101 | 105 | ) -> ModuleGraph { |
|
| 102 | 106 | return ModuleGraph { |
|
| 103 | 107 | entries: storage, |
|
| 104 | 108 | entriesLen: 0, |
|
| 105 | 109 | pool, |
|
| 106 | - | arena, |
|
| 110 | + | arena: arena as *unsafe ast::NodeArena, |
|
| 107 | 111 | }; |
|
| 108 | 112 | } |
|
| 109 | 113 | ||
| 110 | 114 | /// Register a root module residing at `path` for a package. |
|
| 111 | - | export fn registerRoot(graph: *mut ModuleGraph, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 115 | + | export fn registerRoot(graph: &mut ModuleGraph, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 112 | 116 | let name = try basenameSlice(filePath); |
|
| 113 | 117 | return try registerRootWithName(graph, packageId, name, filePath); |
|
| 114 | 118 | } |
|
| 115 | 119 | ||
| 116 | 120 | /// Register a root module with an explicit name and file path for a package. |
|
| 117 | 121 | export fn registerRootWithName( |
|
| 118 | - | graph: *mut ModuleGraph, |
|
| 122 | + | graph: &mut ModuleGraph, |
|
| 119 | 123 | packageId: u16, |
|
| 120 | 124 | name: *[u8], |
|
| 121 | 125 | filePath: *[u8] |
|
| 122 | 126 | ) -> u16 throws (ModuleError) { |
|
| 123 | 127 | let m = try allocModule(graph, packageId, name, filePath); |
| 129 | 133 | } |
|
| 130 | 134 | ||
| 131 | 135 | /// Register a child module. |
|
| 132 | 136 | /// Returns the module identifier. |
|
| 133 | 137 | export fn registerChild( |
|
| 134 | - | graph: *mut ModuleGraph, |
|
| 138 | + | graph: &mut ModuleGraph, |
|
| 135 | 139 | parentId: u16, |
|
| 136 | 140 | name: *[u8], |
|
| 137 | 141 | filePath: *[u8] |
|
| 138 | 142 | ) -> u16 throws (ModuleError) { |
|
| 139 | 143 | assert name.len > 0, "registerChild: name must not be empty"; |
| 160 | 164 | ||
| 161 | 165 | return try addChild(parent, m.id); |
|
| 162 | 166 | } |
|
| 163 | 167 | ||
| 164 | 168 | /// Fetch a read-only view of the module identified by `id`. |
|
| 165 | - | export fn get(graph: *ModuleGraph, id: u16) -> ?*ModuleEntry { |
|
| 169 | + | export fn get(graph: &ModuleGraph, id: u16) -> ?*ModuleEntry { |
|
| 166 | 170 | if not isValidId(graph, id) { |
|
| 167 | 171 | return nil; |
|
| 168 | 172 | } |
|
| 169 | 173 | return &graph.entries[id as u32]; |
|
| 170 | 174 | } |
|
| 171 | 175 | ||
| 172 | 176 | /// Access a mutable entry by identifier. |
|
| 173 | - | fn getMut(graph: *mut ModuleGraph, id: u16) -> ?*mut ModuleEntry { |
|
| 177 | + | fn getMut(graph: &mut ModuleGraph, id: u16) -> ?*mut ModuleEntry { |
|
| 174 | 178 | if not isValidId(graph, id) { |
|
| 175 | 179 | return nil; |
|
| 176 | 180 | } |
|
| 177 | 181 | return &mut graph.entries[id as u32]; |
|
| 178 | 182 | } |
| 193 | 197 | assert m.pathDepth > 0, "moduleQualifiedPath: path must not be empty"; |
|
| 194 | 198 | return &m.path[..m.pathDepth]; |
|
| 195 | 199 | } |
|
| 196 | 200 | ||
| 197 | 201 | /// Retrieve the lifecycle state for `id`. |
|
| 198 | - | export fn state(graph: *ModuleGraph, id: u16) -> ModuleState throws (ModuleError) { |
|
| 202 | + | export fn state(graph: &ModuleGraph, id: u16) -> ModuleState throws (ModuleError) { |
|
| 199 | 203 | let m = get(graph, id) else { |
|
| 200 | 204 | throw ModuleError::NotFound(id); |
|
| 201 | 205 | }; |
|
| 202 | 206 | return m.state; |
|
| 203 | 207 | } |
|
| 204 | 208 | ||
| 205 | 209 | /// Record the parsed AST root for `id`. |
|
| 206 | - | export fn setAst(graph: *mut ModuleGraph, id: u16, root: *mut ast::Node) throws (ModuleError) { |
|
| 210 | + | export fn setAst(graph: &mut ModuleGraph, id: u16, root: *mut ast::Node) throws (ModuleError) { |
|
| 207 | 211 | let m = getMut(graph, id) else throw ModuleError::NotFound(id); |
|
| 208 | 212 | set m.ast = root; |
|
| 209 | 213 | set m.state = ModuleState::Parsed; |
|
| 210 | 214 | } |
|
| 211 | 215 | ||
| 212 | 216 | /// Set the source text for a module. |
|
| 213 | - | export fn setSource(graph: *mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) { |
|
| 217 | + | export fn setSource(graph: &mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) { |
|
| 214 | 218 | let m = getMut(graph, id) else throw ModuleError::NotFound(id); |
|
| 215 | 219 | set m.source = source; |
|
| 216 | 220 | } |
|
| 217 | 221 | ||
| 218 | 222 | /// Look up a child module by name under the given parent. |
|
| 219 | - | export fn findChild(graph: *ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry { |
|
| 223 | + | export fn findChild(graph: &ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry { |
|
| 220 | 224 | assert isValidId(graph, parentId), "findChild: parent identifier is valid"; |
|
| 221 | 225 | ||
| 222 | 226 | let parent = &graph.entries[parentId as u32]; |
|
| 223 | 227 | for i in 0..parent.childrenLen { |
|
| 224 | 228 | let childId = parent.children[i]; |
| 231 | 235 | } |
|
| 232 | 236 | ||
| 233 | 237 | /// Parse a file path into components by splitting on '/'. |
|
| 234 | 238 | /// Expects and removes the '.rad' extension from the last component. |
|
| 235 | 239 | /// Returns the number of components extracted, or `nil` if the path is invalid. |
|
| 236 | - | export fn parsePath(filePath: *[u8], components: *mut [*[u8]]) -> ?u32 { |
|
| 240 | + | export fn parsePath(filePath: *[u8], components: &mut [*[u8]]) -> ?u32 { |
|
| 237 | 241 | let mut count: u32 = 0; |
|
| 238 | 242 | let mut last: u32 = 0; |
|
| 239 | 243 | ||
| 240 | 244 | // Split on '/' to extract all but the last component. |
|
| 241 | 245 | for i in 0..filePath.len { |
| 266 | 270 | ||
| 267 | 271 | /// Register a module from a file path, creating the full hierarchy as needed. |
|
| 268 | 272 | /// The path is split into components and the module hierarchy is built accordingly. |
|
| 269 | 273 | /// If `rootId` is `nil`, registers a new root for the given package. |
|
| 270 | 274 | /// Returns the module ID of the last component. |
|
| 271 | - | export fn registerFromPath(graph: *mut ModuleGraph, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 275 | + | export fn registerFromPath(graph: &mut ModuleGraph, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 272 | 276 | let root = rootId else { |
|
| 273 | 277 | return try registerRoot(graph, packageId, filePath); |
|
| 274 | 278 | }; |
|
| 275 | 279 | let rootEntry = get(graph, root) else { |
|
| 276 | 280 | panic "registerFromPath: root is missing from storage"; |
| 291 | 295 | throw ModuleError::InvalidPath; |
|
| 292 | 296 | } |
|
| 293 | 297 | ||
| 294 | 298 | // Strip the root's qualified path from the parsed components. |
|
| 295 | 299 | let rootPath = moduleQualifiedPath(rootEntry); |
|
| 296 | - | let childPath = stripPathPrefix(rootPath, &parts[..partsLen]) else { |
|
| 300 | + | let prefixLen = stripPathPrefix(rootPath, &parts[..partsLen]) else { |
|
| 297 | 301 | throw ModuleError::InvalidPath; |
|
| 298 | 302 | }; |
|
| 299 | - | if childPath.len == 0 { |
|
| 303 | + | if prefixLen == partsLen { |
|
| 300 | 304 | throw ModuleError::InvalidPath; |
|
| 301 | 305 | } |
|
| 302 | - | let childName = childPath[childPath.len - 1]; |
|
| 306 | + | let childName = parts[partsLen - 1]; |
|
| 303 | 307 | ||
| 304 | 308 | // Navigate through all but the last segment to find the parent. |
|
| 305 | 309 | let mut parentId = root; |
|
| 306 | - | for part in &childPath[..childPath.len - 1] { |
|
| 310 | + | for part in &parts[prefixLen..partsLen - 1] { |
|
| 307 | 311 | let child = findChild(graph, part, parentId) else { |
|
| 308 | 312 | throw ModuleError::MissingParent; |
|
| 309 | 313 | }; |
|
| 310 | 314 | set parentId = child.id; |
|
| 311 | 315 | } |
|
| 312 | 316 | return try registerChild(graph, parentId, childName, filePath); |
|
| 313 | 317 | } |
|
| 314 | 318 | ||
| 315 | 319 | /// Allocate a fresh entry in the graph. |
|
| 316 | - | fn allocModule(graph: *mut ModuleGraph, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) { |
|
| 320 | + | fn allocModule(graph: &mut ModuleGraph, packageId: u16, name: *[u8], filePath: *[u8]) -> *mut ModuleEntry throws (ModuleError) { |
|
| 317 | 321 | if graph.entriesLen >= graph.entries.len { |
|
| 318 | 322 | throw ModuleError::CapacityExceeded; |
|
| 319 | 323 | } |
|
| 320 | 324 | let idx = graph.entriesLen; |
|
| 321 | 325 | set graph.entriesLen += 1; |
| 361 | 365 | ||
| 362 | 366 | return childId; |
|
| 363 | 367 | } |
|
| 364 | 368 | ||
| 365 | 369 | /// Check if `id` points at an allocated entry. |
|
| 366 | - | fn isValidId(graph: *ModuleGraph, id: u16) -> bool { |
|
| 370 | + | fn isValidId(graph: &ModuleGraph, id: u16) -> bool { |
|
| 367 | 371 | return (id as u32) < graph.entriesLen; |
|
| 368 | 372 | } |
|
| 369 | 373 | ||
| 370 | 374 | /// Return the length of the directory prefix for `path`. |
|
| 371 | 375 | /// Return zero if the path has no separator. |
| 401 | 405 | } |
|
| 402 | 406 | } |
|
| 403 | 407 | return &path[..extStart]; |
|
| 404 | 408 | } |
|
| 405 | 409 | ||
| 406 | - | /// Strip prefix from path, and return the suffix. |
|
| 407 | - | fn stripPathPrefix(prefix: *[*[u8]], path: *[*[u8]]) -> ?*[*[u8]] { |
|
| 410 | + | /// Check a path prefix and return its segment count. |
|
| 411 | + | fn stripPathPrefix(prefix: &[*[u8]], path: &[*[u8]]) -> ?u32 { |
|
| 408 | 412 | if prefix.len == 0 { |
|
| 409 | - | return path; |
|
| 413 | + | return 0; |
|
| 410 | 414 | } |
|
| 411 | 415 | if prefix.len > path.len { |
|
| 412 | 416 | return nil; |
|
| 413 | 417 | } |
|
| 414 | 418 | for segment, i in prefix { |
|
| 415 | 419 | if not mem::eq(segment, path[i]) { |
|
| 416 | 420 | return nil; |
|
| 417 | 421 | } |
|
| 418 | 422 | } |
|
| 419 | - | return &path[prefix.len..]; |
|
| 423 | + | return prefix.len; |
|
| 420 | 424 | } |
lib/std/lang/module/printer.rad
+7 -7
| 5 | 5 | use std::io; |
|
| 6 | 6 | use std::lang::sexpr; |
|
| 7 | 7 | use std::lang::alloc; |
|
| 8 | 8 | ||
| 9 | 9 | /// Format a u32 and allocate the result in the arena. |
|
| 10 | - | fn formatId(a: *mut alloc::Arena, id: u32) -> *[u8] { |
|
| 10 | + | fn formatId(a: &mut alloc::Arena, id: u32) -> *[u8] { |
|
| 11 | 11 | let mut digits: [u8; 10] = undefined; |
|
| 12 | - | let text = fmt::formatU32(id, &mut digits[..]); |
|
| 13 | - | let ptr = try alloc::allocSlice(a, 1, 1, text.len) catch { return "?"; }; |
|
| 12 | + | let start = fmt::formatU32(id, &mut digits[..]); |
|
| 13 | + | let ptr = try alloc::allocSlice(a, 1, 1, digits.len - start) catch { return "?"; }; |
|
| 14 | 14 | let slice = ptr as *mut [u8]; |
|
| 15 | - | try mem::copy(slice, text) catch { return "?"; }; |
|
| 15 | + | try mem::copy(slice, &digits[start..]) catch { return "?"; }; |
|
| 16 | 16 | return slice; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | /// Convert a module state into an S-expression symbol. |
|
| 20 | 20 | fn stateToExpr(state: super::ModuleState) -> sexpr::Expr { |
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | /// Recursively convert a module entry and its descendants to an S-expression. |
|
| 32 | 32 | fn subtreeToExpr( |
|
| 33 | - | a: *mut alloc::Arena, |
|
| 34 | - | graph: *super::ModuleGraph, |
|
| 33 | + | a: &mut alloc::Arena, |
|
| 34 | + | graph: &super::ModuleGraph, |
|
| 35 | 35 | entry: *super::ModuleEntry |
|
| 36 | 36 | ) -> sexpr::Expr { |
|
| 37 | 37 | let idText = formatId(a, entry.id as u32); |
|
| 38 | 38 | let path = super::moduleQualifiedPath(entry); |
|
| 39 | 39 | let mut pathBuf: *[sexpr::Expr] = &[]; |
| 62 | 62 | sexpr::Expr::List { head: "::", tail: pathBuf, multiline: false } |
|
| 63 | 63 | ], childBuf); |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Print the entire module graph in S-expression format. |
|
| 67 | - | export fn printGraph(graph: *super::ModuleGraph, arena: *mut alloc::Arena) { |
|
| 67 | + | export unsafe fn printGraph(graph: &super::ModuleGraph, arena: &mut alloc::Arena) { |
|
| 68 | 68 | // Print all root modules. |
|
| 69 | 69 | for i in 0..graph.entriesLen { |
|
| 70 | 70 | let entry = &graph.entries[i]; |
|
| 71 | 71 | if entry.parent == nil { |
|
| 72 | 72 | sexpr::print(subtreeToExpr(arena, graph, entry), 0); |
lib/std/lang/module/tests.rad
+14 -14
| 26 | 26 | for i in 0..expected.len { |
|
| 27 | 27 | try expectSliceEq(actual[i], expected[i]); |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | - | @test fn testRegisterChildren() throws (testing::TestError) { |
|
| 32 | - | let mut storage: [super::ModuleEntry; 4] = undefined; |
|
| 31 | + | @test unsafe fn testRegisterChildren() throws (testing::TestError) { |
|
| 32 | + | static storage: [super::ModuleEntry; 4] = undefined; |
|
| 33 | 33 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 34 | 34 | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 35 | 35 | ||
| 36 | 36 | let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/root.rad") catch { |
|
| 37 | 37 | throw testing::TestError::Failed; |
| 70 | 70 | try testing::expect(parent.childrenLen == 2); |
|
| 71 | 71 | try testing::expect(super::childAt(parent, 0) == firstId); |
|
| 72 | 72 | try testing::expect(super::childAt(parent, 1) == secondId); |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | - | @test fn testRegisterChildReusesExisting() throws (testing::TestError) { |
|
| 76 | - | let mut storage: [super::ModuleEntry; 4] = undefined; |
|
| 75 | + | @test unsafe fn testRegisterChildReusesExisting() throws (testing::TestError) { |
|
| 76 | + | static storage: [super::ModuleEntry; 4] = undefined; |
|
| 77 | 77 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 78 | 78 | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 79 | 79 | ||
| 80 | 80 | let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/main.rad") catch { |
|
| 81 | 81 | throw testing::TestError::Failed; |
| 141 | 141 | let mut components: [*[u8]; 8] = undefined; |
|
| 142 | 142 | let result = super::parsePath(path, &mut components[..]); |
|
| 143 | 143 | try testing::expect(result == nil); |
|
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | - | @test fn testRegisterFromPathHierarchy() throws (testing::TestError) { |
|
| 147 | - | let mut storage: [super::ModuleEntry; 8] = undefined; |
|
| 146 | + | @test unsafe fn testRegisterFromPathHierarchy() throws (testing::TestError) { |
|
| 147 | + | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 148 | 148 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 149 | 149 | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 150 | 150 | ||
| 151 | 151 | // Register root module. |
|
| 152 | 152 | let stdId = try super::registerFromPath(&mut graph, 0, nil, "lib/std.rad") catch { |
| 190 | 190 | try testing::expect(lang.childrenLen == 1); |
|
| 191 | 191 | try testing::expect(super::childAt(lang, 0) == parserId); |
|
| 192 | 192 | try testing::expect(parser.childrenLen == 0); |
|
| 193 | 193 | } |
|
| 194 | 194 | ||
| 195 | - | @test fn testRegisterFromPathMissingParent() throws (testing::TestError) { |
|
| 196 | - | let mut storage: [super::ModuleEntry; 8] = undefined; |
|
| 195 | + | @test unsafe fn testRegisterFromPathMissingParent() throws (testing::TestError) { |
|
| 196 | + | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 197 | 197 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 198 | 198 | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 199 | 199 | ||
| 200 | 200 | let rootId = try super::registerFromPath(&mut graph, 0, nil, "std.rad") catch { |
|
| 201 | 201 | throw testing::TestError::Failed; |
| 205 | 205 | return; |
|
| 206 | 206 | }; |
|
| 207 | 207 | throw testing::TestError::Failed; |
|
| 208 | 208 | } |
|
| 209 | 209 | ||
| 210 | - | @test fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) { |
|
| 211 | - | let mut storage: [super::ModuleEntry; 8] = undefined; |
|
| 210 | + | @test unsafe fn testRegisterFromPathDuplicateRoot() throws (testing::TestError) { |
|
| 211 | + | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 212 | 212 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 213 | 213 | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 214 | 214 | ||
| 215 | 215 | let rootId = try super::registerFromPath(&mut graph, 0, nil, "std.rad") catch { |
|
| 216 | 216 | throw testing::TestError::Failed; |
| 220 | 220 | return; |
|
| 221 | 221 | }; |
|
| 222 | 222 | throw testing::TestError::Failed; |
|
| 223 | 223 | } |
|
| 224 | 224 | ||
| 225 | - | @test fn testRegisterFromPathRegistersRoot() throws (testing::TestError) { |
|
| 226 | - | let mut storage: [super::ModuleEntry; 8] = undefined; |
|
| 225 | + | @test unsafe fn testRegisterFromPathRegistersRoot() throws (testing::TestError) { |
|
| 226 | + | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 227 | 227 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 228 | 228 | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 229 | 229 | ||
| 230 | 230 | let rootId = try super::registerFromPath(&mut graph, 0, nil, "lib/std.rad") catch { |
|
| 231 | 231 | throw testing::TestError::Failed; |
| 239 | 239 | try expectSliceEq(root.name, "std"); |
|
| 240 | 240 | try expectSliceEq(root.filePath, "lib/std.rad"); |
|
| 241 | 241 | try expectPathSegments(root, &["std"]); |
|
| 242 | 242 | } |
|
| 243 | 243 | ||
| 244 | - | @test fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) { |
|
| 245 | - | let mut storage: [super::ModuleEntry; 8] = undefined; |
|
| 244 | + | @test unsafe fn testRegisterFromPathIgnoresLeadingDirectories() throws (testing::TestError) { |
|
| 245 | + | static storage: [super::ModuleEntry; 8] = undefined; |
|
| 246 | 246 | let mut arena = ast::nodeArena(&mut TEST_ARENA[..]); |
|
| 247 | 247 | let mut graph = super::moduleGraph(&mut storage[..], &mut STRING_POOL, &mut arena); |
|
| 248 | 248 | ||
| 249 | 249 | let rootId = try super::registerFromPath(&mut graph, 0, nil, "src/pkg/root.rad") catch { |
|
| 250 | 250 | throw testing::TestError::Failed; |
lib/std/lang/package.rad
+3 -4
| 19 | 19 | rootModuleId: ?u16, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// Initialize `pkg` with the provided name and ID. |
|
| 23 | 23 | export fn init( |
|
| 24 | - | pkg: *mut Package, |
|
| 24 | + | pkg: &mut Package, |
|
| 25 | 25 | id: u16, |
|
| 26 | 26 | name: *[u8], |
|
| 27 | 27 | pool: *mut strings::Pool |
|
| 28 | - | ) -> *mut Package { |
|
| 28 | + | ) { |
|
| 29 | 29 | set pkg.id = id; |
|
| 30 | 30 | set pkg.name = strings::intern(pool, name); |
|
| 31 | 31 | set pkg.rootModuleId = nil; |
|
| 32 | 32 | ||
| 33 | - | return pkg; |
|
| 34 | 33 | } |
|
| 35 | 34 | ||
| 36 | 35 | /// Register a module described by the file path. |
|
| 37 | - | export fn registerModule(pkg: *mut Package, graph: *mut module::ModuleGraph, filePath: *[u8]) -> u16 |
|
| 36 | + | export fn registerModule(pkg: &mut Package, graph: &mut module::ModuleGraph, filePath: *[u8]) -> u16 |
|
| 38 | 37 | throws (module::ModuleError) |
|
| 39 | 38 | { |
|
| 40 | 39 | let modId = try module::registerFromPath(graph, pkg.id, pkg.rootModuleId, filePath); |
|
| 41 | 40 | // First registered module becomes the root. |
|
| 42 | 41 | if pkg.rootModuleId == nil { |
lib/std/lang/parser.rad
+132 -127
| 151 | 151 | current: scanner::Token, |
|
| 152 | 152 | /// The most recently consumed token. |
|
| 153 | 153 | previous: scanner::Token, |
|
| 154 | 154 | /// Collection of errors encountered during parsing. |
|
| 155 | 155 | errors: ErrorList, |
|
| 156 | - | /// Arena for all node allocations. |
|
| 157 | - | arena: *mut ast::NodeArena, |
|
| 156 | + | /// Arena for node allocations. It must outlive the parser. |
|
| 157 | + | arena: *unsafe mut ast::NodeArena, |
|
| 158 | 158 | /// Allocator backed by the node arena. |
|
| 159 | 159 | allocator: alloc::Allocator, |
|
| 160 | 160 | /// Current parsing context (normal or conditional). |
|
| 161 | 161 | context: Context, |
|
| 162 | 162 | } |
|
| 163 | 163 | ||
| 164 | 164 | /// Create a new parser initialized with the given source kind, source and node arena. |
|
| 165 | - | export fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: *mut ast::NodeArena, pool: *mut strings::Pool) -> Parser { |
|
| 165 | + | /// The node arena must outlive the returned parser. |
|
| 166 | + | export unsafe fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: &mut ast::NodeArena, pool: *mut strings::Pool) -> Parser { |
|
| 166 | 167 | return Parser { |
|
| 167 | 168 | scanner: scanner::scanner(sourceLoc, source, pool), |
|
| 168 | 169 | current: scanner::invalid(0, ""), |
|
| 169 | 170 | previous: scanner::invalid(0, ""), |
|
| 170 | 171 | errors: ErrorList { list: undefined, count: 0 }, |
|
| 171 | - | arena, |
|
| 172 | + | arena: arena as *unsafe mut ast::NodeArena, |
|
| 172 | 173 | allocator: alloc::arenaAllocator(&mut arena.arena), |
|
| 173 | 174 | context: Context::Normal, |
|
| 174 | 175 | }; |
|
| 175 | 176 | } |
|
| 176 | 177 | ||
| 177 | 178 | /// Emit a `true` or `false` literal node. |
|
| 178 | - | fn nodeBool(p: *mut Parser, value: bool) -> *ast::Node { |
|
| 179 | + | unsafe fn nodeBool(p: &mut Parser, value: bool) -> *ast::Node { |
|
| 179 | 180 | return node(p, ast::NodeValue::Bool(value)); |
|
| 180 | 181 | } |
|
| 181 | 182 | ||
| 182 | 183 | /// Parse an integer literal while mapping shared errors into parser diagnostics. |
|
| 183 | - | fn parseIntLiteral(p: *mut Parser, text: *[u8]) -> fmt::IntLiteral |
|
| 184 | + | fn parseIntLiteral(p: &mut Parser, text: *[u8]) -> fmt::IntLiteral |
|
| 184 | 185 | throws (ParseError) |
|
| 185 | 186 | { |
|
| 186 | 187 | let literal = try fmt::parseInt(text) catch err { |
|
| 187 | 188 | match err { |
|
| 188 | 189 | case fmt::ParseError::Invalid => |
| 195 | 196 | }; |
|
| 196 | 197 | return literal; |
|
| 197 | 198 | } |
|
| 198 | 199 | ||
| 199 | 200 | /// Emit an integer type node. |
|
| 200 | - | fn nodeTypeInt(p: *mut Parser, width: u8, sign: ast::Signedness) -> *ast::Node { |
|
| 201 | + | unsafe fn nodeTypeInt(p: &mut Parser, width: u8, sign: ast::Signedness) -> *ast::Node { |
|
| 201 | 202 | return node(p, ast::NodeValue::TypeSig( |
|
| 202 | 203 | ast::TypeSig::Integer { width, sign } |
|
| 203 | 204 | )); |
|
| 204 | 205 | } |
|
| 205 | 206 | ||
| 206 | 207 | /// Emit a number literal node with the provided literal metadata. |
|
| 207 | - | fn nodeNumber(p: *mut Parser, literal: fmt::IntLiteral) -> *ast::Node { |
|
| 208 | + | unsafe fn nodeNumber(p: &mut Parser, literal: fmt::IntLiteral) -> *ast::Node { |
|
| 208 | 209 | return node(p, ast::NodeValue::Number(literal)); |
|
| 209 | 210 | } |
|
| 210 | 211 | ||
| 211 | 212 | /// Emit a `super` node. |
|
| 212 | - | fn nodeSuper(p: *mut Parser) -> *ast::Node { |
|
| 213 | + | unsafe fn nodeSuper(p: &mut Parser) -> *ast::Node { |
|
| 213 | 214 | return node(p, ast::NodeValue::Super); |
|
| 214 | 215 | } |
|
| 215 | 216 | ||
| 216 | 217 | /// Emit a single attribute node. |
|
| 217 | - | fn nodeAttribute(p: *mut Parser, attr: ast::Attribute) -> *ast::Node { |
|
| 218 | + | unsafe fn nodeAttribute(p: &mut Parser, attr: ast::Attribute) -> *ast::Node { |
|
| 218 | 219 | return node(p, ast::NodeValue::Attribute(attr)); |
|
| 219 | 220 | } |
|
| 220 | 221 | ||
| 221 | 222 | /// Emit a unary operator node. |
|
| 222 | - | fn nodeUnary(p: *mut Parser, op: ast::UnaryOp, value: *ast::Node) -> *ast::Node { |
|
| 223 | + | unsafe fn nodeUnary(p: &mut Parser, op: ast::UnaryOp, value: *ast::Node) -> *ast::Node { |
|
| 223 | 224 | return node(p, ast::NodeValue::UnOp({ op, value })); |
|
| 224 | 225 | } |
|
| 225 | 226 | ||
| 226 | 227 | /// Parse one expression without inheriting a surrounding condition or pattern context. |
|
| 227 | - | fn parseNormalExpr(p: *mut Parser) -> *ast::Node throws (ParseError) { |
|
| 228 | + | unsafe fn parseNormalExpr(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 228 | 229 | let saved = p.context; |
|
| 229 | 230 | set p.context = Context::Normal; |
|
| 230 | 231 | let expr = try parseExpr(p); |
|
| 231 | 232 | set p.context = saved; |
|
| 232 | 233 | return expr; |
|
| 233 | 234 | } |
|
| 234 | 235 | ||
| 235 | 236 | /// Parse a parenthesized expression without applying postfix operators. |
|
| 236 | - | fn parseParenthesized(p: *mut Parser) -> *ast::Node |
|
| 237 | + | unsafe fn parseParenthesized(p: &mut Parser) -> *ast::Node |
|
| 237 | 238 | throws (ParseError) |
|
| 238 | 239 | { |
|
| 239 | 240 | try expect(p, scanner::TokenKind::LParen, "expected `(`"); |
|
| 240 | 241 | ||
| 241 | 242 | let expr = try parseNormalExpr(p); |
| 244 | 245 | ||
| 245 | 246 | return expr; |
|
| 246 | 247 | } |
|
| 247 | 248 | ||
| 248 | 249 | /// Parse an array literal: `[a, b, c]` or `[item; count]`. |
|
| 249 | - | fn parseArrayLiteral(p: *mut Parser) -> *ast::Node |
|
| 250 | + | unsafe fn parseArrayLiteral(p: &mut Parser) -> *ast::Node |
|
| 250 | 251 | throws (ParseError) |
|
| 251 | 252 | { |
|
| 252 | 253 | try expect(p, scanner::TokenKind::LBracket, "expected `[`"); |
|
| 253 | 254 | if consume(p, scanner::TokenKind::RBracket) { // Empty array: `[]`. |
|
| 254 | 255 | let empty: *mut [*ast::Node] = &mut []; |
| 264 | 265 | return node(p, ast::NodeValue::ArrayRepeatLit( |
|
| 265 | 266 | ast::ArrayRepeatLit { item: firstExpr, count } |
|
| 266 | 267 | )); |
|
| 267 | 268 | } |
|
| 268 | 269 | // Regular array literal: `[a, b, ...]`. |
|
| 269 | - | let mut items = ast::nodeSlice(p.arena, 64).append(firstExpr, p.allocator); |
|
| 270 | + | let mut items = ast::nodeSlice(&mut *p.arena, 64).append(firstExpr, p.allocator); |
|
| 270 | 271 | ||
| 271 | 272 | while consume(p, scanner::TokenKind::Comma) and not check(p, scanner::TokenKind::RBracket) { |
|
| 272 | 273 | let elem = try parseNormalExpr(p); |
|
| 273 | 274 | items.append(elem, p.allocator); |
|
| 274 | 275 | } |
| 276 | 277 | ||
| 277 | 278 | return node(p, ast::NodeValue::ArrayLit(items)); |
|
| 278 | 279 | } |
|
| 279 | 280 | ||
| 280 | 281 | /// Parse a function call expression. |
|
| 281 | - | fn parseCall(p: *mut Parser, callee: *ast::Node) -> *ast::Node |
|
| 282 | + | unsafe fn parseCall(p: &mut Parser, callee: *ast::Node) -> *ast::Node |
|
| 282 | 283 | throws (ParseError) |
|
| 283 | 284 | { |
|
| 284 | 285 | let args = try parseList( |
|
| 285 | 286 | p, |
|
| 286 | 287 | scanner::TokenKind::LParen, |
| 291 | 292 | ast::Call { callee, args } |
|
| 292 | 293 | )); |
|
| 293 | 294 | } |
|
| 294 | 295 | ||
| 295 | 296 | /// Parse zero or more trailing `as` casts applied to `expr`. |
|
| 296 | - | fn parseAsCast(p: *mut Parser, expr: *ast::Node) -> *ast::Node |
|
| 297 | + | unsafe fn parseAsCast(p: &mut Parser, expr: *ast::Node) -> *ast::Node |
|
| 297 | 298 | throws (ParseError) |
|
| 298 | 299 | { |
|
| 299 | 300 | let mut result = expr; |
|
| 300 | 301 | ||
| 301 | 302 | while consume(p, scanner::TokenKind::As) { |
| 311 | 312 | /// Parse an optional conditional expression suffix. |
|
| 312 | 313 | /// |
|
| 313 | 314 | /// `<thenExpr> if <condition> else <elseExpr>` |
|
| 314 | 315 | /// |
|
| 315 | 316 | /// If no `if` keyword follows, returns the input expression unchanged. |
|
| 316 | - | fn parseCondExpr(p: *mut Parser, thenExpr: *ast::Node) -> *ast::Node |
|
| 317 | + | unsafe fn parseCondExpr(p: &mut Parser, thenExpr: *ast::Node) -> *ast::Node |
|
| 317 | 318 | throws (ParseError) |
|
| 318 | 319 | { |
|
| 319 | 320 | // Only parse conditional expressions in normal context. |
|
| 320 | 321 | // In conditional context, `if` is used for guards. |
|
| 321 | 322 | if p.context <> Context::Normal { |
| 332 | 333 | ast::CondExpr { condition, thenExpr, elseExpr } |
|
| 333 | 334 | )); |
|
| 334 | 335 | } |
|
| 335 | 336 | ||
| 336 | 337 | /// Parse array subscript or slice expression after `[`. |
|
| 337 | - | fn parseSubscriptOrSlice(p: *mut Parser, container: *ast::Node) -> *ast::Node |
|
| 338 | + | unsafe fn parseSubscriptOrSlice(p: &mut Parser, container: *ast::Node) -> *ast::Node |
|
| 338 | 339 | throws (ParseError) |
|
| 339 | 340 | { |
|
| 340 | 341 | try expect(p, scanner::TokenKind::LBracket, "expected `[`"); |
|
| 341 | 342 | ||
| 342 | 343 | let mut index: *ast::Node = undefined; |
| 372 | 373 | ||
| 373 | 374 | return node(p, ast::NodeValue::Subscript { container, index }); |
|
| 374 | 375 | } |
|
| 375 | 376 | ||
| 376 | 377 | /// Parse postfix operators (eg. field access, function call etc.) |
|
| 377 | - | fn parsePostfix(p: *mut Parser, expr: *ast::Node) -> *ast::Node |
|
| 378 | + | unsafe fn parsePostfix(p: &mut Parser, expr: *ast::Node) -> *ast::Node |
|
| 378 | 379 | throws (ParseError) |
|
| 379 | 380 | { |
|
| 380 | 381 | let mut result = expr; |
|
| 381 | 382 | ||
| 382 | 383 | loop { |
| 413 | 414 | } |
|
| 414 | 415 | return result; |
|
| 415 | 416 | } |
|
| 416 | 417 | ||
| 417 | 418 | /// Parse a conditional expression. |
|
| 418 | - | export fn parseCond(p: *mut Parser) -> *ast::Node throws (ParseError) { |
|
| 419 | + | export unsafe fn parseCond(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 419 | 420 | let saved = p.context; |
|
| 420 | 421 | set p.context = Context::Condition; |
|
| 421 | 422 | let expr = try parseExpr(p); |
|
| 422 | 423 | set p.context = saved; |
|
| 423 | 424 | ||
| 424 | 425 | return expr; |
|
| 425 | 426 | } |
|
| 426 | 427 | ||
| 427 | 428 | /// Parse unary expression followed by optional `as` cast. |
|
| 428 | 429 | /// `as` has higher precedence than binary ops but lower than unary. |
|
| 429 | - | fn parseUnary(p: *mut Parser) -> *ast::Node throws (ParseError) { |
|
| 430 | + | unsafe fn parseUnary(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 430 | 431 | let unary = try parseUnaryExpr(p); |
|
| 431 | 432 | return try parseAsCast(p, unary); |
|
| 432 | 433 | } |
|
| 433 | 434 | ||
| 434 | 435 | /// Parse prefix unary expressions and defer to primary expressions otherwise. |
|
| 435 | - | fn parseUnaryExpr(p: *mut Parser) -> *ast::Node |
|
| 436 | + | unsafe fn parseUnaryExpr(p: &mut Parser) -> *ast::Node |
|
| 436 | 437 | throws (ParseError) |
|
| 437 | 438 | { |
|
| 438 | 439 | match p.current.kind { |
|
| 439 | 440 | case scanner::TokenKind::Not => { |
|
| 440 | 441 | advance(p); |
| 496 | 497 | return false, |
|
| 497 | 498 | } |
|
| 498 | 499 | } |
|
| 499 | 500 | ||
| 500 | 501 | /// Build a range expression node with an optional start and end expression. |
|
| 501 | - | fn parseRangeExpr(p: *mut Parser, start: ?*ast::Node) -> *ast::Node |
|
| 502 | + | unsafe fn parseRangeExpr(p: &mut Parser, start: ?*ast::Node) -> *ast::Node |
|
| 502 | 503 | throws (ParseError) |
|
| 503 | 504 | { |
|
| 504 | 505 | let mut endExpr: ?*ast::Node = nil; |
|
| 505 | 506 | ||
| 506 | 507 | if not isRangeTerminator(p.current.kind) { |
| 511 | 512 | ast::Range { start, end: endExpr } |
|
| 512 | 513 | )); |
|
| 513 | 514 | } |
|
| 514 | 515 | ||
| 515 | 516 | /// Parse binary expressions using precedence climbing. |
|
| 516 | - | fn parseBinary(p: *mut Parser, left: *ast::Node, minPrec: i32) -> *ast::Node |
|
| 517 | + | unsafe fn parseBinary(p: &mut Parser, left: *ast::Node, minPrec: i32) -> *ast::Node |
|
| 517 | 518 | throws (ParseError) |
|
| 518 | 519 | { |
|
| 519 | 520 | let mut result = left; |
|
| 520 | 521 | ||
| 521 | 522 | loop { |
| 579 | 580 | else => return true, |
|
| 580 | 581 | } |
|
| 581 | 582 | } |
|
| 582 | 583 | ||
| 583 | 584 | /// Parse a primary leaf expression without postfix operators. |
|
| 584 | - | fn parseLeaf(p: *mut Parser) -> *ast::Node |
|
| 585 | + | unsafe fn parseLeaf(p: &mut Parser) -> *ast::Node |
|
| 585 | 586 | throws (ParseError) |
|
| 586 | 587 | { |
|
| 587 | 588 | match p.current.kind { |
|
| 588 | 589 | case scanner::TokenKind::True => { |
|
| 589 | 590 | advance(p); |
| 601 | 602 | advance(p); |
|
| 602 | 603 | return nodeSuper(p); |
|
| 603 | 604 | } |
|
| 604 | 605 | case scanner::TokenKind::Number => { |
|
| 605 | 606 | advance(p); |
|
| 606 | - | let literal = try parseIntLiteral(p, p.previous.source); |
|
| 607 | + | let source = p.previous.source; |
|
| 608 | + | let literal = try parseIntLiteral(p, source); |
|
| 607 | 609 | return nodeNumber(p, literal); |
|
| 608 | 610 | } |
|
| 609 | 611 | case scanner::TokenKind::LParen => { |
|
| 610 | 612 | return try parseParenthesized(p); |
|
| 611 | 613 | } |
| 666 | 668 | } |
|
| 667 | 669 | } |
|
| 668 | 670 | } |
|
| 669 | 671 | ||
| 670 | 672 | /// Parse a primary expression (leaf nodes followed by postfix operators). |
|
| 671 | - | fn parsePrimary(p: *mut Parser) -> *ast::Node |
|
| 673 | + | unsafe fn parsePrimary(p: &mut Parser) -> *ast::Node |
|
| 672 | 674 | throws (ParseError) |
|
| 673 | 675 | { |
|
| 674 | 676 | let leaf = try parseLeaf(p); |
|
| 675 | 677 | return try parsePostfix(p, leaf); |
|
| 676 | 678 | } |
|
| 677 | 679 | ||
| 678 | 680 | /// Parse a builtin function call like `@sizeOf(T)` or `@alignOf(T)`. |
|
| 679 | - | fn parseBuiltin(p: *mut Parser) -> *ast::Node |
|
| 681 | + | unsafe fn parseBuiltin(p: &mut Parser) -> *ast::Node |
|
| 680 | 682 | throws (ParseError) |
|
| 681 | 683 | { |
|
| 682 | 684 | // Skip the '@' to get the name. |
|
| 683 | 685 | let ident = p.current.source; |
|
| 684 | 686 | advance(p); |
| 696 | 698 | } |
|
| 697 | 699 | try expect(p, scanner::TokenKind::LParen, "expected `(` after builtin name"); |
|
| 698 | 700 | ||
| 699 | 701 | // Parse arguments into a list. Use capacity 4 to handle any valid argument count |
|
| 700 | 702 | // plus some extra for error recovery. |
|
| 701 | - | let mut args = ast::nodeSlice(p.arena, 4); |
|
| 703 | + | let mut args = ast::nodeSlice(&mut *p.arena, 4); |
|
| 702 | 704 | ||
| 703 | 705 | if kind == ast::Builtin::SliceOf { |
|
| 704 | 706 | // Parse comma-separated expressions until closing paren. |
|
| 705 | 707 | // Argument count validation is done in semantic analysis. |
|
| 706 | 708 | while not check(p, scanner::TokenKind::RParen) { |
| 719 | 721 | ||
| 720 | 722 | /// Parse a single expression. |
|
| 721 | 723 | /// |
|
| 722 | 724 | /// Parses unary and binary operators using precedence climbing. |
|
| 723 | 725 | /// Conditional expressions (`x if cond else y`) have lowest precedence. |
|
| 724 | - | export fn parseExpr(p: *mut Parser) -> *ast::Node |
|
| 726 | + | export unsafe fn parseExpr(p: &mut Parser) -> *ast::Node |
|
| 725 | 727 | throws (ParseError) |
|
| 726 | 728 | { |
|
| 727 | 729 | let left = try parseUnary(p); |
|
| 728 | 730 | let expr = try parseBinary(p, left, -1); |
|
| 729 | 731 | return try parseCondExpr(p, expr); |
|
| 730 | 732 | } |
|
| 731 | 733 | ||
| 732 | 734 | /// Try to consume a compound assignment operator and return its binary op. |
|
| 733 | - | fn tryCompoundAssignOp(p: *mut Parser) -> ?ast::BinaryOp { |
|
| 735 | + | fn tryCompoundAssignOp(p: &mut Parser) -> ?ast::BinaryOp { |
|
| 734 | 736 | match p.current.kind { |
|
| 735 | 737 | case scanner::TokenKind::PlusEqual => { advance(p); return ast::BinaryOp::Add; } |
|
| 736 | 738 | case scanner::TokenKind::MinusEqual => { advance(p); return ast::BinaryOp::Sub; } |
|
| 737 | 739 | case scanner::TokenKind::StarEqual => { advance(p); return ast::BinaryOp::Mul; } |
|
| 738 | 740 | case scanner::TokenKind::SlashEqual => { advance(p); return ast::BinaryOp::Div; } |
| 745 | 747 | else => return nil, |
|
| 746 | 748 | } |
|
| 747 | 749 | } |
|
| 748 | 750 | ||
| 749 | 751 | /// Parse an expression statement. |
|
| 750 | - | export fn parseExprStmt(p: *mut Parser) -> *ast::Node |
|
| 752 | + | export unsafe fn parseExprStmt(p: &mut Parser) -> *ast::Node |
|
| 751 | 753 | throws (ParseError) |
|
| 752 | 754 | { |
|
| 753 | 755 | let expr = try parseExpr(p); |
|
| 754 | 756 | return node(p, ast::NodeValue::ExprStmt(expr)); |
|
| 755 | 757 | } |
|
| 756 | 758 | ||
| 757 | 759 | /// Parse a `set` statement assignment. |
|
| 758 | - | fn parseSetStmt(p: *mut Parser) -> *ast::Node |
|
| 760 | + | unsafe fn parseSetStmt(p: &mut Parser) -> *ast::Node |
|
| 759 | 761 | throws (ParseError) |
|
| 760 | 762 | { |
|
| 761 | 763 | let target = try parseUnary(p); |
|
| 762 | 764 | if not ast::isPlaceExpr(target) { |
|
| 763 | 765 | throw failParsing(p, "invalid assignment target"); |
| 781 | 783 | } |
|
| 782 | 784 | throw failParsing(p, "expected assignment after `set`"); |
|
| 783 | 785 | } |
|
| 784 | 786 | ||
| 785 | 787 | /// Parse leading attributes and declaration modifiers. |
|
| 786 | - | fn parseAttributes(p: *mut Parser) -> ?ast::Attributes { |
|
| 787 | - | let mut attrs = ast::nodeSlice(p.arena, 4); |
|
| 788 | + | unsafe fn parseAttributes(p: &mut Parser) -> ?ast::Attributes { |
|
| 789 | + | let mut attrs = ast::nodeSlice(&mut *p.arena, 4); |
|
| 788 | 790 | ||
| 789 | 791 | if let attr = tryParseAnnotation(p) { |
|
| 790 | 792 | attrs.append(attr, p.allocator); |
|
| 791 | 793 | } |
|
| 792 | 794 | if consume(p, scanner::TokenKind::Export) { |
| 803 | 805 | ||
| 804 | 806 | /// Try to parse an annotation like `@default`. |
|
| 805 | 807 | /// |
|
| 806 | 808 | /// Returns `nil` if not a known annotation (e.g. `@sizeOf` or `@alignOf` which are builtins). |
|
| 807 | 809 | /// Only consumes tokens if a valid annotation is found. |
|
| 808 | - | fn tryParseAnnotation(p: *mut Parser) -> ?*ast::Node { |
|
| 810 | + | unsafe fn tryParseAnnotation(p: &mut Parser) -> ?*ast::Node { |
|
| 809 | 811 | if not check(p, scanner::TokenKind::AtIdent) { |
|
| 810 | 812 | return nil; |
|
| 811 | 813 | } |
|
| 812 | 814 | // Token is @identifier, skip the '@' to get the name. |
|
| 813 | 815 | let ident = &p.current.source[..]; |
| 827 | 829 | } |
|
| 828 | 830 | ||
| 829 | 831 | /// Parse a single statement. |
|
| 830 | 832 | /// |
|
| 831 | 833 | /// Dispatches to the appropriate statement parser based on the current token. |
|
| 832 | - | export fn parseStmt(p: *mut Parser) -> *ast::Node |
|
| 834 | + | export unsafe fn parseStmt(p: &mut Parser) -> *ast::Node |
|
| 833 | 835 | throws (ParseError) |
|
| 834 | 836 | { |
|
| 835 | 837 | // TODO: Why is `parseStmt` checking for attributes? |
|
| 836 | 838 | // We should have a `parseDecl` which is top-level, and `parseStmt` which |
|
| 837 | 839 | // is inside functions. |
|
| 838 | 840 | let attrs = parseAttributes(p); |
|
| 839 | 841 | if let list = attrs { |
|
| 840 | 842 | if ast::attributesContains(&list, ast::Attribute::Unsafe) |
|
| 841 | 843 | and p.current.kind <> scanner::TokenKind::Fn |
|
| 842 | - | and p.current.kind <> scanner::TokenKind::Mod |
|
| 843 | 844 | { |
|
| 844 | - | throw failParsing(p, "`unsafe` is only allowed on functions and modules"); |
|
| 845 | + | throw failParsing(p, "`unsafe` is only allowed on functions"); |
|
| 845 | 846 | } |
|
| 846 | 847 | let allowed: bool = |
|
| 847 | 848 | p.current.kind == scanner::TokenKind::Fn or |
|
| 848 | 849 | p.current.kind == scanner::TokenKind::Union or |
|
| 849 | 850 | p.current.kind == scanner::TokenKind::Record or |
| 945 | 946 | } |
|
| 946 | 947 | ||
| 947 | 948 | /// Parse statements until the specified ending token is encountered. |
|
| 948 | 949 | /// |
|
| 949 | 950 | /// Adds each parsed statement to the given block's statement list. |
|
| 950 | - | export fn parseStmtsUntil(p: *mut Parser, end: scanner::TokenKind, blk: *mut ast::Block) |
|
| 951 | + | export unsafe fn parseStmtsUntil(p: &mut Parser, end: scanner::TokenKind, blk: &mut ast::Block) |
|
| 951 | 952 | throws (ParseError) |
|
| 952 | 953 | { |
|
| 953 | 954 | while not check(p, end) { |
|
| 954 | 955 | let stmt = try parseStmt(p); |
|
| 955 | 956 | blk.statements.append(stmt, p.allocator); |
| 965 | 966 | } |
|
| 966 | 967 | } |
|
| 967 | 968 | } |
|
| 968 | 969 | ||
| 969 | 970 | /// Parse a block of statements enclosed in curly braces. |
|
| 970 | - | export fn parseBlock(p: *mut Parser) -> *ast::Node |
|
| 971 | + | export unsafe fn parseBlock(p: &mut Parser) -> *ast::Node |
|
| 971 | 972 | throws (ParseError) |
|
| 972 | 973 | { |
|
| 973 | 974 | let start = p.current; |
|
| 974 | 975 | let mut blk = mkBlock(p, 64); |
|
| 975 | 976 |
| 981 | 982 | ||
| 982 | 983 | return node(p, ast::NodeValue::Block(blk)); |
|
| 983 | 984 | } |
|
| 984 | 985 | ||
| 985 | 986 | /// Create an empty block with no statements. |
|
| 986 | - | fn mkBlock(p: *mut Parser, cap: u32) -> ast::Block { |
|
| 987 | - | return ast::Block { statements: ast::nodeSlice(p.arena, cap) }; |
|
| 987 | + | unsafe fn mkBlock(p: &mut Parser, cap: u32) -> ast::Block { |
|
| 988 | + | return ast::Block { statements: ast::nodeSlice(&mut *p.arena, cap) }; |
|
| 988 | 989 | } |
|
| 989 | 990 | ||
| 990 | 991 | /// Create a block containing a single statement node. |
|
| 991 | - | fn mkBlockWith(p: *mut Parser, node: *ast::Node) -> ast::Block { |
|
| 992 | - | let stmts = ast::nodeSlice(p.arena, 1).append(node, p.allocator); |
|
| 992 | + | unsafe fn mkBlockWith(p: &mut Parser, node: *ast::Node) -> ast::Block { |
|
| 993 | + | let stmts = ast::nodeSlice(&mut *p.arena, 1).append(node, p.allocator); |
|
| 993 | 994 | return ast::Block { statements: stmts }; |
|
| 994 | 995 | } |
|
| 995 | 996 | ||
| 996 | 997 | /// Parse the branch that follows `else` in let-else style constructs. |
|
| 997 | 998 | /// |
|
| 998 | 999 | /// Allows either a block, a single statement like `return`, |
|
| 999 | 1000 | /// or a standalone expression which is returned directly. |
|
| 1000 | - | fn parseLetElseBranch(p: *mut Parser) -> *ast::Node |
|
| 1001 | + | unsafe fn parseLetElseBranch(p: &mut Parser) -> *ast::Node |
|
| 1001 | 1002 | throws (ParseError) |
|
| 1002 | 1003 | { |
|
| 1003 | 1004 | if check(p, scanner::TokenKind::LBrace) { |
|
| 1004 | 1005 | return try parseBlock(p); |
|
| 1005 | 1006 | } |
| 1010 | 1011 | } |
|
| 1011 | 1012 | return branch; |
|
| 1012 | 1013 | } |
|
| 1013 | 1014 | ||
| 1014 | 1015 | /// Allocate a new node from the parser's arena. |
|
| 1015 | - | fn node(p: *mut Parser, value: ast::NodeValue) -> *mut ast::Node { |
|
| 1016 | + | unsafe fn node(p: &mut Parser, value: ast::NodeValue) -> *mut ast::Node { |
|
| 1016 | 1017 | let span = ast::Span { |
|
| 1017 | 1018 | offset: p.previous.offset, |
|
| 1018 | 1019 | length: p.previous.source.len, |
|
| 1019 | 1020 | }; |
|
| 1020 | - | let n = ast::allocNode(p.arena, span, value); |
|
| 1021 | + | let n = ast::allocNode(&mut *p.arena, span, value); |
|
| 1021 | 1022 | finishSpan(p, n); |
|
| 1022 | 1023 | ||
| 1023 | 1024 | return n; |
|
| 1024 | 1025 | } |
|
| 1025 | 1026 | ||
| 1026 | 1027 | /// Update the span of `node` using the most recently consumed token. |
|
| 1027 | - | fn finishSpan(p: *mut Parser, node: *mut ast::Node) { |
|
| 1028 | + | fn finishSpan(p: &mut Parser, node: *mut ast::Node) { |
|
| 1028 | 1029 | let start: u32 = node.span.offset; |
|
| 1029 | 1030 | let mut end: u32 = p.previous.offset + p.previous.source.len; |
|
| 1030 | 1031 | ||
| 1031 | 1032 | if end >= start { |
|
| 1032 | 1033 | set node.span.length = end - start; |
| 1034 | 1035 | set node.span.length = 0; |
|
| 1035 | 1036 | } |
|
| 1036 | 1037 | } |
|
| 1037 | 1038 | ||
| 1038 | 1039 | /// Save parser state for speculative parsing. |
|
| 1039 | - | fn saveState(p: *Parser) -> SavedState { |
|
| 1040 | + | unsafe fn saveState(p: &Parser) -> SavedState { |
|
| 1040 | 1041 | return SavedState { |
|
| 1041 | 1042 | parser: *p, |
|
| 1042 | 1043 | arena: alloc::save(&p.arena.arena), |
|
| 1043 | 1044 | nextId: p.arena.nextId, |
|
| 1044 | 1045 | }; |
|
| 1045 | 1046 | } |
|
| 1046 | 1047 | ||
| 1047 | 1048 | /// Restore parser state from a snapshot, fully undoing any |
|
| 1048 | 1049 | /// side effects of a failed speculative parse. |
|
| 1049 | - | fn restoreState(p: *mut Parser, s: *SavedState) { |
|
| 1050 | + | unsafe fn restoreState(p: &mut Parser, s: &SavedState) { |
|
| 1050 | 1051 | set *p = s.parser; |
|
| 1051 | 1052 | alloc::restore(&mut p.arena.arena, s.arena); |
|
| 1052 | 1053 | set p.arena.nextId = s.nextId; |
|
| 1053 | 1054 | } |
|
| 1054 | 1055 | ||
| 1055 | 1056 | /// Report a parser error. |
|
| 1056 | - | fn reportError(p: *mut Parser, token: scanner::Token, message: *[u8]) { |
|
| 1057 | + | fn reportError(p: &mut Parser, token: scanner::Token, message: *[u8]) { |
|
| 1057 | 1058 | assert message.len > 0; |
|
| 1058 | 1059 | ||
| 1059 | 1060 | // Ignore errors once the error list is full. |
|
| 1060 | 1061 | if p.errors.count < p.errors.list.len { |
|
| 1061 | 1062 | set p.errors.list[p.errors.count] = Error { message, token }; |
|
| 1062 | 1063 | set p.errors.count += 1; |
|
| 1063 | 1064 | } |
|
| 1064 | 1065 | } |
|
| 1065 | 1066 | ||
| 1066 | 1067 | /// Fail the parsing process with the given error. |
|
| 1067 | - | fn failParsing(p: *mut Parser, err: *[u8]) -> ParseError { |
|
| 1068 | - | reportError(p, p.current, err); |
|
| 1068 | + | fn failParsing(p: &mut Parser, err: *[u8]) -> ParseError { |
|
| 1069 | + | let token = p.current; |
|
| 1070 | + | reportError(p, token, err); |
|
| 1069 | 1071 | return ParseError::UnexpectedToken; |
|
| 1070 | 1072 | } |
|
| 1071 | 1073 | ||
| 1072 | 1074 | /// Print all errors that have been collected during parsing. |
|
| 1073 | - | export fn printErrors(p: *Parser) { |
|
| 1075 | + | export fn printErrors(p: &Parser) { |
|
| 1074 | 1076 | for i in 0..p.errors.count { |
|
| 1075 | 1077 | let e = p.errors.list[i]; |
|
| 1076 | 1078 | if let loc = scanner::getLocation( |
|
| 1077 | 1079 | p.scanner.sourceLoc, p.scanner.source, e.token.offset |
|
| 1078 | 1080 | ) { |
| 1099 | 1101 | io::print("\n"); |
|
| 1100 | 1102 | } |
|
| 1101 | 1103 | } |
|
| 1102 | 1104 | ||
| 1103 | 1105 | /// Check whether the current token matches the expected kind. |
|
| 1104 | - | export fn check(p: *Parser, kind: scanner::TokenKind) -> bool { |
|
| 1106 | + | export fn check(p: &Parser, kind: scanner::TokenKind) -> bool { |
|
| 1105 | 1107 | return p.current.kind == kind; |
|
| 1106 | 1108 | } |
|
| 1107 | 1109 | ||
| 1108 | 1110 | /// Advance the parser by one token. |
|
| 1109 | - | export fn advance(p: *mut Parser) { |
|
| 1111 | + | export fn advance(p: &mut Parser) { |
|
| 1110 | 1112 | set p.previous = p.current; |
|
| 1111 | 1113 | set p.current = scanner::next(&mut p.scanner); |
|
| 1112 | 1114 | } |
|
| 1113 | 1115 | ||
| 1114 | 1116 | /// Parse an `if let` pattern matching statement. |
|
| 1115 | 1117 | /// |
|
| 1116 | 1118 | /// Syntax: `if let binding = scrutinee { ... }` |
|
| 1117 | 1119 | /// Syntax: `if let mut binding = scrutinee { ... }` |
|
| 1118 | - | fn parseIfLet(p: *mut Parser) -> *ast::Node throws (ParseError) { |
|
| 1120 | + | unsafe fn parseIfLet(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 1119 | 1121 | try expect(p, scanner::TokenKind::Let, "expected `let`"); |
|
| 1120 | 1122 | ||
| 1121 | 1123 | // Parse pattern: either `case <pattern>`, `mut <ident>`, or simple `<ident>`. |
|
| 1122 | 1124 | let mut pattern: *ast::Node = undefined; |
|
| 1123 | 1125 | let mut kind = ast::PatternKind::Binding; |
| 1157 | 1159 | elseBranch, |
|
| 1158 | 1160 | })); |
|
| 1159 | 1161 | } |
|
| 1160 | 1162 | ||
| 1161 | 1163 | /// Parse a `while let` statement. |
|
| 1162 | - | fn parseWhileLet(p: *mut Parser) -> *ast::Node |
|
| 1164 | + | unsafe fn parseWhileLet(p: &mut Parser) -> *ast::Node |
|
| 1163 | 1165 | throws (ParseError) |
|
| 1164 | 1166 | { |
|
| 1165 | 1167 | try expect(p, scanner::TokenKind::Let, "expected `let`"); |
|
| 1166 | 1168 | ||
| 1167 | 1169 | // Parse pattern: either `case <pattern>`, `mut <ident>`, or simple `<ident>`. |
| 1195 | 1197 | elseBranch, |
|
| 1196 | 1198 | })); |
|
| 1197 | 1199 | } |
|
| 1198 | 1200 | ||
| 1199 | 1201 | /// Parse a `while` statement. |
|
| 1200 | - | fn parseWhile(p: *mut Parser) -> *ast::Node |
|
| 1202 | + | unsafe fn parseWhile(p: &mut Parser) -> *ast::Node |
|
| 1201 | 1203 | throws (ParseError) |
|
| 1202 | 1204 | { |
|
| 1203 | 1205 | try expect(p, scanner::TokenKind::While, "expected `while`"); |
|
| 1204 | 1206 | ||
| 1205 | 1207 | // Check for `while let` or `while let case` syntax. |
| 1217 | 1219 | condition, body, elseBranch, |
|
| 1218 | 1220 | })); |
|
| 1219 | 1221 | } |
|
| 1220 | 1222 | ||
| 1221 | 1223 | /// Parse a `loop` statement. |
|
| 1222 | - | fn parseLoop(p: *mut Parser) -> *ast::Node |
|
| 1224 | + | unsafe fn parseLoop(p: &mut Parser) -> *ast::Node |
|
| 1223 | 1225 | throws (ParseError) |
|
| 1224 | 1226 | { |
|
| 1225 | 1227 | try expect(p, scanner::TokenKind::Loop, "expected `loop`"); |
|
| 1226 | 1228 | let body = try parseBlock(p); |
|
| 1227 | 1229 | ||
| 1228 | 1230 | return node(p, ast::NodeValue::Loop { body }); |
|
| 1229 | 1231 | } |
|
| 1230 | 1232 | ||
| 1231 | 1233 | /// Parse a `for` statement. |
|
| 1232 | - | fn parseFor(p: *mut Parser) -> *ast::Node |
|
| 1234 | + | unsafe fn parseFor(p: &mut Parser) -> *ast::Node |
|
| 1233 | 1235 | throws (ParseError) |
|
| 1234 | 1236 | { |
|
| 1235 | 1237 | try expect(p, scanner::TokenKind::For, "expected `for`"); |
|
| 1236 | 1238 | ||
| 1237 | 1239 | let binding = try parseIdentOrPlaceholder(p, "expected identifier or `_`"); |
| 1253 | 1255 | binding, index, iterable, body, elseBranch, |
|
| 1254 | 1256 | })); |
|
| 1255 | 1257 | } |
|
| 1256 | 1258 | ||
| 1257 | 1259 | /// Parse a `return` statement. |
|
| 1258 | - | fn parseReturn(p: *mut Parser) -> *ast::Node |
|
| 1260 | + | unsafe fn parseReturn(p: &mut Parser) -> *ast::Node |
|
| 1259 | 1261 | throws (ParseError) |
|
| 1260 | 1262 | { |
|
| 1261 | 1263 | try expect(p, scanner::TokenKind::Return, "expected `return`"); |
|
| 1262 | 1264 | ||
| 1263 | 1265 | // Speculatively try to parse a return value expression. |
| 1268 | 1270 | } |
|
| 1269 | 1271 | return node(p, ast::NodeValue::Return { value }); |
|
| 1270 | 1272 | } |
|
| 1271 | 1273 | ||
| 1272 | 1274 | /// Parse a `throw` statement. |
|
| 1273 | - | fn parseThrow(p: *mut Parser) -> *ast::Node |
|
| 1275 | + | unsafe fn parseThrow(p: &mut Parser) -> *ast::Node |
|
| 1274 | 1276 | throws (ParseError) |
|
| 1275 | 1277 | { |
|
| 1276 | 1278 | try expect(p, scanner::TokenKind::Throw, "expected `throw`"); |
|
| 1277 | 1279 | let expr = try parseExpr(p); |
|
| 1278 | 1280 | ||
| 1279 | 1281 | return node(p, ast::NodeValue::Throw { expr }); |
|
| 1280 | 1282 | } |
|
| 1281 | 1283 | ||
| 1282 | 1284 | /// Parse a `panic` statement. |
|
| 1283 | - | fn parsePanic(p: *mut Parser) -> *ast::Node |
|
| 1285 | + | unsafe fn parsePanic(p: &mut Parser) -> *ast::Node |
|
| 1284 | 1286 | throws (ParseError) |
|
| 1285 | 1287 | { |
|
| 1286 | 1288 | try expect(p, scanner::TokenKind::Panic, "expected `panic`"); |
|
| 1287 | 1289 | ||
| 1288 | 1290 | // `panic { expr }`. |
| 1304 | 1306 | /// |
|
| 1305 | 1307 | /// Forms: |
|
| 1306 | 1308 | /// `assert <expr>` |
|
| 1307 | 1309 | /// `assert <expr>, "message"` |
|
| 1308 | 1310 | /// `assert { <expr> }, "message"` |
|
| 1309 | - | fn parseAssert(p: *mut Parser) -> *ast::Node |
|
| 1311 | + | unsafe fn parseAssert(p: &mut Parser) -> *ast::Node |
|
| 1310 | 1312 | throws (ParseError) |
|
| 1311 | 1313 | { |
|
| 1312 | 1314 | try expect(p, scanner::TokenKind::Assert, "expected `assert`"); |
|
| 1313 | 1315 | ||
| 1314 | 1316 | // `assert { expr }` block form or `assert <expr>`. |
| 1325 | 1327 | } |
|
| 1326 | 1328 | return node(p, ast::NodeValue::Assert { condition, message }); |
|
| 1327 | 1329 | } |
|
| 1328 | 1330 | ||
| 1329 | 1331 | /// Parse a `try` expression with optional `catch` clause(s). |
|
| 1330 | - | fn parseTryExpr(p: *mut Parser) -> *ast::Node |
|
| 1332 | + | unsafe fn parseTryExpr(p: &mut Parser) -> *ast::Node |
|
| 1331 | 1333 | throws (ParseError) |
|
| 1332 | 1334 | { |
|
| 1333 | 1335 | try expect(p, scanner::TokenKind::Try, "expected `try`"); |
|
| 1334 | 1336 | ||
| 1335 | 1337 | let shouldPanic = consume(p, scanner::TokenKind::Bang); |
|
| 1336 | 1338 | let returnsOptional = consume(p, scanner::TokenKind::Question); |
|
| 1337 | 1339 | let expr = try parseUnaryExpr(p); |
|
| 1338 | - | let mut catches = ast::nodeSlice(p.arena, 4); |
|
| 1340 | + | let mut catches = ast::nodeSlice(&mut *p.arena, 4); |
|
| 1339 | 1341 | ||
| 1340 | 1342 | while consume(p, scanner::TokenKind::Catch) { |
|
| 1341 | 1343 | let mut binding: ?*ast::Node = nil; |
|
| 1342 | 1344 | let mut typeNode: ?*ast::Node = nil; |
|
| 1343 | 1345 |
| 1398 | 1400 | /// d |
|
| 1399 | 1401 | /// } |
|
| 1400 | 1402 | /// } |
|
| 1401 | 1403 | /// } |
|
| 1402 | 1404 | /// |
|
| 1403 | - | fn parseIf(p: *mut Parser) -> *ast::Node throws (ParseError) { |
|
| 1405 | + | unsafe fn parseIf(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 1404 | 1406 | try expect(p, scanner::TokenKind::If, "expected `if`"); |
|
| 1405 | 1407 | ||
| 1406 | 1408 | // Check for `if let` or `if let case` syntax. |
|
| 1407 | 1409 | if check(p, scanner::TokenKind::Let) { |
|
| 1408 | 1410 | return try parseIfLet(p); |
| 1428 | 1430 | condition: cond, thenBranch, elseBranch, |
|
| 1429 | 1431 | })); |
|
| 1430 | 1432 | } |
|
| 1431 | 1433 | ||
| 1432 | 1434 | /// Parse a `match` statement. |
|
| 1433 | - | fn parseMatch(p: *mut Parser) -> *ast::Node |
|
| 1435 | + | unsafe fn parseMatch(p: &mut Parser) -> *ast::Node |
|
| 1434 | 1436 | throws (ParseError) |
|
| 1435 | 1437 | { |
|
| 1436 | 1438 | try expect(p, scanner::TokenKind::Match, "expected `match`"); |
|
| 1437 | 1439 | ||
| 1438 | 1440 | let subject = try parseCond(p); |
|
| 1439 | 1441 | try expect(p, scanner::TokenKind::LBrace, "expected `{` before match prongs"); |
|
| 1440 | 1442 | ||
| 1441 | - | let mut prongs = ast::nodeSlice(p.arena, 128); |
|
| 1443 | + | let mut prongs = ast::nodeSlice(&mut *p.arena, 128); |
|
| 1442 | 1444 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 1443 | 1445 | not check(p, scanner::TokenKind::Eof) // TODO: We shouldn't have to manually check for EOF. |
|
| 1444 | 1446 | { |
|
| 1445 | 1447 | let prongNode = try parseMatchProng(p); |
|
| 1446 | 1448 | prongs.append(prongNode, p.allocator); |
| 1452 | 1454 | ast::Match { subject, prongs } |
|
| 1453 | 1455 | )); |
|
| 1454 | 1456 | } |
|
| 1455 | 1457 | ||
| 1456 | 1458 | /// Parse a single `match` prong. |
|
| 1457 | - | fn parseMatchProng(p: *mut Parser) -> *ast::Node |
|
| 1459 | + | unsafe fn parseMatchProng(p: &mut Parser) -> *ast::Node |
|
| 1458 | 1460 | throws (ParseError) |
|
| 1459 | 1461 | { |
|
| 1460 | 1462 | let mut guard: ?*ast::Node = nil; |
|
| 1461 | 1463 | ||
| 1462 | 1464 | // Case prong: `case <pattern>, ... if <guard> => <body>`. |
|
| 1463 | 1465 | if consume(p, scanner::TokenKind::Case) { |
|
| 1464 | - | let mut patterns = ast::nodeSlice(p.arena, 16); |
|
| 1466 | + | let mut patterns = ast::nodeSlice(&mut *p.arena, 16); |
|
| 1465 | 1467 | loop { |
|
| 1466 | 1468 | let pattern = try parseMatchPattern(p); |
|
| 1467 | 1469 | patterns.append(pattern, p.allocator); |
|
| 1468 | 1470 | ||
| 1469 | 1471 | if not consume(p, scanner::TokenKind::Comma) { |
| 1511 | 1513 | )); |
|
| 1512 | 1514 | } |
|
| 1513 | 1515 | ||
| 1514 | 1516 | /// Parse a pattern expression used by `case` constructs. |
|
| 1515 | 1517 | /// Uses `Pattern` context to allow record literals but not conditional expressions. |
|
| 1516 | - | fn parseMatchPattern(p: *mut Parser) -> *ast::Node |
|
| 1518 | + | unsafe fn parseMatchPattern(p: &mut Parser) -> *ast::Node |
|
| 1517 | 1519 | throws (ParseError) |
|
| 1518 | 1520 | { |
|
| 1519 | 1521 | let saved = p.context; |
|
| 1520 | 1522 | set p.context = Context::Pattern; |
|
| 1521 | 1523 | let pattern = try parseExpr(p); |
| 1523 | 1525 | ||
| 1524 | 1526 | return pattern; |
|
| 1525 | 1527 | } |
|
| 1526 | 1528 | ||
| 1527 | 1529 | /// Parse an identifier. |
|
| 1528 | - | fn parseIdent(p: *mut Parser, err: *[u8]) -> *ast::Node |
|
| 1530 | + | unsafe fn parseIdent(p: &mut Parser, err: *[u8]) -> *ast::Node |
|
| 1529 | 1531 | throws (ParseError) |
|
| 1530 | 1532 | { |
|
| 1531 | 1533 | let source = try expect(p, scanner::TokenKind::Ident, err); |
|
| 1532 | 1534 | return node(p, ast::NodeValue::Ident(source)); |
|
| 1533 | 1535 | } |
|
| 1534 | 1536 | ||
| 1535 | 1537 | /// Parse either an identifier or a placeholder (`_`). |
|
| 1536 | - | fn parseIdentOrPlaceholder(p: *mut Parser, err: *[u8]) -> *ast::Node |
|
| 1538 | + | unsafe fn parseIdentOrPlaceholder(p: &mut Parser, err: *[u8]) -> *ast::Node |
|
| 1537 | 1539 | throws (ParseError) |
|
| 1538 | 1540 | { |
|
| 1539 | 1541 | if consume(p, scanner::TokenKind::Underscore) { |
|
| 1540 | 1542 | return node(p, ast::NodeValue::Placeholder); |
|
| 1541 | 1543 | } |
| 1543 | 1545 | } |
|
| 1544 | 1546 | ||
| 1545 | 1547 | /// Parse an alignment specifier. |
|
| 1546 | 1548 | /// |
|
| 1547 | 1549 | /// Syntax: `align(N)` where N is a power of 2. |
|
| 1548 | - | fn parseAlign(p: *mut Parser) -> *ast::Node |
|
| 1550 | + | unsafe fn parseAlign(p: &mut Parser) -> *ast::Node |
|
| 1549 | 1551 | throws (ParseError) |
|
| 1550 | 1552 | { |
|
| 1551 | 1553 | try expect(p, scanner::TokenKind::Align, "expected `align`"); |
|
| 1552 | 1554 | let value = try parseParenthesized(p); |
|
| 1553 | 1555 | return node(p, ast::NodeValue::Align { value }); |
| 1555 | 1557 | ||
| 1556 | 1558 | /// Parse a comma-separated list of record fields. |
|
| 1557 | 1559 | /// The opening delimiter should already be consumed. |
|
| 1558 | 1560 | /// For labeled fields: `{ name: T, ... }`. |
|
| 1559 | 1561 | /// For unlabeled fields: `(T, T, ...)`. |
|
| 1560 | - | fn parseRecordFields( |
|
| 1561 | - | p: *mut Parser, |
|
| 1562 | + | unsafe fn parseRecordFields( |
|
| 1563 | + | p: &mut Parser, |
|
| 1562 | 1564 | mode: RecordFieldMode |
|
| 1563 | 1565 | ) -> *mut [*ast::Node] |
|
| 1564 | 1566 | throws (ParseError) |
|
| 1565 | 1567 | { |
|
| 1566 | 1568 | let terminator = scanner::TokenKind::RBrace if mode == RecordFieldMode::Labeled |
|
| 1567 | 1569 | else scanner::TokenKind::RParen; |
|
| 1568 | - | let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS); |
|
| 1570 | + | let mut fields = ast::nodeSlice(&mut *p.arena, MAX_RECORD_FIELDS); |
|
| 1569 | 1571 | while not check(p, terminator) { |
|
| 1570 | 1572 | let mut recordField: ast::NodeValue = undefined; |
|
| 1571 | 1573 | match mode { |
|
| 1572 | 1574 | case RecordFieldMode::Labeled => { |
|
| 1573 | 1575 | // Allow optional `let` keyword before field name. |
| 1604 | 1606 | ||
| 1605 | 1607 | return fields; |
|
| 1606 | 1608 | } |
|
| 1607 | 1609 | ||
| 1608 | 1610 | /// Parse an optional derives list (`: Trait + Trait`). |
|
| 1609 | - | fn parseDerives(p: *mut Parser) -> *mut [*ast::Node] throws (ParseError) { |
|
| 1610 | - | let mut derives = ast::nodeSlice(p.arena, 4); |
|
| 1611 | + | unsafe fn parseDerives(p: &mut Parser) -> *mut [*ast::Node] throws (ParseError) { |
|
| 1612 | + | let mut derives = ast::nodeSlice(&mut *p.arena, 4); |
|
| 1611 | 1613 | ||
| 1612 | 1614 | if not consume(p, scanner::TokenKind::Colon) { |
|
| 1613 | 1615 | return derives; |
|
| 1614 | 1616 | } |
|
| 1615 | 1617 | loop { |
| 1623 | 1625 | return derives; |
|
| 1624 | 1626 | } |
|
| 1625 | 1627 | ||
| 1626 | 1628 | /// Parse a single record literal field. |
|
| 1627 | 1629 | /// Can be either labeled, or shorthand. |
|
| 1628 | - | fn parseRecordLitField(p: *mut Parser) -> *ast::Node |
|
| 1630 | + | unsafe fn parseRecordLitField(p: &mut Parser) -> *ast::Node |
|
| 1629 | 1631 | throws (ParseError) |
|
| 1630 | 1632 | { |
|
| 1631 | 1633 | let name = try parseIdent(p, "expected field name"); |
|
| 1632 | 1634 | if consume(p, scanner::TokenKind::Colon) { |
|
| 1633 | 1635 | // Labeled field: `name: value`. |
| 1643 | 1645 | } |
|
| 1644 | 1646 | ||
| 1645 | 1647 | /// Parse a record literal body. |
|
| 1646 | 1648 | /// Eg. `{ x: 1, y: 2 }` |
|
| 1647 | 1649 | /// Eg. `{ x: 1, .. }` |
|
| 1648 | - | fn parseRecordLit(p: *mut Parser, typeName: ?*ast::Node) -> *ast::Node |
|
| 1650 | + | unsafe fn parseRecordLit(p: &mut Parser, typeName: ?*ast::Node) -> *ast::Node |
|
| 1649 | 1651 | throws (ParseError) |
|
| 1650 | 1652 | { |
|
| 1651 | - | let mut fields = ast::nodeSlice(p.arena, MAX_RECORD_FIELDS); |
|
| 1653 | + | let mut fields = ast::nodeSlice(&mut *p.arena, MAX_RECORD_FIELDS); |
|
| 1652 | 1654 | let mut ignoreRest = false; |
|
| 1653 | 1655 | try expect(p, scanner::TokenKind::LBrace, "expected `{` to begin record literal"); |
|
| 1654 | 1656 | ||
| 1655 | 1657 | while not check(p, scanner::TokenKind::RBrace) { |
|
| 1656 | 1658 | // Check for `..` to ignore remaining fields. |
| 1672 | 1674 | )); |
|
| 1673 | 1675 | } |
|
| 1674 | 1676 | ||
| 1675 | 1677 | /// Parse a named record declaration. |
|
| 1676 | 1678 | /// `record Point { x: i32, y: i32 }`, or `record Pair(i32, i32);` |
|
| 1677 | - | fn parseRecordDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 1679 | + | unsafe fn parseRecordDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 1678 | 1680 | throws (ParseError) |
|
| 1679 | 1681 | { |
|
| 1680 | 1682 | try expect(p, scanner::TokenKind::Record, "expected `record`"); |
|
| 1681 | 1683 | ||
| 1682 | 1684 | let name = try parseIdent(p, "expected record name"); |
| 1697 | 1699 | } |
|
| 1698 | 1700 | } |
|
| 1699 | 1701 | ||
| 1700 | 1702 | /// Parse a union declaration. |
|
| 1701 | 1703 | /// Example: `union Color { Red, Green, Blue = 5 }` |
|
| 1702 | - | fn parseUnionDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 1704 | + | unsafe fn parseUnionDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 1703 | 1705 | throws (ParseError) |
|
| 1704 | 1706 | { |
|
| 1705 | 1707 | try expect(p, scanner::TokenKind::Union, "expected `union`"); |
|
| 1706 | 1708 | ||
| 1707 | 1709 | let name = try parseIdent(p, "expected union name"); |
|
| 1708 | 1710 | let derives = try parseDerives(p); |
|
| 1709 | 1711 | ||
| 1710 | 1712 | try expect(p, scanner::TokenKind::LBrace, "expected `{` before union body"); |
|
| 1711 | 1713 | ||
| 1712 | - | let mut variants = ast::nodeSlice(p.arena, 128); |
|
| 1714 | + | let mut variants = ast::nodeSlice(&mut *p.arena, 128); |
|
| 1713 | 1715 | while not check(p, scanner::TokenKind::RBrace) { |
|
| 1714 | 1716 | // Allow optional `case` keyword before variant name. |
|
| 1715 | 1717 | consume(p, scanner::TokenKind::Case); |
|
| 1716 | 1718 | ||
| 1717 | 1719 | let variantName = try parseIdent(p, "expected variant name"); |
| 1729 | 1731 | ast::TypeSig::Record { fields, labeled: true } |
|
| 1730 | 1732 | )); |
|
| 1731 | 1733 | } else if consume(p, scanner::TokenKind::Equal) { |
|
| 1732 | 1734 | // TODO: Support constant expressions. |
|
| 1733 | 1735 | try expect(p, scanner::TokenKind::Number, "expected integer literal after `=`"); |
|
| 1734 | - | let literal = try parseIntLiteral(p, p.previous.source); |
|
| 1736 | + | let source = p.previous.source; |
|
| 1737 | + | let literal = try parseIntLiteral(p, source); |
|
| 1735 | 1738 | set explicitValue = nodeNumber(p, literal); |
|
| 1736 | 1739 | } |
|
| 1737 | 1740 | ||
| 1738 | 1741 | let variant = node(p, ast::NodeValue::UnionDeclVariant( |
|
| 1739 | 1742 | ast::UnionDeclVariant { |
| 1752 | 1755 | ast::UnionDecl { name, variants, attrs, derives } |
|
| 1753 | 1756 | )); |
|
| 1754 | 1757 | } |
|
| 1755 | 1758 | ||
| 1756 | 1759 | /// Parse a function parameter. |
|
| 1757 | - | fn parseFnParam(p: *mut Parser) -> *ast::Node |
|
| 1760 | + | unsafe fn parseFnParam(p: &mut Parser) -> *ast::Node |
|
| 1758 | 1761 | throws (ParseError) |
|
| 1759 | 1762 | { |
|
| 1760 | 1763 | let ntv = try parseNameTypeValue(p); |
|
| 1761 | 1764 | let type = ntv.type |
|
| 1762 | 1765 | else throw failParsing(p, "missing type in function parameter"); |
| 1765 | 1768 | ast::FnParam { name: ntv.name, type } |
|
| 1766 | 1769 | )); |
|
| 1767 | 1770 | } |
|
| 1768 | 1771 | ||
| 1769 | 1772 | /// Parse an optional `throws` clause and return the collected type list. |
|
| 1770 | - | fn parseThrowList(p: *mut Parser) -> *mut [*ast::Node] |
|
| 1773 | + | unsafe fn parseThrowList(p: &mut Parser) -> *mut [*ast::Node] |
|
| 1771 | 1774 | throws (ParseError) |
|
| 1772 | 1775 | { |
|
| 1773 | 1776 | if not consume(p, scanner::TokenKind::Throws) { |
|
| 1774 | - | return ast::nodeSlice(p.arena, 0); |
|
| 1777 | + | return ast::nodeSlice(&mut *p.arena, 0); |
|
| 1775 | 1778 | } |
|
| 1776 | 1779 | return try parseList( |
|
| 1777 | 1780 | p, |
|
| 1778 | 1781 | scanner::TokenKind::LParen, |
|
| 1779 | 1782 | scanner::TokenKind::RParen, |
|
| 1780 | 1783 | parseType |
|
| 1781 | 1784 | ); |
|
| 1782 | 1785 | } |
|
| 1783 | 1786 | ||
| 1784 | 1787 | /// Parse a function type signature. |
|
| 1785 | - | fn parseFnType(p: *mut Parser) -> *ast::Node |
|
| 1788 | + | unsafe fn parseFnType(p: &mut Parser) -> *ast::Node |
|
| 1786 | 1789 | throws (ParseError) |
|
| 1787 | 1790 | { |
|
| 1791 | + | let isUnsafe = consume(p, scanner::TokenKind::Unsafe); |
|
| 1788 | 1792 | try expect(p, scanner::TokenKind::Fn, "expected `fn`"); |
|
| 1789 | 1793 | let params = try parseList( |
|
| 1790 | 1794 | p, |
|
| 1791 | 1795 | scanner::TokenKind::LParen, |
|
| 1792 | 1796 | scanner::TokenKind::RParen, |
| 1798 | 1802 | set returnType = try parseType(p); |
|
| 1799 | 1803 | } |
|
| 1800 | 1804 | let throwList = try parseThrowList(p); |
|
| 1801 | 1805 | let sig = ast::FnSig { params, returnType, throwList }; |
|
| 1802 | 1806 | return node(p, ast::NodeValue::TypeSig( |
|
| 1803 | - | ast::TypeSig::Fn(sig) |
|
| 1807 | + | ast::TypeSig::Fn { sig, isUnsafe } |
|
| 1804 | 1808 | )); |
|
| 1805 | 1809 | } |
|
| 1806 | 1810 | ||
| 1807 | 1811 | /// Parse a function signature following the function name. |
|
| 1808 | - | fn parseFnTypeSig(p: *mut Parser) -> ast::FnSig |
|
| 1812 | + | unsafe fn parseFnTypeSig(p: &mut Parser) -> ast::FnSig |
|
| 1809 | 1813 | throws (ParseError) |
|
| 1810 | 1814 | { |
|
| 1811 | 1815 | try expect(p, scanner::TokenKind::LParen, "expected `(` after function name"); |
|
| 1812 | - | let mut params = ast::nodeSlice(p.arena, 8); |
|
| 1816 | + | let mut params = ast::nodeSlice(&mut *p.arena, 8); |
|
| 1813 | 1817 | ||
| 1814 | 1818 | while not check(p, scanner::TokenKind::RParen) { |
|
| 1815 | 1819 | let param = try parseFnParam(p); |
|
| 1816 | 1820 | params.append(param, p.allocator); |
|
| 1817 | 1821 |
| 1829 | 1833 | ||
| 1830 | 1834 | return ast::FnSig { params, returnType, throwList }; |
|
| 1831 | 1835 | } |
|
| 1832 | 1836 | ||
| 1833 | 1837 | /// Parse a function declaration. |
|
| 1834 | - | fn parseFnDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 1838 | + | unsafe fn parseFnDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 1835 | 1839 | throws (ParseError) |
|
| 1836 | 1840 | { |
|
| 1837 | 1841 | try expect(p, scanner::TokenKind::Fn, "expected `fn`"); |
|
| 1838 | 1842 | ||
| 1839 | 1843 | // Method syntax: `fn (recv: *Type) name(params) { body }`. |
| 1847 | 1851 | ||
| 1848 | 1852 | if consume(p, scanner::TokenKind::Semicolon) { |
|
| 1849 | 1853 | if let a = attrs; ast::attributesContains(&a, ast::Attribute::Extern) { |
|
| 1850 | 1854 | // Keep existing attributes unchanged. |
|
| 1851 | 1855 | } else { |
|
| 1852 | - | let mut list = ast::nodeSlice(p.arena, 4); |
|
| 1856 | + | let mut list = ast::nodeSlice(&mut *p.arena, 4); |
|
| 1853 | 1857 | if let a = attrs { |
|
| 1854 | 1858 | for i in 0..a.list.len { |
|
| 1855 | 1859 | list.append(a.list[i], p.allocator); |
|
| 1856 | 1860 | } |
|
| 1857 | 1861 | } |
| 1866 | 1870 | ast::FnDecl { name, sig, body, attrs: fnAttrs } |
|
| 1867 | 1871 | )); |
|
| 1868 | 1872 | } |
|
| 1869 | 1873 | ||
| 1870 | 1874 | /// Parse a pointer-like type after its ownership prefix. |
|
| 1871 | - | fn parsePointerLikeType( |
|
| 1872 | - | p: *mut Parser, |
|
| 1875 | + | unsafe fn parsePointerLikeType( |
|
| 1876 | + | p: &mut Parser, |
|
| 1873 | 1877 | class: types::PointerClass, |
|
| 1874 | 1878 | ) -> *ast::Node throws (ParseError) { |
|
| 1875 | 1879 | let mutable = consume(p, scanner::TokenKind::Mut); |
|
| 1876 | 1880 | ||
| 1877 | 1881 | if consume(p, scanner::TokenKind::LBracket) { |
| 1902 | 1906 | ast::TypeSig::Pointer { class, valueType, mutable } |
|
| 1903 | 1907 | )); |
|
| 1904 | 1908 | } |
|
| 1905 | 1909 | ||
| 1906 | 1910 | /// Parse an array type. |
|
| 1907 | - | fn parseArrayType(p: *mut Parser) -> *ast::Node |
|
| 1911 | + | unsafe fn parseArrayType(p: &mut Parser) -> *ast::Node |
|
| 1908 | 1912 | throws (ParseError) |
|
| 1909 | 1913 | { |
|
| 1910 | 1914 | try expect(p, scanner::TokenKind::LBracket, "expected `[`"); |
|
| 1911 | 1915 | let itemType = try parseType(p); |
|
| 1912 | 1916 |
| 1919 | 1923 | )); |
|
| 1920 | 1924 | } |
|
| 1921 | 1925 | ||
| 1922 | 1926 | /// Parse a type path: an identifier optionally followed by `::` scope access. |
|
| 1923 | 1927 | /// Returns an identifier node or a scope access chain. |
|
| 1924 | - | fn parseTypePath(p: *mut Parser) -> *ast::Node |
|
| 1928 | + | unsafe fn parseTypePath(p: &mut Parser) -> *ast::Node |
|
| 1925 | 1929 | throws (ParseError) |
|
| 1926 | 1930 | { |
|
| 1927 | 1931 | let mut path: *ast::Node = undefined; |
|
| 1928 | 1932 | if p.current.kind == scanner::TokenKind::Super { |
|
| 1929 | 1933 | advance(p); |
| 1939 | 1943 | } |
|
| 1940 | 1944 | return path; |
|
| 1941 | 1945 | } |
|
| 1942 | 1946 | ||
| 1943 | 1947 | /// Parse a type annotation. |
|
| 1944 | - | export fn parseType(p: *mut Parser) -> *ast::Node |
|
| 1948 | + | export unsafe fn parseType(p: &mut Parser) -> *ast::Node |
|
| 1945 | 1949 | throws (ParseError) |
|
| 1946 | 1950 | { |
|
| 1947 | 1951 | match p.current.kind { |
|
| 1948 | 1952 | case scanner::TokenKind::Question => { |
|
| 1949 | 1953 | advance(p); |
| 2012 | 2016 | } |
|
| 2013 | 2017 | case scanner::TokenKind::Opaque => { |
|
| 2014 | 2018 | advance(p); |
|
| 2015 | 2019 | return node(p, ast::NodeValue::TypeSig(ast::TypeSig::Opaque)); |
|
| 2016 | 2020 | } |
|
| 2017 | - | case scanner::TokenKind::Fn => { |
|
| 2021 | + | case scanner::TokenKind::Fn, scanner::TokenKind::Unsafe => { |
|
| 2018 | 2022 | return try parseFnType(p); |
|
| 2019 | 2023 | } |
|
| 2020 | 2024 | else => { |
|
| 2021 | 2025 | throw failParsing(p, "expected type"); |
|
| 2022 | 2026 | } |
| 2025 | 2029 | ||
| 2026 | 2030 | /// Parse a name, optional type, and optional value. |
|
| 2027 | 2031 | /// |
|
| 2028 | 2032 | /// Used for record field declarations, variable declarations, |
|
| 2029 | 2033 | /// and record field initializations. |
|
| 2030 | - | fn parseNameTypeValue(p: *mut Parser) -> NameTypeValue |
|
| 2034 | + | unsafe fn parseNameTypeValue(p: &mut Parser) -> NameTypeValue |
|
| 2031 | 2035 | throws (ParseError) |
|
| 2032 | 2036 | { |
|
| 2033 | 2037 | let name = try parseIdentOrPlaceholder(p, "expected identifier or `_`"); |
|
| 2034 | 2038 | let mut type: ?*ast::Node = nil; |
|
| 2035 | 2039 | let mut alignment: ?*ast::Node = nil; |
| 2047 | 2051 | } |
|
| 2048 | 2052 | return NameTypeValue { name, type, value, alignment }; |
|
| 2049 | 2053 | } |
|
| 2050 | 2054 | ||
| 2051 | 2055 | /// Parse a constant declaration. |
|
| 2052 | - | fn parseConst(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2056 | + | unsafe fn parseConst(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2053 | 2057 | throws (ParseError) |
|
| 2054 | 2058 | { |
|
| 2055 | 2059 | try expect(p, scanner::TokenKind::Constant, "expected `constant`"); |
|
| 2056 | 2060 | ||
| 2057 | 2061 | let ident = try parseIdent(p, "expected identifier in constant declaration"); |
| 2066 | 2070 | ast::ConstDecl { ident, type, value, attrs } |
|
| 2067 | 2071 | )); |
|
| 2068 | 2072 | } |
|
| 2069 | 2073 | ||
| 2070 | 2074 | /// Parse a static declaration. |
|
| 2071 | - | fn parseStatic(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2075 | + | unsafe fn parseStatic(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2072 | 2076 | throws (ParseError) |
|
| 2073 | 2077 | { |
|
| 2074 | 2078 | try expect(p, scanner::TokenKind::Static, "expected `static`"); |
|
| 2075 | 2079 | ||
| 2076 | 2080 | let ident = try parseIdent(p, "expected identifier in static declaration"); |
| 2085 | 2089 | ast::StaticDecl { ident, type, value, attrs } |
|
| 2086 | 2090 | )); |
|
| 2087 | 2091 | } |
|
| 2088 | 2092 | ||
| 2089 | 2093 | /// Parse a `use` declaration. |
|
| 2090 | - | fn parseUse(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2094 | + | unsafe fn parseUse(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2091 | 2095 | throws (ParseError) |
|
| 2092 | 2096 | { |
|
| 2093 | 2097 | try expect(p, scanner::TokenKind::Use, "expected `use`"); |
|
| 2094 | 2098 | ||
| 2095 | 2099 | // Allow `super` or identifier as the first part of the path. |
| 2115 | 2119 | ast::Use { path, wildcard: false, attrs } |
|
| 2116 | 2120 | )); |
|
| 2117 | 2121 | } |
|
| 2118 | 2122 | ||
| 2119 | 2123 | /// Parse a `mod` declaration. |
|
| 2120 | - | fn parseMod(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2124 | + | unsafe fn parseMod(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2121 | 2125 | throws (ParseError) |
|
| 2122 | 2126 | { |
|
| 2123 | 2127 | try expect(p, scanner::TokenKind::Mod, "expected `mod`"); |
|
| 2124 | 2128 | let name = try parseIdent(p, "expected module name after `mod`"); |
|
| 2125 | 2129 |
| 2132 | 2136 | /// |
|
| 2133 | 2137 | /// Eg. `let case <pattern> = <expr> else { ... };` |
|
| 2134 | 2138 | /// Eg. `let case <pattern> = <expr> if <guard> else { ... };` |
|
| 2135 | 2139 | /// |
|
| 2136 | 2140 | /// Expects `let case` tokens to have already been consumed. |
|
| 2137 | - | fn parseLetCase(p: *mut Parser) -> *ast::Node throws (ParseError) { |
|
| 2141 | + | unsafe fn parseLetCase(p: &mut Parser) -> *ast::Node throws (ParseError) { |
|
| 2138 | 2142 | let pattern = try parseMatchPattern(p); |
|
| 2139 | 2143 | ||
| 2140 | 2144 | try expect(p, scanner::TokenKind::Equal, "expected `=` after pattern"); |
|
| 2141 | 2145 | let expr = try parseCond(p); |
|
| 2142 | 2146 |
| 2161 | 2165 | /// Eg. `let mut <ident> = <expr> else { ... };` |
|
| 2162 | 2166 | /// Eg. `let <ident> = <expr> if <guard> else { ... };` |
|
| 2163 | 2167 | /// Eg. `mut <ident> = <expr>;` |
|
| 2164 | 2168 | /// |
|
| 2165 | 2169 | /// Expects `let` or `mut` token to have already been consumed. |
|
| 2166 | - | fn parseLet(p: *mut Parser, mutable: bool) -> *ast::Node throws (ParseError) { |
|
| 2170 | + | unsafe fn parseLet(p: &mut Parser, mutable: bool) -> *ast::Node throws (ParseError) { |
|
| 2167 | 2171 | let binding = try parseNameTypeValue(p); |
|
| 2168 | 2172 | let value = binding.value |
|
| 2169 | 2173 | else throw failParsing(p, "expected value initializer"); |
|
| 2170 | 2174 | ||
| 2171 | 2175 | // Check for optional `else` clause (let-else). |
| 2181 | 2185 | ident: binding.name, type: binding.type, value, alignment: binding.alignment, mutable, |
|
| 2182 | 2186 | })); |
|
| 2183 | 2187 | } |
|
| 2184 | 2188 | ||
| 2185 | 2189 | /// Parse a module from source text using the provided arena for node storage. |
|
| 2186 | - | export fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: *mut ast::NodeArena, pool: *mut strings::Pool) -> *mut ast::Node |
|
| 2190 | + | export unsafe fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: &mut ast::NodeArena, pool: *mut strings::Pool) -> *mut ast::Node |
|
| 2187 | 2191 | throws (ParseError) |
|
| 2188 | 2192 | { |
|
| 2189 | 2193 | let mut p = mkParser(sourceLoc, input, arena, pool); |
|
| 2190 | 2194 | return try parseModule(&mut p) catch { |
|
| 2191 | 2195 | printErrors(&p); |
| 2195 | 2199 | ||
| 2196 | 2200 | /// Parse a complete module into a block of top-level statements. |
|
| 2197 | 2201 | /// |
|
| 2198 | 2202 | /// This is the main entry point for parsing an entire Radiance source file. |
|
| 2199 | 2203 | /// The parser must already be initialized with source code. |
|
| 2200 | - | export fn parseModule(p: *mut Parser) -> *mut ast::Node |
|
| 2204 | + | export unsafe fn parseModule(p: &mut Parser) -> *mut ast::Node |
|
| 2201 | 2205 | throws (ParseError) |
|
| 2202 | 2206 | { |
|
| 2203 | 2207 | advance(p); // Set the parser up with a first token. |
|
| 2204 | 2208 | ||
| 2205 | 2209 | let mut blk = mkBlock(p, 512); |
| 2208 | 2212 | ||
| 2209 | 2213 | return node(p, ast::NodeValue::Block(blk)); |
|
| 2210 | 2214 | } |
|
| 2211 | 2215 | ||
| 2212 | 2216 | /// Consume a token of the given kind if present. |
|
| 2213 | - | export fn consume(p: *mut Parser, kind: scanner::TokenKind) -> bool { |
|
| 2217 | + | export fn consume(p: &mut Parser, kind: scanner::TokenKind) -> bool { |
|
| 2214 | 2218 | if check(p, kind) { |
|
| 2215 | 2219 | advance(p); |
|
| 2216 | 2220 | return true; |
|
| 2217 | 2221 | } |
|
| 2218 | 2222 | return false; |
|
| 2219 | 2223 | } |
|
| 2220 | 2224 | ||
| 2221 | 2225 | /// Expect a token of the given kind or report an error. |
|
| 2222 | - | export fn expect(p: *mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8] |
|
| 2226 | + | export fn expect(p: &mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8] |
|
| 2223 | 2227 | throws (ParseError) |
|
| 2224 | 2228 | { |
|
| 2225 | 2229 | if not consume(p, kind) { |
|
| 2226 | - | reportError(p, p.current, message); |
|
| 2230 | + | let token = p.current; |
|
| 2231 | + | reportError(p, token, message); |
|
| 2227 | 2232 | throw ParseError::UnexpectedToken; |
|
| 2228 | 2233 | } |
|
| 2229 | 2234 | return p.previous.source; |
|
| 2230 | 2235 | } |
|
| 2231 | 2236 |
| 2242 | 2247 | } |
|
| 2243 | 2248 | } |
|
| 2244 | 2249 | ||
| 2245 | 2250 | /// Parse a trait declaration. |
|
| 2246 | 2251 | /// Syntax: `trait Name { fn (*Trait) method(...) -> T; ... }` |
|
| 2247 | - | fn parseTraitDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2252 | + | unsafe fn parseTraitDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2248 | 2253 | throws (ParseError) |
|
| 2249 | 2254 | { |
|
| 2250 | 2255 | try expect(p, scanner::TokenKind::Trait, "expected `trait`"); |
|
| 2251 | 2256 | let name = try parseIdent(p, "expected trait name"); |
|
| 2252 | 2257 | let supertraits = try parseDerives(p); |
|
| 2253 | 2258 | try expect(p, scanner::TokenKind::LBrace, "expected `{` after trait name"); |
|
| 2254 | 2259 | ||
| 2255 | - | let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2260 | + | let mut methods = ast::nodeSlice(&mut *p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2256 | 2261 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 2257 | 2262 | not check(p, scanner::TokenKind::Eof) |
|
| 2258 | 2263 | { |
|
| 2259 | 2264 | let method = try parseTraitMethodSig(p); |
|
| 2260 | 2265 | methods.append(method, p.allocator); |
| 2264 | 2269 | return node(p, ast::NodeValue::TraitDecl { name, supertraits, methods, attrs }); |
|
| 2265 | 2270 | } |
|
| 2266 | 2271 | ||
| 2267 | 2272 | /// Parse a trait method signature. |
|
| 2268 | 2273 | /// Syntax: `fn (*Trait) fnord(<params>) -> ReturnType;` |
|
| 2269 | - | fn parseTraitMethodSig(p: *mut Parser) -> *ast::Node |
|
| 2274 | + | unsafe fn parseTraitMethodSig(p: &mut Parser) -> *ast::Node |
|
| 2270 | 2275 | throws (ParseError) |
|
| 2271 | 2276 | { |
|
| 2272 | 2277 | let attrs = parseAttributes(p); |
|
| 2273 | 2278 | try expect(p, scanner::TokenKind::Fn, "expected `fn`"); |
|
| 2274 | 2279 | try expect(p, scanner::TokenKind::LParen, "expected `(` before receiver"); |
| 2287 | 2292 | /// Parse an instance block. |
|
| 2288 | 2293 | /// Syntax: `instance Trait for Type { fn (t: *mut Type) fnord(..) {..} }` |
|
| 2289 | 2294 | /// |
|
| 2290 | 2295 | /// Instance declarations do not accept attributes (e.g. `export`). |
|
| 2291 | 2296 | /// Visibility is determined by the trait declaration itself. |
|
| 2292 | - | fn parseInstanceDecl(p: *mut Parser) -> *ast::Node |
|
| 2297 | + | unsafe fn parseInstanceDecl(p: &mut Parser) -> *ast::Node |
|
| 2293 | 2298 | throws (ParseError) |
|
| 2294 | 2299 | { |
|
| 2295 | 2300 | try expect(p, scanner::TokenKind::Instance, "expected `instance`"); |
|
| 2296 | 2301 | let traitName = try parseTypePath(p); |
|
| 2297 | 2302 | try expect(p, scanner::TokenKind::For, "expected `for` after trait name"); |
|
| 2298 | 2303 | let targetType = try parseTypePath(p); |
|
| 2299 | 2304 | try expect(p, scanner::TokenKind::LBrace, "expected `{` after target type"); |
|
| 2300 | 2305 | ||
| 2301 | - | let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2306 | + | let mut methods = ast::nodeSlice(&mut *p.arena, ast::MAX_TRAIT_METHODS); |
|
| 2302 | 2307 | ||
| 2303 | 2308 | while not check(p, scanner::TokenKind::RBrace) and |
|
| 2304 | 2309 | not check(p, scanner::TokenKind::Eof) |
|
| 2305 | 2310 | { |
|
| 2306 | 2311 | let attrs = parseAttributes(p); |
| 2317 | 2322 | /// Parse a method declaration with a receiver. |
|
| 2318 | 2323 | /// Syntax: `fn (t: *mut Type) fnord(<params>) -> ReturnType { body }` |
|
| 2319 | 2324 | /// |
|
| 2320 | 2325 | /// Used both inside `instance` blocks and as standalone methods at the top level. |
|
| 2321 | 2326 | /// Expects the `fn` token to have already been consumed. |
|
| 2322 | - | fn parseMethodDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2327 | + | unsafe fn parseMethodDecl(p: &mut Parser, attrs: ?ast::Attributes) -> *ast::Node |
|
| 2323 | 2328 | throws (ParseError) |
|
| 2324 | 2329 | { |
|
| 2325 | 2330 | try expect(p, scanner::TokenKind::LParen, "expected `(` before receiver"); |
|
| 2326 | 2331 | ||
| 2327 | 2332 | let receiverName = try parseIdent(p, "expected receiver name"); |
| 2338 | 2343 | name, receiverName, receiverType, sig, body, attrs, |
|
| 2339 | 2344 | }); |
|
| 2340 | 2345 | } |
|
| 2341 | 2346 | ||
| 2342 | 2347 | /// Parse a comma-separated list enclosed by the given delimiters. |
|
| 2343 | - | fn parseList( |
|
| 2344 | - | p: *mut Parser, |
|
| 2348 | + | unsafe fn parseList( |
|
| 2349 | + | p: &mut Parser, |
|
| 2345 | 2350 | open: scanner::TokenKind, |
|
| 2346 | 2351 | close: scanner::TokenKind, |
|
| 2347 | - | parseItem: fn (*mut Parser) -> *ast::Node throws (ParseError) |
|
| 2352 | + | parseItem: unsafe fn (&mut Parser) -> *ast::Node throws (ParseError) |
|
| 2348 | 2353 | ) -> *mut [*ast::Node] throws (ParseError) { |
|
| 2349 | 2354 | try expect(p, open, listExpectMessage(open)); |
|
| 2350 | - | let mut items = ast::nodeSlice(p.arena, 8); |
|
| 2355 | + | let mut items = ast::nodeSlice(&mut *p.arena, 8); |
|
| 2351 | 2356 | ||
| 2352 | 2357 | while not check(p, close) { |
|
| 2353 | 2358 | let item = try parseItem(p); |
|
| 2354 | 2359 | items.append(item, p.allocator); |
|
| 2355 | 2360 |
lib/std/lang/parser/tests.rad
+182 -177
| 70 | 70 | try testing::expect(range.end == nil); |
|
| 71 | 71 | } |
|
| 72 | 72 | } |
|
| 73 | 73 | ||
| 74 | 74 | /// Parse multiple statements from a string. |
|
| 75 | - | fn parseStmtsStr(input: *[u8]) -> *ast::Node |
|
| 75 | + | unsafe fn parseStmtsStr(input: *[u8]) -> *ast::Node |
|
| 76 | 76 | throws (testing::TestError) |
|
| 77 | 77 | { |
|
| 78 | 78 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 79 | 79 | let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL); |
|
| 80 | 80 | return try super::parseModule(&mut parser) catch { |
|
| 81 | 81 | throw testing::TestError::Failed; |
|
| 82 | 82 | }; |
|
| 83 | 83 | } |
|
| 84 | 84 | ||
| 85 | 85 | /// Parse a single type from a string. |
|
| 86 | - | fn parseTypeStr(input: *[u8]) -> *ast::Node |
|
| 86 | + | unsafe fn parseTypeStr(input: *[u8]) -> *ast::Node |
|
| 87 | 87 | throws (super::ParseError) |
|
| 88 | 88 | { |
|
| 89 | 89 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 90 | 90 | let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL); |
|
| 91 | 91 | super::advance(&mut parser); |
| 94 | 94 | ||
| 95 | 95 | return root; |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | 98 | /// Parse a single expression from a string. |
|
| 99 | - | export fn parseExprStr(input: *[u8]) -> *ast::Node |
|
| 99 | + | export unsafe fn parseExprStr(input: *[u8]) -> *ast::Node |
|
| 100 | 100 | throws (super::ParseError) |
|
| 101 | 101 | { |
|
| 102 | 102 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 103 | 103 | let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL); |
|
| 104 | 104 | super::advance(&mut parser); |
|
| 105 | 105 | return try super::parseExpr(&mut parser); |
|
| 106 | 106 | } |
|
| 107 | 107 | ||
| 108 | 108 | /// Parse a single statement from a string. |
|
| 109 | - | fn parseStmtStr(input: *[u8]) -> *ast::Node |
|
| 109 | + | unsafe fn parseStmtStr(input: *[u8]) -> *ast::Node |
|
| 110 | 110 | throws (super::ParseError) |
|
| 111 | 111 | { |
|
| 112 | 112 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 113 | 113 | let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL); |
|
| 114 | 114 | super::advance(&mut parser); |
| 118 | 118 | ||
| 119 | 119 | return root; |
|
| 120 | 120 | } |
|
| 121 | 121 | ||
| 122 | 122 | /// Parse an expression expected to be a number literal and return its payload. |
|
| 123 | - | fn parseNumberLiteral(text: *[u8]) -> fmt::IntLiteral |
|
| 123 | + | unsafe fn parseNumberLiteral(text: *[u8]) -> fmt::IntLiteral |
|
| 124 | 124 | throws (testing::TestError) |
|
| 125 | 125 | { |
|
| 126 | 126 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 127 | 127 | let mut parser = super::mkParser(scanner::SourceLoc::String, text, &mut arena, &mut STRING_POOL); |
|
| 128 | 128 | super::advance(&mut parser); |
| 137 | 137 | ||
| 138 | 138 | return lit; |
|
| 139 | 139 | } |
|
| 140 | 140 | ||
| 141 | 141 | /// Ensure that parsing the supplied literal source fails. |
|
| 142 | - | fn expectNumberLiteralFail(text: *[u8]) |
|
| 142 | + | unsafe fn expectNumberLiteralFail(text: *[u8]) |
|
| 143 | 143 | throws (testing::TestError) |
|
| 144 | 144 | { |
|
| 145 | 145 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 146 | 146 | let mut parser = super::mkParser(scanner::SourceLoc::String, text, &mut arena, &mut STRING_POOL); |
|
| 147 | 147 | super::advance(&mut parser); |
| 260 | 260 | } |
|
| 261 | 261 | return block.statements[block.statements.len - 1]; |
|
| 262 | 262 | } |
|
| 263 | 263 | ||
| 264 | 264 | /// Test parsing boolean literals (`true` and `false`). |
|
| 265 | - | @test fn testParseBool() throws (testing::TestError) { |
|
| 265 | + | @test unsafe fn testParseBool() throws (testing::TestError) { |
|
| 266 | 266 | let r1 = try! parseExprStr("true"); |
|
| 267 | 267 | let case ast::NodeValue::Bool(v1) = r1.value if v1 |
|
| 268 | 268 | else throw testing::TestError::Failed; |
|
| 269 | 269 | ||
| 270 | 270 | let r2 = try! parseExprStr("false"); |
|
| 271 | 271 | let case ast::NodeValue::Bool(v2) = r2.value if not v2 |
|
| 272 | 272 | else throw testing::TestError::Failed; |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// Test parsing number literals. |
|
| 276 | - | @test fn testParseNumber() throws (testing::TestError) { |
|
| 276 | + | @test unsafe fn testParseNumber() throws (testing::TestError) { |
|
| 277 | 277 | let r1 = try! parseExprStr("4519"); |
|
| 278 | 278 | try expectNumber(r1, "4519"); |
|
| 279 | 279 | } |
|
| 280 | 280 | ||
| 281 | 281 | /// Verify that decimal literals record magnitude and base metadata. |
|
| 282 | - | @test fn testParseDecimalLiteralMetadata() throws (testing::TestError) { |
|
| 282 | + | @test unsafe fn testParseDecimalLiteralMetadata() throws (testing::TestError) { |
|
| 283 | 283 | let lit = try parseNumberLiteral("1234"); |
|
| 284 | 284 | try testing::expect(lit.magnitude == 1234); |
|
| 285 | 285 | try testing::expect(lit.radix == fmt::Radix::Decimal); |
|
| 286 | 286 | } |
|
| 287 | 287 | ||
| 288 | 288 | /// Verify that hexadecimal literals record magnitude and radix metadata. |
|
| 289 | - | @test fn testParseNumberMetadata() throws (testing::TestError) { |
|
| 289 | + | @test unsafe fn testParseNumberMetadata() throws (testing::TestError) { |
|
| 290 | 290 | let lit = try parseNumberLiteral("0xFF"); |
|
| 291 | 291 | try testing::expect(lit.magnitude == 0xFF); |
|
| 292 | 292 | try testing::expect(lit.radix == fmt::Radix::Hex); |
|
| 293 | 293 | } |
|
| 294 | 294 | ||
| 295 | 295 | /// Verify that binary literals capture their radix. |
|
| 296 | - | @test fn testParseBinaryLiteralMetadata() throws (testing::TestError) { |
|
| 296 | + | @test unsafe fn testParseBinaryLiteralMetadata() throws (testing::TestError) { |
|
| 297 | 297 | let lit = try parseNumberLiteral("0b1010"); |
|
| 298 | 298 | try testing::expect(lit.magnitude == 0b1010); |
|
| 299 | 299 | try testing::expect(lit.radix == fmt::Radix::Binary); |
|
| 300 | 300 | } |
|
| 301 | 301 | ||
| 302 | 302 | /// Negative literals parse as unary negation of an unsigned number. |
|
| 303 | - | @test fn testParseNegativeLiteral() throws (testing::TestError) { |
|
| 303 | + | @test unsafe fn testParseNegativeLiteral() throws (testing::TestError) { |
|
| 304 | 304 | let node = try! parseExprStr("-99"); |
|
| 305 | 305 | let case ast::NodeValue::UnOp(neg) = node.value |
|
| 306 | 306 | else throw testing::TestError::Failed; |
|
| 307 | 307 | try testing::expect(neg.op == ast::UnaryOp::Neg); |
|
| 308 | 308 | let case ast::NodeValue::Number(lit) = neg.value.value |
|
| 309 | 309 | else throw testing::TestError::Failed; |
|
| 310 | 310 | try testing::expect(lit.magnitude == 99); |
|
| 311 | 311 | } |
|
| 312 | 312 | ||
| 313 | 313 | /// Unary plus is not part of the expression grammar. |
|
| 314 | - | @test fn testRejectUnaryPlus() throws (testing::TestError) { |
|
| 314 | + | @test unsafe fn testRejectUnaryPlus() throws (testing::TestError) { |
|
| 315 | 315 | try expectNumberLiteralFail("+1"); |
|
| 316 | 316 | try expectNumberLiteralFail("+value"); |
|
| 317 | 317 | try expectNumberLiteralFail("+(value)"); |
|
| 318 | 318 | } |
|
| 319 | 319 | ||
| 320 | 320 | /// Range expressions parse with explicit start and end bounds. |
|
| 321 | - | @test fn testParseRangeExpr() throws (testing::TestError) { |
|
| 321 | + | @test unsafe fn testParseRangeExpr() throws (testing::TestError) { |
|
| 322 | 322 | let node = try! parseExprStr("0..5"); |
|
| 323 | 323 | try expectRangeNumbers(node, "0", "5"); |
|
| 324 | 324 | } |
|
| 325 | 325 | ||
| 326 | 326 | /// Range expressions allow a missing end bound. |
|
| 327 | - | @test fn testParseRangeExprNoEnd() throws (testing::TestError) { |
|
| 327 | + | @test unsafe fn testParseRangeExprNoEnd() throws (testing::TestError) { |
|
| 328 | 328 | let node = try! parseExprStr("0.."); |
|
| 329 | 329 | try expectRangeNumbers(node, "0", nil); |
|
| 330 | 330 | } |
|
| 331 | 331 | ||
| 332 | 332 | /// Range expressions allow a missing start bound. |
|
| 333 | - | @test fn testParseRangeExprNoStart() throws (testing::TestError) { |
|
| 333 | + | @test unsafe fn testParseRangeExprNoStart() throws (testing::TestError) { |
|
| 334 | 334 | let node = try! parseExprStr("..5"); |
|
| 335 | 335 | try expectRangeNumbers(node, nil, "5"); |
|
| 336 | 336 | } |
|
| 337 | 337 | ||
| 338 | 338 | /// Literals with out-of-range digits for their base are rejected. |
|
| 339 | - | @test fn testParseInvalidIntLiteral() throws (testing::TestError) { |
|
| 339 | + | @test unsafe fn testParseInvalidIntLiteral() throws (testing::TestError) { |
|
| 340 | 340 | // 2^64 overflows u64. |
|
| 341 | 341 | try expectNumberLiteralFail("18446744073709551616"); |
|
| 342 | 342 | try expectNumberLiteralFail("0x10000000000000000"); |
|
| 343 | 343 | try expectNumberLiteralFail("0x1G"); |
|
| 344 | 344 | try expectNumberLiteralFail("0b102"); |
|
| 345 | 345 | } |
|
| 346 | 346 | ||
| 347 | 347 | /// Test parsing nil literal. |
|
| 348 | - | @test fn testParseNil() throws (testing::TestError) { |
|
| 348 | + | @test unsafe fn testParseNil() throws (testing::TestError) { |
|
| 349 | 349 | let r1 = try! parseExprStr("nil"); |
|
| 350 | 350 | let case ast::NodeValue::Nil = r1.value |
|
| 351 | 351 | else throw testing::TestError::Failed; |
|
| 352 | 352 | } |
|
| 353 | 353 | ||
| 354 | 354 | /// Test parsing undefined literal. |
|
| 355 | - | @test fn testParseUndefined() throws (testing::TestError) { |
|
| 355 | + | @test unsafe fn testParseUndefined() throws (testing::TestError) { |
|
| 356 | 356 | let r1 = try! parseExprStr("undefined"); |
|
| 357 | 357 | let case ast::NodeValue::Undef = r1.value |
|
| 358 | 358 | else throw testing::TestError::Failed; |
|
| 359 | 359 | } |
|
| 360 | 360 | ||
| 361 | 361 | /// Test parsing character literals. |
|
| 362 | - | @test fn testParseChar() throws (testing::TestError) { |
|
| 362 | + | @test unsafe fn testParseChar() throws (testing::TestError) { |
|
| 363 | 363 | let r1 = try! parseExprStr("'a'"); |
|
| 364 | 364 | let case ast::NodeValue::Char(c1) = r1.value if c1 == 'a' |
|
| 365 | 365 | else throw testing::TestError::Failed; |
|
| 366 | 366 | ||
| 367 | 367 | let r2 = try! parseExprStr("'\\n'"); |
| 372 | 372 | let case ast::NodeValue::Char(c3) = r3.value if c3 == '\t' |
|
| 373 | 373 | else throw testing::TestError::Failed; |
|
| 374 | 374 | } |
|
| 375 | 375 | ||
| 376 | 376 | /// Test parsing string literals. |
|
| 377 | - | @test fn testParseString() throws (testing::TestError) { |
|
| 377 | + | @test unsafe fn testParseString() throws (testing::TestError) { |
|
| 378 | 378 | let r1 = try! parseExprStr("\"hello\""); |
|
| 379 | 379 | let case ast::NodeValue::String(s1) = r1.value if mem::eq(s1, "hello") |
|
| 380 | 380 | else throw testing::TestError::Failed; |
|
| 381 | 381 | ||
| 382 | 382 | let r2 = try! parseExprStr("\"\""); |
|
| 383 | 383 | let case ast::NodeValue::String(s2) = r2.value if s2.len == 0 |
|
| 384 | 384 | else throw testing::TestError::Failed; |
|
| 385 | 385 | } |
|
| 386 | 386 | ||
| 387 | 387 | /// Test string escape sequence processing. |
|
| 388 | - | @test fn testParseStringEscape() throws (testing::TestError) { |
|
| 388 | + | @test unsafe fn testParseStringEscape() throws (testing::TestError) { |
|
| 389 | 389 | // Tab and newline. |
|
| 390 | 390 | let r1 = try! parseExprStr("\"hello\\tworld\\n\""); |
|
| 391 | 391 | let case ast::NodeValue::String(s1) = r1.value if s1.len == 12 |
|
| 392 | 392 | else throw testing::TestError::Failed; |
|
| 393 | 393 | if s1[5] <> '\t' { |
| 433 | 433 | throw testing::TestError::Failed; |
|
| 434 | 434 | } |
|
| 435 | 435 | } |
|
| 436 | 436 | ||
| 437 | 437 | /// Test parsing placeholder/underscore. |
|
| 438 | - | @test fn testParsePlaceholder() throws (testing::TestError) { |
|
| 438 | + | @test unsafe fn testParsePlaceholder() throws (testing::TestError) { |
|
| 439 | 439 | let r1 = try! parseExprStr("_"); |
|
| 440 | 440 | let case ast::NodeValue::Placeholder = r1.value |
|
| 441 | 441 | else throw testing::TestError::Failed; |
|
| 442 | 442 | } |
|
| 443 | 443 | ||
| 444 | 444 | /// Test parsing array literals. |
|
| 445 | - | @test fn testParseArrayLiteral() throws (testing::TestError) { |
|
| 445 | + | @test unsafe fn testParseArrayLiteral() throws (testing::TestError) { |
|
| 446 | 446 | let r1 = try! parseExprStr("[]"); |
|
| 447 | 447 | let case ast::NodeValue::ArrayLit(items1) = r1.value if items1.len == 0 |
|
| 448 | 448 | else throw testing::TestError::Failed; |
|
| 449 | 449 | ||
| 450 | 450 | let r2 = try! parseExprStr("[1]"); |
| 455 | 455 | let case ast::NodeValue::ArrayLit(items3) = r3.value if items3.len == 3 |
|
| 456 | 456 | else throw testing::TestError::Failed; |
|
| 457 | 457 | } |
|
| 458 | 458 | ||
| 459 | 459 | /// Test parsing array repeat literals. |
|
| 460 | - | @test fn testParseArrayRepeatLiteral() throws (testing::TestError) { |
|
| 460 | + | @test unsafe fn testParseArrayRepeatLiteral() throws (testing::TestError) { |
|
| 461 | 461 | let r1 = try! parseExprStr("[42; 10]"); |
|
| 462 | 462 | let case ast::NodeValue::ArrayRepeatLit(a) = r1.value |
|
| 463 | 463 | else throw testing::TestError::Failed; |
|
| 464 | 464 | ||
| 465 | 465 | try expectNumber(a.item, "42"); |
|
| 466 | 466 | try expectNumber(a.count, "10"); |
|
| 467 | 467 | } |
|
| 468 | 468 | ||
| 469 | 469 | /// Test parsing a simple `if` statement without an `else` clause. |
|
| 470 | - | @test fn testParseIf() throws (testing::TestError) { |
|
| 470 | + | @test unsafe fn testParseIf() throws (testing::TestError) { |
|
| 471 | 471 | let r1 = try parseStmtStr("if condition { body; }") catch { |
|
| 472 | 472 | throw testing::TestError::Failed; |
|
| 473 | 473 | }; |
|
| 474 | 474 | let case ast::NodeValue::If(n) = r1.value |
|
| 475 | 475 | else throw testing::TestError::Failed; |
| 478 | 478 | try expectBlockExprStmt(n.thenBranch, ast::NodeValue::Ident("body")); |
|
| 479 | 479 | try testing::expect(n.elseBranch == nil); |
|
| 480 | 480 | } |
|
| 481 | 481 | ||
| 482 | 482 | /// Test parsing an `if-else` statement. |
|
| 483 | - | @test fn testParseIfElse() throws (testing::TestError) { |
|
| 483 | + | @test unsafe fn testParseIfElse() throws (testing::TestError) { |
|
| 484 | 484 | let r1 = try! parseStmtStr("if condition { left; } else { right; }") catch { |
|
| 485 | 485 | throw testing::TestError::Failed; |
|
| 486 | 486 | }; |
|
| 487 | 487 | let case ast::NodeValue::If(n) = r1.value |
|
| 488 | 488 | else throw testing::TestError::Failed; |
| 495 | 495 | ||
| 496 | 496 | try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("right")); |
|
| 497 | 497 | } |
|
| 498 | 498 | ||
| 499 | 499 | /// Test parsing an `if-else if-else` chain. |
|
| 500 | - | @test fn testParseIfElseIf() throws (testing::TestError) { |
|
| 500 | + | @test unsafe fn testParseIfElseIf() throws (testing::TestError) { |
|
| 501 | 501 | let root = try! parseStmtStr("if x { a; } else if y { b; } else { c; }") catch { |
|
| 502 | 502 | throw testing::TestError::Failed; |
|
| 503 | 503 | }; |
|
| 504 | 504 | let case ast::NodeValue::If(top) = root.value |
|
| 505 | 505 | else throw testing::TestError::Failed; |
| 520 | 520 | else throw testing::TestError::Failed; |
|
| 521 | 521 | try expectBlockExprStmt(innerElse, ast::NodeValue::Ident("c")); |
|
| 522 | 522 | } |
|
| 523 | 523 | ||
| 524 | 524 | /// Test parsing a block with multiple statements where the last lacks a semicolon. |
|
| 525 | - | @test fn testParseBlockMultiStmt() throws (testing::TestError) { |
|
| 525 | + | @test unsafe fn testParseBlockMultiStmt() throws (testing::TestError) { |
|
| 526 | 526 | let root = try! parseStmtStr("{ first; second; third }"); |
|
| 527 | 527 | let case ast::NodeValue::Block(body) = root.value |
|
| 528 | 528 | else throw testing::TestError::Failed; |
|
| 529 | 529 | try testing::expect(body.statements.len == 3); |
|
| 530 | 530 |
| 543 | 543 | else throw testing::TestError::Failed; |
|
| 544 | 544 | try expectIdent(thirdExpr, "third"); |
|
| 545 | 545 | } |
|
| 546 | 546 | ||
| 547 | 547 | /// Test parsing a block that keeps a trailing `;` delimiter. |
|
| 548 | - | @test fn testParseBlockTrailingSemicolon() throws (testing::TestError) { |
|
| 548 | + | @test unsafe fn testParseBlockTrailingSemicolon() throws (testing::TestError) { |
|
| 549 | 549 | let root = try! parseStmtStr("if cond { only; }") catch { |
|
| 550 | 550 | throw testing::TestError::Failed; |
|
| 551 | 551 | }; |
|
| 552 | 552 | let case ast::NodeValue::If(node) = root.value |
|
| 553 | 553 | else throw testing::TestError::Failed; |
| 561 | 561 | else throw testing::TestError::Failed; |
|
| 562 | 562 | try expectIdent(expr, "only"); |
|
| 563 | 563 | } |
|
| 564 | 564 | ||
| 565 | 565 | /// Test that missing delimiters between block statements produce an error. |
|
| 566 | - | @test fn testParseBlockMissingDelimiter() throws (testing::TestError) { |
|
| 566 | + | @test unsafe fn testParseBlockMissingDelimiter() throws (testing::TestError) { |
|
| 567 | 567 | let parsed: ?*ast::Node = |
|
| 568 | 568 | try? parseStmtStr("if cond { first second }"); |
|
| 569 | 569 | try testing::expect(parsed == nil); |
|
| 570 | 570 | } |
|
| 571 | 571 | ||
| 572 | 572 | /// Test parsing a `let` binding without a type annotation. |
|
| 573 | - | @test fn testParseLet() throws (testing::TestError) { |
|
| 573 | + | @test unsafe fn testParseLet() throws (testing::TestError) { |
|
| 574 | 574 | let r1 = try! parseStmtStr("let x = y;") catch { |
|
| 575 | 575 | throw testing::TestError::Failed; |
|
| 576 | 576 | }; |
|
| 577 | 577 | let case ast::NodeValue::Let(n) = r1.value |
|
| 578 | 578 | else throw testing::TestError::Failed; |
| 582 | 582 | try testing::expect(not n.mutable); |
|
| 583 | 583 | try testing::expect(n.type == nil); |
|
| 584 | 584 | } |
|
| 585 | 585 | ||
| 586 | 586 | /// Test parsing a `let` binding with a type annotation. |
|
| 587 | - | @test fn testParseLetTyped() throws (testing::TestError) { |
|
| 587 | + | @test unsafe fn testParseLetTyped() throws (testing::TestError) { |
|
| 588 | 588 | let r1 = try! parseStmtStr("let x: i32 = y;"); |
|
| 589 | 589 | let case ast::NodeValue::Let(n) = r1.value |
|
| 590 | 590 | else throw testing::TestError::Failed; |
|
| 591 | 591 | ||
| 592 | 592 | try expectIdent(n.ident, "x"); |
| 599 | 599 | width: 4, sign: ast::Signedness::Signed |
|
| 600 | 600 | }); |
|
| 601 | 601 | } |
|
| 602 | 602 | ||
| 603 | 603 | /// Test parsing a `let` binding with alignment modifier. |
|
| 604 | - | @test fn testParseLetAlign() throws (testing::TestError) { |
|
| 604 | + | @test unsafe fn testParseLetAlign() throws (testing::TestError) { |
|
| 605 | 605 | let r1 = try! parseStmtStr("let x: i32 align(16) = 13;"); |
|
| 606 | 606 | let case ast::NodeValue::Let(n) = r1.value |
|
| 607 | 607 | else throw testing::TestError::Failed; |
|
| 608 | 608 | ||
| 609 | 609 | try expectIdent(n.ident, "x"); |
| 622 | 622 | else throw testing::TestError::Failed; |
|
| 623 | 623 | try expectNumber(a, "16"); |
|
| 624 | 624 | } |
|
| 625 | 625 | ||
| 626 | 626 | /// Test parsing let-else statement. |
|
| 627 | - | @test fn testParseLetElse() throws (testing::TestError) { |
|
| 627 | + | @test unsafe fn testParseLetElse() throws (testing::TestError) { |
|
| 628 | 628 | let root = try! parseStmtStr("let x = opt else { return };"); |
|
| 629 | 629 | let case ast::NodeValue::LetElse(letElse) = root.value |
|
| 630 | 630 | else throw testing::TestError::Failed; |
|
| 631 | 631 | ||
| 632 | 632 | try expectIdent(letElse.pattern.pattern, "x"); |
| 635 | 635 | else throw testing::TestError::Failed; |
|
| 636 | 636 | try testing::expect(elseBlock.statements.len == 1); |
|
| 637 | 637 | } |
|
| 638 | 638 | ||
| 639 | 639 | /// Test parsing let-else with single statement. |
|
| 640 | - | @test fn testParseLetElseSingleStmt() throws (testing::TestError) { |
|
| 640 | + | @test unsafe fn testParseLetElseSingleStmt() throws (testing::TestError) { |
|
| 641 | 641 | let root = try! parseStmtStr("let y = val else return;"); |
|
| 642 | 642 | let case ast::NodeValue::LetElse(letElse) = root.value |
|
| 643 | 643 | else throw testing::TestError::Failed; |
|
| 644 | 644 | ||
| 645 | 645 | try expectIdent(letElse.pattern.pattern, "y"); |
| 647 | 647 | let case ast::NodeValue::Return(_) = letElse.elseBranch.value |
|
| 648 | 648 | else throw testing::TestError::Failed; |
|
| 649 | 649 | } |
|
| 650 | 650 | ||
| 651 | 651 | /// Test parsing let-else with expression branch. |
|
| 652 | - | @test fn testParseLetElseExpr() throws (testing::TestError) { |
|
| 652 | + | @test unsafe fn testParseLetElseExpr() throws (testing::TestError) { |
|
| 653 | 653 | let root = try! parseStmtStr("let z = opt else y;"); |
|
| 654 | 654 | let case ast::NodeValue::LetElse(letElse) = root.value |
|
| 655 | 655 | else throw testing::TestError::Failed; |
|
| 656 | 656 | ||
| 657 | 657 | try expectIdent(letElse.pattern.pattern, "z"); |
|
| 658 | 658 | try expectIdent(letElse.elseBranch, "y"); |
|
| 659 | 659 | } |
|
| 660 | 660 | ||
| 661 | 661 | /// Test parsing let-case-else statement. |
|
| 662 | - | @test fn testParseLetCaseElse() throws (testing::TestError) { |
|
| 662 | + | @test unsafe fn testParseLetCaseElse() throws (testing::TestError) { |
|
| 663 | 663 | let root = try! parseStmtStr("let case Variant(x) = opt else { return };"); |
|
| 664 | 664 | let case ast::NodeValue::LetElse(letElse) = root.value |
|
| 665 | 665 | else throw testing::TestError::Failed; |
|
| 666 | 666 | ||
| 667 | 667 | try testing::expect(letElse.pattern.guard == nil); |
|
| 668 | 668 | } |
|
| 669 | 669 | ||
| 670 | 670 | /// Test parsing let-case-else with guard. |
|
| 671 | - | @test fn testParseLetCaseElseWithGuard() throws (testing::TestError) { |
|
| 671 | + | @test unsafe fn testParseLetCaseElseWithGuard() throws (testing::TestError) { |
|
| 672 | 672 | let root = try! parseStmtStr("let case Variant(x) = opt if x > 0 else { return };"); |
|
| 673 | 673 | let case ast::NodeValue::LetElse(letElse) = root.value |
|
| 674 | 674 | else throw testing::TestError::Failed; |
|
| 675 | 675 | ||
| 676 | 676 | try testing::expect(letElse.pattern.guard <> nil); |
| 680 | 680 | else throw testing::TestError::Failed; |
|
| 681 | 681 | try testing::expect(cmp.op == ast::BinaryOp::Gt); |
|
| 682 | 682 | } |
|
| 683 | 683 | ||
| 684 | 684 | /// Test parsing a `let mut` declaration. |
|
| 685 | - | @test fn testParseMut() throws (testing::TestError) { |
|
| 685 | + | @test unsafe fn testParseMut() throws (testing::TestError) { |
|
| 686 | 686 | let r1 = try! parseStmtStr("let mut x = 42;"); |
|
| 687 | 687 | let case ast::NodeValue::Let(n) = r1.value |
|
| 688 | 688 | else throw testing::TestError::Failed; |
|
| 689 | 689 | ||
| 690 | 690 | try expectIdent(n.ident, "x"); |
| 692 | 692 | try testing::expect(n.mutable); |
|
| 693 | 693 | try testing::expect(n.type == nil); |
|
| 694 | 694 | } |
|
| 695 | 695 | ||
| 696 | 696 | /// Test parsing a `let mut` declaration with type annotation. |
|
| 697 | - | @test fn testParseMutTyped() throws (testing::TestError) { |
|
| 697 | + | @test unsafe fn testParseMutTyped() throws (testing::TestError) { |
|
| 698 | 698 | let r1 = try! parseStmtStr("let mut x: i32 = 42;"); |
|
| 699 | 699 | let case ast::NodeValue::Let(n) = r1.value |
|
| 700 | 700 | else throw testing::TestError::Failed; |
|
| 701 | 701 | ||
| 702 | 702 | try expectIdent(n.ident, "x"); |
| 709 | 709 | width: 4, sign: ast::Signedness::Signed |
|
| 710 | 710 | }); |
|
| 711 | 711 | } |
|
| 712 | 712 | ||
| 713 | 713 | /// Test parsing a `constant` declaration. |
|
| 714 | - | @test fn testParseConst() throws (testing::TestError) { |
|
| 714 | + | @test unsafe fn testParseConst() throws (testing::TestError) { |
|
| 715 | 715 | let node = try! parseStmtStr("constant ANSWER: i32 = 42;"); |
|
| 716 | 716 | let case ast::NodeValue::ConstDecl(decl) = node.value |
|
| 717 | 717 | else throw testing::TestError::Failed; |
|
| 718 | 718 | ||
| 719 | 719 | try expectIdent(decl.ident, "ANSWER"); |
| 722 | 722 | }); |
|
| 723 | 723 | try expectNumber(decl.value, "42"); |
|
| 724 | 724 | } |
|
| 725 | 725 | ||
| 726 | 726 | /// Test parsing a `static` declaration. |
|
| 727 | - | @test fn testParseStatic() throws (testing::TestError) { |
|
| 727 | + | @test unsafe fn testParseStatic() throws (testing::TestError) { |
|
| 728 | 728 | let node = try! parseStmtStr("static COUNTER: i32 = 0;"); |
|
| 729 | 729 | let case ast::NodeValue::StaticDecl(decl) = node.value |
|
| 730 | 730 | else throw testing::TestError::Failed; |
|
| 731 | 731 | ||
| 732 | 732 | try expectIdent(decl.ident, "COUNTER"); |
| 735 | 735 | }); |
|
| 736 | 736 | try expectNumber(decl.value, "0"); |
|
| 737 | 737 | } |
|
| 738 | 738 | ||
| 739 | 739 | /// Test parsing a `use` declaration. |
|
| 740 | - | @test fn testParseUse() throws (testing::TestError) { |
|
| 740 | + | @test unsafe fn testParseUse() throws (testing::TestError) { |
|
| 741 | 741 | let node = try! parseStmtStr("use module::item;"); |
|
| 742 | 742 | let case ast::NodeValue::Use(decl) = node.value |
|
| 743 | 743 | else throw testing::TestError::Failed; |
|
| 744 | 744 | ||
| 745 | 745 | let case ast::NodeValue::ScopeAccess(scope) = decl.path.value |
| 748 | 748 | try expectIdent(scope.parent, "module"); |
|
| 749 | 749 | try expectIdent(scope.child, "item"); |
|
| 750 | 750 | } |
|
| 751 | 751 | ||
| 752 | 752 | /// Test parsing a `mod` declaration. |
|
| 753 | - | @test fn testParseMod() throws (testing::TestError) { |
|
| 753 | + | @test unsafe fn testParseMod() throws (testing::TestError) { |
|
| 754 | 754 | let node = try! parseStmtStr("mod io;"); |
|
| 755 | 755 | let case ast::NodeValue::Mod(decl) = node.value |
|
| 756 | 756 | else throw testing::TestError::Failed; |
|
| 757 | 757 | ||
| 758 | 758 | try expectIdent(decl.name, "io"); |
|
| 759 | 759 | try testing::expect(decl.attrs == nil); |
|
| 760 | 760 | } |
|
| 761 | 761 | ||
| 762 | 762 | /// Test parsing a module declaration with attributes. |
|
| 763 | - | @test fn testParseModAttributes() throws (testing::TestError) { |
|
| 763 | + | @test unsafe fn testParseModAttributes() throws (testing::TestError) { |
|
| 764 | 764 | let node = try! parseStmtStr("export mod io;"); |
|
| 765 | 765 | let case ast::NodeValue::Mod(decl) = node.value |
|
| 766 | 766 | else throw testing::TestError::Failed; |
|
| 767 | 767 | ||
| 768 | 768 | try expectIdent(decl.name, "io"); |
| 777 | 777 | try testing::expect(attr == ast::Attribute::Export); |
|
| 778 | 778 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Export)); |
|
| 779 | 779 | } |
|
| 780 | 780 | ||
| 781 | 781 | /// Test parsing an optional type. |
|
| 782 | - | @test fn testParseTypeOptional() throws (testing::TestError) { |
|
| 782 | + | @test unsafe fn testParseTypeOptional() throws (testing::TestError) { |
|
| 783 | 783 | let node = try! parseTypeStr("?i32"); |
|
| 784 | 784 | let case ast::NodeValue::TypeSig(optional) = node.value |
|
| 785 | 785 | else throw testing::TestError::Failed; |
|
| 786 | 786 | let case ast::TypeSig::Optional(opt) = optional |
|
| 787 | 787 | else throw testing::TestError::Failed; |
|
| 788 | 788 | ||
| 789 | 789 | try expectIntType(opt, 4, ast::Signedness::Signed); |
|
| 790 | 790 | } |
|
| 791 | 791 | ||
| 792 | 792 | /// Parse an `i32` pointer type and verify its class and mutability. |
|
| 793 | - | fn expectI32Pointer( |
|
| 793 | + | unsafe fn expectI32Pointer( |
|
| 794 | 794 | source: *[u8], |
|
| 795 | 795 | class: types::PointerClass, |
|
| 796 | 796 | mutable: bool, |
|
| 797 | 797 | ) throws (testing::TestError) { |
|
| 798 | 798 | let node = try! parseTypeStr(source); |
| 806 | 806 | try expectIntType(valueType, 4, ast::Signedness::Signed); |
|
| 807 | 807 | assert actualMutable == mutable; |
|
| 808 | 808 | } |
|
| 809 | 809 | ||
| 810 | 810 | /// Test parsing a mutable owned pointer. |
|
| 811 | - | @test fn testParseTypePointer() throws (testing::TestError) { |
|
| 811 | + | @test unsafe fn testParseTypePointer() throws (testing::TestError) { |
|
| 812 | 812 | try expectI32Pointer("*mut i32", types::PointerClass::Owned, true); |
|
| 813 | 813 | } |
|
| 814 | 814 | ||
| 815 | 815 | /// Test parsing an immutable owned pointer. |
|
| 816 | - | @test fn testParseTypePointerImmutable() throws (testing::TestError) { |
|
| 816 | + | @test unsafe fn testParseTypePointerImmutable() throws (testing::TestError) { |
|
| 817 | 817 | try expectI32Pointer("*i32", types::PointerClass::Owned, false); |
|
| 818 | 818 | } |
|
| 819 | 819 | ||
| 820 | 820 | /// Test parsing immutable and mutable references. |
|
| 821 | - | @test fn testParseTypeRef() throws (testing::TestError) { |
|
| 821 | + | @test unsafe fn testParseTypeRef() throws (testing::TestError) { |
|
| 822 | 822 | try expectI32Pointer("&i32", types::PointerClass::Ref, false); |
|
| 823 | 823 | try expectI32Pointer("&mut i32", types::PointerClass::Ref, true); |
|
| 824 | 824 | } |
|
| 825 | 825 | ||
| 826 | 826 | /// Test parsing immutable and mutable unsafe pointers. |
|
| 827 | - | @test fn testParseTypeUnsafePointer() throws (testing::TestError) { |
|
| 827 | + | @test unsafe fn testParseTypeUnsafePointer() throws (testing::TestError) { |
|
| 828 | 828 | try expectI32Pointer("*unsafe i32", types::PointerClass::Unsafe, false); |
|
| 829 | 829 | try expectI32Pointer("*unsafe mut i32", types::PointerClass::Unsafe, true); |
|
| 830 | 830 | } |
|
| 831 | 831 | ||
| 832 | 832 | /// Test parsing a slice type. |
|
| 833 | - | @test fn testParseTypeSlice() throws (testing::TestError) { |
|
| 833 | + | @test unsafe fn testParseTypeSlice() throws (testing::TestError) { |
|
| 834 | 834 | let node = try! parseTypeStr("*[u8]"); |
|
| 835 | 835 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 836 | 836 | else throw testing::TestError::Failed; |
|
| 837 | 837 | let case ast::TypeSig::Slice { class, itemType, mutable } = sig |
|
| 838 | 838 | else throw testing::TestError::Failed; |
| 841 | 841 | try expectIntType(itemType, 1, ast::Signedness::Unsigned); |
|
| 842 | 842 | assert not mutable; |
|
| 843 | 843 | } |
|
| 844 | 844 | ||
| 845 | 845 | /// Test parsing a mutable slice type. |
|
| 846 | - | @test fn testParseTypeSliceMutable() throws (testing::TestError) { |
|
| 846 | + | @test unsafe fn testParseTypeSliceMutable() throws (testing::TestError) { |
|
| 847 | 847 | let node = try! parseTypeStr("*mut [u8]"); |
|
| 848 | 848 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 849 | 849 | else throw testing::TestError::Failed; |
|
| 850 | 850 | let case ast::TypeSig::Slice { class, itemType, mutable } = sig |
|
| 851 | 851 | else throw testing::TestError::Failed; |
| 854 | 854 | try expectIntType(itemType, 1, ast::Signedness::Unsigned); |
|
| 855 | 855 | assert mutable; |
|
| 856 | 856 | } |
|
| 857 | 857 | ||
| 858 | 858 | /// Test parsing reference and unsafe slice classes. |
|
| 859 | - | @test fn testParseTypeSliceClasses() throws (testing::TestError) { |
|
| 859 | + | @test unsafe fn testParseTypeSliceClasses() throws (testing::TestError) { |
|
| 860 | 860 | let refNode = try! parseTypeStr("&[u8]"); |
|
| 861 | 861 | let case ast::NodeValue::TypeSig(ast::TypeSig::Slice { |
|
| 862 | 862 | class: refClass, .. |
|
| 863 | 863 | }) = refNode.value else throw testing::TestError::Failed; |
|
| 864 | 864 | assert refClass == types::PointerClass::Ref; |
| 869 | 869 | }) = unsafeNode.value else throw testing::TestError::Failed; |
|
| 870 | 870 | assert unsafeClass == types::PointerClass::Unsafe; |
|
| 871 | 871 | } |
|
| 872 | 872 | ||
| 873 | 873 | /// Test parsing trait object pointer classes. |
|
| 874 | - | @test fn testParseTypeTraitObjectClasses() throws (testing::TestError) { |
|
| 874 | + | @test unsafe fn testParseTypeTraitObjectClasses() throws (testing::TestError) { |
|
| 875 | 875 | let ownedNode = try! parseTypeStr("*opaque Read"); |
|
| 876 | 876 | let case ast::NodeValue::TypeSig(ast::TypeSig::TraitObject { |
|
| 877 | 877 | class: ownedClass, .. |
|
| 878 | 878 | }) = ownedNode.value else throw testing::TestError::Failed; |
|
| 879 | 879 | assert ownedClass == types::PointerClass::Owned; |
| 890 | 890 | }) = unsafeNode.value else throw testing::TestError::Failed; |
|
| 891 | 891 | assert unsafeClass == types::PointerClass::Unsafe; |
|
| 892 | 892 | } |
|
| 893 | 893 | ||
| 894 | 894 | /// Test parsing an array type. |
|
| 895 | - | @test fn testParseTypeArray() throws (testing::TestError) { |
|
| 895 | + | @test unsafe fn testParseTypeArray() throws (testing::TestError) { |
|
| 896 | 896 | let node = try! parseTypeStr("[i32; 4]"); |
|
| 897 | 897 | let case ast::NodeValue::TypeSig(sig) = node.value |
|
| 898 | 898 | else throw testing::TestError::Failed; |
|
| 899 | 899 | let case ast::TypeSig::Array { itemType, length } = sig |
|
| 900 | 900 | else throw testing::TestError::Failed; |
| 902 | 902 | try expectIntType(itemType, 4, ast::Signedness::Signed); |
|
| 903 | 903 | try expectNumber(length, "4"); |
|
| 904 | 904 | } |
|
| 905 | 905 | ||
| 906 | 906 | /// Test parsing a named record declaration without derives. |
|
| 907 | - | @test fn testParseRecordDecl() throws (testing::TestError) { |
|
| 907 | + | @test unsafe fn testParseRecordDecl() throws (testing::TestError) { |
|
| 908 | 908 | let node = try! parseStmtStr("record R { x: bool, y: i32 }"); |
|
| 909 | 909 | let case ast::NodeValue::RecordDecl(decl) = node.value |
|
| 910 | 910 | else throw testing::TestError::Failed; |
|
| 911 | 911 | ||
| 912 | 912 | try expectIdent(decl.name, "R"); |
| 919 | 919 | sign: ast::Signedness::Signed, |
|
| 920 | 920 | }); |
|
| 921 | 921 | } |
|
| 922 | 922 | ||
| 923 | 923 | /// Test parsing a record declaration with derives. |
|
| 924 | - | @test fn testParseRecordDeclDerives() throws (testing::TestError) { |
|
| 924 | + | @test unsafe fn testParseRecordDeclDerives() throws (testing::TestError) { |
|
| 925 | 925 | let node = try! parseStmtStr("record R: Eq + Debug { field: i32 }"); |
|
| 926 | 926 | let case ast::NodeValue::RecordDecl(decl) = node.value |
|
| 927 | 927 | else throw testing::TestError::Failed; |
|
| 928 | 928 | ||
| 929 | 929 | try expectIdent(decl.name, "R"); |
| 931 | 931 | try expectIdent(decl.derives[0], "Eq"); |
|
| 932 | 932 | try expectIdent(decl.derives[1], "Debug"); |
|
| 933 | 933 | } |
|
| 934 | 934 | ||
| 935 | 935 | /// Test parsing a record declaration with field initializers. |
|
| 936 | - | @test fn testParseRecordDeclFieldDefaults() throws (testing::TestError) { |
|
| 936 | + | @test unsafe fn testParseRecordDeclFieldDefaults() throws (testing::TestError) { |
|
| 937 | 937 | let node = try! parseStmtStr("record Config { size: opaque = 42, flag: bool = true }"); |
|
| 938 | 938 | let case ast::NodeValue::RecordDecl(decl) = node.value |
|
| 939 | 939 | else throw testing::TestError::Failed; |
|
| 940 | 940 | ||
| 941 | 941 | try expectIdent(decl.name, "Config"); |
| 958 | 958 | else throw testing::TestError::Failed; |
|
| 959 | 959 | try testing::expect(flagValue); |
|
| 960 | 960 | } |
|
| 961 | 961 | ||
| 962 | 962 | /// Test parsing an unlabeled record declaration. |
|
| 963 | - | @test fn testParseTupleRecordDecl() throws (testing::TestError) { |
|
| 963 | + | @test unsafe fn testParseTupleRecordDecl() throws (testing::TestError) { |
|
| 964 | 964 | let node = try! parseStmtStr("record Pair(bool, i32);"); |
|
| 965 | 965 | let case ast::NodeValue::RecordDecl(decl) = node.value |
|
| 966 | 966 | else throw testing::TestError::Failed; |
|
| 967 | 967 | ||
| 968 | 968 | try expectIdent(decl.name, "Pair"); |
| 976 | 976 | sign: ast::Signedness::Signed, |
|
| 977 | 977 | }); |
|
| 978 | 978 | } |
|
| 979 | 979 | ||
| 980 | 980 | /// Test parsing a single-field unlabeled record. |
|
| 981 | - | @test fn testParseTupleRecordSingleField() throws (testing::TestError) { |
|
| 981 | + | @test unsafe fn testParseTupleRecordSingleField() throws (testing::TestError) { |
|
| 982 | 982 | let node = try! parseStmtStr("record R(bool);"); |
|
| 983 | 983 | let case ast::NodeValue::RecordDecl(decl) = node.value |
|
| 984 | 984 | else throw testing::TestError::Failed; |
|
| 985 | 985 | ||
| 986 | 986 | try expectIdent(decl.name, "R"); |
| 989 | 989 | ||
| 990 | 990 | try expectFieldSig(decl.fields, 0, nil, ast::TypeSig::Bool); |
|
| 991 | 991 | } |
|
| 992 | 992 | ||
| 993 | 993 | /// Test parsing empty record literals. |
|
| 994 | - | @test fn testParseEmptyRecordLiteral() throws (testing::TestError) { |
|
| 994 | + | @test unsafe fn testParseEmptyRecordLiteral() throws (testing::TestError) { |
|
| 995 | 995 | let r1 = try! parseExprStr("{}"); |
|
| 996 | 996 | let case ast::NodeValue::RecordLit(lit) = r1.value |
|
| 997 | 997 | else throw testing::TestError::Failed; |
|
| 998 | 998 | ||
| 999 | 999 | try testing::expect(lit.typeName == nil); |
| 1007 | 1007 | try expectIdent(typeName, "Point"); |
|
| 1008 | 1008 | try testing::expect(lit2.fields.len == 0); |
|
| 1009 | 1009 | } |
|
| 1010 | 1010 | ||
| 1011 | 1011 | /// Test parsing a function type with parameters and return type. |
|
| 1012 | - | @test fn testParseTypeFn() throws (testing::TestError) { |
|
| 1012 | + | @test unsafe fn testParseTypeFn() throws (testing::TestError) { |
|
| 1013 | 1013 | let node = try! parseTypeStr("fn (i32, *u8) -> bool"); |
|
| 1014 | 1014 | let case ast::NodeValue::TypeSig(sigValue) = node.value |
|
| 1015 | 1015 | else throw testing::TestError::Failed; |
|
| 1016 | - | let case ast::TypeSig::Fn(sig) = sigValue |
|
| 1016 | + | let case ast::TypeSig::Fn { sig, .. } = sigValue |
|
| 1017 | 1017 | else throw testing::TestError::Failed; |
|
| 1018 | 1018 | ||
| 1019 | 1019 | try testing::expect(sig.params.len == 2); |
|
| 1020 | 1020 | ||
| 1021 | 1021 | let param0 = sig.params[0]; |
| 1030 | 1030 | ||
| 1031 | 1031 | try testing::expect(sig.returnType <> nil); |
|
| 1032 | 1032 | } |
|
| 1033 | 1033 | ||
| 1034 | 1034 | /// Test parsing a function type with a throws clause. |
|
| 1035 | - | @test fn testParseTypeFnThrows() throws (testing::TestError) { |
|
| 1035 | + | @test unsafe fn testParseTypeFnThrows() throws (testing::TestError) { |
|
| 1036 | 1036 | let node = try! parseTypeStr("fn (i32) -> bool throws (Error, Other)"); |
|
| 1037 | 1037 | let case ast::NodeValue::TypeSig(sigValue) = node.value |
|
| 1038 | 1038 | else throw testing::TestError::Failed; |
|
| 1039 | - | let case ast::TypeSig::Fn(sig) = sigValue |
|
| 1039 | + | let case ast::TypeSig::Fn { sig, .. } = sigValue |
|
| 1040 | 1040 | else throw testing::TestError::Failed; |
|
| 1041 | 1041 | ||
| 1042 | 1042 | try testing::expect(sig.params.len == 1); |
|
| 1043 | 1043 | try expectIntType(sig.params[0], 4, ast::Signedness::Signed); |
|
| 1044 | 1044 |
| 1050 | 1050 | else throw testing::TestError::Failed; |
|
| 1051 | 1051 | try expectType(returnType, ast::TypeSig::Bool); |
|
| 1052 | 1052 | } |
|
| 1053 | 1053 | ||
| 1054 | 1054 | /// Test parsing a function declaration without parameters. |
|
| 1055 | - | @test fn testParseFnDeclEmpty() throws (testing::TestError) { |
|
| 1055 | + | @test unsafe fn testParseFnDeclEmpty() throws (testing::TestError) { |
|
| 1056 | 1056 | let node = try! parseStmtStr("fn main() {}"); |
|
| 1057 | 1057 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1058 | 1058 | else throw testing::TestError::Failed; |
|
| 1059 | 1059 | ||
| 1060 | 1060 | try expectIdent(decl.name, "main"); |
| 1066 | 1066 | let case ast::NodeValue::Block(_) = body.value |
|
| 1067 | 1067 | else throw testing::TestError::Failed; |
|
| 1068 | 1068 | } |
|
| 1069 | 1069 | ||
| 1070 | 1070 | /// Test parsing a function declaration with parameters and return type. |
|
| 1071 | - | @test fn testParseFnDeclParams() throws (testing::TestError) { |
|
| 1071 | + | @test unsafe fn testParseFnDeclParams() throws (testing::TestError) { |
|
| 1072 | 1072 | let node = try! parseStmtStr("fn add(x: i32, y: *u8) -> bool {}"); |
|
| 1073 | 1073 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1074 | 1074 | else throw testing::TestError::Failed; |
|
| 1075 | 1075 | ||
| 1076 | 1076 | try expectIdent(decl.name, "add"); |
| 1104 | 1104 | else throw testing::TestError::Failed; |
|
| 1105 | 1105 | } |
|
| 1106 | 1106 | } |
|
| 1107 | 1107 | ||
| 1108 | 1108 | /// Test parsing a function declaration with a throws clause. |
|
| 1109 | - | @test fn testParseFnDeclThrows() throws (testing::TestError) { |
|
| 1109 | + | @test unsafe fn testParseFnDeclThrows() throws (testing::TestError) { |
|
| 1110 | 1110 | let node = try! parseStmtStr("fn handle() throws (Error, Crash) {}"); |
|
| 1111 | 1111 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1112 | 1112 | else throw testing::TestError::Failed; |
|
| 1113 | 1113 | ||
| 1114 | 1114 | try expectIdent(decl.name, "handle"); |
| 1122 | 1122 | let case ast::NodeValue::Block(_) = body.value |
|
| 1123 | 1123 | else throw testing::TestError::Failed; |
|
| 1124 | 1124 | } |
|
| 1125 | 1125 | ||
| 1126 | 1126 | /// Test scanning source-level `void` produces an identifier, not a type keyword. |
|
| 1127 | - | @test fn testParseTypeVoidRejected() throws (testing::TestError) { |
|
| 1127 | + | @test unsafe fn testParseTypeVoidRejected() throws (testing::TestError) { |
|
| 1128 | 1128 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 1129 | 1129 | let mut parser = super::mkParser(scanner::SourceLoc::String, "void", &mut arena, &mut STRING_POOL); |
|
| 1130 | 1130 | super::advance(&mut parser); |
|
| 1131 | 1131 | try testing::expect(super::check(&parser, scanner::TokenKind::Ident)); |
|
| 1132 | 1132 | } |
|
| 1133 | 1133 | ||
| 1134 | 1134 | /// Test parsing the unsafe function modifier. |
|
| 1135 | - | @test fn testParseUnsafeFnDecl() throws (testing::TestError) { |
|
| 1135 | + | @test unsafe fn testParseUnsafeFnDecl() throws (testing::TestError) { |
|
| 1136 | 1136 | let node = try! parseStmtStr("unsafe fn run() {}"); |
|
| 1137 | 1137 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1138 | 1138 | else throw testing::TestError::Failed; |
|
| 1139 | 1139 | let attrs = decl.attrs |
|
| 1140 | 1140 | else throw testing::TestError::Failed; |
| 1142 | 1142 | try testing::expect(attrs.list.len == 1); |
|
| 1143 | 1143 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Unsafe)); |
|
| 1144 | 1144 | } |
|
| 1145 | 1145 | ||
| 1146 | 1146 | /// Test rejecting `unsafe` on declarations where it has no semantics. |
|
| 1147 | - | @test fn testParseUnsafeUnsupportedDecl() throws (testing::TestError) { |
|
| 1147 | + | @test unsafe fn testParseUnsafeUnsupportedDecl() throws (testing::TestError) { |
|
| 1148 | 1148 | let recordDecl: ?*ast::Node = try? parseStmtStr("unsafe record R {}"); |
|
| 1149 | 1149 | try testing::expect(recordDecl == nil); |
|
| 1150 | 1150 | let constDecl: ?*ast::Node = try? parseStmtStr("unsafe constant X = 1;"); |
|
| 1151 | 1151 | try testing::expect(constDecl == nil); |
|
| 1152 | 1152 | } |
|
| 1153 | 1153 | ||
| 1154 | - | /// Test `unsafe` on the other declaration forms that support it. |
|
| 1155 | - | @test fn testParseUnsafeMethodAndModule() throws (testing::TestError) { |
|
| 1156 | - | let moduleNode = try! parseStmtStr("unsafe mod io;"); |
|
| 1157 | - | let case ast::NodeValue::Mod(moduleDecl) = moduleNode.value |
|
| 1158 | - | else throw testing::TestError::Failed; |
|
| 1159 | - | let moduleAttrs = moduleDecl.attrs else throw testing::TestError::Failed; |
|
| 1160 | - | assert ast::attributesContains(&moduleAttrs, ast::Attribute::Unsafe); |
|
| 1161 | - | ||
| 1154 | + | /// Test unsafe method declarations. |
|
| 1155 | + | @test unsafe fn testParseUnsafeMethods() throws (testing::TestError) { |
|
| 1162 | 1156 | let instanceNode = try! parseStmtStr( |
|
| 1163 | 1157 | "instance Read for Value { unsafe fn (value: &Value) get() {} }" |
|
| 1164 | 1158 | ); |
|
| 1165 | 1159 | let case ast::NodeValue::InstanceDecl { methods, .. } = instanceNode.value |
|
| 1166 | 1160 | else throw testing::TestError::Failed; |
| 1168 | 1162 | let case ast::NodeValue::MethodDecl { attrs, .. } = methods[0].value |
|
| 1169 | 1163 | else throw testing::TestError::Failed; |
|
| 1170 | 1164 | let methodAttrs = attrs else throw testing::TestError::Failed; |
|
| 1171 | 1165 | assert ast::attributesContains(&methodAttrs, ast::Attribute::Unsafe); |
|
| 1172 | 1166 | ||
| 1173 | - | let mut printStorage: [u8; 4096] = undefined; |
|
| 1167 | + | static printStorage: [u8; 4096] = undefined; |
|
| 1174 | 1168 | let mut printArena = alloc::new(&mut printStorage[..]); |
|
| 1175 | 1169 | let methodExpr = printer::toExpr(&mut printArena, methods[0]); |
|
| 1176 | 1170 | let case sexpr::Expr::Block { items: methodItems, .. } = methodExpr |
|
| 1177 | 1171 | else throw testing::TestError::Failed; |
|
| 1178 | 1172 | let case sexpr::Expr::List { |
| 1205 | 1199 | else throw testing::TestError::Failed; |
|
| 1206 | 1200 | assert mem::eq(traitAttr, "@unsafe"); |
|
| 1207 | 1201 | } |
|
| 1208 | 1202 | ||
| 1209 | 1203 | /// Test parsing a function declaration with attributes. |
|
| 1210 | - | @test fn testParseFnDeclAttributes() throws (testing::TestError) { |
|
| 1204 | + | @test unsafe fn testParseFnDeclAttributes() throws (testing::TestError) { |
|
| 1211 | 1205 | let node = try! parseStmtStr("export fn run();"); |
|
| 1212 | 1206 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1213 | 1207 | else throw testing::TestError::Failed; |
|
| 1214 | 1208 | ||
| 1215 | 1209 | try expectIdent(decl.name, "run"); |
| 1231 | 1225 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern)); |
|
| 1232 | 1226 | try testing::expect(decl.body == nil); |
|
| 1233 | 1227 | } |
|
| 1234 | 1228 | ||
| 1235 | 1229 | /// Test parsing a top-level function declaration with inferred extern from `;`. |
|
| 1236 | - | @test fn testParseFnDeclInferredExtern() throws (testing::TestError) { |
|
| 1230 | + | @test unsafe fn testParseFnDeclInferredExtern() throws (testing::TestError) { |
|
| 1237 | 1231 | let node = try! parseStmtStr("fn run();"); |
|
| 1238 | 1232 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1239 | 1233 | else throw testing::TestError::Failed; |
|
| 1240 | 1234 | ||
| 1241 | 1235 | try expectIdent(decl.name, "run"); |
| 1252 | 1246 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern)); |
|
| 1253 | 1247 | try testing::expect(decl.body == nil); |
|
| 1254 | 1248 | } |
|
| 1255 | 1249 | ||
| 1256 | 1250 | /// Test parsing a top-level exported function declaration with inferred extern from `;`. |
|
| 1257 | - | @test fn testParseFnDeclExportInferredExtern() throws (testing::TestError) { |
|
| 1251 | + | @test unsafe fn testParseFnDeclExportInferredExtern() throws (testing::TestError) { |
|
| 1258 | 1252 | let node = try! parseStmtStr("export fn run();"); |
|
| 1259 | 1253 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1260 | 1254 | else throw testing::TestError::Failed; |
|
| 1261 | 1255 | ||
| 1262 | 1256 | try expectIdent(decl.name, "run"); |
| 1278 | 1272 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern)); |
|
| 1279 | 1273 | try testing::expect(decl.body == nil); |
|
| 1280 | 1274 | } |
|
| 1281 | 1275 | ||
| 1282 | 1276 | /// Test parsing a scoped identifier type. |
|
| 1283 | - | @test fn testParseTypeScopedIdent() throws (testing::TestError) { |
|
| 1277 | + | @test unsafe fn testParseTypeScopedIdent() throws (testing::TestError) { |
|
| 1284 | 1278 | let node = try! parseTypeStr("module::Type"); |
|
| 1285 | 1279 | let case ast::NodeValue::TypeSig(ts) = node.value |
|
| 1286 | 1280 | else throw testing::TestError::Failed; |
|
| 1287 | 1281 | let case ast::TypeSig::Nominal(name) = ts |
|
| 1288 | 1282 | else throw testing::TestError::Failed; |
| 1292 | 1286 | try expectIdent(access.parent, "module"); |
|
| 1293 | 1287 | try expectIdent(access.child, "Type"); |
|
| 1294 | 1288 | } |
|
| 1295 | 1289 | ||
| 1296 | 1290 | /// Test parsing the `bool` type. |
|
| 1297 | - | @test fn testParseTypeBool() throws (testing::TestError) { |
|
| 1291 | + | @test unsafe fn testParseTypeBool() throws (testing::TestError) { |
|
| 1298 | 1292 | let node = try! parseTypeStr("bool"); |
|
| 1299 | 1293 | try expectType(node, ast::TypeSig::Bool); |
|
| 1300 | 1294 | } |
|
| 1301 | 1295 | ||
| 1302 | 1296 | /// Test parsing an unsigned integer type. |
|
| 1303 | - | @test fn testParseTypeUnsigned() throws (testing::TestError) { |
|
| 1297 | + | @test unsafe fn testParseTypeUnsigned() throws (testing::TestError) { |
|
| 1304 | 1298 | let node = try! parseTypeStr("u8"); |
|
| 1305 | 1299 | try expectType(node, ast::TypeSig::Integer { |
|
| 1306 | 1300 | width: 1, |
|
| 1307 | 1301 | sign: ast::Signedness::Unsigned, |
|
| 1308 | 1302 | }); |
|
| 1309 | 1303 | } |
|
| 1310 | 1304 | ||
| 1311 | 1305 | /// Test parsing a simple `if let` statement without guard or else. |
|
| 1312 | - | @test fn testParseIfLet() throws (testing::TestError) { |
|
| 1306 | + | @test unsafe fn testParseIfLet() throws (testing::TestError) { |
|
| 1313 | 1307 | let root = try! parseStmtStr("if let value = opt { body; }"); |
|
| 1314 | 1308 | let case ast::NodeValue::IfLet(node) = root.value |
|
| 1315 | 1309 | else throw testing::TestError::Failed; |
|
| 1316 | 1310 | ||
| 1317 | 1311 | try expectIdent(node.pattern.pattern, "value"); |
| 1321 | 1315 | try expectBlockExprStmt(node.thenBranch, ast::NodeValue::Ident("body")); |
|
| 1322 | 1316 | try testing::expect(node.elseBranch == nil); |
|
| 1323 | 1317 | } |
|
| 1324 | 1318 | ||
| 1325 | 1319 | /// Test parsing an `if let` statement with guard and else branches. |
|
| 1326 | - | @test fn testParseIfLetGuardElse() throws (testing::TestError) { |
|
| 1320 | + | @test unsafe fn testParseIfLetGuardElse() throws (testing::TestError) { |
|
| 1327 | 1321 | let root = try! parseStmtStr( |
|
| 1328 | 1322 | "if let value = opt; guard { body; } else { alt; }" |
|
| 1329 | 1323 | ); |
|
| 1330 | 1324 | let case ast::NodeValue::IfLet(node) = root.value |
|
| 1331 | 1325 | else throw testing::TestError::Failed; |
| 1343 | 1337 | else throw testing::TestError::Failed; |
|
| 1344 | 1338 | try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt")); |
|
| 1345 | 1339 | } |
|
| 1346 | 1340 | ||
| 1347 | 1341 | /// Test parsing an `if let` statement with an `else if` chain. |
|
| 1348 | - | @test fn testParseIfLetElseIf() throws (testing::TestError) { |
|
| 1342 | + | @test unsafe fn testParseIfLetElseIf() throws (testing::TestError) { |
|
| 1349 | 1343 | let root = try! parseStmtStr( |
|
| 1350 | 1344 | "if let value = opt { body; } else if cond { alt; }" |
|
| 1351 | 1345 | ); |
|
| 1352 | 1346 | let case ast::NodeValue::IfLet(node) = root.value |
|
| 1353 | 1347 | else throw testing::TestError::Failed; |
| 1365 | 1359 | try expectBlockExprStmt(inner.thenBranch, ast::NodeValue::Ident("alt")); |
|
| 1366 | 1360 | try testing::expect(inner.elseBranch == nil); |
|
| 1367 | 1361 | } |
|
| 1368 | 1362 | ||
| 1369 | 1363 | /// Test parsing `if let mut` binding. |
|
| 1370 | - | @test fn testParseIfLetMut() throws (testing::TestError) { |
|
| 1364 | + | @test unsafe fn testParseIfLetMut() throws (testing::TestError) { |
|
| 1371 | 1365 | let root = try! parseStmtStr("if let mut value = opt { body; }"); |
|
| 1372 | 1366 | let case ast::NodeValue::IfLet(node) = root.value |
|
| 1373 | 1367 | else throw testing::TestError::Failed; |
|
| 1374 | 1368 | ||
| 1375 | 1369 | try expectIdent(node.pattern.pattern, "value"); |
| 1379 | 1373 | try expectBlockExprStmt(node.thenBranch, ast::NodeValue::Ident("body")); |
|
| 1380 | 1374 | try testing::expect(node.elseBranch == nil); |
|
| 1381 | 1375 | } |
|
| 1382 | 1376 | ||
| 1383 | 1377 | /// Test parsing `let mut ... else` binding. |
|
| 1384 | - | @test fn testParseLetMutElse() throws (testing::TestError) { |
|
| 1378 | + | @test unsafe fn testParseLetMutElse() throws (testing::TestError) { |
|
| 1385 | 1379 | let root = try! parseStmtStr("let mut x = opt else { return };"); |
|
| 1386 | 1380 | let case ast::NodeValue::LetElse(letElse) = root.value |
|
| 1387 | 1381 | else throw testing::TestError::Failed; |
|
| 1388 | 1382 | ||
| 1389 | 1383 | try expectIdent(letElse.pattern.pattern, "x"); |
|
| 1390 | 1384 | try testing::expect(letElse.pattern.mutable); |
|
| 1391 | 1385 | } |
|
| 1392 | 1386 | ||
| 1393 | 1387 | /// Test that `if let` without `mut` is not mutable. |
|
| 1394 | - | @test fn testParseIfLetNotMutable() throws (testing::TestError) { |
|
| 1388 | + | @test unsafe fn testParseIfLetNotMutable() throws (testing::TestError) { |
|
| 1395 | 1389 | let root = try! parseStmtStr("if let value = opt { body; }"); |
|
| 1396 | 1390 | let case ast::NodeValue::IfLet(node) = root.value |
|
| 1397 | 1391 | else throw testing::TestError::Failed; |
|
| 1398 | 1392 | ||
| 1399 | 1393 | try testing::expect(not node.pattern.mutable); |
|
| 1400 | 1394 | } |
|
| 1401 | 1395 | ||
| 1402 | 1396 | /// Test that `let ... else` without `mut` is not mutable. |
|
| 1403 | - | @test fn testParseLetElseNotMutable() throws (testing::TestError) { |
|
| 1397 | + | @test unsafe fn testParseLetElseNotMutable() throws (testing::TestError) { |
|
| 1404 | 1398 | let root = try! parseStmtStr("let x = opt else { return };"); |
|
| 1405 | 1399 | let case ast::NodeValue::LetElse(letElse) = root.value |
|
| 1406 | 1400 | else throw testing::TestError::Failed; |
|
| 1407 | 1401 | ||
| 1408 | 1402 | try testing::expect(not letElse.pattern.mutable); |
|
| 1409 | 1403 | } |
|
| 1410 | 1404 | ||
| 1411 | 1405 | /// Test parsing a simple `if let case` statement. |
|
| 1412 | - | @test fn testParseIfCase() throws (testing::TestError) { |
|
| 1406 | + | @test unsafe fn testParseIfCase() throws (testing::TestError) { |
|
| 1413 | 1407 | let root = try! parseStmtStr("if let case pat = value { body; }"); |
|
| 1414 | 1408 | let case ast::NodeValue::IfLet(node) = root.value |
|
| 1415 | 1409 | else throw testing::TestError::Failed; |
|
| 1416 | 1410 | ||
| 1417 | 1411 | try expectIdent(node.pattern.pattern, "pat"); |
| 1420 | 1414 | try expectBlockExprStmt(node.thenBranch, ast::NodeValue::Ident("body")); |
|
| 1421 | 1415 | try testing::expect(node.elseBranch == nil); |
|
| 1422 | 1416 | } |
|
| 1423 | 1417 | ||
| 1424 | 1418 | /// Test parsing an `if let case` statement with guard and else branches. |
|
| 1425 | - | @test fn testParseIfCaseGuardElse() throws (testing::TestError) { |
|
| 1419 | + | @test unsafe fn testParseIfCaseGuardElse() throws (testing::TestError) { |
|
| 1426 | 1420 | let root = try! parseStmtStr( |
|
| 1427 | 1421 | "if let case pat = value; guard { body; } else { alt; }" |
|
| 1428 | 1422 | ); |
|
| 1429 | 1423 | let case ast::NodeValue::IfLet(node) = root.value |
|
| 1430 | 1424 | else throw testing::TestError::Failed; |
| 1442 | 1436 | else throw testing::TestError::Failed; |
|
| 1443 | 1437 | try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt")); |
|
| 1444 | 1438 | } |
|
| 1445 | 1439 | ||
| 1446 | 1440 | /// Test parsing an `if let case` statement with an `else if` chain. |
|
| 1447 | - | @test fn testParseIfCaseElseIf() throws (testing::TestError) { |
|
| 1441 | + | @test unsafe fn testParseIfCaseElseIf() throws (testing::TestError) { |
|
| 1448 | 1442 | let root = try! parseStmtStr( |
|
| 1449 | 1443 | "if let case pat = value { body; } else if cond { alt; }" |
|
| 1450 | 1444 | ); |
|
| 1451 | 1445 | let case ast::NodeValue::IfLet(node) = root.value |
|
| 1452 | 1446 | else throw testing::TestError::Failed; |
| 1462 | 1456 | try expectBlockExprStmt(inner.thenBranch, ast::NodeValue::Ident("alt")); |
|
| 1463 | 1457 | try testing::expect(inner.elseBranch == nil); |
|
| 1464 | 1458 | } |
|
| 1465 | 1459 | ||
| 1466 | 1460 | /// Test parsing a simple `while` loop without an `else` branch. |
|
| 1467 | - | @test fn testParseWhile() throws (testing::TestError) { |
|
| 1461 | + | @test unsafe fn testParseWhile() throws (testing::TestError) { |
|
| 1468 | 1462 | let root = try! parseStmtStr("while cond { body; }"); |
|
| 1469 | 1463 | let case ast::NodeValue::While(loopNode) = root.value |
|
| 1470 | 1464 | else throw testing::TestError::Failed; |
|
| 1471 | 1465 | ||
| 1472 | 1466 | try expectIdent(loopNode.condition, "cond"); |
|
| 1473 | 1467 | try expectBlockExprStmt(loopNode.body, ast::NodeValue::Ident("body")); |
|
| 1474 | 1468 | try testing::expect(loopNode.elseBranch == nil); |
|
| 1475 | 1469 | } |
|
| 1476 | 1470 | ||
| 1477 | 1471 | /// Test parsing a `while` loop with an `else` branch. |
|
| 1478 | - | @test fn testParseWhileElse() throws (testing::TestError) { |
|
| 1472 | + | @test unsafe fn testParseWhileElse() throws (testing::TestError) { |
|
| 1479 | 1473 | let root = try! parseStmtStr("while cond { body; } else { alt; }"); |
|
| 1480 | 1474 | let case ast::NodeValue::While(loopNode) = root.value |
|
| 1481 | 1475 | else throw testing::TestError::Failed; |
|
| 1482 | 1476 | ||
| 1483 | 1477 | try expectIdent(loopNode.condition, "cond"); |
| 1487 | 1481 | else throw testing::TestError::Failed; |
|
| 1488 | 1482 | try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt")); |
|
| 1489 | 1483 | } |
|
| 1490 | 1484 | ||
| 1491 | 1485 | /// Test parsing a simple `while let case` loop. |
|
| 1492 | - | @test fn testParseWhileCase() throws (testing::TestError) { |
|
| 1486 | + | @test unsafe fn testParseWhileCase() throws (testing::TestError) { |
|
| 1493 | 1487 | let root = try! parseStmtStr("while let case pat = value { body; }"); |
|
| 1494 | 1488 | let case ast::NodeValue::WhileLet(node) = root.value |
|
| 1495 | 1489 | else throw testing::TestError::Failed; |
|
| 1496 | 1490 | ||
| 1497 | 1491 | try expectIdent(node.pattern.pattern, "pat"); |
| 1500 | 1494 | try expectBlockExprStmt(node.body, ast::NodeValue::Ident("body")); |
|
| 1501 | 1495 | try testing::expect(node.elseBranch == nil); |
|
| 1502 | 1496 | } |
|
| 1503 | 1497 | ||
| 1504 | 1498 | /// Test parsing a `while let case` loop with guard and else branches. |
|
| 1505 | - | @test fn testParseWhileCaseGuardElse() throws (testing::TestError) { |
|
| 1499 | + | @test unsafe fn testParseWhileCaseGuardElse() throws (testing::TestError) { |
|
| 1506 | 1500 | let root = try! parseStmtStr( |
|
| 1507 | 1501 | "while let case pat = value; guard { body; } else { alt; }" |
|
| 1508 | 1502 | ); |
|
| 1509 | 1503 | let case ast::NodeValue::WhileLet(node) = root.value |
|
| 1510 | 1504 | else throw testing::TestError::Failed; |
| 1519 | 1513 | else throw testing::TestError::Failed; |
|
| 1520 | 1514 | try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt")); |
|
| 1521 | 1515 | } |
|
| 1522 | 1516 | ||
| 1523 | 1517 | /// Test parsing a simple `try` expression without catch. |
|
| 1524 | - | @test fn testParseTry() throws (testing::TestError) { |
|
| 1518 | + | @test unsafe fn testParseTry() throws (testing::TestError) { |
|
| 1525 | 1519 | let root = try! parseExprStr("try value"); |
|
| 1526 | 1520 | let case ast::NodeValue::Try(node) = root.value |
|
| 1527 | 1521 | else throw testing::TestError::Failed; |
|
| 1528 | 1522 | ||
| 1529 | 1523 | try expectIdent(node.expr, "value"); |
|
| 1530 | 1524 | try testing::expect(node.catches.len == 0); |
|
| 1531 | 1525 | try testing::expect(not node.shouldPanic); |
|
| 1532 | 1526 | } |
|
| 1533 | 1527 | ||
| 1534 | 1528 | /// Test that `try?` consumes a unary operand. |
|
| 1535 | - | @test fn testParseTryOptionalUnary() throws (testing::TestError) { |
|
| 1529 | + | @test unsafe fn testParseTryOptionalUnary() throws (testing::TestError) { |
|
| 1536 | 1530 | let root = try! parseExprStr("try? -value"); |
|
| 1537 | 1531 | let case ast::NodeValue::Try(node) = root.value |
|
| 1538 | 1532 | else throw testing::TestError::Failed; |
|
| 1539 | 1533 | let case ast::NodeValue::UnOp(neg) = node.expr.value |
|
| 1540 | 1534 | else throw testing::TestError::Failed; |
| 1542 | 1536 | try expectIdent(neg.value, "value"); |
|
| 1543 | 1537 | try testing::expect(node.returnsOptional); |
|
| 1544 | 1538 | } |
|
| 1545 | 1539 | ||
| 1546 | 1540 | /// Test parsing a `try!` expression that panics on error. |
|
| 1547 | - | @test fn testParseTryBang() throws (testing::TestError) { |
|
| 1541 | + | @test unsafe fn testParseTryBang() throws (testing::TestError) { |
|
| 1548 | 1542 | let root = try! parseExprStr("try! value"); |
|
| 1549 | 1543 | let case ast::NodeValue::Try(node) = root.value |
|
| 1550 | 1544 | else throw testing::TestError::Failed; |
|
| 1551 | 1545 | ||
| 1552 | 1546 | try expectIdent(node.expr, "value"); |
|
| 1553 | 1547 | try testing::expect(node.catches.len == 0); |
|
| 1554 | 1548 | try testing::expect(node.shouldPanic); |
|
| 1555 | 1549 | } |
|
| 1556 | 1550 | ||
| 1557 | 1551 | /// Test parsing a `try` expression with a `catch` block. |
|
| 1558 | - | @test fn testParseTryCatchBlock() throws (testing::TestError) { |
|
| 1552 | + | @test unsafe fn testParseTryCatchBlock() throws (testing::TestError) { |
|
| 1559 | 1553 | let root = try! parseExprStr("try value catch { alt; }"); |
|
| 1560 | 1554 | let case ast::NodeValue::Try(node) = root.value |
|
| 1561 | 1555 | else throw testing::TestError::Failed; |
|
| 1562 | 1556 | ||
| 1563 | 1557 | try expectIdent(node.expr, "value"); |
| 1569 | 1563 | try testing::expect(clause.typeNode == nil); |
|
| 1570 | 1564 | try expectBlockExprStmt(clause.body, ast::NodeValue::Ident("alt")); |
|
| 1571 | 1565 | } |
|
| 1572 | 1566 | ||
| 1573 | 1567 | /// Test that `catch` without a block is rejected. |
|
| 1574 | - | @test fn testParseTryCatchExprRejected() throws (testing::TestError) { |
|
| 1568 | + | @test unsafe fn testParseTryCatchExprRejected() throws (testing::TestError) { |
|
| 1575 | 1569 | let parsed: ?*ast::Node = try? parseExprStr("try value catch alternate"); |
|
| 1576 | 1570 | try testing::expect(parsed == nil); |
|
| 1577 | 1571 | } |
|
| 1578 | 1572 | ||
| 1579 | 1573 | /// Test parsing a `break` statement. |
|
| 1580 | - | @test fn testParseBreak() throws (testing::TestError) { |
|
| 1574 | + | @test unsafe fn testParseBreak() throws (testing::TestError) { |
|
| 1581 | 1575 | let root = try! parseStmtStr("break"); |
|
| 1582 | 1576 | let case ast::NodeValue::Break= root.value |
|
| 1583 | 1577 | else throw testing::TestError::Failed; |
|
| 1584 | 1578 | } |
|
| 1585 | 1579 | ||
| 1586 | 1580 | /// Test parsing a `continue` statement. |
|
| 1587 | - | @test fn testParseContinue() throws (testing::TestError) { |
|
| 1581 | + | @test unsafe fn testParseContinue() throws (testing::TestError) { |
|
| 1588 | 1582 | let root = try! parseStmtStr("continue"); |
|
| 1589 | 1583 | let case ast::NodeValue::Continue= root.value |
|
| 1590 | 1584 | else throw testing::TestError::Failed; |
|
| 1591 | 1585 | } |
|
| 1592 | 1586 | ||
| 1593 | 1587 | /// Test parsing a `return` statement without value. |
|
| 1594 | - | @test fn testParseReturnVoid() throws (testing::TestError) { |
|
| 1588 | + | @test unsafe fn testParseReturnVoid() throws (testing::TestError) { |
|
| 1595 | 1589 | let root = try! parseStmtStr("return"); |
|
| 1596 | 1590 | let case ast::NodeValue::Return(retValue) = root.value |
|
| 1597 | 1591 | else throw testing::TestError::Failed; |
|
| 1598 | 1592 | ||
| 1599 | 1593 | try testing::expect(retValue == nil); |
|
| 1600 | 1594 | } |
|
| 1601 | 1595 | ||
| 1602 | 1596 | /// Test parsing a `return` statement with a value. |
|
| 1603 | - | @test fn testParseReturnValue() throws (testing::TestError) { |
|
| 1597 | + | @test unsafe fn testParseReturnValue() throws (testing::TestError) { |
|
| 1604 | 1598 | let root = try! parseStmtStr("return result"); |
|
| 1605 | 1599 | let case ast::NodeValue::Return(retValue) = root.value |
|
| 1606 | 1600 | else throw testing::TestError::Failed; |
|
| 1607 | 1601 | ||
| 1608 | 1602 | let value = retValue |
|
| 1609 | 1603 | else throw testing::TestError::Failed; |
|
| 1610 | 1604 | try expectIdent(value, "result"); |
|
| 1611 | 1605 | } |
|
| 1612 | 1606 | ||
| 1613 | 1607 | /// Test parsing a `throw` statement. |
|
| 1614 | - | @test fn testParseThrow() throws (testing::TestError) { |
|
| 1608 | + | @test unsafe fn testParseThrow() throws (testing::TestError) { |
|
| 1615 | 1609 | let root = try! parseStmtStr("throw error"); |
|
| 1616 | 1610 | let case ast::NodeValue::Throw(throwExpr) = root.value |
|
| 1617 | 1611 | else throw testing::TestError::Failed; |
|
| 1618 | 1612 | ||
| 1619 | 1613 | try expectIdent(throwExpr, "error"); |
|
| 1620 | 1614 | } |
|
| 1621 | 1615 | ||
| 1622 | 1616 | /// Test parsing a `panic` statement without a message. |
|
| 1623 | - | @test fn testParsePanicEmpty() throws (testing::TestError) { |
|
| 1617 | + | @test unsafe fn testParsePanicEmpty() throws (testing::TestError) { |
|
| 1624 | 1618 | let root = try! parseStmtStr("panic"); |
|
| 1625 | 1619 | let case ast::NodeValue::Panic(panicMsg) = root.value |
|
| 1626 | 1620 | else throw testing::TestError::Failed; |
|
| 1627 | 1621 | ||
| 1628 | 1622 | try testing::expect(panicMsg == nil); |
|
| 1629 | 1623 | } |
|
| 1630 | 1624 | ||
| 1631 | 1625 | /// Test parsing a `panic` statement with a message. |
|
| 1632 | - | @test fn testParsePanicMessage() throws (testing::TestError) { |
|
| 1626 | + | @test unsafe fn testParsePanicMessage() throws (testing::TestError) { |
|
| 1633 | 1627 | let root = try! parseStmtStr("panic \"something went wrong\""); |
|
| 1634 | 1628 | let case ast::NodeValue::Panic(panicMsg) = root.value |
|
| 1635 | 1629 | else throw testing::TestError::Failed; |
|
| 1636 | 1630 | ||
| 1637 | 1631 | let message = panicMsg |
| 1639 | 1633 | let case ast::NodeValue::String(msgStr) = message.value if mem::eq(msgStr, "something went wrong") |
|
| 1640 | 1634 | else throw testing::TestError::Failed; |
|
| 1641 | 1635 | } |
|
| 1642 | 1636 | ||
| 1643 | 1637 | /// Test parsing a `panic` statement with braces. |
|
| 1644 | - | @test fn testParsePanicBraces() throws (testing::TestError) { |
|
| 1638 | + | @test unsafe fn testParsePanicBraces() throws (testing::TestError) { |
|
| 1645 | 1639 | let root = try! parseStmtStr("panic { \"error\" }"); |
|
| 1646 | 1640 | let case ast::NodeValue::Panic(panicMsg) = root.value |
|
| 1647 | 1641 | else throw testing::TestError::Failed; |
|
| 1648 | 1642 | ||
| 1649 | 1643 | let message = panicMsg |
| 1651 | 1645 | let case ast::NodeValue::String(msgStr) = message.value if mem::eq(msgStr, "error") |
|
| 1652 | 1646 | else throw testing::TestError::Failed; |
|
| 1653 | 1647 | } |
|
| 1654 | 1648 | ||
| 1655 | 1649 | /// Test parsing a simple `match` statement with one case. |
|
| 1656 | - | @test fn testParseMatchSingle() throws (testing::TestError) { |
|
| 1650 | + | @test unsafe fn testParseMatchSingle() throws (testing::TestError) { |
|
| 1657 | 1651 | let root = try! parseStmtStr("match subject { case pattern => body }"); |
|
| 1658 | 1652 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1659 | 1653 | else throw testing::TestError::Failed; |
|
| 1660 | 1654 | ||
| 1661 | 1655 | try expectIdent(sw.subject, "subject"); |
| 1677 | 1671 | if mem::eq(name, "body") |
|
| 1678 | 1672 | else throw testing::TestError::Failed; |
|
| 1679 | 1673 | } |
|
| 1680 | 1674 | ||
| 1681 | 1675 | /// Test parsing a `match` case with guard and multiple patterns. |
|
| 1682 | - | @test fn testParseMatchGuard() throws (testing::TestError) { |
|
| 1676 | + | @test unsafe fn testParseMatchGuard() throws (testing::TestError) { |
|
| 1683 | 1677 | let root = try! parseStmtStr( |
|
| 1684 | 1678 | "match subject { case left, right if cond => handle }" |
|
| 1685 | 1679 | ); |
|
| 1686 | 1680 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1687 | 1681 | else throw testing::TestError::Failed; |
| 1700 | 1694 | else throw testing::TestError::Failed; |
|
| 1701 | 1695 | try expectIdent(guard, "cond"); |
|
| 1702 | 1696 | } |
|
| 1703 | 1697 | ||
| 1704 | 1698 | /// Test parsing `match` prongs whose bodies omit trailing semicolons. |
|
| 1705 | - | @test fn testParseMatchReturnNoSemicolon() throws (testing::TestError) { |
|
| 1699 | + | @test unsafe fn testParseMatchReturnNoSemicolon() throws (testing::TestError) { |
|
| 1706 | 1700 | let root = try! parseStmtStr( |
|
| 1707 | 1701 | "match subject { case First => return, case Second => return }" |
|
| 1708 | 1702 | ); |
|
| 1709 | 1703 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1710 | 1704 | else throw testing::TestError::Failed; |
| 1725 | 1719 | else throw testing::TestError::Failed; |
|
| 1726 | 1720 | try testing::expect(secondRetVal == nil); |
|
| 1727 | 1721 | } |
|
| 1728 | 1722 | ||
| 1729 | 1723 | /// Test parsing a `match` statement with multiple branches. |
|
| 1730 | - | @test fn testParseMatchMultipleCases() throws (testing::TestError) { |
|
| 1724 | + | @test unsafe fn testParseMatchMultipleCases() throws (testing::TestError) { |
|
| 1731 | 1725 | let root = try! parseStmtStr( |
|
| 1732 | 1726 | "match subject { case First => first, case Second => second }" |
|
| 1733 | 1727 | ); |
|
| 1734 | 1728 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1735 | 1729 | else throw testing::TestError::Failed; |
| 1763 | 1757 | else throw testing::TestError::Failed; |
|
| 1764 | 1758 | } |
|
| 1765 | 1759 | } |
|
| 1766 | 1760 | ||
| 1767 | 1761 | /// Test parsing a `match` case whose body is a block. |
|
| 1768 | - | @test fn testParseMatchProngBlock() throws (testing::TestError) { |
|
| 1762 | + | @test unsafe fn testParseMatchProngBlock() throws (testing::TestError) { |
|
| 1769 | 1763 | let root = try! parseStmtStr("match subject { case Pattern => { body; } }"); |
|
| 1770 | 1764 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1771 | 1765 | else throw testing::TestError::Failed; |
|
| 1772 | 1766 | ||
| 1773 | 1767 | try testing::expect(sw.prongs.len == 1); |
| 1775 | 1769 | else throw testing::TestError::Failed; |
|
| 1776 | 1770 | try expectBlockExprStmt(prong.body, ast::NodeValue::Ident("body")); |
|
| 1777 | 1771 | } |
|
| 1778 | 1772 | ||
| 1779 | 1773 | /// Test parsing a `match` statement with an `else` case. |
|
| 1780 | - | @test fn testParseMatchElse() throws (testing::TestError) { |
|
| 1774 | + | @test unsafe fn testParseMatchElse() throws (testing::TestError) { |
|
| 1781 | 1775 | let root = try! parseStmtStr("match subject { else => body }"); |
|
| 1782 | 1776 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1783 | 1777 | else throw testing::TestError::Failed; |
|
| 1784 | 1778 | ||
| 1785 | 1779 | try testing::expect(sw.prongs.len == 1); |
| 1796 | 1790 | if mem::eq(name, "body") |
|
| 1797 | 1791 | else throw testing::TestError::Failed; |
|
| 1798 | 1792 | } |
|
| 1799 | 1793 | ||
| 1800 | 1794 | /// Test parsing a `match` statement with a binding prong. |
|
| 1801 | - | @test fn testParseMatchBinding() throws (testing::TestError) { |
|
| 1795 | + | @test unsafe fn testParseMatchBinding() throws (testing::TestError) { |
|
| 1802 | 1796 | let root = try! parseStmtStr("match subject { x => body }"); |
|
| 1803 | 1797 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1804 | 1798 | else throw testing::TestError::Failed; |
|
| 1805 | 1799 | ||
| 1806 | 1800 | try testing::expect(sw.prongs.len == 1); |
| 1816 | 1810 | else throw testing::TestError::Failed; |
|
| 1817 | 1811 | try expectIdent(bodyStmt, "body"); |
|
| 1818 | 1812 | } |
|
| 1819 | 1813 | ||
| 1820 | 1814 | /// Test parsing a `match` statement with a guarded binding prong. |
|
| 1821 | - | @test fn testParseMatchBindingGuard() throws (testing::TestError) { |
|
| 1815 | + | @test unsafe fn testParseMatchBindingGuard() throws (testing::TestError) { |
|
| 1822 | 1816 | let root = try! parseStmtStr("match subject { x if x > 0 => body }"); |
|
| 1823 | 1817 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1824 | 1818 | else throw testing::TestError::Failed; |
|
| 1825 | 1819 | ||
| 1826 | 1820 | try testing::expect(sw.prongs.len == 1); |
| 1836 | 1830 | else throw testing::TestError::Failed; |
|
| 1837 | 1831 | try testing::expect(binop.op == ast::BinaryOp::Gt); |
|
| 1838 | 1832 | } |
|
| 1839 | 1833 | ||
| 1840 | 1834 | /// Test parsing a `match` statement with a `_` wildcard. |
|
| 1841 | - | @test fn testParseMatchWildcard() throws (testing::TestError) { |
|
| 1835 | + | @test unsafe fn testParseMatchWildcard() throws (testing::TestError) { |
|
| 1842 | 1836 | let root = try! parseStmtStr("match subject { _ => body }"); |
|
| 1843 | 1837 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1844 | 1838 | else throw testing::TestError::Failed; |
|
| 1845 | 1839 | ||
| 1846 | 1840 | try testing::expect(sw.prongs.len == 1); |
| 1857 | 1851 | else throw testing::TestError::Failed; |
|
| 1858 | 1852 | try expectIdent(bodyStmt, "body"); |
|
| 1859 | 1853 | } |
|
| 1860 | 1854 | ||
| 1861 | 1855 | /// Test parsing a `match` statement with a guarded `_` wildcard. |
|
| 1862 | - | @test fn testParseMatchWildcardGuard() throws (testing::TestError) { |
|
| 1856 | + | @test unsafe fn testParseMatchWildcardGuard() throws (testing::TestError) { |
|
| 1863 | 1857 | let root = try! parseStmtStr("match subject { _ if cond => body }"); |
|
| 1864 | 1858 | let case ast::NodeValue::Match(sw) = root.value |
|
| 1865 | 1859 | else throw testing::TestError::Failed; |
|
| 1866 | 1860 | ||
| 1867 | 1861 | try testing::expect(sw.prongs.len == 1); |
| 1879 | 1873 | else throw testing::TestError::Failed; |
|
| 1880 | 1874 | try expectIdent(bodyStmt, "body"); |
|
| 1881 | 1875 | } |
|
| 1882 | 1876 | ||
| 1883 | 1877 | /// Test parsing a `while let` loop with guard and else branches. |
|
| 1884 | - | @test fn testParseWhileLet() throws (testing::TestError) { |
|
| 1878 | + | @test unsafe fn testParseWhileLet() throws (testing::TestError) { |
|
| 1885 | 1879 | let root = try! parseStmtStr( |
|
| 1886 | 1880 | "while let value = opt; guard { body; } else { alt; }" |
|
| 1887 | 1881 | ); |
|
| 1888 | 1882 | let case ast::NodeValue::WhileLet(loopNode) = root.value |
|
| 1889 | 1883 | else throw testing::TestError::Failed; |
| 1901 | 1895 | else throw testing::TestError::Failed; |
|
| 1902 | 1896 | try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt")); |
|
| 1903 | 1897 | } |
|
| 1904 | 1898 | ||
| 1905 | 1899 | /// Test parsing a simple `loop` statement. |
|
| 1906 | - | @test fn testParseLoop() throws (testing::TestError) { |
|
| 1900 | + | @test unsafe fn testParseLoop() throws (testing::TestError) { |
|
| 1907 | 1901 | let root = try! parseStmtStr("loop { body; }"); |
|
| 1908 | 1902 | let case ast::NodeValue::Loop(loopBody) = root.value |
|
| 1909 | 1903 | else throw testing::TestError::Failed; |
|
| 1910 | 1904 | ||
| 1911 | 1905 | try expectBlockExprStmt(loopBody, ast::NodeValue::Ident("body")); |
|
| 1912 | 1906 | } |
|
| 1913 | 1907 | ||
| 1914 | 1908 | /// Test parsing a `for` loop without index or else branches. |
|
| 1915 | - | @test fn testParseFor() throws (testing::TestError) { |
|
| 1909 | + | @test unsafe fn testParseFor() throws (testing::TestError) { |
|
| 1916 | 1910 | let root = try! parseStmtStr("for item in items { body; }"); |
|
| 1917 | 1911 | let case ast::NodeValue::For(loopNode) = root.value |
|
| 1918 | 1912 | else throw testing::TestError::Failed; |
|
| 1919 | 1913 | ||
| 1920 | 1914 | try expectIdent(loopNode.binding, "item"); |
| 1924 | 1918 | try expectBlockExprStmt(loopNode.body, ast::NodeValue::Ident("body")); |
|
| 1925 | 1919 | try testing::expect(loopNode.elseBranch == nil); |
|
| 1926 | 1920 | } |
|
| 1927 | 1921 | ||
| 1928 | 1922 | /// Test parsing a `for` loop over a range expression. |
|
| 1929 | - | @test fn testParseForRangeIterable() throws (testing::TestError) { |
|
| 1923 | + | @test unsafe fn testParseForRangeIterable() throws (testing::TestError) { |
|
| 1930 | 1924 | let root = try! parseStmtStr("for item in 0..5 {}"); |
|
| 1931 | 1925 | let case ast::NodeValue::For(loopNode) = root.value |
|
| 1932 | 1926 | else throw testing::TestError::Failed; |
|
| 1933 | 1927 | ||
| 1934 | 1928 | try expectIdent(loopNode.binding, "item"); |
| 1940 | 1934 | try testing::expect(body.statements.len == 0); |
|
| 1941 | 1935 | try testing::expect(loopNode.elseBranch == nil); |
|
| 1942 | 1936 | } |
|
| 1943 | 1937 | ||
| 1944 | 1938 | /// Test parsing a `for` loop with index and else branches. |
|
| 1945 | - | @test fn testParseForIndexElse() throws (testing::TestError) { |
|
| 1939 | + | @test unsafe fn testParseForIndexElse() throws (testing::TestError) { |
|
| 1946 | 1940 | let root = try! parseStmtStr( |
|
| 1947 | 1941 | "for value, idx in items { body; } else { alt; }" |
|
| 1948 | 1942 | ); |
|
| 1949 | 1943 | let case ast::NodeValue::For(loopNode) = root.value |
|
| 1950 | 1944 | else throw testing::TestError::Failed; |
| 1962 | 1956 | else throw testing::TestError::Failed; |
|
| 1963 | 1957 | try expectBlockExprStmt(elseBranch, ast::NodeValue::Ident("alt")); |
|
| 1964 | 1958 | } |
|
| 1965 | 1959 | ||
| 1966 | 1960 | /// Test parsing field access expression. |
|
| 1967 | - | @test fn testParseFieldAccess() throws (testing::TestError) { |
|
| 1961 | + | @test unsafe fn testParseFieldAccess() throws (testing::TestError) { |
|
| 1968 | 1962 | let root = try! parseExprStr("obj.field"); |
|
| 1969 | 1963 | let case ast::NodeValue::FieldAccess(access) = root.value |
|
| 1970 | 1964 | else throw testing::TestError::Failed; |
|
| 1971 | 1965 | ||
| 1972 | 1966 | try expectIdent(access.parent, "obj"); |
|
| 1973 | 1967 | try expectIdent(access.child, "field"); |
|
| 1974 | 1968 | } |
|
| 1975 | 1969 | ||
| 1976 | 1970 | /// Test parsing scope access expression. |
|
| 1977 | - | @test fn testParseScopeAccess() throws (testing::TestError) { |
|
| 1971 | + | @test unsafe fn testParseScopeAccess() throws (testing::TestError) { |
|
| 1978 | 1972 | let root = try! parseExprStr("module::item"); |
|
| 1979 | 1973 | let case ast::NodeValue::ScopeAccess(access) = root.value |
|
| 1980 | 1974 | else throw testing::TestError::Failed; |
|
| 1981 | 1975 | ||
| 1982 | 1976 | try expectIdent(access.parent, "module"); |
|
| 1983 | 1977 | try expectIdent(access.child, "item"); |
|
| 1984 | 1978 | } |
|
| 1985 | 1979 | ||
| 1986 | 1980 | /// Test parsing array subscript expression. |
|
| 1987 | - | @test fn testParseArraySubscript() throws (testing::TestError) { |
|
| 1981 | + | @test unsafe fn testParseArraySubscript() throws (testing::TestError) { |
|
| 1988 | 1982 | let root = try! parseExprStr("array[index]"); |
|
| 1989 | 1983 | let case ast::NodeValue::Subscript { container, index } = root.value |
|
| 1990 | 1984 | else throw testing::TestError::Failed; |
|
| 1991 | 1985 | ||
| 1992 | 1986 | try expectIdent(container, "array"); |
|
| 1993 | 1987 | try expectIdent(index, "index"); |
|
| 1994 | 1988 | } |
|
| 1995 | 1989 | ||
| 1996 | 1990 | /// Test parsing array slicing expressions. |
|
| 1997 | - | @test fn testParseArraySlicing() throws (testing::TestError) { |
|
| 1991 | + | @test unsafe fn testParseArraySlicing() throws (testing::TestError) { |
|
| 1998 | 1992 | // Test `array[start..end]`. |
|
| 1999 | 1993 | { |
|
| 2000 | 1994 | let expr = try! parseExprStr("array[1..10]"); |
|
| 2001 | 1995 | let case ast::NodeValue::Subscript { container: subContainer, index: subIndex } = expr.value |
|
| 2002 | 1996 | else throw testing::TestError::Failed; |
| 2052 | 2046 | try testing::expect(range.end == nil); |
|
| 2053 | 2047 | } |
|
| 2054 | 2048 | } |
|
| 2055 | 2049 | ||
| 2056 | 2050 | /// Test parsing function call expression. |
|
| 2057 | - | @test fn testParseFunctionCall() throws (testing::TestError) { |
|
| 2051 | + | @test unsafe fn testParseFunctionCall() throws (testing::TestError) { |
|
| 2058 | 2052 | let root = try! parseExprStr("func(x, y)"); |
|
| 2059 | 2053 | let case ast::NodeValue::Call(call) = root.value |
|
| 2060 | 2054 | else throw testing::TestError::Failed; |
|
| 2061 | 2055 | ||
| 2062 | 2056 | try expectIdent(call.callee, "func"); |
| 2064 | 2058 | try expectIdent(call.args[0], "x"); |
|
| 2065 | 2059 | try expectIdent(call.args[1], "y"); |
|
| 2066 | 2060 | } |
|
| 2067 | 2061 | ||
| 2068 | 2062 | /// Test parsing chained postfix operators. |
|
| 2069 | - | @test fn testParseChainedPostfix() throws (testing::TestError) { |
|
| 2063 | + | @test unsafe fn testParseChainedPostfix() throws (testing::TestError) { |
|
| 2070 | 2064 | let root = try! parseExprStr("obj.method(arg)[0]"); |
|
| 2071 | 2065 | let case ast::NodeValue::Subscript { container: subContainer, index: subIndex } = root.value |
|
| 2072 | 2066 | else throw testing::TestError::Failed; |
|
| 2073 | 2067 | ||
| 2074 | 2068 | let case ast::NodeValue::Call(call) = subContainer.value |
| 2082 | 2076 | try testing::expect(call.args.len == 1); |
|
| 2083 | 2077 | try expectIdent(call.args[0], "arg"); |
|
| 2084 | 2078 | } |
|
| 2085 | 2079 | ||
| 2086 | 2080 | /// Test parsing a literal cast using `as`. |
|
| 2087 | - | @test fn testParseAsCastLiteral() throws (testing::TestError) { |
|
| 2081 | + | @test unsafe fn testParseAsCastLiteral() throws (testing::TestError) { |
|
| 2088 | 2082 | let root = try! parseExprStr("1 as i32"); |
|
| 2089 | 2083 | let case ast::NodeValue::As(asExpr) = root.value |
|
| 2090 | 2084 | else throw testing::TestError::Failed; |
|
| 2091 | 2085 | ||
| 2092 | 2086 | let case ast::NodeValue::Number(_) = asExpr.value.value |
| 2094 | 2088 | ||
| 2095 | 2089 | try expectIntType(asExpr.type, 4, ast::Signedness::Signed); |
|
| 2096 | 2090 | } |
|
| 2097 | 2091 | ||
| 2098 | 2092 | /// Test parsing a cast following chained postfix expressions. |
|
| 2099 | - | @test fn testParseAsCastWithPostfix() throws (testing::TestError) { |
|
| 2093 | + | @test unsafe fn testParseAsCastWithPostfix() throws (testing::TestError) { |
|
| 2100 | 2094 | let root = try! parseExprStr("value.method(arg) as u32"); |
|
| 2101 | 2095 | let case ast::NodeValue::As(asExpr) = root.value |
|
| 2102 | 2096 | else throw testing::TestError::Failed; |
|
| 2103 | 2097 | ||
| 2104 | 2098 | let case ast::NodeValue::Call(call) = asExpr.value.value |
| 2113 | 2107 | try expectIdent(call.args[0], "arg"); |
|
| 2114 | 2108 | try expectIntType(asExpr.type, 4, ast::Signedness::Unsigned); |
|
| 2115 | 2109 | } |
|
| 2116 | 2110 | ||
| 2117 | 2111 | /// Test parsing @sizeOf builtin. |
|
| 2118 | - | @test fn testParseBuiltinSizeOf() throws (testing::TestError) { |
|
| 2112 | + | @test unsafe fn testParseBuiltinSizeOf() throws (testing::TestError) { |
|
| 2119 | 2113 | let expr = try! parseExprStr("@sizeOf(i32)"); |
|
| 2120 | 2114 | let case ast::NodeValue::BuiltinCall { kind: builtinKind, args: builtinArgs } = expr.value |
|
| 2121 | 2115 | else throw testing::TestError::Failed; |
|
| 2122 | 2116 | ||
| 2123 | 2117 | try testing::expect(builtinKind == ast::Builtin::SizeOf); |
| 2127 | 2121 | sign: ast::Signedness::Signed, |
|
| 2128 | 2122 | }); |
|
| 2129 | 2123 | } |
|
| 2130 | 2124 | ||
| 2131 | 2125 | /// Test parsing @alignOf builtin. |
|
| 2132 | - | @test fn testParseBuiltinAlignOf() throws (testing::TestError) { |
|
| 2126 | + | @test unsafe fn testParseBuiltinAlignOf() throws (testing::TestError) { |
|
| 2133 | 2127 | let expr = try! parseExprStr("@alignOf(i32)"); |
|
| 2134 | 2128 | let case ast::NodeValue::BuiltinCall { kind: builtinKind, args: builtinArgs } = expr.value |
|
| 2135 | 2129 | else throw testing::TestError::Failed; |
|
| 2136 | 2130 | ||
| 2137 | 2131 | try testing::expect(builtinKind == ast::Builtin::AlignOf); |
| 2141 | 2135 | sign: ast::Signedness::Signed, |
|
| 2142 | 2136 | }); |
|
| 2143 | 2137 | } |
|
| 2144 | 2138 | ||
| 2145 | 2139 | /// Test parsing @sliceOf with varying argument counts. |
|
| 2146 | - | @test fn testParseBuiltinSliceOf() throws (testing::TestError) { |
|
| 2140 | + | @test unsafe fn testParseBuiltinSliceOf() throws (testing::TestError) { |
|
| 2147 | 2141 | // Two arguments. |
|
| 2148 | 2142 | { |
|
| 2149 | 2143 | let expr = try! parseExprStr("@sliceOf(ptr, len)"); |
|
| 2150 | 2144 | let case ast::NodeValue::BuiltinCall { kind: builtinKind, args: builtinArgs } = expr.value |
|
| 2151 | 2145 | else throw testing::TestError::Failed; |
| 2169 | 2163 | try testing::expect(builtinArgs.len == 3); |
|
| 2170 | 2164 | } |
|
| 2171 | 2165 | } |
|
| 2172 | 2166 | ||
| 2173 | 2167 | /// Test parsing prefix unary operators. |
|
| 2174 | - | @test fn testParseUnaryOperators() throws (testing::TestError) { |
|
| 2168 | + | @test unsafe fn testParseUnaryOperators() throws (testing::TestError) { |
|
| 2175 | 2169 | { |
|
| 2176 | 2170 | let notExpr = try! parseExprStr("not flag"); |
|
| 2177 | 2171 | let case ast::NodeValue::UnOp(notNode) = notExpr.value |
|
| 2178 | 2172 | else throw testing::TestError::Failed; |
|
| 2179 | 2173 | try testing::expect(notNode.op == ast::UnaryOp::Not); |
| 2194 | 2188 | try expectIdent(bitNotNode.value, "mask"); |
|
| 2195 | 2189 | } |
|
| 2196 | 2190 | } |
|
| 2197 | 2191 | ||
| 2198 | 2192 | /// Test parsing dereference expressions. |
|
| 2199 | - | @test fn testParseDereference() throws (testing::TestError) { |
|
| 2193 | + | @test unsafe fn testParseDereference() throws (testing::TestError) { |
|
| 2200 | 2194 | { |
|
| 2201 | 2195 | let derefExpr = try! parseExprStr("*ptr"); |
|
| 2202 | 2196 | let case ast::NodeValue::Deref(target) = derefExpr.value |
|
| 2203 | 2197 | else throw testing::TestError::Failed; |
|
| 2204 | 2198 | try expectIdent(target, "ptr"); |
| 2213 | 2207 | try expectIdent(access.child, "field"); |
|
| 2214 | 2208 | } |
|
| 2215 | 2209 | } |
|
| 2216 | 2210 | ||
| 2217 | 2211 | /// Test parsing reference (address-of) expressions. |
|
| 2218 | - | @test fn testParseRefs() throws (testing::TestError) { |
|
| 2212 | + | @test unsafe fn testParseRefs() throws (testing::TestError) { |
|
| 2219 | 2213 | { |
|
| 2220 | 2214 | let refExpr = try! parseExprStr("&foo"); |
|
| 2221 | 2215 | let case ast::NodeValue::AddressOf(refNode) = refExpr.value |
|
| 2222 | 2216 | else throw testing::TestError::Failed; |
|
| 2223 | 2217 | try testing::expect(refNode.mutable == false); |
| 2251 | 2245 | try expectIdent(access.child, "field"); |
|
| 2252 | 2246 | } |
|
| 2253 | 2247 | } |
|
| 2254 | 2248 | ||
| 2255 | 2249 | /// Test unary operator precedence relative to binary and postfix expressions. |
|
| 2256 | - | @test fn testParseUnaryPrecedence() throws (testing::TestError) { |
|
| 2250 | + | @test unsafe fn testParseUnaryPrecedence() throws (testing::TestError) { |
|
| 2257 | 2251 | { |
|
| 2258 | 2252 | let expr = try! parseExprStr("not a and b"); |
|
| 2259 | 2253 | let case ast::NodeValue::BinOp(bin) = expr.value |
|
| 2260 | 2254 | else throw testing::TestError::Failed; |
|
| 2261 | 2255 | try testing::expect(bin.op == ast::BinaryOp::And); |
| 2286 | 2280 | try expectIdent(call.callee, "func"); |
|
| 2287 | 2281 | } |
|
| 2288 | 2282 | } |
|
| 2289 | 2283 | ||
| 2290 | 2284 | /// Test parsing assignment expressions. |
|
| 2291 | - | @test fn testParseAssignment() throws (testing::TestError) { |
|
| 2285 | + | @test unsafe fn testParseAssignment() throws (testing::TestError) { |
|
| 2292 | 2286 | { |
|
| 2293 | 2287 | let assign = try! parseStmtStr("set x = 1;"); |
|
| 2294 | 2288 | let case ast::NodeValue::Assign(node) = assign.value |
|
| 2295 | 2289 | else throw testing::TestError::Failed; |
|
| 2296 | 2290 | try expectIdent(node.left, "x"); |
| 2334 | 2328 | try expectIdent(access.child, "VAR"); |
|
| 2335 | 2329 | } |
|
| 2336 | 2330 | } |
|
| 2337 | 2331 | ||
| 2338 | 2332 | /// Test that assignment syntax requires the `set` statement. |
|
| 2339 | - | @test fn testAssignmentRequiresSetKeyword() throws (testing::TestError) { |
|
| 2333 | + | @test unsafe fn testAssignmentRequiresSetKeyword() throws (testing::TestError) { |
|
| 2340 | 2334 | let assign: ?*ast::Node = try? parseStmtStr("x = 1;"); |
|
| 2341 | 2335 | try testing::expect(assign == nil); |
|
| 2342 | 2336 | ||
| 2343 | 2337 | let compoundAssign: ?*ast::Node = try? parseStmtStr("x += 1;"); |
|
| 2344 | 2338 | try testing::expect(compoundAssign == nil); |
|
| 2345 | 2339 | } |
|
| 2346 | 2340 | ||
| 2347 | 2341 | /// Test that `set` requires an assignable target. |
|
| 2348 | - | @test fn testSetRequiresAssignableTarget() throws (testing::TestError) { |
|
| 2342 | + | @test unsafe fn testSetRequiresAssignableTarget() throws (testing::TestError) { |
|
| 2349 | 2343 | let binTarget: ?*ast::Node = try? parseStmtStr("set x + y = 1;"); |
|
| 2350 | 2344 | try testing::expect(binTarget == nil); |
|
| 2351 | 2345 | ||
| 2352 | 2346 | let condTarget: ?*ast::Node = try? parseStmtStr("set x if ok else y = 1;"); |
|
| 2353 | 2347 | try testing::expect(condTarget == nil); |
|
| 2354 | 2348 | } |
|
| 2355 | 2349 | ||
| 2356 | 2350 | /// Test parsing basic arithmetic binary operators (+, -, *, /, %). |
|
| 2357 | - | @test fn testParseBinOpArithmetic() throws (testing::TestError) { |
|
| 2351 | + | @test unsafe fn testParseBinOpArithmetic() throws (testing::TestError) { |
|
| 2358 | 2352 | let add = try! parseExprStr("a + b"); |
|
| 2359 | 2353 | let case ast::NodeValue::BinOp(op1) = add.value |
|
| 2360 | 2354 | else throw testing::TestError::Failed; |
|
| 2361 | 2355 | try testing::expect(op1.op == ast::BinaryOp::Add); |
|
| 2362 | 2356 | try expectIdent(op1.left, "a"); |
| 2382 | 2376 | else throw testing::TestError::Failed; |
|
| 2383 | 2377 | try testing::expect(op5.op == ast::BinaryOp::Mod); |
|
| 2384 | 2378 | } |
|
| 2385 | 2379 | ||
| 2386 | 2380 | /// Test parsing comparison binary operators (==, <>). |
|
| 2387 | - | @test fn testParseBinOpEq() throws (testing::TestError) { |
|
| 2381 | + | @test unsafe fn testParseBinOpEq() throws (testing::TestError) { |
|
| 2388 | 2382 | let eq = try! parseExprStr("a == b"); |
|
| 2389 | 2383 | let case ast::NodeValue::BinOp(op1) = eq.value |
|
| 2390 | 2384 | else throw testing::TestError::Failed; |
|
| 2391 | 2385 | try testing::expect(op1.op == ast::BinaryOp::Eq); |
|
| 2392 | 2386 |
| 2395 | 2389 | else throw testing::TestError::Failed; |
|
| 2396 | 2390 | try testing::expect(op2.op == ast::BinaryOp::Ne); |
|
| 2397 | 2391 | } |
|
| 2398 | 2392 | ||
| 2399 | 2393 | /// Test parsing comparison binary operators. |
|
| 2400 | - | @test fn testParseBinOpGtLt() throws (testing::TestError) { |
|
| 2394 | + | @test unsafe fn testParseBinOpGtLt() throws (testing::TestError) { |
|
| 2401 | 2395 | let lt = try! parseExprStr("a < b"); |
|
| 2402 | 2396 | let case ast::NodeValue::BinOp(op1) = lt.value |
|
| 2403 | 2397 | else throw testing::TestError::Failed; |
|
| 2404 | 2398 | try testing::expect(op1.op == ast::BinaryOp::Lt); |
|
| 2405 | 2399 |
| 2418 | 2412 | else throw testing::TestError::Failed; |
|
| 2419 | 2413 | try testing::expect(op4.op == ast::BinaryOp::Gte); |
|
| 2420 | 2414 | } |
|
| 2421 | 2415 | ||
| 2422 | 2416 | /// Test parsing bitwise binary operators (&, |, ^, <<, >>). |
|
| 2423 | - | @test fn testParseBinOpBitwise() throws (testing::TestError) { |
|
| 2417 | + | @test unsafe fn testParseBinOpBitwise() throws (testing::TestError) { |
|
| 2424 | 2418 | let bitAnd = try! parseExprStr("a & b"); |
|
| 2425 | 2419 | let case ast::NodeValue::BinOp(op1) = bitAnd.value |
|
| 2426 | 2420 | else throw testing::TestError::Failed; |
|
| 2427 | 2421 | try testing::expect(op1.op == ast::BinaryOp::BitAnd); |
|
| 2428 | 2422 |
| 2446 | 2440 | else throw testing::TestError::Failed; |
|
| 2447 | 2441 | try testing::expect(op5.op == ast::BinaryOp::Shr); |
|
| 2448 | 2442 | } |
|
| 2449 | 2443 | ||
| 2450 | 2444 | /// Test parsing logical binary operators (and, or). |
|
| 2451 | - | @test fn testParseBinOpLogical() throws (testing::TestError) { |
|
| 2445 | + | @test unsafe fn testParseBinOpLogical() throws (testing::TestError) { |
|
| 2452 | 2446 | let andOp = try! parseExprStr("a and b"); |
|
| 2453 | 2447 | let case ast::NodeValue::BinOp(op1) = andOp.value |
|
| 2454 | 2448 | else throw testing::TestError::Failed; |
|
| 2455 | 2449 | try testing::expect(op1.op == ast::BinaryOp::And); |
|
| 2456 | 2450 | try expectIdent(op1.left, "a"); |
| 2463 | 2457 | try expectIdent(op2.left, "x"); |
|
| 2464 | 2458 | try expectIdent(op2.right, "y"); |
|
| 2465 | 2459 | } |
|
| 2466 | 2460 | ||
| 2467 | 2461 | /// Test operator precedence: multiplication before addition. |
|
| 2468 | - | @test fn testParseBinOpPrecedenceMulAdd() throws (testing::TestError) { |
|
| 2462 | + | @test unsafe fn testParseBinOpPrecedenceMulAdd() throws (testing::TestError) { |
|
| 2469 | 2463 | let root = try! parseExprStr("a + b * c"); |
|
| 2470 | 2464 | let case ast::NodeValue::BinOp(add) = root.value |
|
| 2471 | 2465 | else throw testing::TestError::Failed; |
|
| 2472 | 2466 | ||
| 2473 | 2467 | try testing::expect(add.op == ast::BinaryOp::Add); |
| 2480 | 2474 | try expectIdent(mul.left, "b"); |
|
| 2481 | 2475 | try expectIdent(mul.right, "c"); |
|
| 2482 | 2476 | } |
|
| 2483 | 2477 | ||
| 2484 | 2478 | /// Test operator precedence: shifts before bitwise operations. |
|
| 2485 | - | @test fn testParseBinOpPrecedenceShiftBitwise() throws (testing::TestError) { |
|
| 2479 | + | @test unsafe fn testParseBinOpPrecedenceShiftBitwise() throws (testing::TestError) { |
|
| 2486 | 2480 | let root = try! parseExprStr("a & b << c"); |
|
| 2487 | 2481 | let case ast::NodeValue::BinOp(bitAnd) = root.value |
|
| 2488 | 2482 | else throw testing::TestError::Failed; |
|
| 2489 | 2483 | ||
| 2490 | 2484 | try testing::expect(bitAnd.op == ast::BinaryOp::BitAnd); |
| 2497 | 2491 | try expectIdent(shl.left, "b"); |
|
| 2498 | 2492 | try expectIdent(shl.right, "c"); |
|
| 2499 | 2493 | } |
|
| 2500 | 2494 | ||
| 2501 | 2495 | /// Test operator precedence: comparison before logical AND. |
|
| 2502 | - | @test fn testParseBinOpPrecedenceCompareLogical() throws (testing::TestError) { |
|
| 2496 | + | @test unsafe fn testParseBinOpPrecedenceCompareLogical() throws (testing::TestError) { |
|
| 2503 | 2497 | let root = try! parseExprStr("a < b and c > d"); |
|
| 2504 | 2498 | let case ast::NodeValue::BinOp(andOp) = root.value |
|
| 2505 | 2499 | else throw testing::TestError::Failed; |
|
| 2506 | 2500 | ||
| 2507 | 2501 | try testing::expect(andOp.op == ast::BinaryOp::And); |
| 2518 | 2512 | try expectIdent(gt.left, "c"); |
|
| 2519 | 2513 | try expectIdent(gt.right, "d"); |
|
| 2520 | 2514 | } |
|
| 2521 | 2515 | ||
| 2522 | 2516 | /// Test left associativity of addition. |
|
| 2523 | - | @test fn testParseBinOpAssociativityAdd() throws (testing::TestError) { |
|
| 2517 | + | @test unsafe fn testParseBinOpAssociativityAdd() throws (testing::TestError) { |
|
| 2524 | 2518 | let root = try! parseExprStr("a + b + c"); |
|
| 2525 | 2519 | let case ast::NodeValue::BinOp(add2) = root.value |
|
| 2526 | 2520 | else throw testing::TestError::Failed; |
|
| 2527 | 2521 | ||
| 2528 | 2522 | try testing::expect(add2.op == ast::BinaryOp::Add); |
| 2534 | 2528 | try expectIdent(add1.left, "a"); |
|
| 2535 | 2529 | try expectIdent(add1.right, "b"); |
|
| 2536 | 2530 | } |
|
| 2537 | 2531 | ||
| 2538 | 2532 | /// Test complex expression with multiple operators and precedence. |
|
| 2539 | - | @test fn testParseBinOpComplex() throws (testing::TestError) { |
|
| 2533 | + | @test unsafe fn testParseBinOpComplex() throws (testing::TestError) { |
|
| 2540 | 2534 | let root = try! parseExprStr("a + b * c - d / e"); |
|
| 2541 | 2535 | let case ast::NodeValue::BinOp(sub) = root.value |
|
| 2542 | 2536 | else throw testing::TestError::Failed; |
|
| 2543 | 2537 | ||
| 2544 | 2538 | try testing::expect(sub.op == ast::BinaryOp::Sub); |
| 2556 | 2550 | else throw testing::TestError::Failed; |
|
| 2557 | 2551 | try testing::expect(div.op == ast::BinaryOp::Div); |
|
| 2558 | 2552 | } |
|
| 2559 | 2553 | ||
| 2560 | 2554 | /// Test binary operators with parentheses override precedence. |
|
| 2561 | - | @test fn testParseBinOpParentheses() throws (testing::TestError) { |
|
| 2555 | + | @test unsafe fn testParseBinOpParentheses() throws (testing::TestError) { |
|
| 2562 | 2556 | let root = try! parseExprStr("(a + b) * c"); |
|
| 2563 | 2557 | let case ast::NodeValue::BinOp(mul) = root.value |
|
| 2564 | 2558 | else throw testing::TestError::Failed; |
|
| 2565 | 2559 | ||
| 2566 | 2560 | try testing::expect(mul.op == ast::BinaryOp::Mul); |
| 2572 | 2566 | try expectIdent(add.left, "a"); |
|
| 2573 | 2567 | try expectIdent(add.right, "b"); |
|
| 2574 | 2568 | } |
|
| 2575 | 2569 | ||
| 2576 | 2570 | /// Test parsing a simple union without payloads. |
|
| 2577 | - | @test fn testParseEnumSimple() throws (testing::TestError) { |
|
| 2571 | + | @test unsafe fn testParseEnumSimple() throws (testing::TestError) { |
|
| 2578 | 2572 | let node = try! parseStmtStr("union Color { Red, Green, Blue }"); |
|
| 2579 | 2573 | let case ast::NodeValue::UnionDecl(decl) = node.value |
|
| 2580 | 2574 | else throw testing::TestError::Failed; |
|
| 2581 | 2575 | ||
| 2582 | 2576 | try expectIdent(decl.name, "Color"); |
| 2609 | 2603 | try testing::expect(var2.type == nil); |
|
| 2610 | 2604 | try testing::expect(var2.value == nil); |
|
| 2611 | 2605 | } |
|
| 2612 | 2606 | ||
| 2613 | 2607 | /// Test parsing a union with trailing comma. |
|
| 2614 | - | @test fn testParseEnumTrailingComma() throws (testing::TestError) { |
|
| 2608 | + | @test unsafe fn testParseEnumTrailingComma() throws (testing::TestError) { |
|
| 2615 | 2609 | let node = try! parseStmtStr("union Letter { A, B, C, }"); |
|
| 2616 | 2610 | let case ast::NodeValue::UnionDecl(decl) = node.value |
|
| 2617 | 2611 | else throw testing::TestError::Failed; |
|
| 2618 | 2612 | ||
| 2619 | 2613 | try expectIdent(decl.name, "Letter"); |
|
| 2620 | 2614 | try testing::expect(decl.variants.len == 3); |
|
| 2621 | 2615 | } |
|
| 2622 | 2616 | ||
| 2623 | 2617 | /// Test parsing a union with explicit values. |
|
| 2624 | - | @test fn testParseEnumExplicitValues() throws (testing::TestError) { |
|
| 2618 | + | @test unsafe fn testParseEnumExplicitValues() throws (testing::TestError) { |
|
| 2625 | 2619 | let node = try! parseStmtStr("union Status { Ok = 0, Error = 1, Pending = 5 }"); |
|
| 2626 | 2620 | let case ast::NodeValue::UnionDecl(decl) = node.value |
|
| 2627 | 2621 | else throw testing::TestError::Failed; |
|
| 2628 | 2622 | ||
| 2629 | 2623 | try expectIdent(decl.name, "Status"); |
| 2653 | 2647 | try testing::expect(var2.index == 2); |
|
| 2654 | 2648 | try testing::expect(var2.value <> nil); |
|
| 2655 | 2649 | } |
|
| 2656 | 2650 | ||
| 2657 | 2651 | /// Test parsing a union with payload and tag-only variants. |
|
| 2658 | - | @test fn testParseEnumWithPayloads() throws (testing::TestError) { |
|
| 2652 | + | @test unsafe fn testParseEnumWithPayloads() throws (testing::TestError) { |
|
| 2659 | 2653 | let node = try! parseStmtStr("union Result { Ok(bool), Error }"); |
|
| 2660 | 2654 | let case ast::NodeValue::UnionDecl(decl) = node.value |
|
| 2661 | 2655 | else throw testing::TestError::Failed; |
|
| 2662 | 2656 | ||
| 2663 | 2657 | try expectIdent(decl.name, "Result"); |
| 2671 | 2665 | let errFields = try expectVariant(decl.variants[1], "Error", 1); |
|
| 2672 | 2666 | try testing::expect(errFields == nil); |
|
| 2673 | 2667 | } |
|
| 2674 | 2668 | ||
| 2675 | 2669 | /// Test parsing a union with derives. |
|
| 2676 | - | @test fn testParseEnumWithDerives() throws (testing::TestError) { |
|
| 2670 | + | @test unsafe fn testParseEnumWithDerives() throws (testing::TestError) { |
|
| 2677 | 2671 | let node = try! parseStmtStr("union Option: Debug + Eq { None, Some(i32) }"); |
|
| 2678 | 2672 | let case ast::NodeValue::UnionDecl(decl) = node.value |
|
| 2679 | 2673 | else throw testing::TestError::Failed; |
|
| 2680 | 2674 | ||
| 2681 | 2675 | try expectIdent(decl.name, "Option"); |
| 2698 | 2692 | try expectIdent(var1.name, "Some"); |
|
| 2699 | 2693 | try testing::expect(var1.type <> nil); |
|
| 2700 | 2694 | } |
|
| 2701 | 2695 | ||
| 2702 | 2696 | /// Test parsing named record literals with named fields. |
|
| 2703 | - | @test fn testParseNamedRecordLiteralNamed() throws (testing::TestError) { |
|
| 2697 | + | @test unsafe fn testParseNamedRecordLiteralNamed() throws (testing::TestError) { |
|
| 2704 | 2698 | let r1 = try! parseExprStr("Point { x: 5, y: 10 }"); |
|
| 2705 | 2699 | let case ast::NodeValue::RecordLit(lit) = r1.value |
|
| 2706 | 2700 | else throw testing::TestError::Failed; |
|
| 2707 | 2701 | ||
| 2708 | 2702 | let typeName = lit.typeName else throw testing::TestError::Failed; |
| 2716 | 2710 | try expectIdent(label0, "x"); |
|
| 2717 | 2711 | try expectNumber(arg0.value, "5"); |
|
| 2718 | 2712 | } |
|
| 2719 | 2713 | ||
| 2720 | 2714 | /// Test parsing anonymous record literals with named fields. |
|
| 2721 | - | @test fn testParseAnonymousRecordLiteralNamed() throws (testing::TestError) { |
|
| 2715 | + | @test unsafe fn testParseAnonymousRecordLiteralNamed() throws (testing::TestError) { |
|
| 2722 | 2716 | let r1 = try! parseExprStr("{ x: 10, y: 20 }"); |
|
| 2723 | 2717 | let case ast::NodeValue::RecordLit(lit) = r1.value |
|
| 2724 | 2718 | else throw testing::TestError::Failed; |
|
| 2725 | 2719 | ||
| 2726 | 2720 | try testing::expect(lit.typeName == nil); |
| 2741 | 2735 | try expectNumber(arg1.value, "20"); |
|
| 2742 | 2736 | } |
|
| 2743 | 2737 | ||
| 2744 | 2738 | /// Test parsing record literals with shorthand field syntax. |
|
| 2745 | 2739 | /// `{ x, y }` is equivalent to `{ x: x, y: y }`. |
|
| 2746 | - | @test fn testParseRecordLiteralShorthand() throws (testing::TestError) { |
|
| 2740 | + | @test unsafe fn testParseRecordLiteralShorthand() throws (testing::TestError) { |
|
| 2747 | 2741 | let r1 = try! parseExprStr("Point { x, y }"); |
|
| 2748 | 2742 | let case ast::NodeValue::RecordLit(lit) = r1.value |
|
| 2749 | 2743 | else throw testing::TestError::Failed; |
|
| 2750 | 2744 | ||
| 2751 | 2745 | let typeName = lit.typeName else throw testing::TestError::Failed; |
| 2771 | 2765 | try expectIdent(arg1.value, "y"); |
|
| 2772 | 2766 | try testing::expect(label1 == arg1.value); |
|
| 2773 | 2767 | } |
|
| 2774 | 2768 | ||
| 2775 | 2769 | /// Test parsing record literals with mixed shorthand and explicit fields. |
|
| 2776 | - | @test fn testParseRecordLiteralMixedShorthand() throws (testing::TestError) { |
|
| 2770 | + | @test unsafe fn testParseRecordLiteralMixedShorthand() throws (testing::TestError) { |
|
| 2777 | 2771 | let r1 = try! parseExprStr("Point { x, y: 10 }"); |
|
| 2778 | 2772 | let case ast::NodeValue::RecordLit(lit) = r1.value |
|
| 2779 | 2773 | else throw testing::TestError::Failed; |
|
| 2780 | 2774 | ||
| 2781 | 2775 | try testing::expect(lit.fields.len == 2); |
| 2796 | 2790 | try expectIdent(label1, "y"); |
|
| 2797 | 2791 | try expectNumber(arg1.value, "10"); |
|
| 2798 | 2792 | } |
|
| 2799 | 2793 | ||
| 2800 | 2794 | /// Test that positional brace initializers are rejected: `{ 1, 2 }`. |
|
| 2801 | - | @test fn testParsePositionalBraceInitializerAnonymous() throws (testing::TestError) { |
|
| 2795 | + | @test unsafe fn testParsePositionalBraceInitializerAnonymous() throws (testing::TestError) { |
|
| 2802 | 2796 | let parsed: ?*ast::Node = try? parseExprStr("{ 1, 2 }"); |
|
| 2803 | 2797 | try testing::expect(parsed == nil); |
|
| 2804 | 2798 | } |
|
| 2805 | 2799 | ||
| 2806 | 2800 | /// Test that positional brace initializers are rejected: `Point { 1, 2 }`. |
|
| 2807 | - | @test fn testParsePositionalBraceInitializerNamed() throws (testing::TestError) { |
|
| 2801 | + | @test unsafe fn testParsePositionalBraceInitializerNamed() throws (testing::TestError) { |
|
| 2808 | 2802 | let parsed: ?*ast::Node = try? parseExprStr("Point { 1, 2 }"); |
|
| 2809 | 2803 | try testing::expect(parsed == nil); |
|
| 2810 | 2804 | } |
|
| 2811 | 2805 | ||
| 2812 | 2806 | /// Test that mixed labeled/positional brace initializers are rejected: `Pt { x: 1, 2 }`. |
|
| 2813 | - | @test fn testParseMixedBraceInitializer() throws (testing::TestError) { |
|
| 2807 | + | @test unsafe fn testParseMixedBraceInitializer() throws (testing::TestError) { |
|
| 2814 | 2808 | let parsed: ?*ast::Node = try? parseExprStr("Pt { x: 1, 2 }"); |
|
| 2815 | 2809 | try testing::expect(parsed == nil); |
|
| 2816 | 2810 | } |
|
| 2817 | 2811 | ||
| 2818 | - | @test fn testParseModule() throws (testing::TestError) { |
|
| 2812 | + | @test unsafe fn testParseModule() throws (testing::TestError) { |
|
| 2819 | 2813 | let r = try! parseStmtsStr("fn f() {} fn g() {}"); |
|
| 2820 | 2814 | ||
| 2821 | 2815 | let case ast::NodeValue::Block(module) = r.value |
|
| 2822 | 2816 | else throw testing::TestError::Failed; |
|
| 2823 | 2817 | try testing::expect(module.statements.len == 2); |
| 2832 | 2826 | else throw testing::TestError::Failed; |
|
| 2833 | 2827 | try expectIdent(gDecl.name, "g"); |
|
| 2834 | 2828 | } |
|
| 2835 | 2829 | ||
| 2836 | 2830 | /// Test parsing a simple conditional expression. |
|
| 2837 | - | @test fn testParseCondExpr() throws (testing::TestError) { |
|
| 2831 | + | @test unsafe fn testParseCondExpr() throws (testing::TestError) { |
|
| 2838 | 2832 | let r = try! parseExprStr("a if cond else b") catch { |
|
| 2839 | 2833 | throw testing::TestError::Failed; |
|
| 2840 | 2834 | }; |
|
| 2841 | 2835 | let case ast::NodeValue::CondExpr(cond) = r.value |
|
| 2842 | 2836 | else throw testing::TestError::Failed; |
| 2845 | 2839 | try expectIdent(cond.condition, "cond"); |
|
| 2846 | 2840 | try expectIdent(cond.elseExpr, "b"); |
|
| 2847 | 2841 | } |
|
| 2848 | 2842 | ||
| 2849 | 2843 | /// Test parsing a conditional expression with `as` casts. |
|
| 2850 | - | @test fn testParseCondExprWithAsCast() throws (testing::TestError) { |
|
| 2844 | + | @test unsafe fn testParseCondExprWithAsCast() throws (testing::TestError) { |
|
| 2851 | 2845 | let r = try! parseExprStr("x as i32 if cond else y as i32") catch { |
|
| 2852 | 2846 | throw testing::TestError::Failed; |
|
| 2853 | 2847 | }; |
|
| 2854 | 2848 | let case ast::NodeValue::CondExpr(cond) = r.value |
|
| 2855 | 2849 | else throw testing::TestError::Failed; |
| 2869 | 2863 | try expectIdent(elseAs.value, "y"); |
|
| 2870 | 2864 | try expectIntType(elseAs.type, 4, ast::Signedness::Signed); |
|
| 2871 | 2865 | } |
|
| 2872 | 2866 | ||
| 2873 | 2867 | /// Test parsing a nested conditional expression (right-associative). |
|
| 2874 | - | @test fn testParseCondExprNested() throws (testing::TestError) { |
|
| 2868 | + | @test unsafe fn testParseCondExprNested() throws (testing::TestError) { |
|
| 2875 | 2869 | let r = try! parseExprStr("a if x else b if y else c") catch { |
|
| 2876 | 2870 | throw testing::TestError::Failed; |
|
| 2877 | 2871 | }; |
|
| 2878 | 2872 | let case ast::NodeValue::CondExpr(outer) = r.value |
|
| 2879 | 2873 | else throw testing::TestError::Failed; |
| 2889 | 2883 | try expectIdent(inner.condition, "y"); |
|
| 2890 | 2884 | try expectIdent(inner.elseExpr, "c"); |
|
| 2891 | 2885 | } |
|
| 2892 | 2886 | ||
| 2893 | 2887 | /// Test that trailing commas are allowed in all comma-separated lists. |
|
| 2894 | - | @test fn testTrailingCommas() throws (testing::TestError) { |
|
| 2888 | + | @test unsafe fn testTrailingCommas() throws (testing::TestError) { |
|
| 2895 | 2889 | // Function call arguments. |
|
| 2896 | 2890 | let call = try! parseExprStr("f(1, 2, 3,)"); |
|
| 2897 | 2891 | let case ast::NodeValue::Call(c) = call.value else throw testing::TestError::Failed; |
|
| 2898 | 2892 | try testing::expect(c.args.len == 3); |
|
| 2899 | 2893 |
| 2928 | 2922 | try testing::expect(items.len == 3); |
|
| 2929 | 2923 | ||
| 2930 | 2924 | // Function type parameters. |
|
| 2931 | 2925 | let fnType = try! parseTypeStr("fn (i32, bool,)"); |
|
| 2932 | 2926 | let case ast::NodeValue::TypeSig(sigValue) = fnType.value else throw testing::TestError::Failed; |
|
| 2933 | - | let case ast::TypeSig::Fn(sig) = sigValue else throw testing::TestError::Failed; |
|
| 2927 | + | let case ast::TypeSig::Fn { sig, .. } = sigValue else throw testing::TestError::Failed; |
|
| 2934 | 2928 | try testing::expect(sig.params.len == 2); |
|
| 2935 | 2929 | try testing::expect(sig.returnType == nil); |
|
| 2936 | 2930 | ||
| 2937 | 2931 | // Throws lists. |
|
| 2938 | 2932 | let throwsNode = try! parseStmtStr("fn handle() throws (Error, Other,) {}"); |
|
| 2939 | 2933 | let case ast::NodeValue::FnDecl(throwsDecl) = throwsNode.value else throw testing::TestError::Failed; |
|
| 2940 | 2934 | try testing::expect(throwsDecl.sig.throwList.len == 2); |
|
| 2941 | 2935 | } |
|
| 2936 | + | ||
| 2937 | + | /// Unsafe function types preserve their call requirement and signature. |
|
| 2938 | + | @test unsafe fn testParseUnsafeFunctionType() throws (testing::TestError) { |
|
| 2939 | + | let node = try! parseTypeStr("unsafe fn(&u32) -> u32 throws (Error)"); |
|
| 2940 | + | let case ast::NodeValue::TypeSig(ast::TypeSig::Fn { sig, isUnsafe }) = node.value |
|
| 2941 | + | else throw testing::TestError::Failed; |
|
| 2942 | + | assert isUnsafe; |
|
| 2943 | + | assert sig.params.len == 1; |
|
| 2944 | + | assert sig.returnType <> nil; |
|
| 2945 | + | assert sig.throwList.len == 1; |
|
| 2946 | + | } |
lib/std/lang/resolver.rad
+488 -408
| 213 | 213 | /// Coerce a concrete pointer to a trait object. |
|
| 214 | 214 | TraitObject { |
|
| 215 | 215 | /// Trait type information. |
|
| 216 | 216 | traitInfo: *TraitType, |
|
| 217 | 217 | /// Instance entry for v-table lookup. |
|
| 218 | - | inst: *InstanceEntry, |
|
| 218 | + | inst: *unsafe InstanceEntry, |
|
| 219 | 219 | }, |
|
| 220 | 220 | } |
|
| 221 | 221 | ||
| 222 | 222 | /// Result of resolving a module path. |
|
| 223 | 223 | record ResolvedModule: Copy { |
| 625 | 625 | InvalidRefPosition, |
|
| 626 | 626 | /// A reference cannot be bound to a local. |
|
| 627 | 627 | RefBinding, |
|
| 628 | 628 | /// Call arguments contain overlapping incompatible loans. |
|
| 629 | 629 | BorrowConflict(*[u8]), |
|
| 630 | - | /// Unsafe pointer operation outside an `unsafe` declaration. |
|
| 630 | + | /// Unsafe pointer operation outside an `unsafe` function. |
|
| 631 | 631 | UnsafeOperation, |
|
| 632 | 632 | /// Safe code cannot call an `unsafe` function. |
|
| 633 | 633 | UnsafeCall, |
|
| 634 | 634 | /// Internal error. |
|
| 635 | 635 | Internal, |
| 684 | 684 | traitInfo: *TraitType, |
|
| 685 | 685 | /// Method index in the v-table. |
|
| 686 | 686 | methodIndex: u32, |
|
| 687 | 687 | }, |
|
| 688 | 688 | /// Standalone method call metadata. |
|
| 689 | - | MethodCall { method: *MethodEntry }, |
|
| 689 | + | MethodCall { method: *unsafe MethodEntry }, |
|
| 690 | 690 | /// Slice `.append(val, allocator)` method call. |
|
| 691 | 691 | SliceAppend { elemType: *Type }, |
|
| 692 | 692 | /// Slice `.delete(index)` method call. |
|
| 693 | 693 | SliceDelete { elemType: *Type }, |
|
| 694 | 694 | } |
| 812 | 812 | /// Function-local exact-use checker state. |
|
| 813 | 813 | /// Read loop arrays only at indices below `loopDepth`. |
|
| 814 | 814 | /// `enterLinearLoop` initializes each slot before it increases `loopDepth`. |
|
| 815 | 815 | record LinearChecker: Copy { |
|
| 816 | 816 | /// Resolver that owns the symbols and diagnostics. |
|
| 817 | - | resolver: *mut Resolver, |
|
| 817 | + | resolver: *unsafe mut Resolver, |
|
| 818 | 818 | /// Binding count at entry to each active loop. |
|
| 819 | 819 | loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH], |
|
| 820 | 820 | /// Available bindings at entry to each active loop. |
|
| 821 | 821 | loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH], |
|
| 822 | 822 | /// Available bindings shared by the exits from each active loop. |
| 847 | 847 | /// Stack of loop contexts for nested loops. |
|
| 848 | 848 | loopStack: [LoopCtx; MAX_LOOP_DEPTH], |
|
| 849 | 849 | /// Current loop depth, indexes into loop stack. |
|
| 850 | 850 | loopDepth: u32, |
|
| 851 | 851 | /// Signature of the function currently being analyzed. |
|
| 852 | - | currentFn: ?*FnType, |
|
| 852 | + | currentFn: ?*unsafe FnType, |
|
| 853 | 853 | /// Current module being analyzed. |
|
| 854 | 854 | currentMod: u16, |
|
| 855 | - | /// Nesting depth of unsafe modules and function bodies. |
|
| 856 | - | unsafeDepth: u32, |
|
| 855 | + | /// Whether the current function permits unsafe operations. |
|
| 856 | + | inUnsafeFn: bool, |
|
| 857 | 857 | /// Configuration for semantic analysis. |
|
| 858 | 858 | config: Config, |
|
| 859 | 859 | /// Unified arena for symbols, scopes, and nominal type. |
|
| 860 | 860 | arena: alloc::Arena, |
|
| 861 | 861 | /// Combined semantic metadata table indexed by node ID. |
| 863 | 863 | /// Linked list of interned types. |
|
| 864 | 864 | types: ?*TypeNode, |
|
| 865 | 865 | /// Diagnostics recorded so far. |
|
| 866 | 866 | errors: *mut [Error], |
|
| 867 | 867 | /// Module graph for the current package. |
|
| 868 | - | moduleGraph: *module::ModuleGraph, |
|
| 868 | + | moduleGraph: *unsafe module::ModuleGraph, |
|
| 869 | 869 | /// Cache of module scopes indexed by module ID. |
|
| 870 | 870 | moduleScopes: [?*mut Scope; module::MAX_MODULES], |
|
| 871 | 871 | /// Trait instance registry. |
|
| 872 | 872 | instances: [InstanceEntry; MAX_INSTANCES], |
|
| 873 | 873 | /// Number of registered instances. |
| 888 | 888 | ty: Type, |
|
| 889 | 889 | next: ?*TypeNode, |
|
| 890 | 890 | } |
|
| 891 | 891 | ||
| 892 | 892 | /// Allocate and intern a type in the arena, returning a pointer for deduplication. |
|
| 893 | - | export fn allocType(self: *mut Resolver, ty: Type) -> *Type { |
|
| 893 | + | export fn allocType(self: &mut Resolver, ty: Type) -> *Type { |
|
| 894 | 894 | // Search existing types for a match. |
|
| 895 | 895 | let mut cursor = self.types; |
|
| 896 | 896 | while let node = cursor { |
|
| 897 | 897 | if node.ty == ty { |
|
| 898 | 898 | return &node.ty; |
| 909 | 909 | ||
| 910 | 910 | return &node.ty; |
|
| 911 | 911 | } |
|
| 912 | 912 | ||
| 913 | 913 | /// Allocate a nominal type descriptor and return a pointer to it. |
|
| 914 | - | fn allocNominalType(self: *mut Resolver, info: NominalType) -> *mut NominalType { |
|
| 914 | + | fn allocNominalType(self: &mut Resolver, info: NominalType) -> *mut NominalType { |
|
| 915 | 915 | // Nb. We don't attempt to de-duplicate nominal type entries, |
|
| 916 | 916 | // since they don't carry node information and we create |
|
| 917 | 917 | // placeholder entries when binding symbols. |
|
| 918 | 918 | let entry = try! alloc::alloc( |
|
| 919 | 919 | &mut self.arena, @sizeOf(NominalType), @alignOf(NominalType) |
| 923 | 923 | ||
| 924 | 924 | return entry; |
|
| 925 | 925 | } |
|
| 926 | 926 | ||
| 927 | 927 | /// Allocate a function type descriptor and return a pointer to it. |
|
| 928 | - | fn allocFnType(self: *mut Resolver, info: FnType) -> *FnType { |
|
| 928 | + | fn allocFnType(self: &mut Resolver, info: FnType) -> *FnType { |
|
| 929 | 929 | let entry = try! alloc::alloc( |
|
| 930 | 930 | &mut self.arena, @sizeOf(FnType), @alignOf(FnType) |
|
| 931 | 931 | ) as *mut FnType; |
|
| 932 | 932 | ||
| 933 | 933 | set *entry = info; |
|
| 934 | 934 | ||
| 935 | 935 | return entry; |
|
| 936 | 936 | } |
|
| 937 | 937 | ||
| 938 | 938 | /// Returns an error, if any, associated with the given node. |
|
| 939 | - | fn errorForNode(self: *Resolver, node: *ast::Node) -> ?*Error { |
|
| 939 | + | fn errorForNode(self: &Resolver, node: *ast::Node) -> ?*Error { |
|
| 940 | 940 | for i in 0..self.errors.len { |
|
| 941 | 941 | let err = &self.errors[i]; |
|
| 942 | 942 | if err.node == node { |
|
| 943 | 943 | return err; |
|
| 944 | 944 | } |
| 1009 | 1009 | pkgScope: storage.pkgScope, |
|
| 1010 | 1010 | loopStack: undefined, |
|
| 1011 | 1011 | loopDepth: 0, |
|
| 1012 | 1012 | currentFn: nil, |
|
| 1013 | 1013 | currentMod: 0, |
|
| 1014 | - | unsafeDepth: 0, |
|
| 1014 | + | inUnsafeFn: false, |
|
| 1015 | 1015 | config, |
|
| 1016 | 1016 | arena, |
|
| 1017 | 1017 | nodeData: NodeDataTable { entries: storage.nodeData }, |
|
| 1018 | 1018 | types: nil, |
|
| 1019 | 1019 | errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len), |
| 1026 | 1026 | methodsLen: 0, |
|
| 1027 | 1027 | }; |
|
| 1028 | 1028 | } |
|
| 1029 | 1029 | ||
| 1030 | 1030 | /// Return `true` if there are no errors in the diagnostics. |
|
| 1031 | - | export fn success(diag: *Diagnostics) -> bool { |
|
| 1031 | + | export fn success(diag: &Diagnostics) -> bool { |
|
| 1032 | 1032 | return diag.errors.len == 0; |
|
| 1033 | 1033 | } |
|
| 1034 | 1034 | ||
| 1035 | 1035 | /// Retrieve an error diagnostic by index, if present. |
|
| 1036 | 1036 | export fn errorAt(errs: *[Error], index: u32) -> ?*Error { |
| 1039 | 1039 | } |
|
| 1040 | 1040 | return &errs[index]; |
|
| 1041 | 1041 | } |
|
| 1042 | 1042 | ||
| 1043 | 1043 | /// Record an error diagnostic and return an error sentinel suitable for throwing. |
|
| 1044 | - | fn emitError(self: *mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 1044 | + | fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 1045 | 1045 | // If our error list is full, just return an error without recording it. |
|
| 1046 | 1046 | if self.errors.len >= self.errors.cap { |
|
| 1047 | 1047 | return ResolveError::Failure; |
|
| 1048 | 1048 | } |
|
| 1049 | 1049 | // Don't record more than one error per node. |
| 1056 | 1056 | ||
| 1057 | 1057 | return ResolveError::Failure; |
|
| 1058 | 1058 | } |
|
| 1059 | 1059 | ||
| 1060 | 1060 | /// Like [`emitError`], but for type mismatches specifically. |
|
| 1061 | - | fn emitTypeMismatch(self: *mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError { |
|
| 1061 | + | fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError { |
|
| 1062 | 1062 | return emitError(self, node, ErrorKind::TypeMismatch(mismatch)); |
|
| 1063 | 1063 | } |
|
| 1064 | 1064 | ||
| 1065 | 1065 | /// Allocate a scope object with the given symbol capacity. |
|
| 1066 | - | fn allocScope(self: *mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope { |
|
| 1066 | + | fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope { |
|
| 1067 | 1067 | // Check for an existing scope for this node, and don't allocate a new |
|
| 1068 | 1068 | // one in that case. |
|
| 1069 | 1069 | if let scope = scopeFor(self, owner) { |
|
| 1070 | 1070 | return scope; |
|
| 1071 | 1071 | } |
| 1085 | 1085 | } |
|
| 1086 | 1086 | ||
| 1087 | 1087 | /// Enter a new local scope that is the child of the current scope. |
|
| 1088 | 1088 | /// This creates a parent/child relationship that means that lookups in the |
|
| 1089 | 1089 | /// child scope can recurse upwards. |
|
| 1090 | - | export fn enterScope(self: *mut Resolver, owner: *ast::Node) -> *Scope { |
|
| 1090 | + | export fn enterScope(self: &mut Resolver, owner: *ast::Node) -> *Scope { |
|
| 1091 | 1091 | let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS); |
|
| 1092 | 1092 | set scope.parent = self.scope; |
|
| 1093 | 1093 | set self.scope = scope; |
|
| 1094 | 1094 | return scope; |
|
| 1095 | 1095 | } |
|
| 1096 | 1096 | ||
| 1097 | 1097 | /// Enter a module scope. Returns an object that can be used to exit the scope. |
|
| 1098 | - | export fn enterModuleScope(self: *mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope { |
|
| 1098 | + | export fn enterModuleScope(self: &mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope { |
|
| 1099 | 1099 | let prevScope = self.scope; |
|
| 1100 | 1100 | let prevMod = self.currentMod; |
|
| 1101 | 1101 | let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS); |
|
| 1102 | 1102 | ||
| 1103 | 1103 | set self.scope = scope; |
| 1108 | 1108 | ||
| 1109 | 1109 | return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod }; |
|
| 1110 | 1110 | } |
|
| 1111 | 1111 | ||
| 1112 | 1112 | /// Enter a sub-module. Changes the current scope into that of the sub-module. |
|
| 1113 | - | fn enterSubModule(self: *mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) { |
|
| 1114 | - | let modEntry = module::findChild(self.moduleGraph, name, self.currentMod) |
|
| 1113 | + | unsafe fn enterSubModule(self: &mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) { |
|
| 1114 | + | let modEntry = module::findChild(&*self.moduleGraph, name, self.currentMod) |
|
| 1115 | 1115 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 1116 | 1116 | let modRoot = modEntry.ast |
|
| 1117 | 1117 | else panic "enterSubModule: analyzing module that wasn't parsed"; |
|
| 1118 | 1118 | ||
| 1119 | 1119 | return enterModuleScope(self, modRoot, modEntry); |
|
| 1120 | 1120 | } |
|
| 1121 | 1121 | ||
| 1122 | 1122 | /// Exit a module scope, given the object returned by `enterModuleScope`. |
|
| 1123 | - | export fn exitModuleScope(self: *mut Resolver, entry: ModuleScope) { |
|
| 1123 | + | export fn exitModuleScope(self: &mut Resolver, entry: ModuleScope) { |
|
| 1124 | 1124 | set self.scope = entry.prevScope; |
|
| 1125 | 1125 | set self.currentMod = entry.prevMod; |
|
| 1126 | 1126 | } |
|
| 1127 | 1127 | ||
| 1128 | 1128 | /// Exit the most recent scope. |
|
| 1129 | - | export fn exitScope(self: *mut Resolver) { |
|
| 1129 | + | export fn exitScope(self: &mut Resolver) { |
|
| 1130 | 1130 | let parent = self.scope.parent else { |
|
| 1131 | 1131 | // TODO: This should be a panic, but one of the tests hits this |
|
| 1132 | 1132 | // clause, which might be a bug in the generator. |
|
| 1133 | 1133 | return; |
|
| 1134 | 1134 | }; |
|
| 1135 | 1135 | set self.scope = parent; |
|
| 1136 | 1136 | } |
|
| 1137 | 1137 | ||
| 1138 | 1138 | /// Visit the body of a loop while tracking nesting depth. |
|
| 1139 | - | fn visitLoop(self: *mut Resolver, body: *ast::Node) -> Type |
|
| 1139 | + | unsafe fn visitLoop(self: &mut Resolver, body: *ast::Node) -> Type |
|
| 1140 | 1140 | throws (ResolveError) |
|
| 1141 | 1141 | { |
|
| 1142 | 1142 | assert self.loopDepth < MAX_LOOP_DEPTH, "visitLoop: loop nesting depth exceeded"; |
|
| 1143 | 1143 | set self.loopStack[self.loopDepth] = LoopCtx { hasBreak: false }; |
|
| 1144 | 1144 | set self.loopDepth += 1; |
| 1156 | 1156 | } |
|
| 1157 | 1157 | return Type::Never; |
|
| 1158 | 1158 | } |
|
| 1159 | 1159 | ||
| 1160 | 1160 | /// Require that loop control statements appear inside a loop. |
|
| 1161 | - | fn ensureInsideLoop(self: *mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 1161 | + | fn ensureInsideLoop(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 1162 | 1162 | if self.loopDepth == 0 { |
|
| 1163 | 1163 | throw emitError(self, node, ErrorKind::InvalidLoopControl); |
|
| 1164 | 1164 | } |
|
| 1165 | 1165 | } |
|
| 1166 | 1166 | ||
| 1167 | 1167 | /// Bind a loop pattern to the provided type. |
|
| 1168 | - | fn bindForLoopPattern(self: *mut Resolver, pattern: *ast::Node, ty: Type, mutable: bool) |
|
| 1168 | + | unsafe fn bindForLoopPattern(self: &mut Resolver, pattern: *ast::Node, ty: Type, mutable: bool) |
|
| 1169 | 1169 | throws (ResolveError) |
|
| 1170 | 1170 | { |
|
| 1171 | 1171 | match pattern.value { |
|
| 1172 | 1172 | case ast::NodeValue::Placeholder, ast::NodeValue::Ident(_) => { |
|
| 1173 | 1173 | let _ = try bindValueIdent(self, pattern, pattern, ty, mutable, 0, 0); |
| 1178 | 1178 | } |
|
| 1179 | 1179 | } |
|
| 1180 | 1180 | } |
|
| 1181 | 1181 | ||
| 1182 | 1182 | /// Set the expected return type for a new function body. |
|
| 1183 | - | fn enterFn(self: *mut Resolver, node: *ast::Node, ty: *FnType) { |
|
| 1183 | + | unsafe fn enterFn(self: &mut Resolver, node: *ast::Node, ty: &FnType) { |
|
| 1184 | 1184 | assert self.currentFn == nil, "enterFn: already in a function"; |
|
| 1185 | - | set self.currentFn = ty; |
|
| 1185 | + | set self.currentFn = ty as *unsafe FnType; |
|
| 1186 | 1186 | enterScope(self, node); |
|
| 1187 | 1187 | } |
|
| 1188 | 1188 | ||
| 1189 | 1189 | /// Clear the expected return type when leaving a function body. |
|
| 1190 | - | fn exitFn(self: *mut Resolver) { |
|
| 1190 | + | fn exitFn(self: &mut Resolver) { |
|
| 1191 | 1191 | if self.currentFn == nil { |
|
| 1192 | 1192 | // TODO: This should be a panic, but one of the tests hits this |
|
| 1193 | 1193 | // clause, which might be a bug in the generator. |
|
| 1194 | 1194 | return; |
|
| 1195 | 1195 | } |
|
| 1196 | 1196 | set self.currentFn = nil; |
|
| 1197 | 1197 | exitScope(self); |
|
| 1198 | 1198 | } |
|
| 1199 | 1199 | ||
| 1200 | 1200 | /// Extract the identifier text from a node. |
|
| 1201 | - | fn nodeName(self: *mut Resolver, node: *ast::Node) -> *[u8] |
|
| 1201 | + | fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8] |
|
| 1202 | 1202 | throws (ResolveError) |
|
| 1203 | 1203 | { |
|
| 1204 | 1204 | let case ast::NodeValue::Ident(name) = node.value |
|
| 1205 | 1205 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
|
| 1206 | 1206 | return name; |
|
| 1207 | 1207 | } |
|
| 1208 | 1208 | ||
| 1209 | 1209 | /// Associate a resolved symbol with an AST node. |
|
| 1210 | - | fn setNodeSymbol(self: *mut Resolver, node: *ast::Node, symbol: *mut Symbol) { |
|
| 1210 | + | fn setNodeSymbol(self: &mut Resolver, node: *ast::Node, symbol: *mut Symbol) { |
|
| 1211 | 1211 | if let existingSym = self.nodeData.entries[node.id].sym { |
|
| 1212 | 1212 | panic "setNodeSymbol: a symbol is already associated with this node"; |
|
| 1213 | 1213 | } |
|
| 1214 | 1214 | set self.nodeData.entries[node.id].sym = symbol; |
|
| 1215 | 1215 | } |
|
| 1216 | 1216 | ||
| 1217 | 1217 | /// Associate a resolved type with an AST node and return it. |
|
| 1218 | - | fn setNodeType(self: *mut Resolver, node: *ast::Node, ty: Type) -> Type { |
|
| 1218 | + | fn setNodeType(self: &mut Resolver, node: *ast::Node, ty: Type) -> Type { |
|
| 1219 | 1219 | if ty == Type::Unknown { |
|
| 1220 | 1220 | // In this case, we simply don't associate a type. |
|
| 1221 | 1221 | return ty; |
|
| 1222 | 1222 | } |
|
| 1223 | 1223 | set self.nodeData.entries[node.id].ty = ty; |
| 1236 | 1236 | } |
|
| 1237 | 1237 | return Type::Void; |
|
| 1238 | 1238 | } |
|
| 1239 | 1239 | ||
| 1240 | 1240 | /// Associate a coercion plan with an AST node. |
|
| 1241 | - | fn setNodeCoercion(self: *mut Resolver, node: *ast::Node, coercion: Coercion) -> Coercion { |
|
| 1241 | + | fn setNodeCoercion(self: &mut Resolver, node: *ast::Node, coercion: Coercion) -> Coercion { |
|
| 1242 | 1242 | if coercion == Coercion::Identity { |
|
| 1243 | 1243 | return coercion; |
|
| 1244 | 1244 | } |
|
| 1245 | 1245 | set self.nodeData.entries[node.id].coercion = coercion; |
|
| 1246 | 1246 | ||
| 1247 | 1247 | return coercion; |
|
| 1248 | 1248 | } |
|
| 1249 | 1249 | ||
| 1250 | 1250 | /// Associate a constant value with an AST node. |
|
| 1251 | - | fn setNodeConstValue(self: *mut Resolver, node: *ast::Node, value: ConstValue) { |
|
| 1251 | + | fn setNodeConstValue(self: &mut Resolver, node: *ast::Node, value: ConstValue) { |
|
| 1252 | 1252 | set self.nodeData.entries[node.id].constValue = value; |
|
| 1253 | 1253 | } |
|
| 1254 | 1254 | ||
| 1255 | 1255 | /// Associate a record field index with a record literal field node. |
|
| 1256 | - | fn setRecordFieldIndex(self: *mut Resolver, node: *ast::Node, index: u32) { |
|
| 1256 | + | fn setRecordFieldIndex(self: &mut Resolver, node: *ast::Node, index: u32) { |
|
| 1257 | 1257 | set self.nodeData.entries[node.id].extra = NodeExtra::RecordField { index }; |
|
| 1258 | 1258 | } |
|
| 1259 | 1259 | ||
| 1260 | 1260 | /// Associate slice range metadata with a subscript expression. |
|
| 1261 | - | fn setSliceRangeInfo(self: *mut Resolver, node: *ast::Node, info: SliceRangeInfo) { |
|
| 1261 | + | fn setSliceRangeInfo(self: &mut Resolver, node: *ast::Node, info: SliceRangeInfo) { |
|
| 1262 | 1262 | set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info); |
|
| 1263 | 1263 | } |
|
| 1264 | 1264 | ||
| 1265 | 1265 | /// Associate union variant metadata with a pattern or constructor node. |
|
| 1266 | - | fn setVariantInfo(self: *mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) { |
|
| 1266 | + | fn setVariantInfo(self: &mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) { |
|
| 1267 | 1267 | set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag }; |
|
| 1268 | 1268 | } |
|
| 1269 | 1269 | ||
| 1270 | 1270 | /// Associate trait method call metadata with a call node. |
|
| 1271 | - | fn setTraitMethodCall(self: *mut Resolver, node: *ast::Node, traitInfo: *TraitType, methodIndex: u32) { |
|
| 1271 | + | fn setTraitMethodCall(self: &mut Resolver, node: *ast::Node, traitInfo: *TraitType, methodIndex: u32) { |
|
| 1272 | 1272 | set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex }; |
|
| 1273 | 1273 | } |
|
| 1274 | 1274 | ||
| 1275 | 1275 | /// Associate for-loop metadata with a for-loop node. |
|
| 1276 | - | fn setForLoopInfo(self: *mut Resolver, node: *ast::Node, info: ForLoopInfo) { |
|
| 1276 | + | fn setForLoopInfo(self: &mut Resolver, node: *ast::Node, info: ForLoopInfo) { |
|
| 1277 | 1277 | set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info); |
|
| 1278 | 1278 | } |
|
| 1279 | 1279 | ||
| 1280 | 1280 | /// Retrieve the constant value associated with a node, if any. |
|
| 1281 | - | export fn constValueEntry(self: *Resolver, node: *ast::Node) -> ?ConstValue { |
|
| 1281 | + | export fn constValueEntry(self: &Resolver, node: *ast::Node) -> ?ConstValue { |
|
| 1282 | 1282 | return self.nodeData.entries[node.id].constValue; |
|
| 1283 | 1283 | } |
|
| 1284 | 1284 | ||
| 1285 | 1285 | /// Get the resolved record field index for a record literal field node. |
|
| 1286 | - | export fn recordFieldIndexFor(self: *Resolver, node: *ast::Node) -> ?u32 { |
|
| 1286 | + | export fn recordFieldIndexFor(self: &Resolver, node: *ast::Node) -> ?u32 { |
|
| 1287 | 1287 | if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra { |
|
| 1288 | 1288 | return index; |
|
| 1289 | 1289 | } |
|
| 1290 | 1290 | return nil; |
|
| 1291 | 1291 | } |
|
| 1292 | 1292 | ||
| 1293 | 1293 | /// Get the slice range metadata for a subscript expression with a range index. |
|
| 1294 | - | export fn sliceRangeInfoFor(self: *Resolver, node: *ast::Node) -> ?SliceRangeInfo { |
|
| 1294 | + | export fn sliceRangeInfoFor(self: &Resolver, node: *ast::Node) -> ?SliceRangeInfo { |
|
| 1295 | 1295 | if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra { |
|
| 1296 | 1296 | return info; |
|
| 1297 | 1297 | } |
|
| 1298 | 1298 | return nil; |
|
| 1299 | 1299 | } |
|
| 1300 | 1300 | ||
| 1301 | 1301 | /// Get the for-loop metadata for a for-loop node. |
|
| 1302 | - | export fn forLoopInfoFor(self: *Resolver, node: *ast::Node) -> ?ForLoopInfo { |
|
| 1302 | + | export fn forLoopInfoFor(self: &Resolver, node: *ast::Node) -> ?ForLoopInfo { |
|
| 1303 | 1303 | if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra { |
|
| 1304 | 1304 | return info; |
|
| 1305 | 1305 | } |
|
| 1306 | 1306 | return nil; |
|
| 1307 | 1307 | } |
|
| 1308 | 1308 | ||
| 1309 | 1309 | /// Associate match prong metadata with a match prong node. |
|
| 1310 | - | fn setProngCatchAll(self: *mut Resolver, node: *ast::Node, catchAll: bool) { |
|
| 1310 | + | fn setProngCatchAll(self: &mut Resolver, node: *ast::Node, catchAll: bool) { |
|
| 1311 | 1311 | set self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll }; |
|
| 1312 | 1312 | } |
|
| 1313 | 1313 | ||
| 1314 | 1314 | /// Check if a prong is catch-all. |
|
| 1315 | - | export fn isProngCatchAll(self: *Resolver, node: *ast::Node) -> bool { |
|
| 1315 | + | export fn isProngCatchAll(self: &Resolver, node: *ast::Node) -> bool { |
|
| 1316 | 1316 | if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra { |
|
| 1317 | 1317 | return catchAll; |
|
| 1318 | 1318 | } |
|
| 1319 | 1319 | return false; |
|
| 1320 | 1320 | } |
|
| 1321 | 1321 | ||
| 1322 | 1322 | /// Set match metadata. |
|
| 1323 | - | fn setMatchConst(self: *mut Resolver, node: *ast::Node, isConst: bool) { |
|
| 1323 | + | fn setMatchConst(self: &mut Resolver, node: *ast::Node, isConst: bool) { |
|
| 1324 | 1324 | set self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst }; |
|
| 1325 | 1325 | } |
|
| 1326 | 1326 | ||
| 1327 | 1327 | /// Check if a match has all constant patterns. |
|
| 1328 | - | export fn isMatchConst(self: *Resolver, node: *ast::Node) -> bool { |
|
| 1328 | + | export fn isMatchConst(self: &Resolver, node: *ast::Node) -> bool { |
|
| 1329 | 1329 | if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra { |
|
| 1330 | 1330 | return isConst; |
|
| 1331 | 1331 | } |
|
| 1332 | 1332 | return false; |
|
| 1333 | 1333 | } |
|
| 1334 | 1334 | ||
| 1335 | 1335 | /// Get the resolver metadata for a node. |
|
| 1336 | - | export fn nodeData(self: *Resolver, node: *ast::Node) -> *NodeData { |
|
| 1336 | + | export fn nodeData(self: &Resolver, node: *ast::Node) -> *NodeData { |
|
| 1337 | 1337 | return &self.nodeData.entries[node.id]; |
|
| 1338 | 1338 | } |
|
| 1339 | 1339 | ||
| 1340 | 1340 | /// Get the type for a node, or `nil` if unknown. |
|
| 1341 | - | export fn typeFor(self: *Resolver, node: *ast::Node) -> ?Type { |
|
| 1341 | + | export fn typeFor(self: &Resolver, node: *ast::Node) -> ?Type { |
|
| 1342 | 1342 | let ty = self.nodeData.entries[node.id].ty; |
|
| 1343 | 1343 | if ty == Type::Unknown { |
|
| 1344 | 1344 | return nil; |
|
| 1345 | 1345 | } |
|
| 1346 | 1346 | return ty; |
|
| 1347 | 1347 | } |
|
| 1348 | 1348 | ||
| 1349 | 1349 | /// Get the scope associated with a node. |
|
| 1350 | - | export fn scopeFor(self: *Resolver, node: *ast::Node) -> ?*mut Scope { |
|
| 1350 | + | export fn scopeFor(self: &Resolver, node: *ast::Node) -> ?*mut Scope { |
|
| 1351 | 1351 | return self.nodeData.entries[node.id].scope; |
|
| 1352 | 1352 | } |
|
| 1353 | 1353 | ||
| 1354 | 1354 | /// Get the symbol bound to a node. |
|
| 1355 | - | export fn symbolFor(self: *Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 1355 | + | export fn symbolFor(self: &Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 1356 | 1356 | return self.nodeData.entries[node.id].sym; |
|
| 1357 | 1357 | } |
|
| 1358 | 1358 | ||
| 1359 | 1359 | /// Get the coercion plan associated with a node, if any. |
|
| 1360 | - | export fn coercionFor(self: *Resolver, node: *ast::Node) -> ?Coercion { |
|
| 1360 | + | export fn coercionFor(self: &Resolver, node: *ast::Node) -> ?Coercion { |
|
| 1361 | 1361 | let c = self.nodeData.entries[node.id].coercion; |
|
| 1362 | 1362 | if c == Coercion::Identity { |
|
| 1363 | 1363 | return nil; |
|
| 1364 | 1364 | } |
|
| 1365 | 1365 | return c; |
|
| 1366 | 1366 | } |
|
| 1367 | 1367 | ||
| 1368 | 1368 | /// Get the module ID for a symbol by walking up its scope chain. |
|
| 1369 | - | export fn moduleIdForSymbol(self: *Resolver, sym: *Symbol) -> ?u16 { |
|
| 1369 | + | export fn moduleIdForSymbol(self: &Resolver, sym: *Symbol) -> ?u16 { |
|
| 1370 | 1370 | // For module-level symbols, return the cached module ID. |
|
| 1371 | 1371 | if let id = sym.moduleId { |
|
| 1372 | 1372 | return id; |
|
| 1373 | 1373 | } |
|
| 1374 | 1374 | // For module symbols, return the module ID directly. |
| 1382 | 1382 | return nil; |
|
| 1383 | 1383 | } |
|
| 1384 | 1384 | ||
| 1385 | 1385 | /// Get the binding node for a variant pattern. |
|
| 1386 | 1386 | /// Returns the argument node if this is a variant constructor with a non-placeholder binding. |
|
| 1387 | - | export fn variantPatternBinding(self: *Resolver, pattern: *ast::Node) -> ?*ast::Node { |
|
| 1387 | + | export fn variantPatternBinding(self: &Resolver, pattern: *ast::Node) -> ?*ast::Node { |
|
| 1388 | 1388 | let case ast::NodeValue::Call(call) = pattern.value |
|
| 1389 | 1389 | else return nil; |
|
| 1390 | 1390 | let sym = symbolFor(self, call.callee) |
|
| 1391 | 1391 | else return nil; |
|
| 1392 | 1392 | let case SymbolData::Variant { .. } = sym.data |
| 1402 | 1402 | } |
|
| 1403 | 1403 | return arg; |
|
| 1404 | 1404 | } |
|
| 1405 | 1405 | ||
| 1406 | 1406 | /// Allocate a new symbol, and return a reference to it. |
|
| 1407 | - | fn allocSymbol(self: *mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol { |
|
| 1407 | + | fn allocSymbol(self: &mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol { |
|
| 1408 | 1408 | let sym = try! alloc::alloc(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *mut Symbol; |
|
| 1409 | 1409 | set *sym = Symbol { name, data, attrs, node, moduleId: nil }; |
|
| 1410 | 1410 | ||
| 1411 | 1411 | return sym; |
|
| 1412 | 1412 | } |
|
| 1413 | 1413 | ||
| 1414 | 1414 | /// Check that a type is boolean, otherwise throw an error. |
|
| 1415 | - | fn checkBoolean(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 1415 | + | unsafe fn checkBoolean(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 1416 | 1416 | return try checkEqual(self, node, Type::Bool); |
|
| 1417 | 1417 | } |
|
| 1418 | 1418 | ||
| 1419 | 1419 | /// Check that a type is numeric, otherwise throw an error. |
|
| 1420 | - | fn checkNumeric(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 1420 | + | unsafe fn checkNumeric(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 1421 | 1421 | let ty = try infer(self, node); |
|
| 1422 | 1422 | if not isNumericType(ty) { |
|
| 1423 | 1423 | throw emitError(self, node, ErrorKind::ExpectedNumeric); |
|
| 1424 | 1424 | } |
|
| 1425 | 1425 | return ty; |
| 1472 | 1472 | } |
|
| 1473 | 1473 | } |
|
| 1474 | 1474 | } |
|
| 1475 | 1475 | ||
| 1476 | 1476 | /// Get the layout of a type or value. |
|
| 1477 | - | export fn getLayout(self: *Resolver, node: *ast::Node, ty: Type) -> Layout { |
|
| 1477 | + | export fn getLayout(self: &Resolver, node: *ast::Node, ty: Type) -> Layout { |
|
| 1478 | 1478 | let mut layout = getTypeLayout(ty); |
|
| 1479 | 1479 | // Check for symbol-specific alignment override. |
|
| 1480 | 1480 | if let sym = symbolFor(self, node) { |
|
| 1481 | 1481 | if let case SymbolData::Value { alignment, .. } = sym.data { |
|
| 1482 | 1482 | if alignment > 0 { |
| 1610 | 1610 | return UnionLayoutInfo { layout: unionLayout, valOffset: unionValOffset, isAllVoid }; |
|
| 1611 | 1611 | } |
|
| 1612 | 1612 | ||
| 1613 | 1613 | /// Compute the discriminant tag for a variant, advancing the iota counter. |
|
| 1614 | 1614 | /// If the variant has an explicit `= N` value, uses that; otherwise uses iota. |
|
| 1615 | - | fn variantTag(variantDecl: ast::UnionDeclVariant, iota: *mut u32) -> u32 { |
|
| 1615 | + | fn variantTag(variantDecl: ast::UnionDeclVariant, iota: &mut u32) -> u32 { |
|
| 1616 | 1616 | let mut tag: u32 = *iota; |
|
| 1617 | 1617 | if let valueNode = variantDecl.value { |
|
| 1618 | 1618 | let case ast::NodeValue::Number(lit) = valueNode.value |
|
| 1619 | 1619 | else panic "variantTag: expected number literal"; |
|
| 1620 | 1620 | set tag = lit.magnitude as u32; |
| 1704 | 1704 | } |
|
| 1705 | 1705 | } |
|
| 1706 | 1706 | } |
|
| 1707 | 1707 | ||
| 1708 | 1708 | /// Ensure all nested nominal types in a type are resolved. |
|
| 1709 | - | fn ensureTypeResolved(self: *mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) { |
|
| 1709 | + | unsafe fn ensureTypeResolved(self: &mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) { |
|
| 1710 | 1710 | match ty { |
|
| 1711 | 1711 | case Type::Nominal(info) => try ensureNominalResolved(self, info, site), |
|
| 1712 | 1712 | case Type::Slice { item, .. } => try ensureTypeResolved(self, *item, site), |
|
| 1713 | 1713 | case Type::Pointer { .. } => {}, // Pointers have fixed layout, don't recurse. |
|
| 1714 | 1714 | case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site), |
| 1716 | 1716 | else => {}, |
|
| 1717 | 1717 | } |
|
| 1718 | 1718 | } |
|
| 1719 | 1719 | ||
| 1720 | 1720 | /// Ensure a nominal type has its body resolved. |
|
| 1721 | - | fn ensureNominalResolved(self: *mut Resolver, tyInfo: *NominalType, site: *ast::Node) |
|
| 1721 | + | unsafe fn ensureNominalResolved(self: &mut Resolver, tyInfo: *NominalType, site: *ast::Node) |
|
| 1722 | 1722 | throws (ResolveError) |
|
| 1723 | 1723 | { |
|
| 1724 | 1724 | if let case NominalType::Placeholder(declNode) = *tyInfo { |
|
| 1725 | 1725 | // When resolving on-demand (e.g. from a child module), switch to the |
|
| 1726 | 1726 | // declaring module's scope so field type lookups find the right symbols. |
| 1751 | 1751 | set self.currentMod = prevMod; |
|
| 1752 | 1752 | } |
|
| 1753 | 1753 | } |
|
| 1754 | 1754 | ||
| 1755 | 1755 | /// Check if all elements in a node list are assignable to the target type. |
|
| 1756 | - | fn isListAssignable(self: *mut Resolver, targetType: Type, items: *mut [*ast::Node]) -> bool { |
|
| 1756 | + | unsafe fn isListAssignable(self: &mut Resolver, targetType: Type, items: *mut [*ast::Node]) -> bool { |
|
| 1757 | 1757 | for itemNode in items { |
|
| 1758 | 1758 | let elemTy = typeFor(self, itemNode) |
|
| 1759 | 1759 | else return false; |
|
| 1760 | 1760 | if let _ = isAssignable(self, targetType, elemTy, itemNode) { |
|
| 1761 | 1761 | // Do nothing. |
| 1770 | 1770 | fn pointerClassesAssignable( |
|
| 1771 | 1771 | to: types::PointerClass, |
|
| 1772 | 1772 | from: types::PointerClass, |
|
| 1773 | 1773 | ) -> bool { |
|
| 1774 | 1774 | return to == from or ( |
|
| 1775 | - | to == types::PointerClass::Owned |
|
| 1776 | - | and from == types::PointerClass::Ref |
|
| 1775 | + | to == types::PointerClass::Ref |
|
| 1776 | + | and from == types::PointerClass::Owned |
|
| 1777 | 1777 | ); |
|
| 1778 | 1778 | } |
|
| 1779 | 1779 | ||
| 1780 | 1780 | /// Check if the `from` type is assignable to the `to` type, and return a |
|
| 1781 | 1781 | /// coercion plan if so. |
|
| 1782 | - | fn isAssignable(self: *mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion { |
|
| 1782 | + | unsafe fn isAssignable(self: &mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion { |
|
| 1783 | + | return isAssignableValue(self, to, from, rval, true); |
|
| 1784 | + | } |
|
| 1785 | + | ||
| 1786 | + | /// Check assignment while preserving function safety in referenced storage. |
|
| 1787 | + | unsafe fn isAssignableValue( |
|
| 1788 | + | self: &mut Resolver, to: Type, from: Type, rval: *ast::Node, |
|
| 1789 | + | allowFnSafetyCoercion: bool |
|
| 1790 | + | ) -> ?Coercion { |
|
| 1783 | 1791 | if to == Type::Unknown or from == Type::Unknown { |
|
| 1784 | 1792 | return nil; |
|
| 1785 | 1793 | } |
|
| 1786 | 1794 | if from == Type::Undefined { |
|
| 1787 | 1795 | // TODO: Don't let `undefined` be used in place of functions and other |
| 1810 | 1818 | return Coercion::Identity; |
|
| 1811 | 1819 | } |
|
| 1812 | 1820 | if lhsMutable and not rhsMutable { |
|
| 1813 | 1821 | return nil; |
|
| 1814 | 1822 | } |
|
| 1815 | - | return isAssignable(self, *lhsTarget, *rhsTarget, rval); |
|
| 1823 | + | return isAssignableValue(self, *lhsTarget, *rhsTarget, rval, false); |
|
| 1816 | 1824 | } |
|
| 1817 | 1825 | if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to { |
|
| 1818 | 1826 | if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from { |
|
| 1819 | 1827 | if not pointerClassesAssignable(lhsClass, rhsClass) |
|
| 1820 | 1828 | or (lhsMutable and not rhsMutable) |
| 1848 | 1856 | } |
|
| 1849 | 1857 | // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts. |
|
| 1850 | 1858 | if *lhsItem == Type::Opaque { |
|
| 1851 | 1859 | return Coercion::Identity; |
|
| 1852 | 1860 | } |
|
| 1853 | - | return isAssignable(self, *lhsItem, *rhsItem, rval); |
|
| 1861 | + | return isAssignableValue(self, *lhsItem, *rhsItem, rval, false); |
|
| 1854 | 1862 | } |
|
| 1855 | 1863 | match to { |
|
| 1856 | 1864 | case Type::Array(lhs) => { |
|
| 1857 | 1865 | let case Type::Array(rhs) = from |
|
| 1858 | 1866 | else return nil; |
| 1874 | 1882 | return Coercion::Identity; |
|
| 1875 | 1883 | } |
|
| 1876 | 1884 | return nil; |
|
| 1877 | 1885 | } |
|
| 1878 | 1886 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
|
| 1879 | - | return isAssignable(self, *lhs.item, *rhs.item, repeat.item); |
|
| 1887 | + | return isAssignableValue(self, *lhs.item, *rhs.item, repeat.item, allowFnSafetyCoercion); |
|
| 1880 | 1888 | } |
|
| 1881 | 1889 | else => { |
|
| 1882 | 1890 | if typesEqual(*lhs.item, *rhs.item) { |
|
| 1883 | 1891 | return Coercion::Identity; |
|
| 1884 | 1892 | } |
| 1889 | 1897 | ||
| 1890 | 1898 | case Type::Optional(inner) => { |
|
| 1891 | 1899 | if from == Type::Nil { |
|
| 1892 | 1900 | return Coercion::OptionalLift(to); |
|
| 1893 | 1901 | } |
|
| 1894 | - | if let _ = isAssignable(self, *inner, from, rval) { |
|
| 1902 | + | if let _ = isAssignableValue(self, *inner, from, rval, allowFnSafetyCoercion) { |
|
| 1895 | 1903 | return Coercion::OptionalLift(to); |
|
| 1896 | 1904 | } |
|
| 1897 | 1905 | if let case Type::Optional(fromInner) = from { |
|
| 1898 | - | return isAssignable(self, *inner, *fromInner, rval); |
|
| 1906 | + | return isAssignableValue(self, *inner, *fromInner, rval, allowFnSafetyCoercion); |
|
| 1899 | 1907 | } |
|
| 1900 | 1908 | return nil; |
|
| 1901 | 1909 | } |
|
| 1902 | 1910 | ||
| 1903 | 1911 | case Type::Fn(toInfo) => { |
|
| 1904 | 1912 | // Allow function type structural matching. |
|
| 1905 | 1913 | if let case Type::Fn(fromInfo) = from { |
|
| 1906 | - | if fnTypeEqual(toInfo, fromInfo) { |
|
| 1914 | + | if fnTypeEqual(toInfo, fromInfo) or ( |
|
| 1915 | + | allowFnSafetyCoercion and toInfo.isUnsafe and not fromInfo.isUnsafe |
|
| 1916 | + | and fnSignatureEqual(toInfo, fromInfo) |
|
| 1917 | + | ) { |
|
| 1907 | 1918 | return Coercion::Identity; |
|
| 1908 | 1919 | } |
|
| 1909 | 1920 | } |
|
| 1910 | 1921 | return nil; |
|
| 1911 | 1922 | } |
| 1941 | 1952 | } |
|
| 1942 | 1953 | return nil; |
|
| 1943 | 1954 | } |
|
| 1944 | 1955 | ||
| 1945 | 1956 | /// Check if two function type descriptors are structurally equivalent. |
|
| 1946 | - | fn fnTypeEqual(a: *FnType, b: *FnType) -> bool { |
|
| 1957 | + | fn fnTypeEqual(a: &FnType, b: *FnType) -> bool { |
|
| 1947 | 1958 | if a.isUnsafe <> b.isUnsafe { |
|
| 1948 | 1959 | return false; |
|
| 1949 | 1960 | } |
|
| 1961 | + | return fnSignatureEqual(a, b); |
|
| 1962 | + | } |
|
| 1963 | + | ||
| 1964 | + | /// Compare parameter, return, and error types of functions. |
|
| 1965 | + | fn fnSignatureEqual(a: &FnType, b: *FnType) -> bool { |
|
| 1950 | 1966 | if a.paramTypes.len <> b.paramTypes.len { |
|
| 1951 | 1967 | return false; |
|
| 1952 | 1968 | } |
|
| 1953 | 1969 | if a.throwList.len <> b.throwList.len { |
|
| 1954 | 1970 | return false; |
| 2174 | 2190 | return false; |
|
| 2175 | 2191 | } |
|
| 2176 | 2192 | ||
| 2177 | 2193 | /// Check if the `from` type is assignable to the `to` type, and return a |
|
| 2178 | 2194 | /// coercion plan if so, or throw an error if not. |
|
| 2179 | - | fn expectAssignable(self: *mut Resolver, to: Type, from: Type, site: *ast::Node) -> Coercion throws (ResolveError) { |
|
| 2195 | + | unsafe fn expectAssignable(self: &mut Resolver, to: Type, from: Type, site: *ast::Node) -> Coercion throws (ResolveError) { |
|
| 2180 | 2196 | // Ensure any nested nominal types are resolved before checking assignability. |
|
| 2181 | 2197 | try ensureTypeResolved(self, to, site); |
|
| 2182 | 2198 | if let coercion = isAssignable(self, to, from, site) { |
|
| 2183 | 2199 | return setNodeCoercion(self, site, coercion); |
|
| 2184 | 2200 | } |
| 2187 | 2203 | actual: from, |
|
| 2188 | 2204 | }); |
|
| 2189 | 2205 | } |
|
| 2190 | 2206 | ||
| 2191 | 2207 | /// Check that a type is optional, otherwise throw an error. |
|
| 2192 | - | fn checkOptional(self: *mut Resolver, node: *ast::Node) -> *Type |
|
| 2208 | + | unsafe fn checkOptional(self: &mut Resolver, node: *ast::Node) -> *Type |
|
| 2193 | 2209 | throws (ResolveError) |
|
| 2194 | 2210 | { |
|
| 2195 | 2211 | if let case Type::Optional(inner) = try infer(self, node) { |
|
| 2196 | 2212 | return inner; |
|
| 2197 | 2213 | } |
|
| 2198 | 2214 | throw emitError(self, node, ErrorKind::ExpectedOptional); |
|
| 2199 | 2215 | } |
|
| 2200 | 2216 | ||
| 2201 | 2217 | /// Check that a node's type is equal to the expected type. |
|
| 2202 | - | fn checkEqual(self: *mut Resolver, node: *ast::Node, expected: Type) -> Type |
|
| 2218 | + | unsafe fn checkEqual(self: &mut Resolver, node: *ast::Node, expected: Type) -> Type |
|
| 2203 | 2219 | throws (ResolveError) |
|
| 2204 | 2220 | { |
|
| 2205 | 2221 | let actualTy = try visit(self, node, expected); |
|
| 2206 | 2222 | if actualTy <> expected { |
|
| 2207 | 2223 | throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: actualTy }); |
| 2209 | 2225 | return actualTy; |
|
| 2210 | 2226 | } |
|
| 2211 | 2227 | ||
| 2212 | 2228 | /// Bind an identifier in the given scope. |
|
| 2213 | 2229 | fn bindIdent( |
|
| 2214 | - | self: *mut Resolver, |
|
| 2230 | + | self: &mut Resolver, |
|
| 2215 | 2231 | name: *[u8], |
|
| 2216 | 2232 | owner: *ast::Node, |
|
| 2217 | 2233 | data: SymbolData, |
|
| 2218 | 2234 | attrs: u32, |
|
| 2219 | 2235 | scope: *mut Scope |
| 2224 | 2240 | ||
| 2225 | 2241 | return sym; |
|
| 2226 | 2242 | } |
|
| 2227 | 2243 | ||
| 2228 | 2244 | /// Add a symbol to the given scope. |
|
| 2229 | - | fn addSymbolToScope(self: *mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) { |
|
| 2245 | + | fn addSymbolToScope(self: &mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) { |
|
| 2230 | 2246 | for i in 0..scope.symbolsLen { |
|
| 2231 | 2247 | if scope.symbols[i].name == sym.name { |
|
| 2232 | 2248 | throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name)); |
|
| 2233 | 2249 | } |
|
| 2234 | 2250 | } |
| 2246 | 2262 | set scope.symbolsLen += 1; |
|
| 2247 | 2263 | } |
|
| 2248 | 2264 | ||
| 2249 | 2265 | /// Bind a value identifier in the current scope. |
|
| 2250 | 2266 | /// Returns `nil` if the identifier is a placeholder (`_`). |
|
| 2251 | - | fn bindValueIdent( |
|
| 2252 | - | self: *mut Resolver, |
|
| 2267 | + | unsafe fn bindValueIdent( |
|
| 2268 | + | self: &mut Resolver, |
|
| 2253 | 2269 | ident: *ast::Node, |
|
| 2254 | 2270 | owner: *ast::Node, |
|
| 2255 | 2271 | type: Type, |
|
| 2256 | 2272 | mutable: bool, |
|
| 2257 | 2273 | alignment: u32, |
| 2261 | 2277 | setNodeType(self, owner, type); |
|
| 2262 | 2278 | return nil; |
|
| 2263 | 2279 | } |
|
| 2264 | 2280 | let name = try nodeName(self, ident); |
|
| 2265 | 2281 | let data = SymbolData::Value { mutable, alignment, type, addressTaken: false }; |
|
| 2266 | - | let sym = try bindIdent(self, name, owner, data, attrs, self.scope); |
|
| 2282 | + | let scope = self.scope; |
|
| 2283 | + | let sym = try bindIdent(self, name, owner, data, attrs, scope); |
|
| 2267 | 2284 | setNodeType(self, owner, type); |
|
| 2268 | 2285 | setNodeType(self, ident, type); |
|
| 2269 | 2286 | ||
| 2270 | 2287 | // Track number of local bindings for lowering stage. |
|
| 2271 | 2288 | if let mut fnType = self.currentFn { |
| 2274 | 2291 | return sym; |
|
| 2275 | 2292 | } |
|
| 2276 | 2293 | ||
| 2277 | 2294 | /// Bind a constant identifier in the current scope. |
|
| 2278 | 2295 | fn bindConstIdent( |
|
| 2279 | - | self: *mut Resolver, |
|
| 2296 | + | self: &mut Resolver, |
|
| 2280 | 2297 | ident: *ast::Node, |
|
| 2281 | 2298 | owner: *ast::Node, |
|
| 2282 | 2299 | type: Type, |
|
| 2283 | 2300 | val: ?ConstValue, |
|
| 2284 | 2301 | attrs: u32 |
|
| 2285 | 2302 | ) -> *mut Symbol throws (ResolveError) { |
|
| 2286 | 2303 | let name = try nodeName(self, ident); |
|
| 2287 | 2304 | let data = SymbolData::Constant { type, value: val }; |
|
| 2288 | - | let sym = try bindIdent(self, name, owner, data, attrs, self.scope); |
|
| 2305 | + | let scope = self.scope; |
|
| 2306 | + | let sym = try bindIdent(self, name, owner, data, attrs, scope); |
|
| 2289 | 2307 | setNodeType(self, owner, type); |
|
| 2290 | 2308 | setNodeType(self, ident, type); |
|
| 2291 | 2309 | ||
| 2292 | 2310 | return sym; |
|
| 2293 | 2311 | } |
|
| 2294 | 2312 | ||
| 2295 | 2313 | /// Bind a module identifier in the given scope. |
|
| 2296 | 2314 | /// This is used when declaring modules with `mod` or |
|
| 2297 | 2315 | /// importing modules with `use`. |
|
| 2298 | 2316 | fn bindModuleIdent( |
|
| 2299 | - | self: *mut Resolver, |
|
| 2317 | + | self: &mut Resolver, |
|
| 2300 | 2318 | entry: *module::ModuleEntry, |
|
| 2301 | 2319 | scope: *mut Scope, |
|
| 2302 | 2320 | owner: *ast::Node, |
|
| 2303 | 2321 | attrs: u32, |
|
| 2304 | 2322 | bindingScope: *mut Scope |
| 2309 | 2327 | return try bindIdent(self, name, owner, data, attrs, bindingScope); |
|
| 2310 | 2328 | } |
|
| 2311 | 2329 | ||
| 2312 | 2330 | /// Bind a type identifier in the current scope. |
|
| 2313 | 2331 | fn bindTypeIdent( |
|
| 2314 | - | self: *mut Resolver, |
|
| 2332 | + | self: &mut Resolver, |
|
| 2315 | 2333 | ident: *ast::Node, |
|
| 2316 | 2334 | owner: *ast::Node, |
|
| 2317 | 2335 | type: *mut NominalType, |
|
| 2318 | 2336 | attrs: u32 |
|
| 2319 | 2337 | ) -> *mut Symbol throws (ResolveError) { |
|
| 2320 | 2338 | let name = try nodeName(self, ident); |
|
| 2321 | 2339 | let data = SymbolData::Type(type); |
|
| 2322 | - | return try bindIdent(self, name, owner, data, attrs, self.scope); |
|
| 2340 | + | let scope = self.scope; |
|
| 2341 | + | return try bindIdent(self, name, owner, data, attrs, scope); |
|
| 2323 | 2342 | } |
|
| 2324 | 2343 | ||
| 2325 | 2344 | /// Predicate that matches any symbol. |
|
| 2326 | 2345 | fn isAnySymbol(_sym: *mut Symbol) -> bool { |
|
| 2327 | 2346 | return true; |
| 2393 | 2412 | return findInScopeRecursive(scope, name, isAnySymbol); |
|
| 2394 | 2413 | } |
|
| 2395 | 2414 | ||
| 2396 | 2415 | /// Flatten an identifier or scope access chain into an array of name segments. |
|
| 2397 | 2416 | /// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`. |
|
| 2398 | - | /// Returns a slice of the segments that were written. |
|
| 2417 | + | /// Return the number of segments written to the buffer. |
|
| 2399 | 2418 | fn flattenPath( |
|
| 2400 | - | self: *mut Resolver, |
|
| 2419 | + | self: &mut Resolver, |
|
| 2401 | 2420 | node: *ast::Node, |
|
| 2402 | - | buf: *mut [*[u8]] |
|
| 2403 | - | ) -> *[*[u8]] throws (ResolveError) { |
|
| 2404 | - | let mut out: *[*[u8]] = &[]; |
|
| 2421 | + | buf: &mut [*[u8]] |
|
| 2422 | + | ) -> u32 throws (ResolveError) { |
|
| 2423 | + | let mut out: u32 = 0; |
|
| 2405 | 2424 | ||
| 2406 | 2425 | match node.value { |
|
| 2407 | 2426 | case ast::NodeValue::Ident(name) if name.len > 0 => { |
|
| 2408 | 2427 | assert buf.len >= 1, "flattenPath: invalid output buffer size"; |
|
| 2409 | 2428 | set buf[0] = name; |
|
| 2410 | - | set out = &buf[..1]; |
|
| 2429 | + | set out = 1; |
|
| 2411 | 2430 | } |
|
| 2412 | 2431 | case ast::NodeValue::ScopeAccess(access) => { |
|
| 2413 | 2432 | // Recursively flatten parent path. |
|
| 2414 | 2433 | let parent = try flattenPath(self, access.parent, buf); |
|
| 2415 | - | assert parent.len < buf.len, "flattenPath: invalid output buffer size"; |
|
| 2434 | + | assert parent < buf.len, "flattenPath: invalid output buffer size"; |
|
| 2416 | 2435 | let child = try nodeName(self, access.child); |
|
| 2417 | - | set buf[parent.len] = child; |
|
| 2418 | - | set out = &buf[..parent.len + 1]; |
|
| 2436 | + | set buf[parent] = child; |
|
| 2437 | + | set out = parent + 1; |
|
| 2419 | 2438 | } |
|
| 2420 | 2439 | case ast::NodeValue::Super => { |
|
| 2421 | 2440 | // `super` is handled by scope adjustment in `checkSuperAccess`. |
|
| 2422 | 2441 | // Return empty prefix so the path continues from the next segment. |
|
| 2423 | - | set out = &buf[..0]; |
|
| 2442 | + | set out = 0; |
|
| 2424 | 2443 | return out; |
|
| 2425 | 2444 | } |
|
| 2426 | 2445 | else => { |
|
| 2427 | 2446 | // Fallthrough to error. |
|
| 2428 | 2447 | } |
|
| 2429 | 2448 | } |
|
| 2430 | - | if out.len < 1 { |
|
| 2449 | + | if out < 1 { |
|
| 2431 | 2450 | throw emitError(self, node, ErrorKind::InvalidIdentifier(node)); |
|
| 2432 | 2451 | } |
|
| 2433 | 2452 | return out; |
|
| 2434 | 2453 | } |
|
| 2435 | 2454 |
| 2449 | 2468 | } |
|
| 2450 | 2469 | } |
|
| 2451 | 2470 | ||
| 2452 | 2471 | /// Get the parent module scope for the current module. |
|
| 2453 | 2472 | /// Returns the scope of the parent module, or `nil` if this is a root module. |
|
| 2454 | - | fn getParentModuleScope(self: *mut Resolver, node: *ast::Node) -> ?*mut Scope throws (ResolveError) { |
|
| 2455 | - | let currentMod = module::get(self.moduleGraph, self.currentMod) |
|
| 2473 | + | unsafe fn getParentModuleScope(self: &mut Resolver, node: *ast::Node) -> ?*mut Scope throws (ResolveError) { |
|
| 2474 | + | let currentMod = module::get(&*self.moduleGraph, self.currentMod) |
|
| 2456 | 2475 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 2457 | 2476 | let parentId = currentMod.parent |
|
| 2458 | 2477 | else return nil; // No parent module. |
|
| 2459 | 2478 | ||
| 2460 | 2479 | return self.moduleScopes[parentId as u32]; |
|
| 2461 | 2480 | } |
|
| 2462 | 2481 | ||
| 2463 | 2482 | /// Check if a node has `super` at its root (e.g. `super::x` or `super::Union::Variant`). |
|
| 2464 | 2483 | /// Returns the parent scope and the original node so `flattenPath` can strip `super`. |
|
| 2465 | - | fn checkSuperAccess( |
|
| 2466 | - | self: *mut Resolver, |
|
| 2484 | + | unsafe fn checkSuperAccess( |
|
| 2485 | + | self: &mut Resolver, |
|
| 2467 | 2486 | node: *ast::Node |
|
| 2468 | 2487 | ) -> ?SuperAccessResult throws (ResolveError) { |
|
| 2469 | 2488 | // TODO: Maybe we should deal with `super` after the path is flattened. |
|
| 2470 | 2489 | if let case ast::NodeValue::ScopeAccess(access) = node.value { |
|
| 2471 | 2490 | // Direct super access: `super::x`. |
| 2505 | 2524 | return symModuleId == currentModuleId; |
|
| 2506 | 2525 | } |
|
| 2507 | 2526 | ||
| 2508 | 2527 | /// Resolve an access node (eg. `lang::resolver::MAX_ERRORS`) to a symbol, |
|
| 2509 | 2528 | /// starting from the given scope. |
|
| 2510 | - | fn resolveAccess( |
|
| 2511 | - | self: *mut Resolver, |
|
| 2529 | + | unsafe fn resolveAccess( |
|
| 2530 | + | self: &mut Resolver, |
|
| 2512 | 2531 | node: *ast::Node, |
|
| 2513 | 2532 | access: ast::Access, |
|
| 2514 | 2533 | scope: *Scope |
|
| 2515 | 2534 | ) -> *mut Symbol throws (ResolveError) { |
|
| 2516 | 2535 | // Handle `super` access by adjusting scope and node. |
| 2521 | 2540 | set pathNode = superAccess.child; |
|
| 2522 | 2541 | } |
|
| 2523 | 2542 | // TODO: It doesn't make sense that `flattenPath` handles identifiers and scope access, |
|
| 2524 | 2543 | // while this function requires a scope access. |
|
| 2525 | 2544 | let mut buffer: [*[u8]; 32] = undefined; |
|
| 2526 | - | let path = try flattenPath(self, pathNode, &mut buffer[..]); |
|
| 2545 | + | let pathLen = try flattenPath(self, pathNode, &mut buffer[..]); |
|
| 2527 | 2546 | ||
| 2528 | - | return try resolvePath(self, node, access, path, startScope); |
|
| 2547 | + | return try resolvePath(self, node, access, &buffer[..pathLen], startScope); |
|
| 2529 | 2548 | } |
|
| 2530 | 2549 | ||
| 2531 | 2550 | /// Resolve a path (eg. ["lang", "resolver", "MAX_ERRORS"]) to a symbol, |
|
| 2532 | 2551 | /// starting from the given scope. |
|
| 2533 | - | fn resolvePath( |
|
| 2534 | - | self: *mut Resolver, |
|
| 2552 | + | unsafe fn resolvePath( |
|
| 2553 | + | self: &mut Resolver, |
|
| 2535 | 2554 | node: *ast::Node, |
|
| 2536 | 2555 | access: ast::Access, |
|
| 2537 | - | path: *[*[u8]], |
|
| 2556 | + | path: &[*[u8]], |
|
| 2538 | 2557 | scope: *Scope |
|
| 2539 | 2558 | ) -> *mut Symbol throws (ResolveError) { |
|
| 2540 | 2559 | assert path.len <> 0, "resolvePath: empty path"; |
|
| 2541 | 2560 | // Start by finding the root of the path. |
|
| 2542 | 2561 | let root = path[0]; |
|
| 2543 | 2562 | let sym = findInScopeRecursive(scope, root, isAnySymbol) |
|
| 2544 | 2563 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root)); |
|
| 2545 | - | let suffix = &path[1..]; |
|
| 2546 | 2564 | ||
| 2547 | 2565 | // Check visibility for symbol. |
|
| 2548 | 2566 | if not isSymbolVisible(sym, scope, self.scope) { |
|
| 2549 | 2567 | throw emitError(self, node, ErrorKind::UnresolvedSymbol(root)); |
|
| 2550 | 2568 | } |
|
| 2551 | 2569 | // End condition. |
|
| 2552 | - | if suffix.len == 0 { |
|
| 2570 | + | if path.len == 1 { |
|
| 2553 | 2571 | return sym; |
|
| 2554 | 2572 | } |
|
| 2555 | 2573 | // Otherwise, we need to enter the next scope with the path suffix. |
|
| 2556 | 2574 | match sym.data { |
|
| 2557 | 2575 | case SymbolData::Module { scope, .. } => { |
|
| 2558 | - | return try resolvePath(self, node, access, suffix, scope); |
|
| 2576 | + | return try resolvePath(self, node, access, &path[1..], scope); |
|
| 2559 | 2577 | } |
|
| 2560 | 2578 | case SymbolData::Type(ty) => { |
|
| 2561 | 2579 | // Lazily resolve union body if not yet done. |
|
| 2562 | 2580 | try ensureNominalResolved(self, ty, node); |
|
| 2563 | 2581 | ||
| 2564 | 2582 | if let case NominalType::Union(unionType) = *ty { |
|
| 2565 | 2583 | // TODO: Recurse with variant so we consolidate everything. |
|
| 2566 | - | if suffix.len > 1 { |
|
| 2584 | + | if path.len > 2 { |
|
| 2567 | 2585 | throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
|
| 2568 | 2586 | } |
|
| 2569 | - | let variantName = suffix[0]; |
|
| 2587 | + | let variantName = path[1]; |
|
| 2570 | 2588 | let variantSym = try resolveUnionVariantAccess( |
|
| 2571 | 2589 | self, node, access, unionType, variantName |
|
| 2572 | 2590 | ); |
|
| 2573 | 2591 | // TODO: This shouldn't be here. |
|
| 2574 | 2592 | setNodeType(self, node, Type::Nominal(ty)); |
| 2580 | 2598 | throw emitError(self, node, ErrorKind::InvalidScopeAccess); |
|
| 2581 | 2599 | } |
|
| 2582 | 2600 | ||
| 2583 | 2601 | /// Resolve a module path (e.g., `foo::bar::baz`) to a module entry and scope. |
|
| 2584 | 2602 | /// This traverses the module hierarchy, checking visibility at each step. |
|
| 2585 | - | fn resolveModulePath( |
|
| 2586 | - | self: *mut Resolver, |
|
| 2603 | + | unsafe fn resolveModulePath( |
|
| 2604 | + | self: &mut Resolver, |
|
| 2587 | 2605 | module: *ast::Node |
|
| 2588 | 2606 | ) -> ResolvedModule throws (ResolveError) { |
|
| 2589 | 2607 | let mut startScope = self.scope; |
|
| 2590 | 2608 | let mut pathNode = module; |
|
| 2591 | 2609 |
| 2593 | 2611 | if let superAccess = try checkSuperAccess(self, module) { |
|
| 2594 | 2612 | set startScope = superAccess.scope; |
|
| 2595 | 2613 | set pathNode = superAccess.child; |
|
| 2596 | 2614 | } |
|
| 2597 | 2615 | let mut pathBuf: [*[u8]; 16] = undefined; |
|
| 2598 | - | let path = try flattenPath(self, pathNode, &mut pathBuf[..]); |
|
| 2599 | - | if path.len == 0 { |
|
| 2616 | + | let pathLen = try flattenPath(self, pathNode, &mut pathBuf[..]); |
|
| 2617 | + | if pathLen == 0 { |
|
| 2600 | 2618 | throw emitError(self, module, ErrorKind::UnresolvedSymbol("")); |
|
| 2601 | 2619 | } |
|
| 2602 | - | let parentName = path[0]; |
|
| 2620 | + | let parentName = pathBuf[0]; |
|
| 2603 | 2621 | ||
| 2604 | 2622 | // First, check if this is a sub-module of the start scope. |
|
| 2605 | 2623 | if let sym = findSymbolInScope(startScope, parentName) { |
|
| 2606 | - | return try resolveModulePathRecursive(self, module, &path[1..], sym); |
|
| 2624 | + | return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym); |
|
| 2607 | 2625 | } |
|
| 2608 | 2626 | // Not a sub-module, so look in the global scope for a package root. |
|
| 2609 | 2627 | let sym = findSymbolInScope(self.pkgScope, parentName) |
|
| 2610 | 2628 | else throw emitError(self, module, ErrorKind::UnresolvedSymbol(parentName)); |
|
| 2611 | 2629 | ||
| 2612 | - | return try resolveModulePathRecursive(self, module, &path[1..], sym); |
|
| 2630 | + | return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym); |
|
| 2613 | 2631 | } |
|
| 2614 | 2632 | ||
| 2615 | 2633 | /// Recursively resolve the remaining path segments by traversing child modules. |
|
| 2616 | 2634 | fn resolveModulePathRecursive( |
|
| 2617 | - | self: *mut Resolver, |
|
| 2635 | + | self: &mut Resolver, |
|
| 2618 | 2636 | node: *ast::Node, |
|
| 2619 | - | path: *[*[u8]], |
|
| 2637 | + | path: &[*[u8]], |
|
| 2620 | 2638 | sym: *Symbol |
|
| 2621 | 2639 | ) -> ResolvedModule throws (ResolveError) { |
|
| 2622 | 2640 | let case SymbolData::Module { entry, scope } = sym.data |
|
| 2623 | 2641 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 2624 | 2642 |
| 2639 | 2657 | childSym |
|
| 2640 | 2658 | ); |
|
| 2641 | 2659 | } |
|
| 2642 | 2660 | ||
| 2643 | 2661 | /// Resolve a type name, which could be an identifier or scoped path. |
|
| 2644 | - | fn resolveTypeName(self: *mut Resolver, node: *ast::Node) -> *NominalType throws (ResolveError) { |
|
| 2662 | + | unsafe fn resolveTypeName(self: &mut Resolver, node: *ast::Node) -> *NominalType throws (ResolveError) { |
|
| 2645 | 2663 | match node.value { |
|
| 2646 | 2664 | case ast::NodeValue::Ident(name) => { |
|
| 2647 | 2665 | let sym = findTypeSymbol(self.scope, name) |
|
| 2648 | 2666 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 2649 | 2667 | let case SymbolData::Type(ty) = sym.data |
| 2652 | 2670 | setNodeSymbol(self, node, sym); |
|
| 2653 | 2671 | ||
| 2654 | 2672 | return ty; |
|
| 2655 | 2673 | } |
|
| 2656 | 2674 | case ast::NodeValue::ScopeAccess(access) => { |
|
| 2657 | - | let sym = try resolveAccess(self, node, access, self.scope); |
|
| 2675 | + | let scope = self.scope; |
|
| 2676 | + | let sym = try resolveAccess(self, node, access, scope); |
|
| 2658 | 2677 | let case SymbolData::Type(ty) = sym.data |
|
| 2659 | 2678 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 2660 | 2679 | ||
| 2661 | 2680 | setNodeSymbol(self, node, sym); |
|
| 2662 | 2681 |
| 2669 | 2688 | /// Visit a top-level declaration in the declaration phase. |
|
| 2670 | 2689 | /// This binds all names and analyzes signatures, types, and initializers. |
|
| 2671 | 2690 | /// Function bodies are deferred to the definition phase. |
|
| 2672 | 2691 | /// |
|
| 2673 | 2692 | /// Nb. User-defined types are already handled by this point. |
|
| 2674 | - | fn visitDecl(self: *mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 2693 | + | unsafe fn visitDecl(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 2675 | 2694 | match node.value { |
|
| 2676 | 2695 | case ast::NodeValue::FnDecl(_), |
|
| 2677 | 2696 | ast::NodeValue::ConstDecl(_), |
|
| 2678 | 2697 | ast::NodeValue::Mod(_), |
|
| 2679 | 2698 | ast::NodeValue::Use(_) => { |
| 2692 | 2711 | // Ignore non-declaration nodes. |
|
| 2693 | 2712 | } |
|
| 2694 | 2713 | } |
|
| 2695 | 2714 | } |
|
| 2696 | 2715 | ||
| 2697 | - | /// Require the current declaration to be unsafe. |
|
| 2698 | - | fn requireUnsafe(self: *mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 2699 | - | if self.unsafeDepth == 0 { |
|
| 2716 | + | /// Require the current function to be unsafe. |
|
| 2717 | + | fn requireUnsafe(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 2718 | + | if not self.inUnsafeFn { |
|
| 2700 | 2719 | throw emitError(self, node, ErrorKind::UnsafeOperation); |
|
| 2701 | 2720 | } |
|
| 2702 | 2721 | } |
|
| 2703 | 2722 | ||
| 2704 | 2723 | /// Reject calls from safe code through unsafe function types. |
|
| 2705 | - | fn checkUnsafeCall(self: *mut Resolver, node: *ast::Node, info: *FnType) |
|
| 2724 | + | fn checkUnsafeCall(self: &mut Resolver, node: *ast::Node, info: *FnType) |
|
| 2706 | 2725 | throws (ResolveError) |
|
| 2707 | 2726 | { |
|
| 2708 | - | if info.isUnsafe and self.unsafeDepth == 0 { |
|
| 2727 | + | if info.isUnsafe and not self.inUnsafeFn { |
|
| 2709 | 2728 | throw emitError(self, node, ErrorKind::UnsafeCall); |
|
| 2710 | 2729 | } |
|
| 2711 | 2730 | } |
|
| 2712 | 2731 | ||
| 2713 | 2732 | /// Visit a top-level definition, recursing into sub-modules. |
|
| 2714 | - | fn visitDef(self: *mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 2733 | + | unsafe fn visitDef(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 2715 | 2734 | match node.value { |
|
| 2716 | 2735 | case ast::NodeValue::FnDecl(decl) => { |
|
| 2717 | 2736 | try resolveFnDeclBody(self, node, decl) catch { |
|
| 2718 | 2737 | return; |
|
| 2719 | 2738 | }; |
| 2724 | 2743 | } |
|
| 2725 | 2744 | let modName = try nodeName(self, decl.name); |
|
| 2726 | 2745 | let submod = try enterSubModule(self, modName, node); |
|
| 2727 | 2746 | let case ast::NodeValue::Block(block) = submod.root.value |
|
| 2728 | 2747 | else panic "visitDef: expected block for module root"; |
|
| 2729 | - | let mut isUnsafe = false; |
|
| 2730 | - | if let attrs = decl.attrs { |
|
| 2731 | - | set isUnsafe = ast::attributesContains(&attrs, ast::Attribute::Unsafe); |
|
| 2732 | - | } |
|
| 2733 | - | if isUnsafe { |
|
| 2734 | - | set self.unsafeDepth += 1; |
|
| 2735 | - | } |
|
| 2736 | 2748 | try resolveModuleDefs(self, &block) catch e { |
|
| 2737 | - | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 2738 | 2749 | exitModuleScope(self, submod); |
|
| 2739 | 2750 | throw e; |
|
| 2740 | 2751 | }; |
|
| 2741 | - | if isUnsafe { |
|
| 2742 | - | set self.unsafeDepth -= 1; |
|
| 2743 | - | } |
|
| 2744 | 2752 | exitModuleScope(self, submod); |
|
| 2745 | 2753 | } |
|
| 2746 | 2754 | case ast::NodeValue::RecordDecl(_), |
|
| 2747 | 2755 | ast::NodeValue::UnionDecl(_), |
|
| 2748 | 2756 | ast::NodeValue::Use(_), |
| 2766 | 2774 | } |
|
| 2767 | 2775 | } |
|
| 2768 | 2776 | } |
|
| 2769 | 2777 | ||
| 2770 | 2778 | /// Try to infer a node's type. |
|
| 2771 | - | fn infer(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 2779 | + | unsafe fn infer(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 2772 | 2780 | return try visit(self, node, Type::Unknown); |
|
| 2773 | 2781 | } |
|
| 2774 | 2782 | ||
| 2775 | 2783 | /// Reject nested references while allowing a direct parameter reference. |
|
| 2776 | - | fn validateValueTypeReferences(self: *mut Resolver, node: *ast::Node, ty: Type) |
|
| 2784 | + | fn validateValueTypeReferences(self: &mut Resolver, node: *ast::Node, ty: Type) |
|
| 2777 | 2785 | throws (ResolveError) |
|
| 2778 | 2786 | { |
|
| 2779 | 2787 | if isRefType(ty) { |
|
| 2780 | 2788 | if let case Type::Pointer { target, .. } = ty { |
|
| 2781 | 2789 | if containsRef(*target) { |
| 2790 | 2798 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 2791 | 2799 | } |
|
| 2792 | 2800 | } |
|
| 2793 | 2801 | ||
| 2794 | 2802 | /// Require a type that may be stored or escape a call. |
|
| 2795 | - | fn ensureStorableType(self: *mut Resolver, node: *ast::Node, ty: Type) |
|
| 2803 | + | fn ensureStorableType(self: &mut Resolver, node: *ast::Node, ty: Type) |
|
| 2796 | 2804 | throws (ResolveError) |
|
| 2797 | 2805 | { |
|
| 2798 | 2806 | if containsRef(ty) { |
|
| 2799 | 2807 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 2800 | 2808 | } |
|
| 2801 | 2809 | } |
|
| 2802 | 2810 | ||
| 2803 | 2811 | /// Resolve a type signature node. |
|
| 2804 | - | fn resolveValueType(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 2812 | + | unsafe fn resolveValueType(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 2805 | 2813 | let ty = try visit(self, node, Type::Unknown); |
|
| 2806 | 2814 | // Opaque value types are not allowed. |
|
| 2807 | 2815 | if ty == Type::Opaque { |
|
| 2808 | 2816 | throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed); |
|
| 2809 | 2817 | } |
|
| 2810 | 2818 | try validateValueTypeReferences(self, node, ty); |
|
| 2811 | 2819 | return ty; |
|
| 2812 | 2820 | } |
|
| 2813 | 2821 | ||
| 2814 | 2822 | /// Analyze a node's type and check that it can be assigned to the expected type. |
|
| 2815 | - | fn checkAssignable(self: *mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) { |
|
| 2823 | + | unsafe fn checkAssignable(self: &mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) { |
|
| 2816 | 2824 | let actual = try visit(self, node, expected); |
|
| 2817 | 2825 | let _ = try expectAssignable(self, expected, actual, node); |
|
| 2818 | 2826 | return actual; |
|
| 2819 | 2827 | } |
|
| 2820 | 2828 | ||
| 2821 | 2829 | /// Analyze a node and propagate the resolved type. |
|
| 2822 | 2830 | /// The `hint` parameter provides type context for inference and validation. |
|
| 2823 | 2831 | /// When `nil`, the type must be inferred from the expression itself. |
|
| 2824 | - | fn visit(self: *mut Resolver, node: *ast::Node, hint: Type) -> Type |
|
| 2832 | + | unsafe fn visit(self: &mut Resolver, node: *ast::Node, hint: Type) -> Type |
|
| 2825 | 2833 | throws (ResolveError) |
|
| 2826 | 2834 | { |
|
| 2827 | 2835 | if let ty = typeFor(self, node) { |
|
| 2828 | 2836 | return ty; |
|
| 2829 | 2837 | } |
| 2974 | 2982 | } |
|
| 2975 | 2983 | } |
|
| 2976 | 2984 | } |
|
| 2977 | 2985 | ||
| 2978 | 2986 | /// Visit an optional node when present. |
|
| 2979 | - | fn visitOptional(self: *mut Resolver, node: ?*ast::Node, hint: Type) -> ?Type |
|
| 2987 | + | unsafe fn visitOptional(self: &mut Resolver, node: ?*ast::Node, hint: Type) -> ?Type |
|
| 2980 | 2988 | throws (ResolveError) |
|
| 2981 | 2989 | { |
|
| 2982 | 2990 | if let n = node { |
|
| 2983 | 2991 | return try visit(self, n, hint); |
|
| 2984 | 2992 | } |
|
| 2985 | 2993 | return nil; |
|
| 2986 | 2994 | } |
|
| 2987 | 2995 | ||
| 2988 | 2996 | /// Visit every node contained in a list, returning the last resolved type. |
|
| 2989 | - | fn visitList(self: *mut Resolver, list: *mut [*ast::Node]) -> Type |
|
| 2997 | + | unsafe fn visitList(self: &mut Resolver, list: *mut [*ast::Node]) -> Type |
|
| 2990 | 2998 | throws (ResolveError) |
|
| 2991 | 2999 | { |
|
| 2992 | 3000 | let mut diverges = false; |
|
| 2993 | 3001 | for item in list { |
|
| 2994 | 3002 | if try infer(self, item) == Type::Never { |
| 3000 | 3008 | } |
|
| 3001 | 3009 | return Type::Void; |
|
| 3002 | 3010 | } |
|
| 3003 | 3011 | ||
| 3004 | 3012 | /// Collect attribute flags applied to a declaration. |
|
| 3005 | - | fn resolveAttributes(self: *mut Resolver, attrs: ?ast::Attributes) -> u32 { |
|
| 3013 | + | fn resolveAttributes(self: &mut Resolver, attrs: ?ast::Attributes) -> u32 { |
|
| 3006 | 3014 | let list = attrs else return 0; |
|
| 3007 | 3015 | let attrNodes = list.list; |
|
| 3008 | 3016 | let mut mask: u32 = 0; |
|
| 3009 | 3017 | ||
| 3010 | 3018 | for node in attrNodes { |
| 3014 | 3022 | } |
|
| 3015 | 3023 | return mask; |
|
| 3016 | 3024 | } |
|
| 3017 | 3025 | ||
| 3018 | 3026 | /// Ensure the `default` attribute is only applied to functions. |
|
| 3019 | - | fn ensureDefaultAttrNotAllowed(self: *mut Resolver, node: *ast::Node, attrs: u32) |
|
| 3027 | + | fn ensureDefaultAttrNotAllowed(self: &mut Resolver, node: *ast::Node, attrs: u32) |
|
| 3020 | 3028 | throws (ResolveError) |
|
| 3021 | 3029 | { |
|
| 3022 | 3030 | let defaultBit = ast::Attribute::Default as u32; |
|
| 3023 | 3031 | if (attrs & defaultBit) <> 0 { |
|
| 3024 | 3032 | throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn); |
|
| 3025 | 3033 | } |
|
| 3026 | 3034 | } |
|
| 3027 | 3035 | ||
| 3028 | 3036 | /// Analyze a block node, allocating a nested lexical scope. |
|
| 3029 | - | fn resolveBlock(self: *mut Resolver, node: *ast::Node, block: ast::Block) -> Type |
|
| 3037 | + | unsafe fn resolveBlock(self: &mut Resolver, node: *ast::Node, block: ast::Block) -> Type |
|
| 3030 | 3038 | throws (ResolveError) |
|
| 3031 | 3039 | { |
|
| 3032 | 3040 | enterScope(self, node); |
|
| 3033 | 3041 | let blockTy = try visitList(self, block.statements) catch { |
|
| 3034 | 3042 | // One of the statements in the block failed analysis. We simply proceed |
| 3041 | 3049 | ||
| 3042 | 3050 | return setNodeType(self, node, blockTy); |
|
| 3043 | 3051 | } |
|
| 3044 | 3052 | ||
| 3045 | 3053 | /// Analyze a `let` declaration and bind its identifier. |
|
| 3046 | - | fn resolveLet(self: *mut Resolver, node: *ast::Node, decl: ast::Let) -> Type |
|
| 3054 | + | unsafe fn resolveLet(self: &mut Resolver, node: *ast::Node, decl: ast::Let) -> Type |
|
| 3047 | 3055 | throws (ResolveError) |
|
| 3048 | 3056 | { |
|
| 3049 | 3057 | let mut alignment: u32 = 0; // Zero is default. |
|
| 3050 | 3058 | let mut bindingTy = Type::Unknown; |
|
| 3051 | 3059 |
| 3102 | 3110 | else => return false, |
|
| 3103 | 3111 | } |
|
| 3104 | 3112 | } |
|
| 3105 | 3113 | ||
| 3106 | 3114 | /// Determine whether a node represents a compile-time constant expression. |
|
| 3107 | - | export fn isConstExpr(self: *Resolver, node: *ast::Node) -> bool { |
|
| 3115 | + | export fn isConstExpr(self: &Resolver, node: *ast::Node) -> bool { |
|
| 3108 | 3116 | match node.value { |
|
| 3109 | 3117 | case ast::NodeValue::Bool(_), |
|
| 3110 | 3118 | ast::NodeValue::Char(_), |
|
| 3111 | 3119 | ast::NodeValue::Number(_), |
|
| 3112 | 3120 | ast::NodeValue::String(_), |
| 3222 | 3230 | return ConstValue::Int(constIntFromBits(raw, bits, true)), |
|
| 3223 | 3231 | } |
|
| 3224 | 3232 | } |
|
| 3225 | 3233 | ||
| 3226 | 3234 | /// Return the constant `u32` value for a slice bound when known. |
|
| 3227 | - | fn constSliceIndex(self: *mut Resolver, node: *ast::Node) -> ?u32 { |
|
| 3235 | + | fn constSliceIndex(self: &mut Resolver, node: *ast::Node) -> ?u32 { |
|
| 3228 | 3236 | let value = constValueEntry(self, node) |
|
| 3229 | 3237 | else return nil; |
|
| 3230 | 3238 | let case ConstValue::Int(int) = value |
|
| 3231 | 3239 | else return nil; |
|
| 3232 | 3240 | if int.negative { |
| 3240 | 3248 | /// This function ensures that a node represents a valid, non-negative integer constant |
|
| 3241 | 3249 | /// that fits within a machine word. It is used for contexts requiring compile-time |
|
| 3242 | 3250 | /// non-negative integers, such as array sizes and alignment specifications. |
|
| 3243 | 3251 | /// |
|
| 3244 | 3252 | /// Returns the unsigned magnitude of the constant as `u32`. |
|
| 3245 | - | fn checkSizeInt(self: *mut Resolver, node: *ast::Node) -> u32 |
|
| 3253 | + | unsafe fn checkSizeInt(self: &mut Resolver, node: *ast::Node) -> u32 |
|
| 3246 | 3254 | throws (ResolveError) |
|
| 3247 | 3255 | { |
|
| 3248 | 3256 | // First traverse the node expect a numeric type. |
|
| 3249 | 3257 | let _ = try checkNumeric(self, node); |
|
| 3250 | 3258 |
| 3267 | 3275 | ||
| 3268 | 3276 | /// Check that constructor arguments match record fields. |
|
| 3269 | 3277 | /// |
|
| 3270 | 3278 | /// Verifies argument count matches field count, and that each argument is |
|
| 3271 | 3279 | /// assignable to its corresponding field type. |
|
| 3272 | - | fn checkRecordConstructorArgs(self: *mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType) |
|
| 3280 | + | unsafe fn checkRecordConstructorArgs(self: &mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType) |
|
| 3273 | 3281 | throws (ResolveError) |
|
| 3274 | 3282 | { |
|
| 3275 | 3283 | try checkRecordArity(self, args, recInfo, node); |
|
| 3276 | 3284 | for arg, i in args { |
|
| 3277 | 3285 | let fieldType = recInfo.fields[i].fieldType; |
|
| 3278 | 3286 | try checkAssignable(self, arg, fieldType); |
|
| 3279 | 3287 | } |
|
| 3280 | 3288 | } |
|
| 3281 | 3289 | ||
| 3282 | 3290 | /// Check that the argument count of a constructor pattern or call matches the record field count. |
|
| 3283 | - | fn checkRecordArity(self: *mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3291 | + | fn checkRecordArity(self: &mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3284 | 3292 | if args.len <> recInfo.fields.len { |
|
| 3285 | 3293 | throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
|
| 3286 | 3294 | expected: recInfo.fields.len as u32, |
|
| 3287 | 3295 | actual: args.len, |
|
| 3288 | 3296 | })); |
|
| 3289 | 3297 | } |
|
| 3290 | 3298 | } |
|
| 3291 | 3299 | ||
| 3292 | 3300 | /// Helper for analyzing `constant` and `static` declarations. |
|
| 3293 | - | fn resolveConstOrStatic( |
|
| 3294 | - | self: *mut Resolver, |
|
| 3301 | + | unsafe fn resolveConstOrStatic( |
|
| 3302 | + | self: &mut Resolver, |
|
| 3295 | 3303 | node: *ast::Node, |
|
| 3296 | 3304 | ident: *ast::Node, |
|
| 3297 | 3305 | typeNode: *ast::Node, |
|
| 3298 | 3306 | valueNode: *ast::Node, |
|
| 3299 | 3307 | attrList: ?ast::Attributes, |
| 3325 | 3333 | ||
| 3326 | 3334 | return Type::Void; |
|
| 3327 | 3335 | } |
|
| 3328 | 3336 | ||
| 3329 | 3337 | /// Analyze a function declaration signature and bind the function name. |
|
| 3330 | - | fn resolveFnDecl(self: *mut Resolver, node: *ast::Node, decl: ast::FnDecl) -> Type |
|
| 3338 | + | unsafe fn resolveFnDecl(self: &mut Resolver, node: *ast::Node, decl: ast::FnDecl) -> Type |
|
| 3331 | 3339 | throws (ResolveError) |
|
| 3332 | 3340 | { |
|
| 3333 | 3341 | let attrMask = resolveAttributes(self, decl.attrs); |
|
| 3334 | 3342 | let mut retTy = Type::Void; |
|
| 3335 | 3343 | if let retNode = decl.sig.returnType { |
| 3390 | 3398 | ||
| 3391 | 3399 | return ty; |
|
| 3392 | 3400 | } |
|
| 3393 | 3401 | ||
| 3394 | 3402 | /// Analyze a function body. |
|
| 3395 | - | fn resolveFnDeclBody(self: *mut Resolver, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) { |
|
| 3403 | + | unsafe fn resolveFnDeclBody(self: &mut Resolver, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) { |
|
| 3396 | 3404 | let sym = symbolFor(self, node) else { |
|
| 3397 | 3405 | // The function declaration failed to type check, therefore |
|
| 3398 | 3406 | // no symbol was associated with it. |
|
| 3399 | 3407 | return; |
|
| 3400 | 3408 | }; |
| 3416 | 3424 | throw emitError(self, node, ErrorKind::FnMissingBody); |
|
| 3417 | 3425 | } |
|
| 3418 | 3426 | } |
|
| 3419 | 3427 | ||
| 3420 | 3428 | /// Resolve a function or method body and restore the enclosing context. |
|
| 3421 | - | fn resolveExecutableBody( |
|
| 3422 | - | self: *mut Resolver, |
|
| 3429 | + | unsafe fn resolveExecutableBody( |
|
| 3430 | + | self: &mut Resolver, |
|
| 3423 | 3431 | node: *ast::Node, |
|
| 3424 | 3432 | fnType: *FnType, |
|
| 3425 | 3433 | receiverName: ?*ast::Node, |
|
| 3426 | 3434 | params: *mut [*ast::Node], |
|
| 3427 | 3435 | body: *ast::Node, |
|
| 3428 | 3436 | ) throws (ResolveError) { |
|
| 3429 | - | let isUnsafe = fnType.isUnsafe; |
|
| 3430 | - | if isUnsafe { |
|
| 3431 | - | set self.unsafeDepth += 1; |
|
| 3432 | - | } |
|
| 3437 | + | let wasUnsafe = self.inUnsafeFn; |
|
| 3438 | + | set self.inUnsafeFn = fnType.isUnsafe; |
|
| 3433 | 3439 | // Enter function scope. |
|
| 3434 | 3440 | enterFn(self, node, fnType); // Enter function scope for body analysis. |
|
| 3435 | 3441 | ||
| 3436 | 3442 | let missingReturn = try checkExecutableBody(self, fnType, receiverName, params, body) catch e { |
|
| 3437 | 3443 | exitFn(self); |
|
| 3438 | - | if isUnsafe { set self.unsafeDepth -= 1; } |
|
| 3444 | + | set self.inUnsafeFn = wasUnsafe; |
|
| 3439 | 3445 | throw e; |
|
| 3440 | 3446 | }; |
|
| 3441 | 3447 | exitFn(self); |
|
| 3442 | - | if isUnsafe { |
|
| 3443 | - | set self.unsafeDepth -= 1; |
|
| 3444 | - | } |
|
| 3448 | + | set self.inUnsafeFn = wasUnsafe; |
|
| 3445 | 3449 | if missingReturn { |
|
| 3446 | 3450 | throw emitError(self, body, ErrorKind::FnMissingReturn); |
|
| 3447 | 3451 | } |
|
| 3448 | 3452 | } |
|
| 3449 | 3453 | ||
| 3450 | 3454 | /// Check parameters, body types, and ownership. |
|
| 3451 | 3455 | /// Return whether a required return is missing. |
|
| 3452 | - | fn checkExecutableBody( |
|
| 3453 | - | self: *mut Resolver, |
|
| 3456 | + | unsafe fn checkExecutableBody( |
|
| 3457 | + | self: &mut Resolver, |
|
| 3454 | 3458 | fnType: *FnType, |
|
| 3455 | 3459 | receiverName: ?*ast::Node, |
|
| 3456 | 3460 | params: *mut [*ast::Node], |
|
| 3457 | 3461 | body: *ast::Node, |
|
| 3458 | 3462 | ) -> bool throws (ResolveError) { |
| 3474 | 3478 | try checkLinearFn(self, receiverName, params, body); |
|
| 3475 | 3479 | return false; |
|
| 3476 | 3480 | } |
|
| 3477 | 3481 | ||
| 3478 | 3482 | /// Analyze a function parameter and bind its identifier. |
|
| 3479 | - | fn resolveFnParam(self: *mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type |
|
| 3483 | + | unsafe fn resolveFnParam(self: &mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type |
|
| 3480 | 3484 | throws (ResolveError) |
|
| 3481 | 3485 | { |
|
| 3482 | 3486 | let ty = try resolveValueType(self, param.type); |
|
| 3483 | 3487 | let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0); |
|
| 3484 | 3488 |
| 3492 | 3496 | /// The declaration permits implicit copies. |
|
| 3493 | 3497 | copy: bool, |
|
| 3494 | 3498 | } |
|
| 3495 | 3499 | ||
| 3496 | 3500 | /// Resolve compiler-known ownership markers from a derive list. |
|
| 3497 | - | fn resolveOwnershipMarkers(self: *mut Resolver, derives: *mut [*ast::Node]) -> OwnershipMarkers |
|
| 3501 | + | unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *mut [*ast::Node]) -> OwnershipMarkers |
|
| 3498 | 3502 | throws (ResolveError) |
|
| 3499 | 3503 | { |
|
| 3500 | 3504 | let mut result = OwnershipMarkers { linear: false, copy: false }; |
|
| 3501 | 3505 | for derive in derives { |
|
| 3502 | 3506 | let name = try nodeName(self, derive); |
| 3523 | 3527 | } |
|
| 3524 | 3528 | return result; |
|
| 3525 | 3529 | } |
|
| 3526 | 3530 | ||
| 3527 | 3531 | /// Resolve record fields from a node list. |
|
| 3528 | - | fn resolveRecordFields(self: *mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType |
|
| 3532 | + | unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType |
|
| 3529 | 3533 | throws (ResolveError) |
|
| 3530 | 3534 | { |
|
| 3531 | 3535 | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 3532 | 3536 | let mut result: *mut [RecordField] = &mut []; |
|
| 3533 | 3537 | let mut currentOffset: u32 = 0; |
| 3587 | 3591 | declaredCopy: false, |
|
| 3588 | 3592 | }; |
|
| 3589 | 3593 | } |
|
| 3590 | 3594 | ||
| 3591 | 3595 | /// Resolve record field types for a named record declaration. |
|
| 3592 | - | fn resolveRecordBody(self: *mut Resolver, node: *ast::Node, decl: ast::RecordDecl) |
|
| 3596 | + | unsafe fn resolveRecordBody(self: &mut Resolver, node: *ast::Node, decl: ast::RecordDecl) |
|
| 3593 | 3597 | throws (ResolveError) |
|
| 3594 | 3598 | { |
|
| 3595 | 3599 | // Get the type symbol that was bound to this declaration node. |
|
| 3596 | 3600 | // If there's no symbol, it's because an earlier phase failed. |
|
| 3597 | 3601 | let sym = symbolFor(self, node) |
| 3617 | 3621 | ||
| 3618 | 3622 | set *nominalTy = NominalType::Record(recordType); |
|
| 3619 | 3623 | } |
|
| 3620 | 3624 | ||
| 3621 | 3625 | /// Bind a type name. |
|
| 3622 | - | fn bindTypeName(self: *mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol |
|
| 3626 | + | fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol |
|
| 3623 | 3627 | throws (ResolveError) |
|
| 3624 | 3628 | { |
|
| 3625 | 3629 | let attrMask = resolveAttributes(self, attrs); |
|
| 3626 | 3630 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
|
| 3627 | 3631 |
| 3631 | 3635 | ||
| 3632 | 3636 | return try bindTypeIdent(self, name, node, nominalTy, attrMask); |
|
| 3633 | 3637 | } |
|
| 3634 | 3638 | ||
| 3635 | 3639 | /// Allocate a trait type descriptor and return a pointer to it. |
|
| 3636 | - | fn allocTraitType(self: *mut Resolver, name: *[u8]) -> *mut TraitType { |
|
| 3640 | + | fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *mut TraitType { |
|
| 3637 | 3641 | let p = try! alloc::alloc(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType)); |
|
| 3638 | 3642 | let entry = p as *mut TraitType; |
|
| 3639 | 3643 | set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] }; |
|
| 3640 | 3644 | ||
| 3641 | 3645 | return entry; |
|
| 3642 | 3646 | } |
|
| 3643 | 3647 | ||
| 3644 | 3648 | /// Bind a trait name in the current scope. |
|
| 3645 | - | fn bindTraitName(self: *mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol |
|
| 3649 | + | fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol |
|
| 3646 | 3650 | throws (ResolveError) |
|
| 3647 | 3651 | { |
|
| 3648 | 3652 | let attrMask = resolveAttributes(self, attrs); |
|
| 3649 | 3653 | try ensureDefaultAttrNotAllowed(self, node, attrMask); |
|
| 3650 | 3654 | ||
| 3651 | 3655 | let traitName = try nodeName(self, name); |
|
| 3652 | 3656 | let traitType = allocTraitType(self, traitName); |
|
| 3653 | 3657 | let data = SymbolData::Trait(traitType); |
|
| 3654 | - | let sym = try bindIdent(self, traitName, node, data, attrMask, self.scope); |
|
| 3658 | + | let scope = self.scope; |
|
| 3659 | + | let sym = try bindIdent(self, traitName, node, data, attrMask, scope); |
|
| 3655 | 3660 | ||
| 3656 | 3661 | setNodeType(self, node, Type::Void); |
|
| 3657 | 3662 | setNodeType(self, name, Type::Void); |
|
| 3658 | 3663 | ||
| 3659 | 3664 | return sym; |
| 3668 | 3673 | } |
|
| 3669 | 3674 | return nil; |
|
| 3670 | 3675 | } |
|
| 3671 | 3676 | ||
| 3672 | 3677 | /// Resolve a trait declaration body: supertrait methods, then own methods. |
|
| 3673 | - | fn resolveTraitBody(self: *mut Resolver, node: *ast::Node, supertraits: *mut [*ast::Node], methods: *mut [*ast::Node]) |
|
| 3678 | + | unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *mut [*ast::Node], methods: *mut [*ast::Node]) |
|
| 3674 | 3679 | throws (ResolveError) |
|
| 3675 | 3680 | { |
|
| 3676 | 3681 | let sym = symbolFor(self, node) |
|
| 3677 | 3682 | else return; |
|
| 3678 | 3683 | let case SymbolData::Trait(traitType) = sym.data |
| 3803 | 3808 | } |
|
| 3804 | 3809 | } |
|
| 3805 | 3810 | ||
| 3806 | 3811 | /// Resolve a name path node to a symbol. |
|
| 3807 | 3812 | /// Used for trait and type references in instance declarations and trait objects. |
|
| 3808 | - | fn resolveNamePath(self: *mut Resolver, node: *ast::Node) -> *mut Symbol |
|
| 3813 | + | unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *mut Symbol |
|
| 3809 | 3814 | throws (ResolveError) |
|
| 3810 | 3815 | { |
|
| 3811 | 3816 | match node.value { |
|
| 3812 | 3817 | case ast::NodeValue::Ident(name) => { |
|
| 3813 | 3818 | let sym = findAnySymbol(self.scope, name) |
|
| 3814 | 3819 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 3815 | 3820 | return sym; |
|
| 3816 | 3821 | } |
|
| 3817 | 3822 | case ast::NodeValue::ScopeAccess(access) => { |
|
| 3818 | - | return try resolveAccess(self, node, access, self.scope); |
|
| 3823 | + | let scope = self.scope; |
|
| 3824 | + | return try resolveAccess(self, node, access, scope); |
|
| 3819 | 3825 | } |
|
| 3820 | 3826 | else => { |
|
| 3821 | 3827 | throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
|
| 3822 | 3828 | } |
|
| 3823 | 3829 | } |
|
| 3824 | 3830 | } |
|
| 3825 | 3831 | ||
| 3826 | 3832 | /// Resolve an instance declaration. |
|
| 3827 | 3833 | /// Validates that the trait exists, the target type exists, and all methods |
|
| 3828 | 3834 | /// match the trait's signatures. |
|
| 3829 | - | fn resolveInstanceDecl( |
|
| 3830 | - | self: *mut Resolver, |
|
| 3835 | + | unsafe fn resolveInstanceDecl( |
|
| 3836 | + | self: &mut Resolver, |
|
| 3831 | 3837 | node: *ast::Node, |
|
| 3832 | 3838 | traitName: *ast::Node, |
|
| 3833 | 3839 | targetType: *ast::Node, |
|
| 3834 | 3840 | methods: *mut [*ast::Node] |
|
| 3835 | 3841 | ) throws (ResolveError) { |
| 4031 | 4037 | ||
| 4032 | 4038 | setNodeType(self, node, Type::Void); |
|
| 4033 | 4039 | } |
|
| 4034 | 4040 | ||
| 4035 | 4041 | /// Resolve instance method bodies. |
|
| 4036 | - | fn resolveInstanceMethodBodies(self: *mut Resolver, methods: *mut [*ast::Node]) |
|
| 4042 | + | unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *mut [*ast::Node]) |
|
| 4037 | 4043 | throws (ResolveError) |
|
| 4038 | 4044 | { |
|
| 4039 | 4045 | for methodNode in methods { |
|
| 4040 | 4046 | let case ast::NodeValue::MethodDecl { |
|
| 4041 | 4047 | name, receiverName, receiverType, sig, body, .. |
| 4050 | 4056 | } |
|
| 4051 | 4057 | } |
|
| 4052 | 4058 | ||
| 4053 | 4059 | /// Resolve a method body shared by instance methods and standalone methods. |
|
| 4054 | 4060 | /// Binds the receiver and parameters, then type-checks the body. |
|
| 4055 | - | fn resolveMethodBody( |
|
| 4056 | - | self: *mut Resolver, |
|
| 4061 | + | unsafe fn resolveMethodBody( |
|
| 4062 | + | self: &mut Resolver, |
|
| 4057 | 4063 | node: *ast::Node, |
|
| 4058 | 4064 | receiverName: *ast::Node, |
|
| 4059 | 4065 | sig: ast::FnSig, |
|
| 4060 | 4066 | body: *ast::Node, |
|
| 4061 | 4067 | ) throws (ResolveError) { |
| 4069 | 4075 | /// Resolve a standalone method declaration (signature only). |
|
| 4070 | 4076 | /// Validates the receiver type and registers the method in the method table. |
|
| 4071 | 4077 | ||
| 4072 | 4078 | /// Extract the type name from a resolved receiver type node. |
|
| 4073 | 4079 | fn receiverTypeName( |
|
| 4074 | - | self: *mut Resolver, |
|
| 4080 | + | self: &mut Resolver, |
|
| 4075 | 4081 | receiverType: *ast::Node, |
|
| 4076 | 4082 | ) -> *[u8] throws (ResolveError) { |
|
| 4077 | 4083 | let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) = |
|
| 4078 | 4084 | receiverType.value |
|
| 4079 | 4085 | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
| 4084 | 4090 | ||
| 4085 | 4091 | return sym.name; |
|
| 4086 | 4092 | } |
|
| 4087 | 4093 | ||
| 4088 | 4094 | /// Resolve and register a standalone method declaration. |
|
| 4089 | - | fn resolveMethodDecl( |
|
| 4090 | - | self: *mut Resolver, |
|
| 4095 | + | unsafe fn resolveMethodDecl( |
|
| 4096 | + | self: &mut Resolver, |
|
| 4091 | 4097 | node: *ast::Node, |
|
| 4092 | 4098 | name: *ast::Node, |
|
| 4093 | 4099 | receiverName: *ast::Node, |
|
| 4094 | 4100 | receiverType: *ast::Node, |
|
| 4095 | 4101 | sig: ast::FnSig, |
| 4196 | 4202 | }; |
|
| 4197 | 4203 | set self.methodsLen += 1; |
|
| 4198 | 4204 | } |
|
| 4199 | 4205 | ||
| 4200 | 4206 | /// Look up an instance entry by trait and concrete type. |
|
| 4201 | - | fn findInstance(self: *Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*InstanceEntry { |
|
| 4207 | + | unsafe fn findInstance(self: &Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*unsafe InstanceEntry { |
|
| 4202 | 4208 | for i in 0..self.instancesLen { |
|
| 4203 | - | let entry = &self.instances[i]; |
|
| 4209 | + | let entry: *unsafe InstanceEntry = &self.instances[i]; |
|
| 4204 | 4210 | if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) { |
|
| 4205 | 4211 | return entry; |
|
| 4206 | 4212 | } |
|
| 4207 | 4213 | } |
|
| 4208 | 4214 | return nil; |
|
| 4209 | 4215 | } |
|
| 4210 | 4216 | ||
| 4211 | 4217 | /// Look up a standalone method by concrete type and name. |
|
| 4212 | - | export fn findMethod(self: *Resolver, concreteType: Type, name: *[u8]) -> ?*MethodEntry { |
|
| 4218 | + | export unsafe fn findMethod(self: &Resolver, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry { |
|
| 4213 | 4219 | for i in 0..self.methodsLen { |
|
| 4214 | - | let entry = &self.methods[i]; |
|
| 4220 | + | let entry: *unsafe MethodEntry = &self.methods[i]; |
|
| 4215 | 4221 | if typesEqual(entry.concreteType, concreteType) and entry.name == name { |
|
| 4216 | 4222 | return entry; |
|
| 4217 | 4223 | } |
|
| 4218 | 4224 | } |
|
| 4219 | 4225 | return nil; |
|
| 4220 | 4226 | } |
|
| 4221 | 4227 | ||
| 4222 | 4228 | /// Look up a standalone method entry by its symbol. |
|
| 4223 | - | export fn findMethodBySymbol(self: *Resolver, sym: *mut Symbol) -> ?*MethodEntry { |
|
| 4229 | + | export unsafe fn findMethodBySymbol(self: &Resolver, sym: *mut Symbol) -> ?*unsafe MethodEntry { |
|
| 4224 | 4230 | for i in 0..self.methodsLen { |
|
| 4225 | - | let entry = &self.methods[i]; |
|
| 4231 | + | let entry: *unsafe MethodEntry = &self.methods[i]; |
|
| 4226 | 4232 | if entry.symbol == sym { |
|
| 4227 | 4233 | return entry; |
|
| 4228 | 4234 | } |
|
| 4229 | 4235 | } |
|
| 4230 | 4236 | return nil; |
|
| 4231 | 4237 | } |
|
| 4232 | 4238 | ||
| 4233 | 4239 | /// Resolve union variant types after all type names are bound (Phase 2 of type resolution). |
|
| 4234 | - | fn resolveUnionBody(self: *mut Resolver, node: *ast::Node, decl: ast::UnionDecl) |
|
| 4240 | + | unsafe fn resolveUnionBody(self: &mut Resolver, node: *ast::Node, decl: ast::UnionDecl) |
|
| 4235 | 4241 | throws (ResolveError) |
|
| 4236 | 4242 | { |
|
| 4237 | 4243 | // Get the type symbol that was bound to this declaration node. |
|
| 4238 | 4244 | // If there's no symbol, it's because an earlier phase failed. |
|
| 4239 | 4245 | let sym = symbolFor(self, node) |
| 4306 | 4312 | declaredCopy: markers.copy, |
|
| 4307 | 4313 | }); |
|
| 4308 | 4314 | } |
|
| 4309 | 4315 | ||
| 4310 | 4316 | /// Check if a module should be analyzed based on its attributes and build configuration. |
|
| 4311 | - | fn shouldAnalyzeModule(self: *Resolver, attrs: ?ast::Attributes) -> bool { |
|
| 4317 | + | fn shouldAnalyzeModule(self: &Resolver, attrs: ?ast::Attributes) -> bool { |
|
| 4312 | 4318 | if let attributes = attrs { |
|
| 4313 | 4319 | // Skip test modules unless we're building in test mode. |
|
| 4314 | 4320 | if ast::attributesContains(&attributes, ast::Attribute::Test) and not self.config.buildTest { |
|
| 4315 | 4321 | return false; |
|
| 4316 | 4322 | } |
|
| 4317 | 4323 | } |
|
| 4318 | 4324 | return true; |
|
| 4319 | 4325 | } |
|
| 4320 | 4326 | ||
| 4321 | 4327 | /// Analyze a module during the graph analysis phase. |
|
| 4322 | - | fn resolveModGraph(self: *mut Resolver, node: *ast::Node, decl: ast::Mod) |
|
| 4328 | + | unsafe fn resolveModGraph(self: &mut Resolver, node: *ast::Node, decl: ast::Mod) |
|
| 4323 | 4329 | throws (ResolveError) |
|
| 4324 | 4330 | { |
|
| 4325 | 4331 | if not shouldAnalyzeModule(self, decl.attrs) { |
|
| 4326 | 4332 | return; |
|
| 4327 | 4333 | } |
| 4338 | 4344 | ||
| 4339 | 4345 | exitModuleScope(self, submod); |
|
| 4340 | 4346 | } |
|
| 4341 | 4347 | ||
| 4342 | 4348 | /// Analyze a module in the declaration phase. |
|
| 4343 | - | fn resolveModDecl(self: *mut Resolver, node: *ast::Node, decl: ast::Mod) |
|
| 4349 | + | unsafe fn resolveModDecl(self: &mut Resolver, node: *ast::Node, decl: ast::Mod) |
|
| 4344 | 4350 | throws (ResolveError) |
|
| 4345 | 4351 | { |
|
| 4346 | 4352 | if not shouldAnalyzeModule(self, decl.attrs) { |
|
| 4347 | 4353 | return; |
|
| 4348 | 4354 | } |
| 4355 | 4361 | ||
| 4356 | 4362 | exitModuleScope(self, submod); |
|
| 4357 | 4363 | } |
|
| 4358 | 4364 | ||
| 4359 | 4365 | /// Analyze a `use` statement and create a symbol for the imported module. |
|
| 4360 | - | fn resolveUse(self: *mut Resolver, node: *ast::Node, decl: ast::Use) -> Type |
|
| 4366 | + | unsafe fn resolveUse(self: &mut Resolver, node: *ast::Node, decl: ast::Use) -> Type |
|
| 4361 | 4367 | throws (ResolveError) |
|
| 4362 | 4368 | { |
|
| 4363 | 4369 | let resolved = try resolveModulePath(self, decl.path); |
|
| 4364 | 4370 | let attrMask = resolveAttributes(self, decl.attrs); |
|
| 4365 | 4371 |
| 4371 | 4377 | if let existing = findSymbolInScope(self.scope, sym.name) { |
|
| 4372 | 4378 | if existing == sym { |
|
| 4373 | 4379 | continue; |
|
| 4374 | 4380 | } |
|
| 4375 | 4381 | } |
|
| 4376 | - | try addSymbolToScope(self, sym, self.scope, node); |
|
| 4382 | + | let scope = self.scope; |
|
| 4383 | + | try addSymbolToScope(self, sym, scope, node); |
|
| 4377 | 4384 | } |
|
| 4378 | 4385 | } |
|
| 4379 | 4386 | } else { |
|
| 4380 | 4387 | // Regular module import. |
|
| 4381 | - | try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, self.scope); |
|
| 4388 | + | let scope = self.scope; |
|
| 4389 | + | try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, scope); |
|
| 4382 | 4390 | } |
|
| 4383 | 4391 | return Type::Void; |
|
| 4384 | 4392 | } |
|
| 4385 | 4393 | ||
| 4386 | 4394 | /// Analyze a standard `if` statement. |
|
| 4387 | - | fn resolveIf(self: *mut Resolver, node: *ast::Node, cond: ast::If) -> Type |
|
| 4395 | + | unsafe fn resolveIf(self: &mut Resolver, node: *ast::Node, cond: ast::If) -> Type |
|
| 4388 | 4396 | throws (ResolveError) |
|
| 4389 | 4397 | { |
|
| 4390 | 4398 | try checkBoolean(self, cond.condition); |
|
| 4391 | 4399 | let thenTy = try visit(self, cond.thenBranch, Type::Void); |
|
| 4392 | 4400 | let elseTy = try visitOptional(self, cond.elseBranch, Type::Void); |
|
| 4393 | 4401 | ||
| 4394 | 4402 | return setNodeType(self, node, unifyBranches(thenTy, elseTy)); |
|
| 4395 | 4403 | } |
|
| 4396 | 4404 | ||
| 4397 | 4405 | /// Analyze a conditional expression. |
|
| 4398 | - | fn resolveCondExpr(self: *mut Resolver, node: *ast::Node, cond: ast::CondExpr) -> Type |
|
| 4406 | + | unsafe fn resolveCondExpr(self: &mut Resolver, node: *ast::Node, cond: ast::CondExpr) -> Type |
|
| 4399 | 4407 | throws (ResolveError) |
|
| 4400 | 4408 | { |
|
| 4401 | 4409 | try checkBoolean(self, cond.condition); |
|
| 4402 | 4410 | let thenTy = try infer(self, cond.thenExpr); |
|
| 4403 | 4411 | let elseTy = try infer(self, cond.elseExpr); |
| 4416 | 4424 | ||
| 4417 | 4425 | return setNodeType(self, node, thenTy); |
|
| 4418 | 4426 | } |
|
| 4419 | 4427 | ||
| 4420 | 4428 | /// Analyze a pattern match structure (used by if-let, while-let). |
|
| 4421 | - | fn resolvePatternMatch(self: *mut Resolver, node: *ast::Node, pat: *ast::PatternMatch) |
|
| 4429 | + | unsafe fn resolvePatternMatch(self: &mut Resolver, node: *ast::Node, pat: &ast::PatternMatch) |
|
| 4422 | 4430 | throws (ResolveError) |
|
| 4423 | 4431 | { |
|
| 4424 | 4432 | match pat.kind { |
|
| 4425 | 4433 | case ast::PatternKind::Case => { |
|
| 4426 | 4434 | // Analyze pattern against scrutinee type. |
| 4441 | 4449 | try checkBoolean(self, guard); |
|
| 4442 | 4450 | } |
|
| 4443 | 4451 | } |
|
| 4444 | 4452 | ||
| 4445 | 4453 | /// Analyze an `if let` or `if let case` pattern binding. |
|
| 4446 | - | fn resolveIfLet(self: *mut Resolver, node: *ast::Node, cond: ast::IfLet) -> Type |
|
| 4454 | + | unsafe fn resolveIfLet(self: &mut Resolver, node: *ast::Node, cond: ast::IfLet) -> Type |
|
| 4447 | 4455 | throws (ResolveError) |
|
| 4448 | 4456 | { |
|
| 4449 | 4457 | enterScope(self, node); |
|
| 4450 | 4458 | try resolvePatternMatch(self, node, &cond.pattern); |
|
| 4451 | 4459 |
| 4479 | 4487 | ||
| 4480 | 4488 | /// Analyze a case pattern for match, if-case, let-case, or while-case. |
|
| 4481 | 4489 | /// |
|
| 4482 | 4490 | /// At the top level, bare identifiers are compared against existing values. |
|
| 4483 | 4491 | /// Inside destructuring patterns (arrays, records), identifiers become bindings. |
|
| 4484 | - | fn resolveCasePattern( |
|
| 4485 | - | self: *mut Resolver, |
|
| 4492 | + | unsafe fn resolveCasePattern( |
|
| 4493 | + | self: &mut Resolver, |
|
| 4486 | 4494 | pattern: *ast::Node, |
|
| 4487 | 4495 | scrutineeTy: Type, |
|
| 4488 | 4496 | mode: IdentMode, |
|
| 4489 | 4497 | matchBy: MatchBy |
|
| 4490 | 4498 | ) throws (ResolveError) { |
| 4545 | 4553 | } |
|
| 4546 | 4554 | } |
|
| 4547 | 4555 | } |
|
| 4548 | 4556 | ||
| 4549 | 4557 | /// Analyze a traditional `while` loop. |
|
| 4550 | - | fn resolveWhile(self: *mut Resolver, node: *ast::Node, loopNode: ast::While) -> Type |
|
| 4558 | + | unsafe fn resolveWhile(self: &mut Resolver, node: *ast::Node, loopNode: ast::While) -> Type |
|
| 4551 | 4559 | throws (ResolveError) |
|
| 4552 | 4560 | { |
|
| 4553 | 4561 | try checkBoolean(self, loopNode.condition); |
|
| 4554 | 4562 | try visitLoop(self, loopNode.body); |
|
| 4555 | 4563 | try visitOptional(self, loopNode.elseBranch, Type::Void); |
|
| 4556 | 4564 | ||
| 4557 | 4565 | return setNodeType(self, node, Type::Void); |
|
| 4558 | 4566 | } |
|
| 4559 | 4567 | ||
| 4560 | 4568 | /// Analyze a `while let` loop with pattern binding. |
|
| 4561 | - | fn resolveWhileLet(self: *mut Resolver, node: *ast::Node, loopNode: ast::WhileLet) -> Type |
|
| 4569 | + | unsafe fn resolveWhileLet(self: &mut Resolver, node: *ast::Node, loopNode: ast::WhileLet) -> Type |
|
| 4562 | 4570 | throws (ResolveError) |
|
| 4563 | 4571 | { |
|
| 4564 | 4572 | enterScope(self, node); |
|
| 4565 | 4573 | try resolvePatternMatch(self, node, &loopNode.pattern); |
|
| 4566 | 4574 |
| 4571 | 4579 | ||
| 4572 | 4580 | return setNodeType(self, node, Type::Void); |
|
| 4573 | 4581 | } |
|
| 4574 | 4582 | ||
| 4575 | 4583 | /// Analyze a `for` loop, binding iteration variables. |
|
| 4576 | - | fn resolveFor(self: *mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type |
|
| 4584 | + | unsafe fn resolveFor(self: &mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type |
|
| 4577 | 4585 | throws (ResolveError) |
|
| 4578 | 4586 | { |
|
| 4579 | 4587 | let iterableTy = try infer(self, forStmt.iterable); |
|
| 4580 | 4588 | ||
| 4581 | 4589 | // Extract binding names for the lowerer. |
| 4688 | 4696 | ||
| 4689 | 4697 | /// Check whether a pattern contains nested sub-patterns that further |
|
| 4690 | 4698 | /// refine the match beyond the outer variant (e.g. nested union variant |
|
| 4691 | 4699 | /// tests or literal comparisons). Used to allow the same outer variant |
|
| 4692 | 4700 | /// to appear in multiple match arms. |
|
| 4693 | - | fn hasNestedRefiningPattern(self: *Resolver, pattern: *ast::Node) -> bool { |
|
| 4701 | + | fn hasNestedRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool { |
|
| 4694 | 4702 | for i in 0..patternSubCount(pattern) { |
|
| 4695 | 4703 | if let sub = patternSubElement(pattern, i) { |
|
| 4696 | 4704 | if isRefiningPattern(self, sub) { |
|
| 4697 | 4705 | return true; |
|
| 4698 | 4706 | } |
| 4703 | 4711 | ||
| 4704 | 4712 | /// Check whether a single pattern node is a refining pattern that tests |
|
| 4705 | 4713 | /// a value rather than just binding it. Union variants, literals, and |
|
| 4706 | 4714 | /// scope accesses are refining; identifiers, placeholders, and plain |
|
| 4707 | 4715 | /// record destructurings are not. |
|
| 4708 | - | fn isRefiningPattern(self: *Resolver, pattern: *ast::Node) -> bool { |
|
| 4716 | + | fn isRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool { |
|
| 4709 | 4717 | match pattern.value { |
|
| 4710 | 4718 | case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => |
|
| 4711 | 4719 | return false, |
|
| 4712 | 4720 | case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => { |
|
| 4713 | 4721 | if let keyNode = patternVariantKeyNode(pattern) { |
| 4770 | 4778 | return true; |
|
| 4771 | 4779 | } |
|
| 4772 | 4780 | ||
| 4773 | 4781 | /// Analyze a match prong, checking for duplicate catch-alls. Returns the |
|
| 4774 | 4782 | /// unified match type. |
|
| 4775 | - | fn resolveMatchProng( |
|
| 4776 | - | self: *mut Resolver, |
|
| 4783 | + | unsafe fn resolveMatchProng( |
|
| 4784 | + | self: &mut Resolver, |
|
| 4777 | 4785 | prongNode: *ast::Node, |
|
| 4778 | 4786 | prong: ast::MatchProng, |
|
| 4779 | 4787 | subjectTy: Type, |
|
| 4780 | - | state: *mut MatchState, |
|
| 4788 | + | state: &mut MatchState, |
|
| 4781 | 4789 | matchType: Type, |
|
| 4782 | 4790 | matchBy: MatchBy |
|
| 4783 | 4791 | ) -> Type throws (ResolveError) { |
|
| 4784 | 4792 | // Whether this prong is catch-all. |
|
| 4785 | 4793 | let mut isCatchAll = false; |
| 4804 | 4812 | return try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy); |
|
| 4805 | 4813 | } |
|
| 4806 | 4814 | ||
| 4807 | 4815 | /// Analyze a `match` expression. Dispatches to specialized functions based on |
|
| 4808 | 4816 | /// the subject type. |
|
| 4809 | - | fn resolveMatch(self: *mut Resolver, node: *ast::Node, sw: ast::Match) -> Type |
|
| 4817 | + | unsafe fn resolveMatch(self: &mut Resolver, node: *ast::Node, sw: ast::Match) -> Type |
|
| 4810 | 4818 | throws (ResolveError) |
|
| 4811 | 4819 | { |
|
| 4812 | 4820 | let subjectTy = try infer(self, sw.subject); |
|
| 4813 | 4821 | let subject = unwrapMatchSubject(subjectTy); |
|
| 4814 | 4822 |
| 4832 | 4840 | }; |
|
| 4833 | 4841 | return ty; |
|
| 4834 | 4842 | } |
|
| 4835 | 4843 | ||
| 4836 | 4844 | /// Analyze a `match` expression on an optional subject. |
|
| 4837 | - | fn resolveMatchOptional( |
|
| 4838 | - | self: *mut Resolver, |
|
| 4845 | + | unsafe fn resolveMatchOptional( |
|
| 4846 | + | self: &mut Resolver, |
|
| 4839 | 4847 | node: *ast::Node, |
|
| 4840 | 4848 | sw: ast::Match, |
|
| 4841 | 4849 | innerTy: *Type, |
|
| 4842 | 4850 | matchBy: MatchBy |
|
| 4843 | 4851 | ) -> Type throws (ResolveError) |
| 4905 | 4913 | } |
|
| 4906 | 4914 | return setNodeType(self, node, matchType); |
|
| 4907 | 4915 | } |
|
| 4908 | 4916 | ||
| 4909 | 4917 | /// Analyze a `match` expression on a union subject. |
|
| 4910 | - | fn resolveMatchUnion( |
|
| 4911 | - | self: *mut Resolver, |
|
| 4918 | + | unsafe fn resolveMatchUnion( |
|
| 4919 | + | self: &mut Resolver, |
|
| 4912 | 4920 | node: *ast::Node, |
|
| 4913 | 4921 | sw: ast::Match, |
|
| 4914 | 4922 | subjectTy: Type, |
|
| 4915 | 4923 | info: UnionType, |
|
| 4916 | 4924 | matchBy: MatchBy |
| 4961 | 4969 | return setNodeType(self, node, matchType); |
|
| 4962 | 4970 | } |
|
| 4963 | 4971 | ||
| 4964 | 4972 | /// Analyze a `match` expression on a generic subject type. Requires exhaustiveness: |
|
| 4965 | 4973 | /// booleans must cover both `true` and `false`, other types require a catch-all. |
|
| 4966 | - | fn resolveMatchGeneric(self: *mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type |
|
| 4974 | + | unsafe fn resolveMatchGeneric(self: &mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type |
|
| 4967 | 4975 | throws (ResolveError) |
|
| 4968 | 4976 | { |
|
| 4969 | 4977 | let prongs = sw.prongs; |
|
| 4970 | 4978 | let mut state = MatchState { catchAll: false, isConst: true }; |
|
| 4971 | 4979 | let mut matchType = Type::Never; |
| 5030 | 5038 | ||
| 5031 | 5039 | return setNodeType(self, node, matchType); |
|
| 5032 | 5040 | } |
|
| 5033 | 5041 | ||
| 5034 | 5042 | /// Analyze a single `match` prong branch. Returns the unified match type. |
|
| 5035 | - | fn visitMatchProng( |
|
| 5036 | - | self: *mut Resolver, |
|
| 5043 | + | unsafe fn visitMatchProng( |
|
| 5044 | + | self: &mut Resolver, |
|
| 5037 | 5045 | node: *ast::Node, |
|
| 5038 | 5046 | prongNode: ast::MatchProng, |
|
| 5039 | 5047 | subjectTy: Type, |
|
| 5040 | 5048 | matchType: Type, |
|
| 5041 | 5049 | matchBy: MatchBy |
| 5050 | 5058 | ||
| 5051 | 5059 | return unifyBranches(matchType, prongTy); |
|
| 5052 | 5060 | } |
|
| 5053 | 5061 | ||
| 5054 | 5062 | /// Analyze the contents of a `match` prong while inside the prong scope. |
|
| 5055 | - | fn resolveMatchProngBody( |
|
| 5056 | - | self: *mut Resolver, |
|
| 5063 | + | unsafe fn resolveMatchProngBody( |
|
| 5064 | + | self: &mut Resolver, |
|
| 5057 | 5065 | prong: ast::MatchProng, |
|
| 5058 | 5066 | subjectTy: Type, |
|
| 5059 | 5067 | matchBy: MatchBy |
|
| 5060 | 5068 | ) -> Type throws (ResolveError) { |
|
| 5061 | 5069 | match prong.arm { |
| 5079 | 5087 | } |
|
| 5080 | 5088 | return try visit(self, prong.body, Type::Void); |
|
| 5081 | 5089 | } |
|
| 5082 | 5090 | ||
| 5083 | 5091 | /// Ensure a scope access pattern references a compatible union variant. |
|
| 5084 | - | fn resolveUnionScopePattern( |
|
| 5085 | - | self: *mut Resolver, |
|
| 5092 | + | unsafe fn resolveUnionScopePattern( |
|
| 5093 | + | self: &mut Resolver, |
|
| 5086 | 5094 | pattern: *ast::Node, |
|
| 5087 | 5095 | access: ast::Access, |
|
| 5088 | 5096 | subjectTy: Type, |
|
| 5089 | 5097 | unionType: UnionType |
|
| 5090 | 5098 | ) throws (ResolveError) { |
| 5105 | 5113 | throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name)); |
|
| 5106 | 5114 | } |
|
| 5107 | 5115 | } |
|
| 5108 | 5116 | ||
| 5109 | 5117 | /// Validate and bind a union constructor call used as a `match` pattern. |
|
| 5110 | - | fn resolveUnionCallPattern( |
|
| 5111 | - | self: *mut Resolver, |
|
| 5118 | + | unsafe fn resolveUnionCallPattern( |
|
| 5119 | + | self: &mut Resolver, |
|
| 5112 | 5120 | pattern: *ast::Node, |
|
| 5113 | 5121 | call: ast::Call, |
|
| 5114 | 5122 | subjectTy: Type, |
|
| 5115 | 5123 | unionType: UnionType, |
|
| 5116 | 5124 | matchBy: MatchBy |
| 5129 | 5137 | throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name)); |
|
| 5130 | 5138 | } |
|
| 5131 | 5139 | } |
|
| 5132 | 5140 | ||
| 5133 | 5141 | /// Bind the payload introduced by a union constructor pattern. |
|
| 5134 | - | fn bindUnionPatternPayload( |
|
| 5135 | - | self: *mut Resolver, |
|
| 5142 | + | unsafe fn bindUnionPatternPayload( |
|
| 5143 | + | self: &mut Resolver, |
|
| 5136 | 5144 | pattern: *ast::Node, |
|
| 5137 | 5145 | call: ast::Call, |
|
| 5138 | 5146 | variantName: *[u8], |
|
| 5139 | 5147 | payloadTy: Type, |
|
| 5140 | 5148 | matchBy: MatchBy |
| 5150 | 5158 | ||
| 5151 | 5159 | try bindRecordPatternFields(self, pattern, recInfo, matchBy); |
|
| 5152 | 5160 | } |
|
| 5153 | 5161 | ||
| 5154 | 5162 | /// Bind a pattern variable. For ref matches, wraps the type in a pointer. |
|
| 5155 | - | fn bindPatternVar(self: *mut Resolver, binding: *ast::Node, ty: Type, matchBy: MatchBy) |
|
| 5163 | + | unsafe fn bindPatternVar(self: &mut Resolver, binding: *ast::Node, ty: Type, matchBy: MatchBy) |
|
| 5156 | 5164 | throws (ResolveError) |
|
| 5157 | 5165 | { |
|
| 5158 | 5166 | let mut bindTy = ty; |
|
| 5159 | 5167 | match matchBy { |
|
| 5160 | 5168 | case MatchBy::Value => {} |
| 5183 | 5191 | } |
|
| 5184 | 5192 | } |
|
| 5185 | 5193 | } |
|
| 5186 | 5194 | ||
| 5187 | 5195 | /// Bind record pattern fields to variables in the current scope. |
|
| 5188 | - | fn bindRecordPatternFields( |
|
| 5189 | - | self: *mut Resolver, |
|
| 5196 | + | unsafe fn bindRecordPatternFields( |
|
| 5197 | + | self: &mut Resolver, |
|
| 5190 | 5198 | pattern: *ast::Node, |
|
| 5191 | 5199 | recInfo: RecordType, |
|
| 5192 | 5200 | matchBy: MatchBy |
|
| 5193 | 5201 | ) throws (ResolveError) { |
|
| 5194 | 5202 | match pattern.value { |
| 5224 | 5232 | else => throw emitError(self, pattern, ErrorKind::Internal) |
|
| 5225 | 5233 | } |
|
| 5226 | 5234 | } |
|
| 5227 | 5235 | ||
| 5228 | 5236 | /// Validate and bind a record literal pattern for matching labeled union variants. |
|
| 5229 | - | fn resolveUnionRecordPattern( |
|
| 5230 | - | self: *mut Resolver, |
|
| 5237 | + | unsafe fn resolveUnionRecordPattern( |
|
| 5238 | + | self: &mut Resolver, |
|
| 5231 | 5239 | pattern: *ast::Node, |
|
| 5232 | 5240 | lit: ast::RecordLit, |
|
| 5233 | 5241 | subjectTy: Type, |
|
| 5234 | 5242 | unionType: UnionType, |
|
| 5235 | 5243 | matchBy: MatchBy |
| 5261 | 5269 | ||
| 5262 | 5270 | try bindRecordPatternFields(self, pattern, recInfo, matchBy); |
|
| 5263 | 5271 | } |
|
| 5264 | 5272 | ||
| 5265 | 5273 | /// Analyze a pattern appearing in a union case. |
|
| 5266 | - | fn resolveUnionPattern( |
|
| 5267 | - | self: *mut Resolver, |
|
| 5274 | + | unsafe fn resolveUnionPattern( |
|
| 5275 | + | self: &mut Resolver, |
|
| 5268 | 5276 | pattern: *ast::Node, |
|
| 5269 | 5277 | subjectTy: Type, |
|
| 5270 | 5278 | unionType: UnionType, |
|
| 5271 | 5279 | matchBy: MatchBy |
|
| 5272 | 5280 | ) throws (ResolveError) { |
| 5318 | 5326 | } |
|
| 5319 | 5327 | return false; |
|
| 5320 | 5328 | } |
|
| 5321 | 5329 | ||
| 5322 | 5330 | /// Analyze a `let-else` guard. |
|
| 5323 | - | fn resolveLetElse(self: *mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type |
|
| 5331 | + | unsafe fn resolveLetElse(self: &mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type |
|
| 5324 | 5332 | throws (ResolveError) |
|
| 5325 | 5333 | { |
|
| 5326 | - | let pat = &letElse.pattern; |
|
| 5334 | + | let pat = letElse.pattern; |
|
| 5327 | 5335 | let exprTy = try infer(self, pat.scrutinee); |
|
| 5328 | 5336 | ||
| 5329 | 5337 | match pat.kind { |
|
| 5330 | 5338 | case ast::PatternKind::Binding => { |
|
| 5331 | 5339 | // Simple binding requires an optional expression. |
| 5365 | 5373 | } |
|
| 5366 | 5374 | return setNodeType(self, node, Type::Void); |
|
| 5367 | 5375 | } |
|
| 5368 | 5376 | ||
| 5369 | 5377 | /// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`. |
|
| 5370 | - | fn resolveBuiltinCall( |
|
| 5371 | - | self: *mut Resolver, |
|
| 5378 | + | unsafe fn resolveBuiltinCall( |
|
| 5379 | + | self: &mut Resolver, |
|
| 5372 | 5380 | node: *ast::Node, |
|
| 5373 | 5381 | kind: ast::Builtin, |
|
| 5374 | 5382 | args: *mut [*ast::Node] |
|
| 5375 | 5383 | ) -> Type throws (ResolveError) { |
|
| 5376 | 5384 | // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`. |
| 5430 | 5438 | return setNodeType(self, node, Type::U32); |
|
| 5431 | 5439 | } |
|
| 5432 | 5440 | ||
| 5433 | 5441 | /// Validate call arguments against a function type: check argument count, |
|
| 5434 | 5442 | /// type-check each argument, and verify that throwing functions use `try`. |
|
| 5435 | - | fn checkCallArgs(self: *mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx) |
|
| 5443 | + | unsafe fn checkCallArgs(self: &mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx) |
|
| 5436 | 5444 | throws (ResolveError) |
|
| 5437 | 5445 | { |
|
| 5438 | 5446 | if ctx == CallCtx::Normal and info.throwList.len > 0 { |
|
| 5439 | 5447 | throw emitError(self, node, ErrorKind::MissingTry); |
|
| 5440 | 5448 | } |
| 5450 | 5458 | try checkAssignable(self, argNode, expectedTy); |
|
| 5451 | 5459 | } |
|
| 5452 | 5460 | } |
|
| 5453 | 5461 | ||
| 5454 | 5462 | /// Analyze a function call expression. |
|
| 5455 | - | fn resolveCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, ctx: CallCtx) -> Type |
|
| 5463 | + | unsafe fn resolveCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, ctx: CallCtx) -> Type |
|
| 5456 | 5464 | throws (ResolveError) |
|
| 5457 | 5465 | { |
|
| 5458 | 5466 | // Intercept method calls on slices before inferring the callee. |
|
| 5459 | 5467 | if let case ast::NodeValue::FieldAccess(access) = call.callee.value { |
|
| 5460 | 5468 | let parentTy = try infer(self, access.parent); |
| 5565 | 5573 | // Associate return type to call. |
|
| 5566 | 5574 | return setNodeType(self, node, *info.returnType); |
|
| 5567 | 5575 | } |
|
| 5568 | 5576 | ||
| 5569 | 5577 | /// Resolve `slice.append(val, allocator)`. |
|
| 5570 | - | fn resolveSliceAppend( |
|
| 5571 | - | self: *mut Resolver, |
|
| 5578 | + | unsafe fn resolveSliceAppend( |
|
| 5579 | + | self: &mut Resolver, |
|
| 5572 | 5580 | node: *ast::Node, |
|
| 5573 | 5581 | parent: *ast::Node, |
|
| 5574 | 5582 | parentType: Type, |
|
| 5575 | 5583 | args: *mut [*ast::Node], |
|
| 5576 | 5584 | elemType: *Type, |
| 5595 | 5603 | // Return the parent's type so the caller can rebind: |
|
| 5596 | 5604 | return setNodeType(self, node, parentType); |
|
| 5597 | 5605 | } |
|
| 5598 | 5606 | ||
| 5599 | 5607 | /// Resolve `slice.delete(index)`. |
|
| 5600 | - | fn resolveSliceDelete( |
|
| 5601 | - | self: *mut Resolver, |
|
| 5608 | + | unsafe fn resolveSliceDelete( |
|
| 5609 | + | self: &mut Resolver, |
|
| 5602 | 5610 | node: *ast::Node, |
|
| 5603 | 5611 | parent: *ast::Node, |
|
| 5604 | 5612 | args: *mut [*ast::Node], |
|
| 5605 | 5613 | elemType: *Type, |
|
| 5606 | 5614 | mutable: bool |
| 5619 | 5627 | ||
| 5620 | 5628 | return setNodeType(self, node, Type::Void); |
|
| 5621 | 5629 | } |
|
| 5622 | 5630 | ||
| 5623 | 5631 | /// Analyze an assignment expression. |
|
| 5624 | - | fn resolveAssign(self: *mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type |
|
| 5632 | + | unsafe fn resolveAssign(self: &mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type |
|
| 5625 | 5633 | throws (ResolveError) |
|
| 5626 | 5634 | { |
|
| 5627 | 5635 | // Slice assignment: `slice[range] = value`. |
|
| 5628 | 5636 | if let case ast::NodeValue::Subscript { container, index } = assign.left.value { |
|
| 5629 | 5637 | if let case ast::NodeValue::Range(range) = index.value { |
| 5682 | 5690 | ||
| 5683 | 5691 | return setNodeType(self, node, leftTy); |
|
| 5684 | 5692 | } |
|
| 5685 | 5693 | ||
| 5686 | 5694 | /// Ensure slice range bounds are valid `u32` values. |
|
| 5687 | - | fn checkSliceRangeIndices(self: *mut Resolver, range: ast::Range) throws (ResolveError) { |
|
| 5695 | + | unsafe fn checkSliceRangeIndices(self: &mut Resolver, range: ast::Range) throws (ResolveError) { |
|
| 5688 | 5696 | if let start = range.start { |
|
| 5689 | 5697 | try checkIndex(self, start); |
|
| 5690 | 5698 | } |
|
| 5691 | 5699 | if let end = range.end { |
|
| 5692 | 5700 | try checkIndex(self, end); |
|
| 5693 | 5701 | } |
|
| 5694 | 5702 | } |
|
| 5695 | 5703 | ||
| 5696 | 5704 | /// Emit an error when a slice range with compile-tyime values exceeds the array length. |
|
| 5697 | - | fn validateArraySliceBounds(self: *mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) { |
|
| 5705 | + | fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) { |
|
| 5698 | 5706 | let mut startVal: ?u32 = nil; |
|
| 5699 | 5707 | let mut endVal: ?u32 = length; |
|
| 5700 | 5708 | ||
| 5701 | 5709 | if let startNode = range.start { |
|
| 5702 | 5710 | if let val = constSliceIndex(self, startNode) { |
| 5722 | 5730 | } |
|
| 5723 | 5731 | ||
| 5724 | 5732 | /// Check that an index expression has an unsigned integer type. |
|
| 5725 | 5733 | /// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals. |
|
| 5726 | 5734 | /// Smaller types are widened to `u32` via a numeric cast coercion. |
|
| 5727 | - | fn checkIndex(self: *mut Resolver, indexNode: *ast::Node) throws (ResolveError) { |
|
| 5735 | + | unsafe fn checkIndex(self: &mut Resolver, indexNode: *ast::Node) throws (ResolveError) { |
|
| 5728 | 5736 | let indexTy = try visit(self, indexNode, Type::U32); |
|
| 5729 | 5737 | if indexTy == Type::Int or indexTy == Type::U32 { |
|
| 5730 | 5738 | let _ = try expectAssignable(self, Type::U32, indexTy, indexNode); |
|
| 5731 | 5739 | return; |
|
| 5732 | 5740 | } |
| 5744 | 5752 | } |
|
| 5745 | 5753 | } |
|
| 5746 | 5754 | } |
|
| 5747 | 5755 | ||
| 5748 | 5756 | /// Analyze an array or slice subscript expression. |
|
| 5749 | - | fn resolveSubscript(self: *mut Resolver, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type |
|
| 5757 | + | unsafe fn resolveSubscript(self: &mut Resolver, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type |
|
| 5750 | 5758 | throws (ResolveError) |
|
| 5751 | 5759 | { |
|
| 5752 | 5760 | // Range subscripts always require `&` to form a slice. |
|
| 5753 | 5761 | if let case ast::NodeValue::Range(range) = indexNode.value { |
|
| 5754 | 5762 | let _ = try infer(self, indexNode); |
| 5775 | 5783 | } |
|
| 5776 | 5784 | } |
|
| 5777 | 5785 | } |
|
| 5778 | 5786 | ||
| 5779 | 5787 | /// Find a record field by name. |
|
| 5780 | - | fn findRecordField(s: *RecordType, fieldName: *[u8]) -> ?u32 { |
|
| 5788 | + | fn findRecordField(s: &RecordType, fieldName: *[u8]) -> ?u32 { |
|
| 5781 | 5789 | for field, i in s.fields { |
|
| 5782 | 5790 | if let name = field.name { |
|
| 5783 | 5791 | if name == fieldName { |
|
| 5784 | 5792 | return i; |
|
| 5785 | 5793 | } |
| 5787 | 5795 | } |
|
| 5788 | 5796 | return nil; |
|
| 5789 | 5797 | } |
|
| 5790 | 5798 | ||
| 5791 | 5799 | /// Analyze a union constructor call with payload. |
|
| 5792 | - | fn resolveUnionConstructorCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *NominalType) -> Type |
|
| 5800 | + | unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *NominalType) -> Type |
|
| 5793 | 5801 | throws (ResolveError) |
|
| 5794 | 5802 | { |
|
| 5795 | 5803 | // Get the union nominal type. |
|
| 5796 | 5804 | let case NominalType::Union(unionType) = *unionNominal |
|
| 5797 | 5805 | else panic "resolveUnionConstructorCall: not a union type"; |
| 5822 | 5830 | /// Analyze an unlabeled record constructor call. |
|
| 5823 | 5831 | /// |
|
| 5824 | 5832 | /// Handles the syntax `R(a, b)` for unlabeled records, checking that the |
|
| 5825 | 5833 | /// number of arguments matches the record's field count and that each argument |
|
| 5826 | 5834 | /// is assignable to its corresponding field type. |
|
| 5827 | - | fn resolveRecordConstructorCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, recordType: *NominalType) -> Type |
|
| 5835 | + | unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *NominalType) -> Type |
|
| 5828 | 5836 | throws (ResolveError) |
|
| 5829 | 5837 | { |
|
| 5830 | 5838 | let case NominalType::Record(recInfo) = *recordType |
|
| 5831 | 5839 | else panic "resolveRecordConstructorCall: not a record type"; |
|
| 5832 | 5840 |
| 5834 | 5842 | return setNodeType(self, node, Type::Nominal(recordType)); |
|
| 5835 | 5843 | } |
|
| 5836 | 5844 | ||
| 5837 | 5845 | /// Resolve the type name of a record literal, handling both record types and |
|
| 5838 | 5846 | /// union variant payloads like `Union::Variant { ... }`. |
|
| 5839 | - | fn resolveRecordLitType( |
|
| 5840 | - | self: *mut Resolver, node: *ast::Node, typeIdent: *ast::Node |
|
| 5847 | + | unsafe fn resolveRecordLitType( |
|
| 5848 | + | self: &mut Resolver, node: *ast::Node, typeIdent: *ast::Node |
|
| 5841 | 5849 | ) -> ResolvedRecordLitType |
|
| 5842 | 5850 | throws (ResolveError) |
|
| 5843 | 5851 | { |
|
| 5844 | 5852 | // Check if this is a scope access that might be a union variant. |
|
| 5845 | 5853 | if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value { |
|
| 5846 | - | let sym = try resolveAccess(self, typeIdent, access, self.scope); |
|
| 5854 | + | let scope = self.scope; |
|
| 5855 | + | let sym = try resolveAccess(self, typeIdent, access, scope); |
|
| 5847 | 5856 | ||
| 5848 | 5857 | // Check if resolved symbol is a union variant. |
|
| 5849 | 5858 | if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data { |
|
| 5850 | 5859 | // Get the union type from the variant's declaration. |
|
| 5851 | 5860 | let declSym = symbolFor(self, decl) |
| 5880 | 5889 | resultType: Type::Nominal(tyInfo), |
|
| 5881 | 5890 | }; |
|
| 5882 | 5891 | } |
|
| 5883 | 5892 | ||
| 5884 | 5893 | /// Analyze a record literal expression. |
|
| 5885 | - | fn resolveRecordLit(self: *mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 5894 | + | unsafe fn resolveRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 5886 | 5895 | throws (ResolveError) |
|
| 5887 | 5896 | { |
|
| 5888 | 5897 | // If no type name, infer an anonymous tuple type. |
|
| 5889 | 5898 | let typeIdent = lit.typeName else { |
|
| 5890 | 5899 | return try resolveAnonRecordLit(self, node, lit, hint); |
| 5937 | 5946 | } |
|
| 5938 | 5947 | return setNodeType(self, node, resultType); |
|
| 5939 | 5948 | } |
|
| 5940 | 5949 | ||
| 5941 | 5950 | /// Analyze an anonymous record literal, checking fields against the hint type. |
|
| 5942 | - | fn resolveAnonRecordLit(self: *mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 5951 | + | unsafe fn resolveAnonRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 5943 | 5952 | throws (ResolveError) |
|
| 5944 | 5953 | { |
|
| 5945 | 5954 | // Unwrap optional hint to get the inner record type. |
|
| 5946 | 5955 | let mut innerHint = hint; |
|
| 5947 | 5956 | if let case Type::Optional(inner) = hint { |
| 5995 | 6004 | } |
|
| 5996 | 6005 | return setNodeType(self, node, innerHint); |
|
| 5997 | 6006 | } |
|
| 5998 | 6007 | ||
| 5999 | 6008 | /// Analyze an array literal expression. |
|
| 6000 | - | fn resolveArrayLit(self: *mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type |
|
| 6009 | + | unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type |
|
| 6001 | 6010 | throws (ResolveError) |
|
| 6002 | 6011 | { |
|
| 6003 | 6012 | let length = items.len; |
|
| 6004 | 6013 | let mut expectedTy: Type = Type::Unknown; |
|
| 6005 | 6014 |
| 6027 | 6036 | let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length }); |
|
| 6028 | 6037 | return setNodeType(self, node, arrayTy); |
|
| 6029 | 6038 | } |
|
| 6030 | 6039 | ||
| 6031 | 6040 | /// Analyze an array repeat literal expression. |
|
| 6032 | - | fn resolveArrayRepeat(self: *mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type |
|
| 6041 | + | unsafe fn resolveArrayRepeat(self: &mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type |
|
| 6033 | 6042 | throws (ResolveError) |
|
| 6034 | 6043 | { |
|
| 6035 | 6044 | let mut itemHint = hint; |
|
| 6036 | 6045 | if let case Type::Array(ary) = hint { |
|
| 6037 | 6046 | set itemHint = *ary.item; |
| 6049 | 6058 | return setNodeType(self, node, arrayTy); |
|
| 6050 | 6059 | } |
|
| 6051 | 6060 | ||
| 6052 | 6061 | /// Resolve union variant access. |
|
| 6053 | 6062 | fn resolveUnionVariantAccess( |
|
| 6054 | - | self: *mut Resolver, |
|
| 6063 | + | self: &mut Resolver, |
|
| 6055 | 6064 | node: *ast::Node, |
|
| 6056 | 6065 | access: ast::Access, |
|
| 6057 | 6066 | unionType: UnionType, |
|
| 6058 | 6067 | variantName: *[u8] |
|
| 6059 | 6068 | ) -> *mut Symbol throws (ResolveError) { |
| 6076 | 6085 | } |
|
| 6077 | 6086 | throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName)); |
|
| 6078 | 6087 | } |
|
| 6079 | 6088 | ||
| 6080 | 6089 | /// Analyze a scope access expression. |
|
| 6081 | - | fn resolveScopeAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type |
|
| 6090 | + | unsafe fn resolveScopeAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type |
|
| 6082 | 6091 | throws (ResolveError) |
|
| 6083 | 6092 | { |
|
| 6084 | - | let sym = try resolveAccess(self, node, access, self.scope); |
|
| 6093 | + | let scope = self.scope; |
|
| 6094 | + | let sym = try resolveAccess(self, node, access, scope); |
|
| 6085 | 6095 | let mut ty: Type = undefined; |
|
| 6086 | 6096 | ||
| 6087 | 6097 | match sym.data { |
|
| 6088 | 6098 | case SymbolData::Value { type, .. } => { |
|
| 6089 | 6099 | setNodeSymbol(self, node, sym); |
| 6124 | 6134 | } |
|
| 6125 | 6135 | return setNodeType(self, node, ty); |
|
| 6126 | 6136 | } |
|
| 6127 | 6137 | ||
| 6128 | 6138 | /// Analyze a field access expression. |
|
| 6129 | - | fn resolveFieldAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type |
|
| 6139 | + | unsafe fn resolveFieldAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type |
|
| 6130 | 6140 | throws (ResolveError) |
|
| 6131 | 6141 | { |
|
| 6132 | 6142 | let parentTy = try infer(self, access.parent); |
|
| 6133 | 6143 | if isUnsafePointerType(parentTy) { |
|
| 6134 | 6144 | try requireUnsafe(self, access.parent); |
| 6203 | 6213 | } |
|
| 6204 | 6214 | } |
|
| 6205 | 6215 | } |
|
| 6206 | 6216 | ||
| 6207 | 6217 | /// Determine whether an expression can yield a mutable location for borrowing. |
|
| 6208 | - | fn canBorrowMutFrom(self: *mut Resolver, node: *ast::Node) -> bool |
|
| 6218 | + | unsafe fn canBorrowMutFrom(self: &mut Resolver, node: *ast::Node) -> bool |
|
| 6209 | 6219 | throws (ResolveError) |
|
| 6210 | 6220 | { |
|
| 6211 | 6221 | match node.value { |
|
| 6212 | 6222 | case ast::NodeValue::Ident(name) => { |
|
| 6213 | 6223 | let sym = findValueSymbol(self.scope, name) |
| 6295 | 6305 | return false; |
|
| 6296 | 6306 | } |
|
| 6297 | 6307 | } |
|
| 6298 | 6308 | } |
|
| 6299 | 6309 | ||
| 6310 | + | /// Return the storage class of an addressed location. |
|
| 6311 | + | fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass { |
|
| 6312 | + | match node.value { |
|
| 6313 | + | case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => { |
|
| 6314 | + | if let sym = symbolFor(self, node) { |
|
| 6315 | + | match sym.node.value { |
|
| 6316 | + | case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) => |
|
| 6317 | + | return types::PointerClass::Owned, |
|
| 6318 | + | else => {} |
|
| 6319 | + | } |
|
| 6320 | + | } |
|
| 6321 | + | } |
|
| 6322 | + | case ast::NodeValue::FieldAccess(access) => { |
|
| 6323 | + | if let ty = typeFor(self, access.parent) { |
|
| 6324 | + | if let case Type::Pointer { class, .. } = ty { |
|
| 6325 | + | return class; |
|
| 6326 | + | } |
|
| 6327 | + | } |
|
| 6328 | + | return addressStorageClass(self, access.parent); |
|
| 6329 | + | } |
|
| 6330 | + | case ast::NodeValue::Subscript { container, .. } => { |
|
| 6331 | + | if let ty = typeFor(self, container) { |
|
| 6332 | + | if let case Type::Slice { class, .. } = autoDeref(ty) { |
|
| 6333 | + | return class; |
|
| 6334 | + | } |
|
| 6335 | + | if let case Type::Pointer { class, .. } = ty { |
|
| 6336 | + | return class; |
|
| 6337 | + | } |
|
| 6338 | + | } |
|
| 6339 | + | return addressStorageClass(self, container); |
|
| 6340 | + | } |
|
| 6341 | + | case ast::NodeValue::Deref(target) => { |
|
| 6342 | + | if let ty = typeFor(self, target) { |
|
| 6343 | + | if let case Type::Pointer { class, .. } = ty { |
|
| 6344 | + | return class; |
|
| 6345 | + | } |
|
| 6346 | + | } |
|
| 6347 | + | return addressStorageClass(self, target); |
|
| 6348 | + | } |
|
| 6349 | + | else => {} |
|
| 6350 | + | } |
|
| 6351 | + | return types::PointerClass::Ref; |
|
| 6352 | + | } |
|
| 6353 | + | ||
| 6354 | + | /// Select an address type without extending the target storage lifetime. |
|
| 6355 | + | fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass |
|
| 6356 | + | throws (ResolveError) |
|
| 6357 | + | { |
|
| 6358 | + | if isUnsafePointerType(hint) { |
|
| 6359 | + | try requireUnsafe(self, target); |
|
| 6360 | + | return types::PointerClass::Unsafe; |
|
| 6361 | + | } |
|
| 6362 | + | if isRefType(hint) { |
|
| 6363 | + | return types::PointerClass::Ref; |
|
| 6364 | + | } |
|
| 6365 | + | match target.value { |
|
| 6366 | + | case ast::NodeValue::ArrayLit(_), ast::NodeValue::ArrayRepeatLit(_) => { |
|
| 6367 | + | if isConstExpr(self, target) { |
|
| 6368 | + | return types::PointerClass::Owned; |
|
| 6369 | + | } |
|
| 6370 | + | } |
|
| 6371 | + | else => {} |
|
| 6372 | + | } |
|
| 6373 | + | return addressStorageClass(self, target); |
|
| 6374 | + | } |
|
| 6375 | + | ||
| 6300 | 6376 | /// Analyze an address-of expression. |
|
| 6301 | - | fn resolveAddressOf(self: *mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type |
|
| 6377 | + | unsafe fn resolveAddressOf(self: &mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type |
|
| 6302 | 6378 | throws (ResolveError) |
|
| 6303 | 6379 | { |
|
| 6304 | - | // Checked and unsafe pointer contexts require a reference source. |
|
| 6305 | - | let class = types::PointerClass::Ref |
|
| 6306 | - | if isRefType(hint) or isUnsafePointerType(hint) |
|
| 6307 | - | else types::PointerClass::Owned; |
|
| 6308 | 6380 | if addr.mutable { |
|
| 6309 | 6381 | if not try canBorrowMutFrom(self, addr.target) { |
|
| 6310 | 6382 | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
|
| 6311 | 6383 | } |
|
| 6312 | 6384 | } |
| 6335 | 6407 | else => { |
|
| 6336 | 6408 | throw emitError(self, container, ErrorKind::ExpectedIndexable); |
|
| 6337 | 6409 | } |
|
| 6338 | 6410 | } |
|
| 6339 | 6411 | } |
|
| 6412 | + | let class = try addressClass(self, addr.target, hint); |
|
| 6340 | 6413 | let sliceTy = Type::Slice { class, item, mutable: addr.mutable }; |
|
| 6341 | 6414 | let alloc = allocType(self, sliceTy); |
|
| 6342 | 6415 | setSliceRangeInfo(self, node, SliceRangeInfo { |
|
| 6343 | 6416 | itemType: item, |
|
| 6344 | 6417 | mutable: addr.mutable, |
| 6352 | 6425 | let mut targetHint: Type = Type::Unknown; |
|
| 6353 | 6426 | if let case Type::Slice { item, .. } = hint { |
|
| 6354 | 6427 | set targetHint = Type::Array(ArrayType { item, length: 0 }); |
|
| 6355 | 6428 | } |
|
| 6356 | 6429 | let targetTy = try visit(self, addr.target, targetHint); |
|
| 6430 | + | let class = try addressClass(self, addr.target, hint); |
|
| 6357 | 6431 | ||
| 6358 | 6432 | // Mark local variable symbols as address-taken so the lowerer |
|
| 6359 | 6433 | // allocates a stack slot eagerly. |
|
| 6360 | 6434 | if let case ast::NodeValue::Ident(name) = addr.target.value { |
|
| 6361 | 6435 | if let sym = findValueSymbol(self.scope, name) { |
| 6384 | 6458 | }; |
|
| 6385 | 6459 | return setNodeType(self, node, pointerTy); |
|
| 6386 | 6460 | } |
|
| 6387 | 6461 | ||
| 6388 | 6462 | /// Analyze a dereference expression. |
|
| 6389 | - | fn resolveDeref(self: *mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type |
|
| 6463 | + | unsafe fn resolveDeref(self: &mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type |
|
| 6390 | 6464 | throws (ResolveError) |
|
| 6391 | 6465 | { |
|
| 6392 | 6466 | let operandTy = try visit(self, targetNode, hint); |
|
| 6393 | 6467 | if let case Type::Pointer { class, target, .. } = operandTy { |
|
| 6394 | 6468 | if class == types::PointerClass::Unsafe { |
| 6492 | 6566 | } |
|
| 6493 | 6567 | return false; |
|
| 6494 | 6568 | } |
|
| 6495 | 6569 | ||
| 6496 | 6570 | /// Analyze an `as` cast expression. |
|
| 6497 | - | fn resolveAs(self: *mut Resolver, node: *ast::Node, expr: ast::As) -> Type |
|
| 6571 | + | unsafe fn resolveAs(self: &mut Resolver, node: *ast::Node, expr: ast::As) -> Type |
|
| 6498 | 6572 | throws (ResolveError) |
|
| 6499 | 6573 | { |
|
| 6500 | 6574 | let targetTy = try infer(self, expr.type); |
|
| 6501 | 6575 | let sourceTy = try visit(self, expr.value, targetTy); |
|
| 6502 | 6576 | if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) { |
| 6552 | 6626 | to: targetTy, |
|
| 6553 | 6627 | })); |
|
| 6554 | 6628 | } |
|
| 6555 | 6629 | ||
| 6556 | 6630 | /// Analyze a range expression. |
|
| 6557 | - | fn resolveRange(self: *mut Resolver, node: *ast::Node, range: ast::Range) -> Type |
|
| 6631 | + | unsafe fn resolveRange(self: &mut Resolver, node: *ast::Node, range: ast::Range) -> Type |
|
| 6558 | 6632 | throws (ResolveError) |
|
| 6559 | 6633 | { |
|
| 6560 | 6634 | let mut start: ?*Type = nil; |
|
| 6561 | 6635 | let mut end: ?*Type = nil; |
|
| 6562 | 6636 |
| 6589 | 6663 | } |
|
| 6590 | 6664 | ||
| 6591 | 6665 | /// Analyze a `try` expression and its handlers. |
|
| 6592 | 6666 | /// The `expected` type is used to determine if the value is discarded (`Void`) |
|
| 6593 | 6667 | /// or if the catch expression needs type checking. |
|
| 6594 | - | fn resolveTry(self: *mut Resolver, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type |
|
| 6668 | + | unsafe fn resolveTry(self: &mut Resolver, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type |
|
| 6595 | 6669 | throws (ResolveError) |
|
| 6596 | 6670 | { |
|
| 6597 | 6671 | let call = tryExpr.expr; |
|
| 6598 | 6672 | let case ast::NodeValue::Call(callExpr) = call.value |
|
| 6599 | 6673 | else throw emitError(self, call, ErrorKind::TryNonThrowing); |
| 6647 | 6721 | return setNodeType(self, node, tryResultTy); |
|
| 6648 | 6722 | } |
|
| 6649 | 6723 | ||
| 6650 | 6724 | /// Check that a `catch` body is assignable to the expected result type, but only |
|
| 6651 | 6725 | /// in expression context (`hint` is neither `Unknown` nor `Void`). |
|
| 6652 | - | fn checkCatchBody(self: *mut Resolver, body: *ast::Node, resultTy: Type, hint: Type) |
|
| 6726 | + | unsafe fn checkCatchBody(self: &mut Resolver, body: *ast::Node, resultTy: Type, hint: Type) |
|
| 6653 | 6727 | throws (ResolveError) |
|
| 6654 | 6728 | { |
|
| 6655 | 6729 | if hint <> Type::Unknown and hint <> Type::Void { |
|
| 6656 | 6730 | try checkAssignable(self, body, resultTy); |
|
| 6657 | 6731 | } |
| 6660 | 6734 | /// Resolve catch clauses for a `try ... catch` expression. |
|
| 6661 | 6735 | /// |
|
| 6662 | 6736 | /// For a single untyped catch (with or without binding), resolves the catch |
|
| 6663 | 6737 | /// body and returns the result type. Multi-error callees with inferred bindings |
|
| 6664 | 6738 | /// are rejected; you must use typed catches. |
|
| 6665 | - | fn resolveTryCatches( |
|
| 6666 | - | self: *mut Resolver, |
|
| 6739 | + | unsafe fn resolveTryCatches( |
|
| 6740 | + | self: &mut Resolver, |
|
| 6667 | 6741 | node: *ast::Node, |
|
| 6668 | 6742 | catches: *mut [*ast::Node], |
|
| 6669 | 6743 | calleeInfo: *FnType, |
|
| 6670 | 6744 | resultTy: Type, |
|
| 6671 | 6745 | hint: Type |
| 6700 | 6774 | ||
| 6701 | 6775 | /// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`). |
|
| 6702 | 6776 | /// |
|
| 6703 | 6777 | /// Validates that each type annotation is in the callee's throw list, that |
|
| 6704 | 6778 | /// there are no duplicate catch types, and that the clauses are exhaustive. |
|
| 6705 | - | fn resolveTypedCatches( |
|
| 6706 | - | self: *mut Resolver, |
|
| 6779 | + | unsafe fn resolveTypedCatches( |
|
| 6780 | + | self: &mut Resolver, |
|
| 6707 | 6781 | node: *ast::Node, |
|
| 6708 | 6782 | catches: *mut [*ast::Node], |
|
| 6709 | 6783 | calleeInfo: *FnType, |
|
| 6710 | 6784 | resultTy: Type, |
|
| 6711 | 6785 | hint: Type |
| 6765 | 6839 | } |
|
| 6766 | 6840 | return resultTy; |
|
| 6767 | 6841 | } |
|
| 6768 | 6842 | ||
| 6769 | 6843 | /// Analyze a `throw` statement. |
|
| 6770 | - | fn resolveThrow(self: *mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type |
|
| 6844 | + | unsafe fn resolveThrow(self: &mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type |
|
| 6771 | 6845 | throws (ResolveError) |
|
| 6772 | 6846 | { |
|
| 6773 | 6847 | let fnInfo = self.currentFn |
|
| 6774 | 6848 | else throw emitError(self, node, ErrorKind::ThrowRequiresThrows); |
|
| 6775 | 6849 | if fnInfo.throwList.len == 0 { |
| 6784 | 6858 | } |
|
| 6785 | 6859 | throw emitError(self, expr, ErrorKind::ThrowIncompatibleError); |
|
| 6786 | 6860 | } |
|
| 6787 | 6861 | ||
| 6788 | 6862 | /// Analyze a `return` statement. |
|
| 6789 | - | fn resolveReturn(self: *mut Resolver, node: *ast::Node, retVal: ?*ast::Node) -> Type |
|
| 6863 | + | unsafe fn resolveReturn(self: &mut Resolver, node: *ast::Node, retVal: ?*ast::Node) -> Type |
|
| 6790 | 6864 | throws (ResolveError) |
|
| 6791 | 6865 | { |
|
| 6792 | 6866 | let f = self.currentFn |
|
| 6793 | 6867 | else throw emitError(self, node, ErrorKind::UnexpectedReturn); |
|
| 6794 | 6868 | let expected = *f.returnType; |
| 6930 | 7004 | } |
|
| 6931 | 7005 | } |
|
| 6932 | 7006 | ||
| 6933 | 7007 | /// Try to constant-fold a binary operation on two resolved operands. |
|
| 6934 | 7008 | /// Only folds when the result type is concrete. |
|
| 6935 | - | fn tryFoldBinOp(self: *mut Resolver, node: *ast::Node, binop: ast::BinOp, resultTy: Type) { |
|
| 7009 | + | fn tryFoldBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp, resultTy: Type) { |
|
| 6936 | 7010 | let leftVal = constValueEntry(self, binop.left) |
|
| 6937 | 7011 | else return; |
|
| 6938 | 7012 | let rightVal = constValueEntry(self, binop.right) |
|
| 6939 | 7013 | else return; |
|
| 6940 | 7014 |
| 6962 | 7036 | } |
|
| 6963 | 7037 | } |
|
| 6964 | 7038 | } |
|
| 6965 | 7039 | ||
| 6966 | 7040 | /// Analyze a binary expression. |
|
| 6967 | - | fn resolveBinOp(self: *mut Resolver, node: *ast::Node, binop: ast::BinOp) -> Type |
|
| 7041 | + | unsafe fn resolveBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp) -> Type |
|
| 6968 | 7042 | throws (ResolveError) |
|
| 6969 | 7043 | { |
|
| 6970 | 7044 | let mut resultTy = Type::Unknown; |
|
| 6971 | 7045 | ||
| 6972 | 7046 | match binop.op { |
| 7077 | 7151 | ||
| 7078 | 7152 | return setNodeType(self, node, resultTy); |
|
| 7079 | 7153 | } |
|
| 7080 | 7154 | ||
| 7081 | 7155 | /// Analyze a unary expression. |
|
| 7082 | - | fn resolveUnOp(self: *mut Resolver, node: *ast::Node, unop: ast::UnOp) -> Type |
|
| 7156 | + | unsafe fn resolveUnOp(self: &mut Resolver, node: *ast::Node, unop: ast::UnOp) -> Type |
|
| 7083 | 7157 | throws (ResolveError) |
|
| 7084 | 7158 | { |
|
| 7085 | 7159 | let mut resultTy = Type::Unknown; |
|
| 7086 | 7160 | ||
| 7087 | 7161 | match unop.op { |
| 7122 | 7196 | }; |
|
| 7123 | 7197 | return setNodeType(self, node, resultTy); |
|
| 7124 | 7198 | } |
|
| 7125 | 7199 | ||
| 7126 | 7200 | /// Resolve a type signature node and set its type. |
|
| 7127 | - | fn inferTypeSig(self: *mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type |
|
| 7201 | + | unsafe fn inferTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type |
|
| 7128 | 7202 | throws (ResolveError) |
|
| 7129 | 7203 | { |
|
| 7130 | 7204 | let resolved = try resolveTypeSig(self, node, sig); |
|
| 7131 | 7205 | ||
| 7132 | 7206 | return setNodeType(self, node, resolved); |
|
| 7133 | 7207 | } |
|
| 7134 | 7208 | ||
| 7135 | 7209 | /// Convert a type signature node into a type value. |
|
| 7136 | - | fn resolveTypeSig(self: *mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type |
|
| 7210 | + | unsafe fn resolveTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type |
|
| 7137 | 7211 | throws (ResolveError) |
|
| 7138 | 7212 | { |
|
| 7139 | 7213 | match sig { |
|
| 7140 | 7214 | case ast::TypeSig::Void => { |
|
| 7141 | 7215 | return Type::Void; |
| 7197 | 7271 | } |
|
| 7198 | 7272 | } |
|
| 7199 | 7273 | let nominalTy = allocNominalType(self, NominalType::Record(recordType)); |
|
| 7200 | 7274 | return Type::Nominal(nominalTy); |
|
| 7201 | 7275 | } |
|
| 7202 | - | case ast::TypeSig::Fn(t) => { |
|
| 7276 | + | case ast::TypeSig::Fn { sig: t, isUnsafe } => { |
|
| 7203 | 7277 | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 7204 | 7278 | let mut paramTypes: *mut [*Type] = &mut []; |
|
| 7205 | 7279 | let mut throwList: *mut [*Type] = &mut []; |
|
| 7206 | 7280 | ||
| 7207 | 7281 | if t.params.len > MAX_FN_PARAMS { |
| 7234 | 7308 | } |
|
| 7235 | 7309 | let fnType = FnType { |
|
| 7236 | 7310 | paramTypes: ¶mTypes[..], |
|
| 7237 | 7311 | returnType: retType, |
|
| 7238 | 7312 | throwList: &throwList[..], |
|
| 7239 | - | isUnsafe: false, |
|
| 7313 | + | isUnsafe, |
|
| 7240 | 7314 | localCount: 0, |
|
| 7241 | 7315 | }; |
|
| 7242 | 7316 | return Type::Fn(allocFnType(self, fnType)); |
|
| 7243 | 7317 | } |
|
| 7244 | 7318 | // Resolve an opaque trait object signature. |
| 7265 | 7339 | else => return true, |
|
| 7266 | 7340 | } |
|
| 7267 | 7341 | } |
|
| 7268 | 7342 | ||
| 7269 | 7343 | /// Analyze a standalone expression by wrapping it in a synthetic function. |
|
| 7270 | - | export fn resolveExpr( |
|
| 7271 | - | self: *mut Resolver, expr: *ast::Node, arena: *mut ast::NodeArena |
|
| 7344 | + | export unsafe fn resolveExpr( |
|
| 7345 | + | self: &mut Resolver, expr: *ast::Node, arena: &mut ast::NodeArena |
|
| 7272 | 7346 | ) -> Diagnostics throws (ResolveError) { |
|
| 7273 | 7347 | let a = alloc::arenaAllocator(&mut arena.arena); |
|
| 7274 | 7348 | let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr)); |
|
| 7275 | 7349 | let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a); |
|
| 7276 | 7350 | let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts); |
| 7288 | 7362 | ||
| 7289 | 7363 | return Diagnostics { errors: self.errors }; |
|
| 7290 | 7364 | } |
|
| 7291 | 7365 | ||
| 7292 | 7366 | /// Analyze a parsed module root, ie. a block of top-level statements. |
|
| 7293 | - | export fn resolveModuleRoot(self: *mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 7367 | + | export unsafe fn resolveModuleRoot(self: &mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 7294 | 7368 | let case ast::NodeValue::Block(block) = root.value |
|
| 7295 | 7369 | else panic "resolveModuleRoot: expected block for module root"; |
|
| 7296 | 7370 | ||
| 7297 | 7371 | enterScope(self, root); |
|
| 7298 | 7372 | try resolveModuleDecls(self, &block) catch { |
| 7308 | 7382 | } |
|
| 7309 | 7383 | ||
| 7310 | 7384 | /// Analyze the module graph. This pass processes `mod` statements, creating symbols |
|
| 7311 | 7385 | /// and scopes for them, and also binds type names in each module so that cross-module |
|
| 7312 | 7386 | /// type references work regardless of declaration order. |
|
| 7313 | - | fn resolveModuleGraph(self: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 7387 | + | unsafe fn resolveModuleGraph(self: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 7314 | 7388 | try bindTypeNames(self, block); |
|
| 7315 | 7389 | ||
| 7316 | 7390 | for node in block.statements { |
|
| 7317 | 7391 | if let case ast::NodeValue::Mod(decl) = node.value { |
|
| 7318 | 7392 | try resolveModGraph(self, node, decl); |
| 7320 | 7394 | } |
|
| 7321 | 7395 | } |
|
| 7322 | 7396 | ||
| 7323 | 7397 | /// Bind all type names in a module. |
|
| 7324 | 7398 | /// Skips declarations that have already been bound. |
|
| 7325 | - | fn bindTypeNames(self: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 7399 | + | fn bindTypeNames(self: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 7326 | 7400 | for node in block.statements { |
|
| 7327 | 7401 | match node.value { |
|
| 7328 | 7402 | case ast::NodeValue::RecordDecl(decl) => { |
|
| 7329 | 7403 | if symbolFor(self, node) == nil { |
|
| 7330 | 7404 | try bindTypeName(self, node, decl.name, decl.attrs) catch {}; |
| 7344 | 7418 | } |
|
| 7345 | 7419 | } |
|
| 7346 | 7420 | } |
|
| 7347 | 7421 | ||
| 7348 | 7422 | /// Resolve all type bodies in a module. |
|
| 7349 | - | fn resolveTypeBodies(self: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 7423 | + | unsafe fn resolveTypeBodies(self: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 7350 | 7424 | for node in block.statements { |
|
| 7351 | 7425 | match node.value { |
|
| 7352 | 7426 | case ast::NodeValue::RecordDecl(decl) => { |
|
| 7353 | 7427 | try resolveRecordBody(self, node, decl) catch { |
|
| 7354 | 7428 | // Continue resolving other types even if one fails. |
| 7377 | 7451 | /// previous pass. |
|
| 7378 | 7452 | /// |
|
| 7379 | 7453 | /// This function uses a two-phase approach: |
|
| 7380 | 7454 | /// Phase 1: Bind all type names to allow forward references and mutual recursion. |
|
| 7381 | 7455 | /// Phase 2: Resolve type bodies, ie. field types, variant types, etc. |
|
| 7382 | - | fn resolveModuleDecls(res: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 7456 | + | unsafe fn resolveModuleDecls(res: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 7383 | 7457 | // Phase 1: Bind all type names as placeholders. |
|
| 7384 | 7458 | try bindTypeNames(res, block); |
|
| 7385 | 7459 | // Phase 2: Process imports so names available from the module graph can |
|
| 7386 | 7460 | // be used in function signatures. |
|
| 7387 | 7461 | for node in block.statements { |
| 7428 | 7502 | try visitDecl(res, stmt); |
|
| 7429 | 7503 | } |
|
| 7430 | 7504 | } |
|
| 7431 | 7505 | ||
| 7432 | 7506 | /// Find a tracked binding by symbol identity. |
|
| 7433 | - | fn findLinearBinding(env: *LinearEnv, sym: *mut Symbol) -> ?u32 { |
|
| 7507 | + | fn findLinearBinding(env: &LinearEnv, sym: *mut Symbol) -> ?u32 { |
|
| 7434 | 7508 | for i in 0..env.len { |
|
| 7435 | 7509 | if env.symbols[i] == sym { |
|
| 7436 | 7510 | return i; |
|
| 7437 | 7511 | } |
|
| 7438 | 7512 | } |
|
| 7439 | 7513 | return nil; |
|
| 7440 | 7514 | } |
|
| 7441 | 7515 | ||
| 7442 | 7516 | /// Return whether a tracked binding is still available. |
|
| 7443 | - | fn linearBindingAvailable(env: *LinearEnv, index: u32) -> bool { |
|
| 7517 | + | fn linearBindingAvailable(env: &LinearEnv, index: u32) -> bool { |
|
| 7444 | 7518 | return (env.available & ((1 as u64) << (index as u64))) <> 0; |
|
| 7445 | 7519 | } |
|
| 7446 | 7520 | ||
| 7447 | 7521 | /// Add a local binding when its resolved type moves by value. |
|
| 7448 | - | fn addLinearBinding(checker: *mut LinearChecker, env: *mut LinearEnv, node: *ast::Node) |
|
| 7522 | + | unsafe fn addLinearBinding(checker: &mut LinearChecker, env: &mut LinearEnv, node: *ast::Node) |
|
| 7449 | 7523 | throws (ResolveError) |
|
| 7450 | 7524 | { |
|
| 7451 | - | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7525 | + | let sym = symbolFor(&mut *checker.resolver, node) else return; |
|
| 7452 | 7526 | let case SymbolData::Value { type: ty, .. } = sym.data else return; |
|
| 7453 | 7527 | if not isMoveOnly(ty) { |
|
| 7454 | 7528 | return; |
|
| 7455 | 7529 | } |
|
| 7456 | 7530 | if env.len >= MAX_LINEAR_BINDINGS { |
|
| 7457 | - | throw emitError(checker.resolver, node, ErrorKind::Internal); |
|
| 7531 | + | throw emitError(&mut *checker.resolver, node, ErrorKind::Internal); |
|
| 7458 | 7532 | } |
|
| 7459 | 7533 | set env.symbols[env.len] = sym; |
|
| 7460 | 7534 | set env.available |= (1 as u64) << (env.len as u64); |
|
| 7461 | 7535 | set env.len += 1; |
|
| 7462 | 7536 | } |
|
| 7463 | 7537 | ||
| 7464 | 7538 | /// Mark a tracked binding as uninitialized. |
|
| 7465 | - | fn markLinearBindingUnavailable(self: *mut Resolver, env: *mut LinearEnv, node: *ast::Node) { |
|
| 7539 | + | fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) { |
|
| 7466 | 7540 | let sym = symbolFor(self, node) else return; |
|
| 7467 | 7541 | let index = findLinearBinding(env, sym) else return; |
|
| 7468 | 7542 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7469 | 7543 | } |
|
| 7470 | 7544 | ||
| 7471 | 7545 | /// Require exact-use bindings introduced after `start` to be consumed. |
|
| 7472 | - | fn finishLinearScope( |
|
| 7473 | - | checker: *mut LinearChecker, |
|
| 7474 | - | env: *mut LinearEnv, |
|
| 7546 | + | unsafe fn finishLinearScope( |
|
| 7547 | + | checker: &mut LinearChecker, |
|
| 7548 | + | env: &mut LinearEnv, |
|
| 7475 | 7549 | start: u32, |
|
| 7476 | 7550 | ) throws (ResolveError) { |
|
| 7477 | 7551 | if not env.terminated { |
|
| 7478 | 7552 | for i in start..env.len { |
|
| 7479 | 7553 | if linearBindingAvailable(env, i) { |
|
| 7480 | 7554 | let sym = env.symbols[i]; |
|
| 7481 | 7555 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7482 | 7556 | else panic "finishLinearScope: expected value symbol"; |
|
| 7483 | 7557 | if isLinear(ty) { |
|
| 7484 | 7558 | throw emitError( |
|
| 7485 | - | checker.resolver, |
|
| 7559 | + | &mut *checker.resolver, |
|
| 7486 | 7560 | sym.node, |
|
| 7487 | 7561 | ErrorKind::LinearNotConsumed(sym.name), |
|
| 7488 | 7562 | ); |
|
| 7489 | 7563 | } |
|
| 7490 | 7564 | } |
| 7492 | 7566 | } |
|
| 7493 | 7567 | set env.len = start; |
|
| 7494 | 7568 | } |
|
| 7495 | 7569 | ||
| 7496 | 7570 | /// Move or consume a tracked identifier once. |
|
| 7497 | - | fn consumeLinearIdent( |
|
| 7498 | - | checker: *mut LinearChecker, |
|
| 7499 | - | env: *mut LinearEnv, |
|
| 7571 | + | unsafe fn consumeLinearIdent( |
|
| 7572 | + | checker: &mut LinearChecker, |
|
| 7573 | + | env: &mut LinearEnv, |
|
| 7500 | 7574 | node: *ast::Node, |
|
| 7501 | 7575 | ) throws (ResolveError) { |
|
| 7502 | - | let sym = symbolFor(checker.resolver, node) else return; |
|
| 7576 | + | let sym = symbolFor(&mut *checker.resolver, node) else return; |
|
| 7503 | 7577 | let index = findLinearBinding(env, sym) else return; |
|
| 7504 | 7578 | if not linearBindingAvailable(env, index) { |
|
| 7505 | 7579 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7506 | 7580 | else panic "consumeLinearIdent: expected value symbol"; |
|
| 7507 | 7581 | let kind = ErrorKind::LinearUseAfterConsume(sym.name) if isLinear(ty) |
|
| 7508 | 7582 | else ErrorKind::AffineUseAfterMove(sym.name); |
|
| 7509 | - | throw emitError(checker.resolver, node, kind); |
|
| 7583 | + | throw emitError(&mut *checker.resolver, node, kind); |
|
| 7510 | 7584 | } |
|
| 7511 | 7585 | set env.available &= ~((1 as u64) << (index as u64)); |
|
| 7512 | 7586 | } |
|
| 7513 | 7587 | ||
| 7514 | 7588 | /// Merge ownership availability across two live branches. |
|
| 7515 | 7589 | /// Validate both inputs before writing to an output that can alias either input. |
|
| 7516 | - | fn joinLinearBranches( |
|
| 7517 | - | checker: *mut LinearChecker, |
|
| 7518 | - | env: *mut LinearEnv, |
|
| 7519 | - | left: *LinearEnv, |
|
| 7520 | - | right: *LinearEnv, |
|
| 7590 | + | unsafe fn joinLinearBranches( |
|
| 7591 | + | checker: &mut LinearChecker, |
|
| 7592 | + | env: &mut LinearEnv, |
|
| 7593 | + | left: &LinearEnv, |
|
| 7594 | + | right: &LinearEnv, |
|
| 7521 | 7595 | node: *ast::Node, |
|
| 7522 | 7596 | ) throws (ResolveError) { |
|
| 7523 | 7597 | if left.terminated and right.terminated { |
|
| 7524 | 7598 | set *env = *left; |
|
| 7525 | 7599 | set env.terminated = true; |
| 7540 | 7614 | let sym = left.symbols[i]; |
|
| 7541 | 7615 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7542 | 7616 | else panic "joinLinearBranches: expected value symbol"; |
|
| 7543 | 7617 | if isLinear(ty) { |
|
| 7544 | 7618 | throw emitError( |
|
| 7545 | - | checker.resolver, |
|
| 7619 | + | &mut *checker.resolver, |
|
| 7546 | 7620 | node, |
|
| 7547 | 7621 | ErrorKind::LinearBranchMismatch(sym.name), |
|
| 7548 | 7622 | ); |
|
| 7549 | 7623 | } |
|
| 7550 | 7624 | set available &= ~((1 as u64) << (i as u64)); |
| 7553 | 7627 | set *env = *left; |
|
| 7554 | 7628 | set env.available = available; |
|
| 7555 | 7629 | } |
|
| 7556 | 7630 | ||
| 7557 | 7631 | /// Require all available exact-use bindings to be consumed at a function exit. |
|
| 7558 | - | fn finishLinearExit( |
|
| 7559 | - | checker: *mut LinearChecker, |
|
| 7560 | - | env: *mut LinearEnv, |
|
| 7632 | + | unsafe fn finishLinearExit( |
|
| 7633 | + | checker: &mut LinearChecker, |
|
| 7634 | + | env: &mut LinearEnv, |
|
| 7561 | 7635 | ) throws (ResolveError) { |
|
| 7562 | 7636 | for i in 0..env.len { |
|
| 7563 | 7637 | if linearBindingAvailable(env, i) { |
|
| 7564 | 7638 | let sym = env.symbols[i]; |
|
| 7565 | 7639 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7566 | 7640 | else panic "finishLinearExit: expected value symbol"; |
|
| 7567 | 7641 | if isLinear(ty) { |
|
| 7568 | 7642 | throw emitError( |
|
| 7569 | - | checker.resolver, |
|
| 7643 | + | &mut *checker.resolver, |
|
| 7570 | 7644 | sym.node, |
|
| 7571 | 7645 | ErrorKind::LinearNotConsumed(sym.name), |
|
| 7572 | 7646 | ); |
|
| 7573 | 7647 | } |
|
| 7574 | 7648 | } |
|
| 7575 | 7649 | } |
|
| 7576 | 7650 | set env.terminated = true; |
|
| 7577 | 7651 | } |
|
| 7578 | 7652 | ||
| 7579 | 7653 | /// Find the local root borrowed or consumed by an argument expression. |
|
| 7580 | - | fn linearRootSymbol(self: *mut Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 7654 | + | fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 7581 | 7655 | match node.value { |
|
| 7582 | 7656 | case ast::NodeValue::Ident(_) => return symbolFor(self, node), |
|
| 7583 | 7657 | case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target), |
|
| 7584 | 7658 | case ast::NodeValue::FieldAccess(access) => |
|
| 7585 | 7659 | return linearRootSymbol(self, access.parent), |
| 7589 | 7663 | else => return nil, |
|
| 7590 | 7664 | } |
|
| 7591 | 7665 | } |
|
| 7592 | 7666 | ||
| 7593 | 7667 | /// Add the value identifiers introduced by a pattern. |
|
| 7594 | - | fn addLinearPatternBindings( |
|
| 7595 | - | checker: *mut LinearChecker, |
|
| 7596 | - | env: *mut LinearEnv, |
|
| 7668 | + | unsafe fn addLinearPatternBindings( |
|
| 7669 | + | checker: &mut LinearChecker, |
|
| 7670 | + | env: &mut LinearEnv, |
|
| 7597 | 7671 | pattern: *ast::Node, |
|
| 7598 | 7672 | ) throws (ResolveError) { |
|
| 7599 | 7673 | match pattern.value { |
|
| 7600 | 7674 | case ast::NodeValue::Ident(_) => try addLinearBinding(checker, env, pattern), |
|
| 7601 | 7675 | case ast::NodeValue::Call(call) => { |
| 7618 | 7692 | else => {} |
|
| 7619 | 7693 | } |
|
| 7620 | 7694 | } |
|
| 7621 | 7695 | ||
| 7622 | 7696 | /// Check a lexical block and exact-use of locals introduced in it. |
|
| 7623 | - | fn checkLinearBlock( |
|
| 7624 | - | checker: *mut LinearChecker, |
|
| 7625 | - | env: *mut LinearEnv, |
|
| 7697 | + | unsafe fn checkLinearBlock( |
|
| 7698 | + | checker: &mut LinearChecker, |
|
| 7699 | + | env: &mut LinearEnv, |
|
| 7626 | 7700 | node: *ast::Node, |
|
| 7627 | 7701 | ) throws (ResolveError) { |
|
| 7628 | 7702 | let start = env.len; |
|
| 7629 | 7703 | let case ast::NodeValue::Block(block) = node.value |
|
| 7630 | 7704 | else panic "checkLinearBlock: expected block"; |
| 7637 | 7711 | try finishLinearScope(checker, env, start); |
|
| 7638 | 7712 | } |
|
| 7639 | 7713 | ||
| 7640 | 7714 | /// Push a repeated-control-flow boundary. |
|
| 7641 | 7715 | /// Initialize all loop state at this depth before increasing `loopDepth`. |
|
| 7642 | - | fn enterLinearLoop(checker: *mut LinearChecker, env: *LinearEnv) { |
|
| 7716 | + | fn enterLinearLoop(checker: &mut LinearChecker, env: &LinearEnv) { |
|
| 7643 | 7717 | assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow"; |
|
| 7644 | 7718 | let depth = checker.loopDepth; |
|
| 7645 | 7719 | set checker.loopMarks[depth] = env.len; |
|
| 7646 | 7720 | set checker.loopAvailable[depth] = env.available; |
|
| 7647 | 7721 | set checker.loopExitAvailable[depth] = env.available; |
| 7649 | 7723 | set checker.loopBreakSeen[depth] = false; |
|
| 7650 | 7724 | set checker.loopDepth += 1; |
|
| 7651 | 7725 | } |
|
| 7652 | 7726 | ||
| 7653 | 7727 | /// Require a repeated body's outer bindings to match its entry state. |
|
| 7654 | - | fn checkLinearLoopBackEdge( |
|
| 7655 | - | checker: *mut LinearChecker, |
|
| 7656 | - | env: *LinearEnv, |
|
| 7728 | + | unsafe fn checkLinearLoopBackEdge( |
|
| 7729 | + | checker: &mut LinearChecker, |
|
| 7730 | + | env: &LinearEnv, |
|
| 7657 | 7731 | node: *ast::Node, |
|
| 7658 | 7732 | ) throws (ResolveError) { |
|
| 7659 | 7733 | if env.terminated { |
|
| 7660 | 7734 | return; |
|
| 7661 | 7735 | } |
| 7666 | 7740 | for i in 0..mark { |
|
| 7667 | 7741 | let bit = (1 as u64) << (i as u64); |
|
| 7668 | 7742 | if (env.available & bit) <> (entryAvailable & bit) { |
|
| 7669 | 7743 | let sym = env.symbols[i]; |
|
| 7670 | 7744 | throw emitError( |
|
| 7671 | - | checker.resolver, |
|
| 7745 | + | &mut *checker.resolver, |
|
| 7672 | 7746 | node, |
|
| 7673 | 7747 | ErrorKind::LinearBranchMismatch(sym.name), |
|
| 7674 | 7748 | ); |
|
| 7675 | 7749 | } |
|
| 7676 | 7750 | } |
|
| 7677 | 7751 | } |
|
| 7678 | 7752 | ||
| 7679 | 7753 | /// Record the ownership state of a loop's condition-false exit. |
|
| 7680 | - | fn setLinearLoopNaturalExit(checker: *mut LinearChecker, env: *LinearEnv) { |
|
| 7754 | + | fn setLinearLoopNaturalExit(checker: &mut LinearChecker, env: &LinearEnv) { |
|
| 7681 | 7755 | assert checker.loopDepth > 0, "linear loop exit outside loop"; |
|
| 7682 | 7756 | let depth = checker.loopDepth - 1; |
|
| 7683 | 7757 | set checker.loopExitAvailable[depth] = env.available; |
|
| 7684 | 7758 | set checker.loopHasNaturalExit[depth] = true; |
|
| 7685 | 7759 | } |
|
| 7686 | 7760 | ||
| 7687 | 7761 | /// Require a break exit to agree with every other exit from this loop. |
|
| 7688 | - | fn checkLinearLoopBreak( |
|
| 7689 | - | checker: *mut LinearChecker, |
|
| 7690 | - | env: *LinearEnv, |
|
| 7762 | + | unsafe fn checkLinearLoopBreak( |
|
| 7763 | + | checker: &mut LinearChecker, |
|
| 7764 | + | env: &LinearEnv, |
|
| 7691 | 7765 | node: *ast::Node, |
|
| 7692 | 7766 | ) throws (ResolveError) { |
|
| 7693 | 7767 | assert checker.loopDepth > 0, "linear loop break outside loop"; |
|
| 7694 | 7768 | let depth = checker.loopDepth - 1; |
|
| 7695 | 7769 | let mark = checker.loopMarks[depth]; |
| 7698 | 7772 | for i in 0..mark { |
|
| 7699 | 7773 | let bit = (1 as u64) << (i as u64); |
|
| 7700 | 7774 | if (env.available & bit) <> (expected & bit) { |
|
| 7701 | 7775 | let sym = env.symbols[i]; |
|
| 7702 | 7776 | throw emitError( |
|
| 7703 | - | checker.resolver, |
|
| 7777 | + | &mut *checker.resolver, |
|
| 7704 | 7778 | node, |
|
| 7705 | 7779 | ErrorKind::LinearBranchMismatch(sym.name), |
|
| 7706 | 7780 | ); |
|
| 7707 | 7781 | } |
|
| 7708 | 7782 | } |
| 7711 | 7785 | } |
|
| 7712 | 7786 | set checker.loopBreakSeen[depth] = true; |
|
| 7713 | 7787 | } |
|
| 7714 | 7788 | ||
| 7715 | 7789 | /// Pop a repeated-control-flow boundary. |
|
| 7716 | - | fn exitLinearLoop(checker: *mut LinearChecker) { |
|
| 7790 | + | fn exitLinearLoop(checker: &mut LinearChecker) { |
|
| 7717 | 7791 | assert checker.loopDepth > 0, "exitLinearLoop: not in loop"; |
|
| 7718 | 7792 | set checker.loopDepth -= 1; |
|
| 7719 | 7793 | } |
|
| 7720 | 7794 | ||
| 7721 | 7795 | /// Check a conditional and merge its ownership states. |
|
| 7722 | - | fn checkLinearIf( |
|
| 7723 | - | checker: *mut LinearChecker, |
|
| 7724 | - | env: *mut LinearEnv, |
|
| 7796 | + | unsafe fn checkLinearIf( |
|
| 7797 | + | checker: &mut LinearChecker, |
|
| 7798 | + | env: &mut LinearEnv, |
|
| 7725 | 7799 | node: *ast::Node, |
|
| 7726 | 7800 | conditional: ast::If, |
|
| 7727 | 7801 | ) throws (ResolveError) { |
|
| 7728 | 7802 | try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume); |
|
| 7729 | 7803 | let base = *env; |
| 7735 | 7809 | } |
|
| 7736 | 7810 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
|
| 7737 | 7811 | } |
|
| 7738 | 7812 | ||
| 7739 | 7813 | /// Check an expression conditional and merge its ownership states. |
|
| 7740 | - | fn checkLinearCondExpr( |
|
| 7741 | - | checker: *mut LinearChecker, |
|
| 7742 | - | env: *mut LinearEnv, |
|
| 7814 | + | unsafe fn checkLinearCondExpr( |
|
| 7815 | + | checker: &mut LinearChecker, |
|
| 7816 | + | env: &mut LinearEnv, |
|
| 7743 | 7817 | node: *ast::Node, |
|
| 7744 | 7818 | conditional: ast::CondExpr, |
|
| 7745 | 7819 | usage: LinearUse, |
|
| 7746 | 7820 | ) throws (ResolveError) { |
|
| 7747 | 7821 | try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume); |
| 7752 | 7826 | try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage); |
|
| 7753 | 7827 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
|
| 7754 | 7828 | } |
|
| 7755 | 7829 | ||
| 7756 | 7830 | /// Check a match expression, including ownership transferred into patterns. |
|
| 7757 | - | fn checkLinearMatch( |
|
| 7758 | - | checker: *mut LinearChecker, |
|
| 7759 | - | env: *mut LinearEnv, |
|
| 7831 | + | unsafe fn checkLinearMatch( |
|
| 7832 | + | checker: &mut LinearChecker, |
|
| 7833 | + | env: &mut LinearEnv, |
|
| 7760 | 7834 | node: *ast::Node, |
|
| 7761 | 7835 | matchExpr: ast::Match, |
|
| 7762 | 7836 | ) throws (ResolveError) { |
|
| 7763 | 7837 | try checkLinearNode(checker, env, matchExpr.subject, LinearUse::Consume); |
|
| 7764 | 7838 | let base = *env; |
| 7784 | 7858 | for i in bindingsStart..branch.len { |
|
| 7785 | 7859 | let sym = branch.symbols[i]; |
|
| 7786 | 7860 | let case SymbolData::Value { type: ty, .. } = sym.data |
|
| 7787 | 7861 | else panic "checkLinearMatch: expected value symbol"; |
|
| 7788 | 7862 | if isLinear(ty) { |
|
| 7789 | - | throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard); |
|
| 7863 | + | throw emitError(&mut *checker.resolver, prongNode, ErrorKind::LinearDiscard); |
|
| 7790 | 7864 | } |
|
| 7791 | 7865 | } |
|
| 7792 | 7866 | } |
|
| 7793 | 7867 | if let guard = prong.guard { |
|
| 7794 | 7868 | try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume); |
|
| 7795 | 7869 | } |
|
| 7796 | 7870 | try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard); |
|
| 7797 | 7871 | try finishLinearScope(checker, &mut branch, bindingsStart); |
|
| 7798 | 7872 | if haveResult { |
|
| 7799 | - | try joinLinearBranches(checker, &mut result, &result, &branch, node); |
|
| 7873 | + | let previous = result; |
|
| 7874 | + | try joinLinearBranches(checker, &mut result, &previous, &branch, node); |
|
| 7800 | 7875 | } else { |
|
| 7801 | 7876 | set result = branch; |
|
| 7802 | 7877 | set haveResult = true; |
|
| 7803 | 7878 | } |
|
| 7804 | 7879 | } |
| 7806 | 7881 | set *env = result; |
|
| 7807 | 7882 | } |
|
| 7808 | 7883 | } |
|
| 7809 | 7884 | ||
| 7810 | 7885 | /// Check call-scoped loans and argument ownership transfers. |
|
| 7811 | - | fn checkLinearCall( |
|
| 7812 | - | checker: *mut LinearChecker, |
|
| 7813 | - | env: *mut LinearEnv, |
|
| 7886 | + | unsafe fn checkLinearCall( |
|
| 7887 | + | checker: &mut LinearChecker, |
|
| 7888 | + | env: &mut LinearEnv, |
|
| 7814 | 7889 | node: *ast::Node, |
|
| 7815 | 7890 | call: ast::Call, |
|
| 7816 | 7891 | ) throws (ResolveError) { |
|
| 7817 | 7892 | try checkLinearNode(checker, env, call.callee, LinearUse::Observe); |
|
| 7818 | 7893 | let mut fnInfo: ?*FnType = nil; |
|
| 7819 | 7894 | match checker.resolver.nodeData.entries[node.id].extra { |
|
| 7820 | 7895 | case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => |
|
| 7821 | 7896 | set fnInfo = traitInfo.methods[methodIndex].fnType, |
|
| 7822 | 7897 | case NodeExtra::MethodCall { method } => set fnInfo = method.fnType, |
|
| 7823 | 7898 | else => { |
|
| 7824 | - | if let calleeTy = typeFor(checker.resolver, call.callee) { |
|
| 7899 | + | if let calleeTy = typeFor(&mut *checker.resolver, call.callee) { |
|
| 7825 | 7900 | if let case Type::Fn(info) = calleeTy { |
|
| 7826 | 7901 | set fnInfo = info; |
|
| 7827 | 7902 | } |
|
| 7828 | 7903 | } |
|
| 7829 | 7904 | } |
| 7859 | 7934 | } |
|
| 7860 | 7935 | else => {} |
|
| 7861 | 7936 | } |
|
| 7862 | 7937 | if haveReceiver { |
|
| 7863 | 7938 | if receiverClass <> types::PointerClass::Unsafe { |
|
| 7864 | - | let root = linearRootSymbol(checker.resolver, access.parent); |
|
| 7939 | + | let root = linearRootSymbol(&mut *checker.resolver, access.parent); |
|
| 7865 | 7940 | if let rootSym = root { |
|
| 7866 | 7941 | set roots[rootsLen] = rootSym; |
|
| 7867 | 7942 | set exclusive[rootsLen] = |
|
| 7868 | 7943 | receiverClass == types::PointerClass::Owned or receiverMutable; |
|
| 7869 | 7944 | set rootsLen += 1; |
| 7877 | 7952 | } |
|
| 7878 | 7953 | } |
|
| 7879 | 7954 | ||
| 7880 | 7955 | for arg, i in call.args { |
|
| 7881 | 7956 | let expected = *info.paramTypes[i]; |
|
| 7882 | - | let root = linearRootSymbol(checker.resolver, arg); |
|
| 7957 | + | let root = linearRootSymbol(&mut *checker.resolver, arg); |
|
| 7883 | 7958 | let mut argExclusive = isMoveOnly(expected); |
|
| 7884 | 7959 | if let case Type::Pointer { class: types::PointerClass::Ref, mutable, .. } = expected { |
|
| 7885 | 7960 | set argExclusive = mutable; |
|
| 7886 | 7961 | } else if let case Type::Slice { class: types::PointerClass::Ref, mutable, .. } = expected { |
|
| 7887 | 7962 | set argExclusive = mutable; |
| 7894 | 7969 | if let rootSym = root { |
|
| 7895 | 7970 | for j in 0..rootsLen { |
|
| 7896 | 7971 | if let previous = roots[j] { |
|
| 7897 | 7972 | if previous == rootSym and (exclusive[j] or argExclusive) { |
|
| 7898 | 7973 | throw emitError( |
|
| 7899 | - | checker.resolver, |
|
| 7974 | + | &mut *checker.resolver, |
|
| 7900 | 7975 | arg, |
|
| 7901 | 7976 | ErrorKind::BorrowConflict(rootSym.name), |
|
| 7902 | 7977 | ); |
|
| 7903 | 7978 | } |
|
| 7904 | 7979 | } |
| 7915 | 7990 | } |
|
| 7916 | 7991 | } |
|
| 7917 | 7992 | } |
|
| 7918 | 7993 | ||
| 7919 | 7994 | /// Check a pattern conditional. Linear scrutinees require an exhaustive match. |
|
| 7920 | - | fn checkLinearIfLet( |
|
| 7921 | - | checker: *mut LinearChecker, |
|
| 7922 | - | env: *mut LinearEnv, |
|
| 7995 | + | unsafe fn checkLinearIfLet( |
|
| 7996 | + | checker: &mut LinearChecker, |
|
| 7997 | + | env: &mut LinearEnv, |
|
| 7923 | 7998 | node: *ast::Node, |
|
| 7924 | 7999 | conditional: ast::IfLet, |
|
| 7925 | 8000 | ) throws (ResolveError) { |
|
| 7926 | - | if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee); |
|
| 8001 | + | if let subjectTy = typeFor(&mut *checker.resolver, conditional.pattern.scrutinee); |
|
| 7927 | 8002 | isLinear(subjectTy) |
|
| 7928 | 8003 | { |
|
| 7929 | 8004 | throw emitError( |
|
| 7930 | - | checker.resolver, |
|
| 8005 | + | &mut *checker.resolver, |
|
| 7931 | 8006 | conditional.pattern.scrutinee, |
|
| 7932 | 8007 | ErrorKind::LinearPartialMove, |
|
| 7933 | 8008 | ); |
|
| 7934 | 8009 | } |
|
| 7935 | 8010 | try checkLinearNode( |
| 7953 | 8028 | } |
|
| 7954 | 8029 | try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node); |
|
| 7955 | 8030 | } |
|
| 7956 | 8031 | ||
| 7957 | 8032 | /// Check one expression or statement under an ownership-use context. |
|
| 7958 | - | fn checkLinearNode( |
|
| 7959 | - | checker: *mut LinearChecker, |
|
| 7960 | - | env: *mut LinearEnv, |
|
| 8033 | + | unsafe fn checkLinearNode( |
|
| 8034 | + | checker: &mut LinearChecker, |
|
| 8035 | + | env: &mut LinearEnv, |
|
| 7961 | 8036 | node: *ast::Node, |
|
| 7962 | 8037 | usage: LinearUse, |
|
| 7963 | 8038 | ) throws (ResolveError) { |
|
| 7964 | 8039 | if env.terminated { |
|
| 7965 | 8040 | return; |
| 7969 | 8044 | if usage == LinearUse::Consume { |
|
| 7970 | 8045 | try consumeLinearIdent(checker, env, node); |
|
| 7971 | 8046 | } |
|
| 7972 | 8047 | } |
|
| 7973 | 8048 | case ast::NodeValue::ExprStmt(expr) => { |
|
| 7974 | - | if let exprTy = typeFor(checker.resolver, expr) { |
|
| 8049 | + | if let exprTy = typeFor(&mut *checker.resolver, expr) { |
|
| 7975 | 8050 | if isLinear(exprTy) { |
|
| 7976 | - | throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard); |
|
| 8051 | + | throw emitError(&mut *checker.resolver, expr, ErrorKind::LinearDiscard); |
|
| 7977 | 8052 | } |
|
| 7978 | 8053 | } |
|
| 7979 | 8054 | try checkLinearNode(checker, env, expr, LinearUse::Consume); |
|
| 7980 | 8055 | } |
|
| 7981 | 8056 | case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node), |
| 7983 | 8058 | let mut isUndefined = false; |
|
| 7984 | 8059 | if let case ast::NodeValue::Undef = binding.value.value { |
|
| 7985 | 8060 | set isUndefined = true; |
|
| 7986 | 8061 | } |
|
| 7987 | 8062 | if isUndefined { |
|
| 7988 | - | if let bindingTy = typeFor(checker.resolver, binding.ident); |
|
| 8063 | + | if let bindingTy = typeFor(&mut *checker.resolver, binding.ident); |
|
| 7989 | 8064 | isLinear(bindingTy) |
|
| 7990 | 8065 | { |
|
| 7991 | 8066 | throw emitError( |
|
| 7992 | - | checker.resolver, |
|
| 8067 | + | &mut *checker.resolver, |
|
| 7993 | 8068 | binding.value, |
|
| 7994 | 8069 | ErrorKind::LinearUndefined, |
|
| 7995 | 8070 | ); |
|
| 7996 | 8071 | } |
|
| 7997 | 8072 | } |
|
| 7998 | 8073 | try checkLinearNode(checker, env, binding.value, LinearUse::Consume); |
|
| 7999 | 8074 | try addLinearBinding(checker, env, node); |
|
| 8000 | 8075 | if isUndefined { |
|
| 8001 | - | markLinearBindingUnavailable(checker.resolver, env, node); |
|
| 8076 | + | markLinearBindingUnavailable(&mut *checker.resolver, env, node); |
|
| 8002 | 8077 | } |
|
| 8003 | 8078 | } |
|
| 8004 | 8079 | case ast::NodeValue::Assign(assign) => { |
|
| 8005 | 8080 | let mut target: ?u32 = nil; |
|
| 8006 | 8081 | let mut targetLinear = false; |
|
| 8007 | - | if let leftTy = typeFor(checker.resolver, assign.left) { |
|
| 8082 | + | if let leftTy = typeFor(&mut *checker.resolver, assign.left) { |
|
| 8008 | 8083 | if isMoveOnly(leftTy) { |
|
| 8009 | 8084 | set targetLinear = isLinear(leftTy); |
|
| 8010 | 8085 | if let case ast::NodeValue::Ident(_) = assign.left.value { |
|
| 8011 | - | if let sym = symbolFor(checker.resolver, assign.left) { |
|
| 8086 | + | if let sym = symbolFor(&mut *checker.resolver, assign.left) { |
|
| 8012 | 8087 | set target = findLinearBinding(env, sym); |
|
| 8013 | 8088 | } |
|
| 8014 | 8089 | } |
|
| 8015 | 8090 | if target == nil { |
|
| 8016 | 8091 | throw emitError( |
|
| 8017 | - | checker.resolver, |
|
| 8092 | + | &mut *checker.resolver, |
|
| 8018 | 8093 | assign.left, |
|
| 8019 | 8094 | ErrorKind::LinearOverwrite, |
|
| 8020 | 8095 | ); |
|
| 8021 | 8096 | } |
|
| 8022 | 8097 | } |
| 8024 | 8099 | try checkLinearNode(checker, env, assign.left, LinearUse::Place); |
|
| 8025 | 8100 | try checkLinearNode(checker, env, assign.right, LinearUse::Consume); |
|
| 8026 | 8101 | if let index = target { |
|
| 8027 | 8102 | if targetLinear and linearBindingAvailable(env, index) { |
|
| 8028 | 8103 | throw emitError( |
|
| 8029 | - | checker.resolver, |
|
| 8104 | + | &mut *checker.resolver, |
|
| 8030 | 8105 | assign.left, |
|
| 8031 | 8106 | ErrorKind::LinearOverwrite, |
|
| 8032 | 8107 | ); |
|
| 8033 | 8108 | } |
|
| 8034 | 8109 | set env.available |= (1 as u64) << (index as u64); |
| 8037 | 8112 | case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call), |
|
| 8038 | 8113 | case ast::NodeValue::AddressOf(addr) => { |
|
| 8039 | 8114 | try checkLinearNode(checker, env, addr.target, LinearUse::Borrow); |
|
| 8040 | 8115 | } |
|
| 8041 | 8116 | case ast::NodeValue::Deref(target) => { |
|
| 8042 | - | if let resultTy = typeFor(checker.resolver, node) { |
|
| 8117 | + | if let resultTy = typeFor(&mut *checker.resolver, node) { |
|
| 8043 | 8118 | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 8044 | - | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8119 | + | throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8045 | 8120 | } |
|
| 8046 | 8121 | } |
|
| 8047 | 8122 | try checkLinearNode(checker, env, target, LinearUse::Observe); |
|
| 8048 | 8123 | } |
|
| 8049 | 8124 | case ast::NodeValue::FieldAccess(access) => { |
|
| 8050 | - | if let resultTy = typeFor(checker.resolver, node) { |
|
| 8125 | + | if let resultTy = typeFor(&mut *checker.resolver, node) { |
|
| 8051 | 8126 | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 8052 | - | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8127 | + | throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8053 | 8128 | } |
|
| 8054 | 8129 | } |
|
| 8055 | 8130 | try checkLinearNode(checker, env, access.parent, LinearUse::Observe); |
|
| 8056 | 8131 | } |
|
| 8057 | 8132 | case ast::NodeValue::ScopeAccess(_) => {} |
|
| 8058 | 8133 | case ast::NodeValue::Subscript { container, index } => { |
|
| 8059 | - | if let resultTy = typeFor(checker.resolver, node) { |
|
| 8134 | + | if let resultTy = typeFor(&mut *checker.resolver, node) { |
|
| 8060 | 8135 | if isMoveOnly(resultTy) and usage == LinearUse::Consume { |
|
| 8061 | - | throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8136 | + | throw emitError(&mut *checker.resolver, node, ErrorKind::LinearPartialMove); |
|
| 8062 | 8137 | } |
|
| 8063 | 8138 | } |
|
| 8064 | 8139 | try checkLinearNode(checker, env, container, LinearUse::Observe); |
|
| 8065 | 8140 | try checkLinearNode(checker, env, index, LinearUse::Consume); |
|
| 8066 | 8141 | } |
| 8075 | 8150 | for item in items { |
|
| 8076 | 8151 | try checkLinearNode(checker, env, item, LinearUse::Consume); |
|
| 8077 | 8152 | } |
|
| 8078 | 8153 | } |
|
| 8079 | 8154 | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
|
| 8080 | - | if let itemTy = typeFor(checker.resolver, repeat.item) { |
|
| 8155 | + | if let itemTy = typeFor(&mut *checker.resolver, repeat.item) { |
|
| 8081 | 8156 | if not isCopy(itemTy) { |
|
| 8082 | 8157 | throw emitError( |
|
| 8083 | - | checker.resolver, |
|
| 8158 | + | &mut *checker.resolver, |
|
| 8084 | 8159 | repeat.item, |
|
| 8085 | 8160 | ErrorKind::LinearDiscard, |
|
| 8086 | 8161 | ); |
|
| 8087 | 8162 | } |
|
| 8088 | 8163 | } |
| 8120 | 8195 | } |
|
| 8121 | 8196 | case ast::NodeValue::IfLet(conditional) => { |
|
| 8122 | 8197 | try checkLinearIfLet(checker, env, node, conditional); |
|
| 8123 | 8198 | } |
|
| 8124 | 8199 | case ast::NodeValue::LetElse(binding) => { |
|
| 8125 | - | if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee); |
|
| 8200 | + | if let subjectTy = typeFor(&mut *checker.resolver, binding.pattern.scrutinee); |
|
| 8126 | 8201 | isLinear(subjectTy) |
|
| 8127 | 8202 | { |
|
| 8128 | 8203 | throw emitError( |
|
| 8129 | - | checker.resolver, |
|
| 8204 | + | &mut *checker.resolver, |
|
| 8130 | 8205 | binding.pattern.scrutinee, |
|
| 8131 | 8206 | ErrorKind::LinearPartialMove, |
|
| 8132 | 8207 | ); |
|
| 8133 | 8208 | } |
|
| 8134 | 8209 | try checkLinearNode( |
| 8161 | 8236 | checker, |
|
| 8162 | 8237 | &mut guardFallbackEnv, |
|
| 8163 | 8238 | binding.elseBranch, |
|
| 8164 | 8239 | LinearUse::Consume, |
|
| 8165 | 8240 | ); |
|
| 8241 | + | let previous = fallbackEnv; |
|
| 8166 | 8242 | try joinLinearBranches( |
|
| 8167 | 8243 | checker, |
|
| 8168 | 8244 | &mut fallbackEnv, |
|
| 8169 | - | &fallbackEnv, |
|
| 8245 | + | &previous, |
|
| 8170 | 8246 | &guardFallbackEnv, |
|
| 8171 | 8247 | binding.elseBranch, |
|
| 8172 | 8248 | ); |
|
| 8173 | 8249 | } |
|
| 8174 | 8250 | if let case ast::PatternKind::Binding = binding.pattern.kind { |
| 8194 | 8270 | if let binding = catchClause.binding { |
|
| 8195 | 8271 | try addLinearBinding(checker, &mut branch, binding); |
|
| 8196 | 8272 | } |
|
| 8197 | 8273 | try checkLinearNode(checker, &mut branch, catchClause.body, usage); |
|
| 8198 | 8274 | try finishLinearScope(checker, &mut branch, start); |
|
| 8199 | - | try joinLinearBranches(checker, env, env, &branch, node); |
|
| 8275 | + | let previous = *env; |
|
| 8276 | + | try joinLinearBranches(checker, env, &previous, &branch, node); |
|
| 8200 | 8277 | } |
|
| 8201 | 8278 | } |
|
| 8202 | 8279 | case ast::NodeValue::While(whileStmt) => { |
|
| 8203 | 8280 | enterLinearLoop(checker, env); |
|
| 8204 | 8281 | try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume); |
| 8219 | 8296 | ); |
|
| 8220 | 8297 | try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node); |
|
| 8221 | 8298 | } |
|
| 8222 | 8299 | } |
|
| 8223 | 8300 | case ast::NodeValue::WhileLet(whileStmt) => { |
|
| 8224 | - | if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee); |
|
| 8301 | + | if let subjectTy = typeFor(&mut *checker.resolver, whileStmt.pattern.scrutinee); |
|
| 8225 | 8302 | isLinear(subjectTy) |
|
| 8226 | 8303 | { |
|
| 8227 | 8304 | throw emitError( |
|
| 8228 | - | checker.resolver, |
|
| 8305 | + | &mut *checker.resolver, |
|
| 8229 | 8306 | whileStmt.pattern.scrutinee, |
|
| 8230 | 8307 | ErrorKind::LinearPartialMove, |
|
| 8231 | 8308 | ); |
|
| 8232 | 8309 | } |
|
| 8233 | 8310 | let base = *env; |
| 8244 | 8321 | try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern); |
|
| 8245 | 8322 | if let guard = whileStmt.pattern.guard { |
|
| 8246 | 8323 | try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume); |
|
| 8247 | 8324 | let mut guardExit = bodyEnv; |
|
| 8248 | 8325 | try finishLinearScope(checker, &mut guardExit, start); |
|
| 8326 | + | let previous = conditionExit; |
|
| 8249 | 8327 | try joinLinearBranches( |
|
| 8250 | 8328 | checker, |
|
| 8251 | 8329 | &mut conditionExit, |
|
| 8252 | - | &conditionExit, |
|
| 8330 | + | &previous, |
|
| 8253 | 8331 | &guardExit, |
|
| 8254 | 8332 | guard, |
|
| 8255 | 8333 | ); |
|
| 8256 | 8334 | } |
|
| 8257 | 8335 | setLinearLoopNaturalExit(checker, &conditionExit); |
| 8270 | 8348 | ); |
|
| 8271 | 8349 | try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node); |
|
| 8272 | 8350 | } |
|
| 8273 | 8351 | } |
|
| 8274 | 8352 | case ast::NodeValue::For(forStmt) => { |
|
| 8275 | - | if let iterableTy = typeFor(checker.resolver, forStmt.iterable) { |
|
| 8353 | + | if let iterableTy = typeFor(&mut *checker.resolver, forStmt.iterable) { |
|
| 8276 | 8354 | if isLinear(iterableTy) { |
|
| 8277 | 8355 | throw emitError( |
|
| 8278 | - | checker.resolver, |
|
| 8356 | + | &mut *checker.resolver, |
|
| 8279 | 8357 | forStmt.iterable, |
|
| 8280 | 8358 | ErrorKind::LinearPartialMove, |
|
| 8281 | 8359 | ); |
|
| 8282 | 8360 | } |
|
| 8283 | 8361 | } |
| 8363 | 8441 | else => {} |
|
| 8364 | 8442 | } |
|
| 8365 | 8443 | } |
|
| 8366 | 8444 | ||
| 8367 | 8445 | /// Check exact-use ownership for one resolved function. |
|
| 8368 | - | fn checkLinearFn( |
|
| 8369 | - | self: *mut Resolver, |
|
| 8446 | + | unsafe fn checkLinearFn( |
|
| 8447 | + | self: &mut Resolver, |
|
| 8370 | 8448 | receiver: ?*ast::Node, |
|
| 8371 | 8449 | params: *mut [*ast::Node], |
|
| 8372 | 8450 | body: *ast::Node, |
|
| 8373 | 8451 | ) throws (ResolveError) { |
|
| 8374 | 8452 | let mut checker = LinearChecker { |
|
| 8375 | - | resolver: self, |
|
| 8453 | + | resolver: self as *unsafe mut Resolver, |
|
| 8376 | 8454 | loopMarks: undefined, |
|
| 8377 | 8455 | loopAvailable: undefined, |
|
| 8378 | 8456 | loopExitAvailable: undefined, |
|
| 8379 | 8457 | loopHasNaturalExit: undefined, |
|
| 8380 | 8458 | loopBreakSeen: undefined, |
| 8397 | 8475 | try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard); |
|
| 8398 | 8476 | try finishLinearScope(&mut checker, &mut env, 0); |
|
| 8399 | 8477 | } |
|
| 8400 | 8478 | ||
| 8401 | 8479 | /// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules. |
|
| 8402 | - | fn resolveModuleDefs(self: *mut Resolver, block: *ast::Block) throws (ResolveError) { |
|
| 8480 | + | unsafe fn resolveModuleDefs(self: &mut Resolver, block: &ast::Block) throws (ResolveError) { |
|
| 8403 | 8481 | for stmt in block.statements { |
|
| 8404 | 8482 | try visitDef(self, stmt); |
|
| 8405 | 8483 | } |
|
| 8406 | 8484 | } |
|
| 8407 | 8485 | ||
| 8408 | 8486 | /// Resolve all packages. |
|
| 8409 | - | export fn resolve(self: *mut Resolver, graph: *module::ModuleGraph, packages: *[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 8410 | - | set self.moduleGraph = graph; |
|
| 8487 | + | /// The graph must outlive later uses of the resolver. |
|
| 8488 | + | export unsafe fn resolve(self: &mut Resolver, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 8489 | + | set self.moduleGraph = graph as *unsafe module::ModuleGraph; |
|
| 8411 | 8490 | ||
| 8412 | 8491 | // 1. Bind all package roots to enable cross-package references. |
|
| 8413 | 8492 | for i in 0..packages.len { |
|
| 8414 | - | let pkg = &packages[i]; |
|
| 8493 | + | let pkg = packages[i]; |
|
| 8415 | 8494 | // Enter a new scope for the module. |
|
| 8416 | 8495 | let enter = enterModuleScope(self, pkg.rootAst, pkg.rootEntry); |
|
| 8417 | 8496 | // Bind the package root module name in the global package scope. |
|
| 8418 | - | try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, self.pkgScope); |
|
| 8497 | + | let scope = self.pkgScope; |
|
| 8498 | + | try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, scope); |
|
| 8419 | 8499 | ||
| 8420 | 8500 | exitModuleScope(self, enter); |
|
| 8421 | 8501 | } |
|
| 8422 | 8502 | // 2. Resolve each package's contents. |
|
| 8423 | 8503 | for i in 0..packages.len { |
|
| 8424 | - | let pkg = &packages[i]; |
|
| 8504 | + | let pkg = packages[i]; |
|
| 8425 | 8505 | let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst); |
|
| 8426 | 8506 | if not success(&diags) { |
|
| 8427 | 8507 | return diags; |
|
| 8428 | 8508 | } |
|
| 8429 | 8509 | } |
|
| 8430 | 8510 | return Diagnostics { errors: self.errors }; |
|
| 8431 | 8511 | } |
|
| 8432 | 8512 | ||
| 8433 | 8513 | /// Resolve a package. |
|
| 8434 | - | fn resolvePackage(self: *mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 8514 | + | unsafe fn resolvePackage(self: &mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 8435 | 8515 | let rootId = rootEntry.id; |
|
| 8436 | 8516 | let scope = self.moduleScopes[rootId as u32] |
|
| 8437 | 8517 | else panic "resolvePackage: module scope not found"; |
|
| 8438 | 8518 | ||
| 8439 | 8519 | // Set up the module scope for this package. |
lib/std/lang/resolver/printer.rad
+5 -5
| 242 | 242 | } |
|
| 243 | 243 | } |
|
| 244 | 244 | } |
|
| 245 | 245 | ||
| 246 | 246 | /// Print a single diagnostic entry. |
|
| 247 | - | fn printError(err: *super::Error, res: *super::Resolver) { |
|
| 247 | + | unsafe fn printError(err: *super::Error, res: &super::Resolver) { |
|
| 248 | 248 | if let node = err.node { |
|
| 249 | 249 | // Find the module containing this error. |
|
| 250 | - | if let moduleEntry = module::get(res.moduleGraph, err.moduleId) { |
|
| 250 | + | if let moduleEntry = module::get(&*res.moduleGraph, err.moduleId) { |
|
| 251 | 251 | // Get the source text if available. |
|
| 252 | 252 | if let source = moduleEntry.source { |
|
| 253 | 253 | // Convert offset to location. |
|
| 254 | 254 | if let loc = scanner::getLocation(scanner::SourceLoc::File(moduleEntry.filePath), source, node.span.offset) { |
|
| 255 | 255 | // Print: filename:line:col: error: message |
| 537 | 537 | } |
|
| 538 | 538 | case super::ErrorKind::BorrowConflict(name) => { |
|
| 539 | 539 | printQuoted("conflicting call-scoped loans of '", name); |
|
| 540 | 540 | } |
|
| 541 | 541 | case super::ErrorKind::UnsafeOperation => { |
|
| 542 | - | io::print("unsafe pointer operation requires an unsafe declaration"); |
|
| 542 | + | io::print("unsafe pointer operation requires an unsafe function"); |
|
| 543 | 543 | } |
|
| 544 | 544 | case super::ErrorKind::UnsafeCall => { |
|
| 545 | - | io::print("calling an unsafe function requires an unsafe declaration"); |
|
| 545 | + | io::print("calling an unsafe function requires an unsafe function"); |
|
| 546 | 546 | } |
|
| 547 | 547 | case super::ErrorKind::Internal => { |
|
| 548 | 548 | io::print("internal compiler error"); |
|
| 549 | 549 | } |
|
| 550 | 550 | case super::ErrorKind::RecordFieldOutOfOrder { .. } => { |
| 565 | 565 | } |
|
| 566 | 566 | io::print("\n"); |
|
| 567 | 567 | } |
|
| 568 | 568 | ||
| 569 | 569 | /// Entry point for printing resolver diagnostics in vim quickfix format. |
|
| 570 | - | export fn printDiagnostics(diag: *super::Diagnostics, res: *super::Resolver) { |
|
| 570 | + | export unsafe fn printDiagnostics(diag: &super::Diagnostics, res: &super::Resolver) { |
|
| 571 | 571 | for i in 0..diag.errors.len { |
|
| 572 | 572 | printError(&diag.errors[i], res); |
|
| 573 | 573 | } |
|
| 574 | 574 | } |
lib/std/lang/resolver/tests.rad
+530 -443
| 67 | 67 | errors: &mut ERROR_STORAGE[..], |
|
| 68 | 68 | }; |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | 71 | /// Construct a resolver backed by test storage and a synthetic module graph. |
|
| 72 | - | fn testResolver() -> super::Resolver { |
|
| 72 | + | unsafe fn testResolver() -> super::Resolver { |
|
| 73 | 73 | // TODO: This should be initialized only once. |
|
| 74 | 74 | for i in 0..LITERALS.len { |
|
| 75 | 75 | strings::intern(&mut STRING_POOL, LITERALS[i]); |
|
| 76 | 76 | } |
|
| 77 | 77 | // TODO: Use local static for this. |
| 83 | 83 | ||
| 84 | 84 | return res; |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Resolve a block of statements by wrapping them in a synthetic function. |
|
| 88 | - | fn resolveStatements( |
|
| 89 | - | self: *mut super::Resolver, block: ast::Block, arena: *mut ast::NodeArena |
|
| 88 | + | unsafe fn resolveStatements( |
|
| 89 | + | self: &mut super::Resolver, block: ast::Block, arena: &mut ast::NodeArena |
|
| 90 | 90 | ) -> TestResult throws (super::ResolveError) { |
|
| 91 | 91 | let module = ast::synthFnModule(arena, super::ANALYZE_BLOCK_FN_NAME, block.statements); |
|
| 92 | 92 | let diagnostics = try super::resolveModuleRoot(self, module.modBody) catch { |
|
| 93 | 93 | return TestResult { diagnostics: super::Diagnostics { errors: self.errors }, root: module.modBody }; |
|
| 94 | 94 | }; |
|
| 95 | 95 | return TestResult { diagnostics, root: module.fnBody }; |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | 98 | /// Parse and analyze an expression string for testing. |
|
| 99 | - | fn resolveExprStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 99 | + | unsafe fn resolveExprStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 100 | 100 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 101 | 101 | let mut p = parser::mkParser(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL); |
|
| 102 | 102 | parser::advance(&mut p); |
|
| 103 | 103 | ||
| 104 | 104 | let expr = try parser::parseExpr(&mut p) catch { |
| 110 | 110 | return TestResult { diagnostics, root: expr }; |
|
| 111 | 111 | } |
|
| 112 | 112 | ||
| 113 | 113 | /// Parse and analyze a module string for testing. |
|
| 114 | 114 | /// Use this for code with `fn`, `record`, `union`, etc. at the top level. |
|
| 115 | - | fn resolveProgramStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 115 | + | unsafe fn resolveProgramStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 116 | 116 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 117 | 117 | let stmt = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch { |
|
| 118 | 118 | panic "resolveProgramStr: parsing failed"; |
|
| 119 | 119 | }; |
|
| 120 | 120 | let diagnostics = try super::resolveModuleRoot(self, stmt) catch { |
| 123 | 123 | return TestResult { diagnostics, root: stmt }; |
|
| 124 | 124 | } |
|
| 125 | 125 | ||
| 126 | 126 | /// Parse and analyze a block of statements (eg. inside a function body) for testing. |
|
| 127 | 127 | /// Use this for code with `let` bindings and expressions, not module-level declarations. |
|
| 128 | - | fn resolveBlockStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 128 | + | unsafe fn resolveBlockStr(self: &mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
|
| 129 | 129 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 130 | 130 | let parsed = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch { |
|
| 131 | 131 | panic "resolveBlockStr: parsing failed"; |
|
| 132 | 132 | }; |
|
| 133 | 133 | let case ast::NodeValue::Block(block) = parsed.value |
| 141 | 141 | root: analysis.root, |
|
| 142 | 142 | }; |
|
| 143 | 143 | } |
|
| 144 | 144 | ||
| 145 | 145 | /// Resolve a module with the full resolution process. |
|
| 146 | - | fn resolveModuleTree( |
|
| 147 | - | res: *mut super::Resolver, |
|
| 146 | + | unsafe fn resolveModuleTree( |
|
| 147 | + | res: &mut super::Resolver, |
|
| 148 | 148 | rootId: u16 |
|
| 149 | 149 | ) -> TestResult throws (testing::TestError) { |
|
| 150 | 150 | let root = module::get(&MODULE_GRAPH, rootId) |
|
| 151 | 151 | else throw testing::TestError::Failed; |
|
| 152 | 152 | let rootAst = root.ast |
|
| 153 | 153 | else throw testing::TestError::Failed; |
|
| 154 | - | let packages: *[super::Pkg] = &[super::Pkg { |
|
| 154 | + | let packages = [super::Pkg { |
|
| 155 | 155 | rootEntry: root, |
|
| 156 | 156 | rootAst, |
|
| 157 | 157 | }]; |
|
| 158 | - | let diagnostics = try super::resolve(res, &MODULE_GRAPH, packages) catch { |
|
| 158 | + | let diagnostics = try super::resolve(res, &MODULE_GRAPH, &packages[..]) catch { |
|
| 159 | 159 | throw testing::TestError::Failed; |
|
| 160 | 160 | }; |
|
| 161 | 161 | return TestResult { diagnostics, root: rootAst }; |
|
| 162 | 162 | } |
|
| 163 | 163 | ||
| 164 | 164 | /// Register a module in the graph and attach a parsed AST to it. |
|
| 165 | 165 | /// If parentId is nil, registers as a root module. |
|
| 166 | - | fn registerModule( |
|
| 167 | - | graph: *mut module::ModuleGraph, |
|
| 166 | + | unsafe fn registerModule( |
|
| 167 | + | graph: &mut module::ModuleGraph, |
|
| 168 | 168 | parentId: ?u16, |
|
| 169 | 169 | name: *[u8], |
|
| 170 | 170 | code: *[u8], |
|
| 171 | - | arena: *mut ast::NodeArena |
|
| 171 | + | arena: &mut ast::NodeArena |
|
| 172 | 172 | ) -> u16 throws (testing::TestError) { |
|
| 173 | 173 | let filePath = "<test>"; |
|
| 174 | 174 | let mut modId: u16 = undefined; |
|
| 175 | 175 | if let parent = parentId { |
|
| 176 | 176 | set modId = try module::registerChild(graph, parent, name, filePath) catch { |
| 189 | 189 | }; |
|
| 190 | 190 | return modId; |
|
| 191 | 191 | } |
|
| 192 | 192 | ||
| 193 | 193 | /// Ensure an expression statement produces the expected type and return the expression node. |
|
| 194 | - | fn expectExprStmtType(self: *super::Resolver, node: *ast::Node, expected: super::Type) -> *ast::Node |
|
| 194 | + | fn expectExprStmtType(self: &super::Resolver, node: *ast::Node, expected: super::Type) -> *ast::Node |
|
| 195 | 195 | throws (testing::TestError) |
|
| 196 | 196 | { |
|
| 197 | 197 | let case ast::NodeValue::ExprStmt(expr) = node.value |
|
| 198 | 198 | else throw testing::TestError::Failed; |
|
| 199 | 199 | try expectType(self, expr, expected); |
|
| 200 | 200 | ||
| 201 | 201 | return expr; |
|
| 202 | 202 | } |
|
| 203 | 203 | ||
| 204 | 204 | /// Assert that the test result contains no diagnostic errors. |
|
| 205 | - | fn expectNoErrors(r: *TestResult) throws (testing::TestError) { |
|
| 205 | + | fn expectNoErrors(r: &TestResult) throws (testing::TestError) { |
|
| 206 | 206 | try testing::expect(super::success(&r.diagnostics)); |
|
| 207 | 207 | } |
|
| 208 | 208 | ||
| 209 | 209 | /// Extract the first error from a test result, failing if none exists. |
|
| 210 | - | fn expectError(result: *TestResult) -> *super::Error throws (testing::TestError) { |
|
| 210 | + | fn expectError(result: &TestResult) -> *super::Error throws (testing::TestError) { |
|
| 211 | 211 | let err = super::errorAt(&result.diagnostics.errors[..], 0) |
|
| 212 | 212 | else throw testing::TestError::Failed; |
|
| 213 | 213 | return err; |
|
| 214 | 214 | } |
|
| 215 | 215 | ||
| 216 | 216 | /// Check if two error kinds match. |
|
| 217 | - | fn errorKindMatches(actual: *super::ErrorKind, expected: super::ErrorKind) -> bool { |
|
| 217 | + | fn errorKindMatches(actual: &super::ErrorKind, expected: super::ErrorKind) -> bool { |
|
| 218 | 218 | if let case super::ErrorKind::DuplicateBinding(expectedName) = expected { |
|
| 219 | 219 | if let case super::ErrorKind::DuplicateBinding(actualName) = *actual { |
|
| 220 | 220 | return mem::eq(actualName, expectedName); |
|
| 221 | 221 | } |
|
| 222 | 222 | return false; |
| 313 | 313 | } |
|
| 314 | 314 | return *actual == expected; |
|
| 315 | 315 | } |
|
| 316 | 316 | ||
| 317 | 317 | /// Extract the first error and ensure it has the expected kind. |
|
| 318 | - | fn expectErrorKind(result: *TestResult, kind: super::ErrorKind) -> *super::Error |
|
| 318 | + | fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> *super::Error |
|
| 319 | 319 | throws (testing::TestError) |
|
| 320 | 320 | { |
|
| 321 | 321 | let err = try expectError(result); |
|
| 322 | 322 | try testing::expect(errorKindMatches(&err.kind, kind)); |
|
| 323 | 323 | return err; |
|
| 324 | 324 | } |
|
| 325 | 325 | ||
| 326 | 326 | /// Ensure an expression resolves to the expected type annotation. |
|
| 327 | - | fn expectType(self: *super::Resolver, expr: *ast::Node, expected: super::Type) |
|
| 327 | + | fn expectType(self: &super::Resolver, expr: *ast::Node, expected: super::Type) |
|
| 328 | 328 | throws (testing::TestError) |
|
| 329 | 329 | { |
|
| 330 | 330 | let actual = super::typeFor(self, expr) |
|
| 331 | 331 | else throw testing::TestError::Failed; |
|
| 332 | 332 |
| 334 | 334 | throw testing::TestError::Failed; |
|
| 335 | 335 | } |
|
| 336 | 336 | } |
|
| 337 | 337 | ||
| 338 | 338 | /// Verify that an error represents a specific type mismatch. |
|
| 339 | - | fn expectTypeMismatch(err: *super::Error, expected: super::Type, actual: super::Type) |
|
| 339 | + | fn expectTypeMismatch(err: &super::Error, expected: super::Type, actual: super::Type) |
|
| 340 | 340 | throws (testing::TestError) |
|
| 341 | 341 | { |
|
| 342 | 342 | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
|
| 343 | 343 | else throw testing::TestError::Failed; |
|
| 344 | 344 | try testing::expect(mismatch.expected == expected); |
|
| 345 | 345 | try testing::expect(mismatch.actual == actual); |
|
| 346 | 346 | } |
|
| 347 | 347 | ||
| 348 | 348 | /// Resolve a program and require successful analysis. |
|
| 349 | - | fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) { |
|
| 349 | + | unsafe fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) { |
|
| 350 | 350 | let mut a = testResolver(); |
|
| 351 | 351 | let result = try resolveProgramStr(&mut a, program); |
|
| 352 | 352 | try expectNoErrors(&result); |
|
| 353 | 353 | } |
|
| 354 | 354 | ||
| 355 | 355 | /// Require an inferred integer type mismatch. |
|
| 356 | - | fn expectIntMismatch(program: *[u8], expected: super::Type) |
|
| 356 | + | unsafe fn expectIntMismatch(program: *[u8], expected: super::Type) |
|
| 357 | 357 | throws (testing::TestError) |
|
| 358 | 358 | { |
|
| 359 | 359 | let mut a = testResolver(); |
|
| 360 | 360 | let result = try resolveProgramStr(&mut a, program); |
|
| 361 | 361 | let err = try expectError(&result); |
| 374 | 374 | } |
|
| 375 | 375 | return body.statements[index]; |
|
| 376 | 376 | } |
|
| 377 | 377 | ||
| 378 | 378 | /// Retrieve a function body block by function name from the program scope. |
|
| 379 | - | fn getFnBody(a: *super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block |
|
| 379 | + | fn getFnBody(a: &super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block |
|
| 380 | 380 | throws (testing::TestError) |
|
| 381 | 381 | { |
|
| 382 | 382 | let scope = super::scopeFor(a, root) |
|
| 383 | 383 | else throw testing::TestError::Failed; |
|
| 384 | 384 | let sym = super::findSymbolInScope(scope, name) |
| 417 | 417 | } |
|
| 418 | 418 | panic "getUnionVariantPayload: variant not found"; |
|
| 419 | 419 | } |
|
| 420 | 420 | ||
| 421 | 421 | /// Get a nominal type by name, in the scope of the given block node. |
|
| 422 | - | fn getTypeInScopeOf(a: *super::Resolver, blk: *ast::Node, name: *[u8]) -> *super::NominalType |
|
| 422 | + | fn getTypeInScopeOf(a: &super::Resolver, blk: *ast::Node, name: *[u8]) -> *super::NominalType |
|
| 423 | 423 | throws (testing::TestError) |
|
| 424 | 424 | { |
|
| 425 | 425 | let scope = super::scopeFor(a, blk) |
|
| 426 | 426 | else throw testing::TestError::Failed; |
|
| 427 | 427 | let sym = super::findSymbolInScope(scope, name) |
| 430 | 430 | else throw testing::TestError::Failed; |
|
| 431 | 431 | return ty; |
|
| 432 | 432 | } |
|
| 433 | 433 | ||
| 434 | 434 | /// Return the resolved type of a syntax node. |
|
| 435 | - | fn typeOf(a: *super::Resolver, node: *ast::Node) -> super::Type |
|
| 435 | + | fn typeOf(a: &super::Resolver, node: *ast::Node) -> super::Type |
|
| 436 | 436 | throws (testing::TestError) |
|
| 437 | 437 | { |
|
| 438 | 438 | let ty = super::typeFor(a, node) |
|
| 439 | 439 | else throw testing::TestError::Failed; |
|
| 440 | 440 | return ty; |
| 472 | 472 | ||
| 473 | 473 | return *target; |
|
| 474 | 474 | } |
|
| 475 | 475 | ||
| 476 | 476 | /// Verify that a node has a constant integer value with the expected magnitude. |
|
| 477 | - | fn expectConstInt(a: *super::Resolver, node: *ast::Node, expected: u32) |
|
| 477 | + | fn expectConstInt(a: &super::Resolver, node: *ast::Node, expected: u32) |
|
| 478 | 478 | throws (testing::TestError) |
|
| 479 | 479 | { |
|
| 480 | 480 | let constVal = super::constValueEntry(a, node) |
|
| 481 | 481 | else throw testing::TestError::Failed; |
|
| 482 | 482 |
| 485 | 485 | ||
| 486 | 486 | try testing::expect(int.magnitude == expected); |
|
| 487 | 487 | } |
|
| 488 | 488 | ||
| 489 | 489 | /// Resolve an expression that should evaluate to a constant, and verify it equals the expected value. |
|
| 490 | - | fn resolveAndExpectConstExpr(expr: *[u8], expected: u32) |
|
| 490 | + | unsafe fn resolveAndExpectConstExpr(expr: *[u8], expected: u32) |
|
| 491 | 491 | throws (testing::TestError) |
|
| 492 | 492 | { |
|
| 493 | 493 | let mut a = testResolver(); |
|
| 494 | 494 | let result = try resolveExprStr(&mut a, expr); |
|
| 495 | 495 | try expectNoErrors(&result); |
|
| 496 | 496 | try expectType(&a, result.root, super::Type::U32); |
|
| 497 | 497 | try expectConstInt(&a, result.root, expected); |
|
| 498 | 498 | } |
|
| 499 | 499 | ||
| 500 | 500 | /// Resolve a statement that should evaluate to a constant, and verify it equals the expected value. |
|
| 501 | - | fn resolveAndExpectConstStmt(expr: *[u8], expected: u32) |
|
| 501 | + | unsafe fn resolveAndExpectConstStmt(expr: *[u8], expected: u32) |
|
| 502 | 502 | throws (testing::TestError) |
|
| 503 | 503 | { |
|
| 504 | 504 | let mut a = testResolver(); |
|
| 505 | 505 | let result = try resolveProgramStr(&mut a, expr); |
|
| 506 | 506 | try expectNoErrors(&result); |
| 509 | 509 | try expectConstInt(&a, expr, expected); |
|
| 510 | 510 | } |
|
| 511 | 511 | ||
| 512 | 512 | // Tests /////////////////////////////////////////////////////////////////////// |
|
| 513 | 513 | ||
| 514 | - | @test fn testResolveLit() throws (testing::TestError) { |
|
| 514 | + | @test unsafe fn testResolveLit() throws (testing::TestError) { |
|
| 515 | 515 | let mut a = testResolver(); |
|
| 516 | 516 | let result = try resolveExprStr(&mut a, "true"); |
|
| 517 | 517 | ||
| 518 | 518 | try expectNoErrors(&result); |
|
| 519 | 519 | try expectType(&a, result.root, super::Type::Bool); |
|
| 520 | 520 | } |
|
| 521 | 521 | ||
| 522 | - | @test fn testResolveStringLiteralType() throws (testing::TestError) { |
|
| 522 | + | @test unsafe fn testResolveStringLiteralType() throws (testing::TestError) { |
|
| 523 | 523 | let mut a = testResolver(); |
|
| 524 | 524 | let result = try resolveExprStr(&mut a, "\"hello\""); |
|
| 525 | 525 | ||
| 526 | 526 | try expectNoErrors(&result); |
|
| 527 | 527 | let ty = try typeOf(&a, result.root); |
|
| 528 | 528 | let elemTy = try expectSliceType(ty, false); |
|
| 529 | 529 | try testing::expect(elemTy == super::Type::U8); |
|
| 530 | 530 | } |
|
| 531 | 531 | ||
| 532 | - | @test fn testResolveAsNumeric() throws (testing::TestError) { |
|
| 532 | + | @test unsafe fn testResolveAsNumeric() throws (testing::TestError) { |
|
| 533 | 533 | { |
|
| 534 | 534 | let mut a = testResolver(); |
|
| 535 | 535 | let result = try resolveExprStr(&mut a, "1 as u32"); |
|
| 536 | 536 | try expectNoErrors(&result); |
|
| 537 | 537 | try expectType(&a, result.root, super::Type::U32); |
| 543 | 543 | let x = try getBlockStmt(result.root, 1); |
|
| 544 | 544 | try expectExprStmtType(&a, x, super::Type::U8); |
|
| 545 | 545 | } |
|
| 546 | 546 | } |
|
| 547 | 547 | ||
| 548 | - | @test fn testResolveAsInvalid() throws (testing::TestError) { |
|
| 548 | + | @test unsafe fn testResolveAsInvalid() throws (testing::TestError) { |
|
| 549 | 549 | let mut a = testResolver(); |
|
| 550 | 550 | let result = try resolveProgramStr(&mut a, "true as u32"); |
|
| 551 | 551 | ||
| 552 | 552 | try expectErrorKind( |
|
| 553 | 553 | &result, |
| 556 | 556 | to: super::Type::U32, |
|
| 557 | 557 | }) |
|
| 558 | 558 | ); |
|
| 559 | 559 | } |
|
| 560 | 560 | ||
| 561 | - | @test fn testResolveAsUnionToInt() throws (testing::TestError) { |
|
| 561 | + | @test unsafe fn testResolveAsUnionToInt() throws (testing::TestError) { |
|
| 562 | 562 | let mut a = testResolver(); |
|
| 563 | 563 | let program = "union Color { Red } Color::Red as u32;"; |
|
| 564 | 564 | let result = try resolveProgramStr(&mut a, program); |
|
| 565 | 565 | try expectNoErrors(&result); |
|
| 566 | 566 | ||
| 567 | 567 | let red = try getBlockStmt(result.root, 1); |
|
| 568 | 568 | try expectExprStmtType(&a, red, super::Type::U32); |
|
| 569 | 569 | } |
|
| 570 | 570 | ||
| 571 | - | @test fn testResolveBinding() throws (testing::TestError) { |
|
| 571 | + | @test unsafe fn testResolveBinding() throws (testing::TestError) { |
|
| 572 | 572 | let mut a = testResolver(); |
|
| 573 | 573 | let result = try resolveBlockStr(&mut a, "let x: bool = true; x;"); |
|
| 574 | 574 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 575 | 575 | ||
| 576 | 576 | try expectNoErrors(&result); |
| 585 | 585 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 586 | 586 | else throw testing::TestError::Failed; |
|
| 587 | 587 | try testing::expect(valType == super::Type::Bool); |
|
| 588 | 588 | } |
|
| 589 | 589 | ||
| 590 | - | @test fn testResolveBindingInvalid() throws (testing::TestError) { |
|
| 590 | + | @test unsafe fn testResolveBindingInvalid() throws (testing::TestError) { |
|
| 591 | 591 | let mut a = testResolver(); |
|
| 592 | 592 | let result = try resolveBlockStr(&mut a, "let x: i32 = true;"); |
|
| 593 | 593 | let err = try expectError(&result); |
|
| 594 | 594 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 595 | 595 | } |
|
| 596 | 596 | ||
| 597 | - | @test fn testResolveDuplicateBinding() throws (testing::TestError) { |
|
| 597 | + | @test unsafe fn testResolveDuplicateBinding() throws (testing::TestError) { |
|
| 598 | 598 | let mut a = testResolver(); |
|
| 599 | 599 | let result = try resolveBlockStr(&mut a, "let x: bool = true; let x: u8 = 1;"); |
|
| 600 | 600 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 601 | 601 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("x")); |
|
| 602 | 602 | } |
|
| 603 | 603 | ||
| 604 | - | @test fn testResolveConstLiteralValue() throws (testing::TestError) { |
|
| 604 | + | @test unsafe fn testResolveConstLiteralValue() throws (testing::TestError) { |
|
| 605 | 605 | let mut a = testResolver(); |
|
| 606 | 606 | let program = "constant ANSWER: i32 = 42;"; |
|
| 607 | 607 | let result = try resolveProgramStr(&mut a, program); |
|
| 608 | 608 | try expectNoErrors(&result); |
|
| 609 | 609 |
| 613 | 613 | let case super::SymbolData::Constant { type: constType, .. } = sym.data |
|
| 614 | 614 | else throw testing::TestError::Failed; |
|
| 615 | 615 | try testing::expect(constType == super::Type::I32); |
|
| 616 | 616 | } |
|
| 617 | 617 | ||
| 618 | - | @test fn testResolveConstRequiresConstantExpr() throws (testing::TestError) { |
|
| 618 | + | @test unsafe fn testResolveConstRequiresConstantExpr() throws (testing::TestError) { |
|
| 619 | 619 | let mut a = testResolver(); |
|
| 620 | 620 | let program = "fn value() -> i32 { return 1 } fn main() { constant ANSWER: i32 = value(); }"; |
|
| 621 | 621 | let result = try resolveProgramStr(&mut a, program); |
|
| 622 | 622 | let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 623 | 623 |
| 625 | 625 | else throw testing::TestError::Failed; |
|
| 626 | 626 | let case ast::NodeValue::Call(_) = errNode.value |
|
| 627 | 627 | else throw testing::TestError::Failed; |
|
| 628 | 628 | } |
|
| 629 | 629 | ||
| 630 | - | @test fn testResolveStaticLiteralValue() throws (testing::TestError) { |
|
| 630 | + | @test unsafe fn testResolveStaticLiteralValue() throws (testing::TestError) { |
|
| 631 | 631 | let mut a = testResolver(); |
|
| 632 | 632 | let program = "static COUNTER: i32 = 0;"; |
|
| 633 | 633 | let result = try resolveProgramStr(&mut a, program); |
|
| 634 | 634 | try expectNoErrors(&result); |
|
| 635 | 635 |
| 639 | 639 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
|
| 640 | 640 | else throw testing::TestError::Failed; |
|
| 641 | 641 | try testing::expect(valType == super::Type::I32); |
|
| 642 | 642 | } |
|
| 643 | 643 | ||
| 644 | - | @test fn testResolveStaticRequiresConstantExpr() throws (testing::TestError) { |
|
| 644 | + | @test unsafe fn testResolveStaticRequiresConstantExpr() throws (testing::TestError) { |
|
| 645 | 645 | let mut a = testResolver(); |
|
| 646 | 646 | let program = "fn seed() -> i32 { return 1; } static COUNTER: i32 = seed();"; |
|
| 647 | 647 | let result = try resolveProgramStr(&mut a, program); |
|
| 648 | 648 | let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 649 | 649 |
| 651 | 651 | else throw testing::TestError::Failed; |
|
| 652 | 652 | let case ast::NodeValue::Call(_) = errNode.value |
|
| 653 | 653 | else throw testing::TestError::Failed; |
|
| 654 | 654 | } |
|
| 655 | 655 | ||
| 656 | - | @test fn testSymbolStoresFnAttributes() throws (testing::TestError) { |
|
| 656 | + | @test unsafe fn testSymbolStoresFnAttributes() throws (testing::TestError) { |
|
| 657 | 657 | let mut a = testResolver(); |
|
| 658 | 658 | let program = "@default export fn f() { return; }"; |
|
| 659 | 659 | let result = try resolveProgramStr(&mut a, program); |
|
| 660 | 660 | try expectNoErrors(&result); |
|
| 661 | 661 |
| 667 | 667 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 668 | 668 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 669 | 669 | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern)); |
|
| 670 | 670 | } |
|
| 671 | 671 | ||
| 672 | - | @test fn testSymbolStoresRecordAttributes() throws (testing::TestError) { |
|
| 672 | + | @test unsafe fn testSymbolStoresRecordAttributes() throws (testing::TestError) { |
|
| 673 | 673 | let mut a = testResolver(); |
|
| 674 | 674 | let program = "export record S { value: i32 }"; |
|
| 675 | 675 | let result = try resolveProgramStr(&mut a, program); |
|
| 676 | 676 | try expectNoErrors(&result); |
|
| 677 | 677 |
| 682 | 682 | ||
| 683 | 683 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 684 | 684 | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 685 | 685 | } |
|
| 686 | 686 | ||
| 687 | - | @test fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) { |
|
| 687 | + | @test unsafe fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) { |
|
| 688 | 688 | let mut a = testResolver(); |
|
| 689 | 689 | let program = "@default record T { value: i32 }"; |
|
| 690 | 690 | let result = try resolveProgramStr(&mut a, program); |
|
| 691 | 691 | try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn); |
|
| 692 | 692 | } |
|
| 693 | 693 | ||
| 694 | - | @test fn testDefaultAttributeRejectedOnUnion() throws (testing::TestError) { |
|
| 694 | + | @test unsafe fn testDefaultAttributeRejectedOnUnion() throws (testing::TestError) { |
|
| 695 | 695 | let mut a = testResolver(); |
|
| 696 | 696 | let program = "@default union Result { Ok, Err }"; |
|
| 697 | 697 | let result = try resolveProgramStr(&mut a, program); |
|
| 698 | 698 | try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn); |
|
| 699 | 699 | } |
|
| 700 | 700 | ||
| 701 | - | @test fn testResolveArrayLiteralTyped() throws (testing::TestError) { |
|
| 701 | + | @test unsafe fn testResolveArrayLiteralTyped() throws (testing::TestError) { |
|
| 702 | 702 | let mut a = testResolver(); |
|
| 703 | 703 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2];"); |
|
| 704 | 704 | try expectNoErrors(&result); |
|
| 705 | 705 | ||
| 706 | 706 | let stmt = try getBlockStmt(result.root, 0); |
| 709 | 709 | let arrayTy = try typeOf(&a, decl.value); |
|
| 710 | 710 | let elemTy = try expectArrayType(arrayTy, 2); |
|
| 711 | 711 | try testing::expect(elemTy == super::Type::I32); |
|
| 712 | 712 | } |
|
| 713 | 713 | ||
| 714 | - | @test fn testResolveArrayLiteralElementMismatch() throws (testing::TestError) { |
|
| 714 | + | @test unsafe fn testResolveArrayLiteralElementMismatch() throws (testing::TestError) { |
|
| 715 | 715 | let mut a = testResolver(); |
|
| 716 | 716 | let result = try resolveProgramStr(&mut a, "let xs: [bool; 2] = [true, 1];"); |
|
| 717 | 717 | let err = try expectError(&result); |
|
| 718 | 718 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 719 | 719 | } |
|
| 720 | 720 | ||
| 721 | - | @test fn testResolveArrayLiteralCannotInfer() throws (testing::TestError) { |
|
| 721 | + | @test unsafe fn testResolveArrayLiteralCannotInfer() throws (testing::TestError) { |
|
| 722 | 722 | let mut a = testResolver(); |
|
| 723 | 723 | let result = try resolveProgramStr(&mut a, "let xs = [1, 2];"); |
|
| 724 | 724 | try expectErrorKind(&result, super::ErrorKind::CannotInferType); |
|
| 725 | 725 | } |
|
| 726 | 726 | ||
| 727 | - | @test fn testResolveArrayLiteralOverflow() throws (testing::TestError) { |
|
| 727 | + | @test unsafe fn testResolveArrayLiteralOverflow() throws (testing::TestError) { |
|
| 728 | 728 | let mut a = testResolver(); |
|
| 729 | 729 | let result = try resolveProgramStr(&mut a, "let xs: [u8; 2] = [1, 256];"); |
|
| 730 | 730 | let err = try expectError(&result); |
|
| 731 | 731 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 732 | 732 | else throw testing::TestError::Failed; |
|
| 733 | 733 | } |
|
| 734 | 734 | ||
| 735 | - | @test fn testResolveArrayLiteralTooFewElements() throws (testing::TestError) { |
|
| 735 | + | @test unsafe fn testResolveArrayLiteralTooFewElements() throws (testing::TestError) { |
|
| 736 | 736 | let mut a = testResolver(); |
|
| 737 | 737 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1];"); |
|
| 738 | 738 | let err = try expectError(&result); |
|
| 739 | 739 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 740 | 740 | else throw testing::TestError::Failed; |
|
| 741 | 741 | } |
|
| 742 | 742 | ||
| 743 | - | @test fn testResolveArrayLiteralTooManyElements() throws (testing::TestError) { |
|
| 743 | + | @test unsafe fn testResolveArrayLiteralTooManyElements() throws (testing::TestError) { |
|
| 744 | 744 | let mut a = testResolver(); |
|
| 745 | 745 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2, 3];"); |
|
| 746 | 746 | let err = try expectError(&result); |
|
| 747 | 747 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 748 | 748 | else throw testing::TestError::Failed; |
|
| 749 | 749 | } |
|
| 750 | 750 | ||
| 751 | - | @test fn testResolveArrayLiteralEmptyWithAnnotation() throws (testing::TestError) { |
|
| 751 | + | @test unsafe fn testResolveArrayLiteralEmptyWithAnnotation() throws (testing::TestError) { |
|
| 752 | 752 | let mut a = testResolver(); |
|
| 753 | 753 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 0] = [];"); |
|
| 754 | 754 | try expectNoErrors(&result); |
|
| 755 | 755 | } |
|
| 756 | 756 | ||
| 757 | - | @test fn testResolveNestedArrayLiteralTyped() throws (testing::TestError) { |
|
| 757 | + | @test unsafe fn testResolveNestedArrayLiteralTyped() throws (testing::TestError) { |
|
| 758 | 758 | let mut a = testResolver(); |
|
| 759 | 759 | let result = try resolveProgramStr(&mut a, "let grid: [[i32; 2]; 2] = [[1, 2], [3, 4]];"); |
|
| 760 | 760 | try expectNoErrors(&result); |
|
| 761 | 761 | ||
| 762 | 762 | let stmt = try getBlockStmt(result.root, 0); |
| 766 | 766 | let rowTy = try expectArrayType(gridTy, 2); |
|
| 767 | 767 | let elemTy = try expectArrayType(rowTy, 2); |
|
| 768 | 768 | try testing::expect(elemTy == super::Type::I32); |
|
| 769 | 769 | } |
|
| 770 | 770 | ||
| 771 | - | @test fn testResolveArrayLiteralWithOptionalElems() throws (testing::TestError) { |
|
| 771 | + | @test unsafe fn testResolveArrayLiteralWithOptionalElems() throws (testing::TestError) { |
|
| 772 | 772 | let mut a = testResolver(); |
|
| 773 | 773 | let result = try resolveProgramStr(&mut a, "let xs: [?i32; 2] = [1, 2];"); |
|
| 774 | 774 | try expectNoErrors(&result); |
|
| 775 | 775 | ||
| 776 | 776 | let stmt = try getBlockStmt(result.root, 0); |
| 781 | 781 | let case super::Type::Optional(inner) = elemTy |
|
| 782 | 782 | else throw testing::TestError::Failed; |
|
| 783 | 783 | try testing::expect(*inner == super::Type::I32); |
|
| 784 | 784 | } |
|
| 785 | 785 | ||
| 786 | - | @test fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) { |
|
| 786 | + | @test unsafe fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) { |
|
| 787 | 787 | let mut a = testResolver(); |
|
| 788 | 788 | let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];"); |
|
| 789 | 789 | let err = try expectError(&result); |
|
| 790 | 790 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 791 | 791 | else throw testing::TestError::Failed; |
|
| 792 | 792 | } |
|
| 793 | 793 | ||
| 794 | - | @test fn testResolveArrayRepeatBasic() throws (testing::TestError) { |
|
| 794 | + | @test unsafe fn testResolveArrayRepeatBasic() throws (testing::TestError) { |
|
| 795 | 795 | let mut a = testResolver(); |
|
| 796 | 796 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3];"); |
|
| 797 | 797 | try expectNoErrors(&result); |
|
| 798 | 798 | ||
| 799 | 799 | let stmt = try getBlockStmt(result.root, 0); |
| 802 | 802 | let arrayTy = try typeOf(&a, decl.value); |
|
| 803 | 803 | let elemTy = try expectArrayType(arrayTy, 3); |
|
| 804 | 804 | try testing::expect(elemTy == super::Type::I32); |
|
| 805 | 805 | } |
|
| 806 | 806 | ||
| 807 | - | @test fn testResolveArrayRepeatWithExpression() throws (testing::TestError) { |
|
| 807 | + | @test unsafe fn testResolveArrayRepeatWithExpression() throws (testing::TestError) { |
|
| 808 | 808 | let mut a = testResolver(); |
|
| 809 | 809 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 5] = [3 + 2; 5];"); |
|
| 810 | 810 | try expectNoErrors(&result); |
|
| 811 | 811 | ||
| 812 | 812 | let stmt = try getBlockStmt(result.root, 0); |
| 815 | 815 | let arrayTy = try typeOf(&a, decl.value); |
|
| 816 | 816 | let elemTy = try expectArrayType(arrayTy, 5); |
|
| 817 | 817 | try testing::expect(elemTy == super::Type::I32); |
|
| 818 | 818 | } |
|
| 819 | 819 | ||
| 820 | - | @test fn testResolveArrayRepeatLiteralArithmetic() throws (testing::TestError) { |
|
| 820 | + | @test unsafe fn testResolveArrayRepeatLiteralArithmetic() throws (testing::TestError) { |
|
| 821 | 821 | let mut a = testResolver(); |
|
| 822 | 822 | // `3 * 1` folds to a compile-time constant, so the repeat count is valid. |
|
| 823 | 823 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3 * 1];"); |
|
| 824 | 824 | try expectNoErrors(&result); |
|
| 825 | 825 | } |
|
| 826 | 826 | ||
| 827 | - | @test fn testResolveArrayRepeatNonConstCount() throws (testing::TestError) { |
|
| 827 | + | @test unsafe fn testResolveArrayRepeatNonConstCount() throws (testing::TestError) { |
|
| 828 | 828 | let mut a = testResolver(); |
|
| 829 | 829 | // A function call is not a constant expression. |
|
| 830 | 830 | let result = try resolveProgramStr(&mut a, "fn f() -> u32 { return 3; } let xs: [i32; 3] = [42; f()];"); |
|
| 831 | 831 | try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
|
| 832 | 832 | } |
|
| 833 | 833 | ||
| 834 | - | @test fn testResolveArrayRepeatCountMismatch() throws (testing::TestError) { |
|
| 834 | + | @test unsafe fn testResolveArrayRepeatCountMismatch() throws (testing::TestError) { |
|
| 835 | 835 | let mut a = testResolver(); |
|
| 836 | 836 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 4] = [1; 3];"); |
|
| 837 | 837 | let err = try expectError(&result); |
|
| 838 | 838 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 839 | 839 | else throw testing::TestError::Failed; |
|
| 840 | 840 | } |
|
| 841 | 841 | ||
| 842 | - | @test fn testResolveArrayIndex() throws (testing::TestError) { |
|
| 842 | + | @test unsafe fn testResolveArrayIndex() throws (testing::TestError) { |
|
| 843 | 843 | let mut a = testResolver(); |
|
| 844 | 844 | let program = "let xs: [i32; 3] = [1, 2, 3]; xs[1];"; |
|
| 845 | 845 | let result = try resolveProgramStr(&mut a, program); |
|
| 846 | 846 | try expectNoErrors(&result); |
|
| 847 | 847 | ||
| 848 | 848 | let stmt = try getBlockStmt(result.root, 1); |
|
| 849 | 849 | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 850 | 850 | } |
|
| 851 | 851 | ||
| 852 | - | @test fn testResolveSliceIndex() throws (testing::TestError) { |
|
| 852 | + | @test unsafe fn testResolveSliceIndex() throws (testing::TestError) { |
|
| 853 | 853 | let mut a = testResolver(); |
|
| 854 | - | let program = "let xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];"; |
|
| 854 | + | let program = "static xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];"; |
|
| 855 | 855 | let result = try resolveProgramStr(&mut a, program); |
|
| 856 | 856 | try expectNoErrors(&result); |
|
| 857 | 857 | ||
| 858 | 858 | let sliceStmt = try getBlockStmt(result.root, 1); |
|
| 859 | 859 | let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value |
| 864 | 864 | ||
| 865 | 865 | let indexStmt = try getBlockStmt(result.root, 2); |
|
| 866 | 866 | try expectExprStmtType(&a, indexStmt, super::Type::I32); |
|
| 867 | 867 | } |
|
| 868 | 868 | ||
| 869 | - | @test fn testResolveSliceFields() throws (testing::TestError) { |
|
| 869 | + | @test unsafe fn testResolveSliceFields() throws (testing::TestError) { |
|
| 870 | 870 | let mut a = testResolver(); |
|
| 871 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; slice.ptr;"; |
|
| 871 | + | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; slice.ptr;"; |
|
| 872 | 872 | let result = try resolveProgramStr(&mut a, program); |
|
| 873 | 873 | try expectNoErrors(&result); |
|
| 874 | 874 | ||
| 875 | 875 | let lenStmt = try getBlockStmt(result.root, 2); |
|
| 876 | 876 | let case ast::NodeValue::ExprStmt(lenExpr) = lenStmt.value |
| 884 | 884 | let ptrTy = try typeOf(&a, ptrExpr); |
|
| 885 | 885 | let targetTy = try expectPointerType(ptrTy, false); |
|
| 886 | 886 | try testing::expect(targetTy == super::Type::I32); |
|
| 887 | 887 | } |
|
| 888 | 888 | ||
| 889 | - | @test fn testResolveSliceLiteralImmutable() throws (testing::TestError) { |
|
| 889 | + | @test unsafe fn testResolveSliceLiteralImmutable() throws (testing::TestError) { |
|
| 890 | 890 | let mut a = testResolver(); |
|
| 891 | 891 | let program = "let slice: *[i32] = &[1, 2, 3];"; |
|
| 892 | 892 | let result = try resolveProgramStr(&mut a, program); |
|
| 893 | 893 | try expectNoErrors(&result); |
|
| 894 | 894 | } |
|
| 895 | 895 | ||
| 896 | 896 | /// Empty array literal infers element type from slice annotation. |
|
| 897 | - | @test fn testResolveSliceLiteralEmpty() throws (testing::TestError) { |
|
| 897 | + | @test unsafe fn testResolveSliceLiteralEmpty() throws (testing::TestError) { |
|
| 898 | 898 | let mut a = testResolver(); |
|
| 899 | 899 | let program = "let slice: *[i32] = &[];"; |
|
| 900 | 900 | let result = try resolveProgramStr(&mut a, program); |
|
| 901 | 901 | try expectNoErrors(&result); |
|
| 902 | 902 | } |
|
| 903 | 903 | ||
| 904 | 904 | /// Nested array literal should infer inner element type from slice annotation. |
|
| 905 | - | @test fn testResolveSliceLiteralNestedArray() throws (testing::TestError) { |
|
| 905 | + | @test unsafe fn testResolveSliceLiteralNestedArray() throws (testing::TestError) { |
|
| 906 | 906 | let mut a = testResolver(); |
|
| 907 | 907 | let program = "let slice: *[[i32; 2]] = &[[1, 2], [3, 4]];"; |
|
| 908 | 908 | let result = try resolveProgramStr(&mut a, program); |
|
| 909 | 909 | try expectNoErrors(&result); |
|
| 910 | 910 | } |
|
| 911 | 911 | ||
| 912 | - | @test fn testResolveSliceFromArray() throws (testing::TestError) { |
|
| 912 | + | @test unsafe fn testResolveSliceFromArray() throws (testing::TestError) { |
|
| 913 | 913 | { |
|
| 914 | 914 | let mut a = testResolver(); |
|
| 915 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];"; |
|
| 915 | + | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];"; |
|
| 916 | 916 | let result = try resolveProgramStr(&mut a, program); |
|
| 917 | 917 | try expectNoErrors(&result); |
|
| 918 | 918 | } { |
|
| 919 | 919 | let mut a = testResolver(); |
|
| 920 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];"; |
|
| 920 | + | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];"; |
|
| 921 | 921 | let result = try resolveProgramStr(&mut a, program); |
|
| 922 | 922 | try expectNoErrors(&result); |
|
| 923 | 923 | } { |
|
| 924 | 924 | let mut a = testResolver(); |
|
| 925 | - | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];"; |
|
| 925 | + | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];"; |
|
| 926 | 926 | let result = try resolveProgramStr(&mut a, program); |
|
| 927 | 927 | try expectNoErrors(&result); |
|
| 928 | 928 | } { |
|
| 929 | 929 | let mut a = testResolver(); |
|
| 930 | - | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];"; |
|
| 930 | + | let program = "static xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];"; |
|
| 931 | 931 | let result = try resolveProgramStr(&mut a, program); |
|
| 932 | 932 | try expectNoErrors(&result); |
|
| 933 | 933 | } |
|
| 934 | 934 | } |
|
| 935 | 935 | ||
| 936 | - | @test fn testResolveSliceLiteralMutableRequiresMut() throws (testing::TestError) { |
|
| 936 | + | @test unsafe fn testResolveSliceLiteralMutableRequiresMut() throws (testing::TestError) { |
|
| 937 | 937 | let mut a = testResolver(); |
|
| 938 | 938 | let program = "let slice: *mut [i32] = &[1, 2, 3];"; |
|
| 939 | 939 | let result = try resolveProgramStr(&mut a, program); |
|
| 940 | 940 | let err = try expectError(&result); |
|
| 941 | 941 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 942 | 942 | else throw testing::TestError::Failed; |
|
| 943 | 943 | } |
|
| 944 | 944 | ||
| 945 | - | @test fn testResolveSliceLiteralMutable() throws (testing::TestError) { |
|
| 945 | + | @test unsafe fn testResolveSliceLiteralMutable() throws (testing::TestError) { |
|
| 946 | 946 | let mut a = testResolver(); |
|
| 947 | 947 | let program = "let slice: *mut [i32] = &mut [1, 2, 3];"; |
|
| 948 | 948 | let result = try resolveProgramStr(&mut a, program); |
|
| 949 | 949 | try expectNoErrors(&result); |
|
| 950 | 950 | } |
|
| 951 | 951 | ||
| 952 | - | @test fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) { |
|
| 952 | + | @test unsafe fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) { |
|
| 953 | 953 | let mut a = testResolver(); |
|
| 954 | 954 | let program = "let x: i32 = 0; let ptr: *mut i32 = &x;"; |
|
| 955 | 955 | let result = try resolveProgramStr(&mut a, program); |
|
| 956 | 956 | let err = try expectError(&result); |
|
| 957 | 957 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 958 | 958 | else throw testing::TestError::Failed; |
|
| 959 | 959 | } |
|
| 960 | 960 | ||
| 961 | - | @test fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) { |
|
| 961 | + | @test unsafe fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) { |
|
| 962 | 962 | let mut a = testResolver(); |
|
| 963 | - | let program = "let mut x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;"; |
|
| 963 | + | let program = "static x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;"; |
|
| 964 | 964 | let result = try resolveProgramStr(&mut a, program); |
|
| 965 | 965 | try expectNoErrors(&result); |
|
| 966 | 966 | } |
|
| 967 | 967 | ||
| 968 | - | @test fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) { |
|
| 968 | + | @test unsafe fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) { |
|
| 969 | 969 | { |
|
| 970 | 970 | let mut a = testResolver(); |
|
| 971 | 971 | let program = "let x: i32 = 0; let ptr = &mut x;"; |
|
| 972 | 972 | let result = try resolveProgramStr(&mut a, program); |
|
| 973 | 973 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 974 | 974 | } { |
|
| 975 | 975 | let mut a = testResolver(); |
|
| 976 | - | let program = "let mut x: i32 = 0; let ptr = &mut x;"; |
|
| 976 | + | let program = "let mut x: i32 = 0; &mut x;"; |
|
| 977 | 977 | let result = try resolveProgramStr(&mut a, program); |
|
| 978 | 978 | try expectNoErrors(&result); |
|
| 979 | 979 | } |
|
| 980 | 980 | } |
|
| 981 | 981 | ||
| 982 | - | @test fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) { |
|
| 982 | + | @test unsafe fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) { |
|
| 983 | 983 | { |
|
| 984 | 984 | let mut a = testResolver(); |
|
| 985 | 985 | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];"; |
|
| 986 | 986 | let result = try resolveProgramStr(&mut a, program); |
|
| 987 | 987 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 988 | 988 | } { |
|
| 989 | 989 | let mut a = testResolver(); |
|
| 990 | - | let program = "let mut xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];"; |
|
| 990 | + | let program = "let mut xs: [i32; 3] = [1, 2, 3]; &mut xs[..];"; |
|
| 991 | 991 | let result = try resolveProgramStr(&mut a, program); |
|
| 992 | 992 | try expectNoErrors(&result); |
|
| 993 | 993 | } |
|
| 994 | 994 | } |
|
| 995 | 995 | ||
| 996 | - | @test fn testResolveSliceCannotAssignToArray() throws (testing::TestError) { |
|
| 996 | + | @test unsafe fn testResolveSliceCannotAssignToArray() throws (testing::TestError) { |
|
| 997 | 997 | let mut a = testResolver(); |
|
| 998 | 998 | let program = "let xs: *[u8] = &[1, 2]; let ys: [u8; 2] = xs;"; |
|
| 999 | 999 | let result = try resolveProgramStr(&mut a, program); |
|
| 1000 | 1000 | let err = try expectError(&result); |
|
| 1001 | 1001 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 1002 | 1002 | else throw testing::TestError::Failed; |
|
| 1003 | 1003 | } |
|
| 1004 | 1004 | ||
| 1005 | - | @test fn testResolveSliceSyntaxRequiresAddressOf() throws (testing::TestError) { |
|
| 1005 | + | @test unsafe fn testResolveSliceSyntaxRequiresAddressOf() throws (testing::TestError) { |
|
| 1006 | 1006 | let mut a = testResolver(); |
|
| 1007 | 1007 | let program = "let xs: [u8; 2] = [1, 2]; xs[..];"; |
|
| 1008 | 1008 | let result = try resolveProgramStr(&mut a, program); |
|
| 1009 | 1009 | try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress); |
|
| 1010 | 1010 | } |
|
| 1011 | 1011 | ||
| 1012 | - | @test fn testResolveSliceResliceRequiresAddressOf() throws (testing::TestError) { |
|
| 1012 | + | @test unsafe fn testResolveSliceResliceRequiresAddressOf() throws (testing::TestError) { |
|
| 1013 | 1013 | let mut a = testResolver(); |
|
| 1014 | 1014 | let program = "fn f(s: *[u8]) -> *[u8] { return s[..]; }"; |
|
| 1015 | 1015 | let result = try resolveProgramStr(&mut a, program); |
|
| 1016 | 1016 | try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress); |
|
| 1017 | 1017 | } |
|
| 1018 | 1018 | ||
| 1019 | - | @test fn testResolveSliceRangeOutOfBounds() throws (testing::TestError) { |
|
| 1019 | + | @test unsafe fn testResolveSliceRangeOutOfBounds() throws (testing::TestError) { |
|
| 1020 | 1020 | { |
|
| 1021 | 1021 | let mut a = testResolver(); |
|
| 1022 | 1022 | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[..3];"; |
|
| 1023 | 1023 | let result = try resolveProgramStr(&mut a, program); |
|
| 1024 | 1024 | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
| 1033 | 1033 | let result = try resolveProgramStr(&mut a, program); |
|
| 1034 | 1034 | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
|
| 1035 | 1035 | } |
|
| 1036 | 1036 | } |
|
| 1037 | 1037 | ||
| 1038 | - | @test fn testResolveArrayLenConstValue() throws (testing::TestError) { |
|
| 1038 | + | @test unsafe fn testResolveArrayLenConstValue() throws (testing::TestError) { |
|
| 1039 | 1039 | let mut a = testResolver(); |
|
| 1040 | 1040 | let program = "let xs: [i32; 3] = [1, 2, 3]; constant LEN: u32 = xs.len;"; |
|
| 1041 | 1041 | let result = try resolveBlockStr(&mut a, program); |
|
| 1042 | 1042 | try expectNoErrors(&result); |
|
| 1043 | 1043 |
| 1050 | 1050 | else throw testing::TestError::Failed; |
|
| 1051 | 1051 | try testing::expect(lenVal.magnitude == 3); |
|
| 1052 | 1052 | try testing::expect(not lenVal.negative); |
|
| 1053 | 1053 | } |
|
| 1054 | 1054 | ||
| 1055 | - | @test fn testResolveIndexNonIndexable() throws (testing::TestError) { |
|
| 1055 | + | @test unsafe fn testResolveIndexNonIndexable() throws (testing::TestError) { |
|
| 1056 | 1056 | let mut a = testResolver(); |
|
| 1057 | 1057 | let program = "let flag: bool = true; flag[0];"; |
|
| 1058 | 1058 | let result = try resolveProgramStr(&mut a, program); |
|
| 1059 | 1059 | try expectErrorKind(&result, super::ErrorKind::ExpectedIndexable); |
|
| 1060 | 1060 | } |
|
| 1061 | 1061 | ||
| 1062 | - | @test fn testResolveSliceFieldUnknown() throws (testing::TestError) { |
|
| 1062 | + | @test unsafe fn testResolveSliceFieldUnknown() throws (testing::TestError) { |
|
| 1063 | 1063 | let mut a = testResolver(); |
|
| 1064 | 1064 | let program = "let xs: [i32; 2] = [1, 2]; (&xs[0..]).unknown;"; |
|
| 1065 | 1065 | let result = try resolveProgramStr(&mut a, program); |
|
| 1066 | 1066 | try expectErrorKind(&result, super::ErrorKind::SliceFieldUnknown("unknown")); |
|
| 1067 | 1067 | } |
|
| 1068 | 1068 | ||
| 1069 | - | @test fn testResolveArrayFieldUnknown() throws (testing::TestError) { |
|
| 1069 | + | @test unsafe fn testResolveArrayFieldUnknown() throws (testing::TestError) { |
|
| 1070 | 1070 | let mut a = testResolver(); |
|
| 1071 | 1071 | let program = "let xs: [i32; 2] = [1, 2]; xs.field;"; |
|
| 1072 | 1072 | let result = try resolveProgramStr(&mut a, program); |
|
| 1073 | 1073 | try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field")); |
|
| 1074 | 1074 | } |
|
| 1075 | 1075 | ||
| 1076 | - | @test fn testResolveIfConditionRequiresBool() throws (testing::TestError) { |
|
| 1076 | + | @test unsafe fn testResolveIfConditionRequiresBool() throws (testing::TestError) { |
|
| 1077 | 1077 | { |
|
| 1078 | 1078 | let mut a = testResolver(); |
|
| 1079 | 1079 | let result = try resolveProgramStr(&mut a, "if 42 {}"); |
|
| 1080 | 1080 | let err = try expectError(&result); |
|
| 1081 | 1081 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 1084 | 1084 | let result = try resolveProgramStr(&mut a, "if true {}"); |
|
| 1085 | 1085 | try expectNoErrors(&result); |
|
| 1086 | 1086 | } |
|
| 1087 | 1087 | } |
|
| 1088 | 1088 | ||
| 1089 | - | @test fn testResolveIfLetScopeBinding() throws (testing::TestError) { |
|
| 1089 | + | @test unsafe fn testResolveIfLetScopeBinding() throws (testing::TestError) { |
|
| 1090 | 1090 | let mut a = testResolver(); |
|
| 1091 | 1091 | let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x }"); |
|
| 1092 | 1092 | try expectNoErrors(&result); |
|
| 1093 | 1093 | ||
| 1094 | 1094 | // Get the if-let statement and verify `x` has type `i32`. |
| 1110 | 1110 | else throw testing::TestError::Failed; |
|
| 1111 | 1111 | ||
| 1112 | 1112 | try testing::expect(valType == super::Type::I32); |
|
| 1113 | 1113 | } |
|
| 1114 | 1114 | ||
| 1115 | - | @test fn testResolveIfLetScopeBindingError() throws (testing::TestError) { |
|
| 1115 | + | @test unsafe fn testResolveIfLetScopeBindingError() throws (testing::TestError) { |
|
| 1116 | 1116 | let mut a = testResolver(); |
|
| 1117 | 1117 | let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x } else { x }"); |
|
| 1118 | 1118 | let err = try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1119 | 1119 | ||
| 1120 | 1120 | // Verify the error comes from the else branch (offset 48). |
| 1122 | 1122 | else throw testing::TestError::Failed; |
|
| 1123 | 1123 | try testing::expect(errNode.span.offset == 48); |
|
| 1124 | 1124 | } |
|
| 1125 | 1125 | ||
| 1126 | 1126 | /// Tests that `if let` with a condition expression binds the variable in scope. |
|
| 1127 | - | @test fn testResolveIfLetConditionBindsVariable() throws (testing::TestError) { |
|
| 1127 | + | @test unsafe fn testResolveIfLetConditionBindsVariable() throws (testing::TestError) { |
|
| 1128 | 1128 | let mut a = testResolver(); |
|
| 1129 | 1129 | let program = "let opt: ?i32 = 42; if let x = opt; x == 1 { x }"; |
|
| 1130 | 1130 | let result = try resolveProgramStr(&mut a, program); |
|
| 1131 | 1131 | try expectNoErrors(&result); |
|
| 1132 | 1132 | } |
|
| 1133 | 1133 | ||
| 1134 | - | @test fn testResolveWhileConditionRequiresBool() throws (testing::TestError) { |
|
| 1134 | + | @test unsafe fn testResolveWhileConditionRequiresBool() throws (testing::TestError) { |
|
| 1135 | 1135 | { |
|
| 1136 | 1136 | let mut a = testResolver(); |
|
| 1137 | 1137 | let result = try resolveProgramStr(&mut a, "while 1 {}"); |
|
| 1138 | 1138 | let err = try expectError(&result); |
|
| 1139 | 1139 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 1142 | 1142 | let result = try resolveProgramStr(&mut a, "while true {}"); |
|
| 1143 | 1143 | try expectNoErrors(&result); |
|
| 1144 | 1144 | } |
|
| 1145 | 1145 | } |
|
| 1146 | 1146 | ||
| 1147 | - | @test fn testResolveWhileLetBindingScope() throws (testing::TestError) { |
|
| 1147 | + | @test unsafe fn testResolveWhileLetBindingScope() throws (testing::TestError) { |
|
| 1148 | 1148 | { |
|
| 1149 | 1149 | let mut a = testResolver(); |
|
| 1150 | 1150 | let program = "let mut opt: ?i32 = 42; while let x = opt; x > 0 { x; opt; }"; |
|
| 1151 | 1151 | let result = try resolveProgramStr(&mut a, program); |
|
| 1152 | 1152 | try expectNoErrors(&result); |
| 1171 | 1171 | let result = try resolveProgramStr(&mut a, program); |
|
| 1172 | 1172 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1173 | 1173 | } |
|
| 1174 | 1174 | } |
|
| 1175 | 1175 | ||
| 1176 | - | @test fn testResolveForArrayBindsElementType() throws (testing::TestError) { |
|
| 1176 | + | @test unsafe fn testResolveForArrayBindsElementType() throws (testing::TestError) { |
|
| 1177 | 1177 | let mut a = testResolver(); |
|
| 1178 | 1178 | let program = "let xs: [i32; 2] = [1, 2]; for x in xs { x; }"; |
|
| 1179 | 1179 | let result = try resolveProgramStr(&mut a, program); |
|
| 1180 | 1180 | try expectNoErrors(&result); |
|
| 1181 | 1181 |
| 1194 | 1194 | let bindingTy = super::typeFor(&a, loopNode.binding) |
|
| 1195 | 1195 | else throw testing::TestError::Failed; |
|
| 1196 | 1196 | try testing::expect(bindingTy == super::Type::I32); |
|
| 1197 | 1197 | } |
|
| 1198 | 1198 | ||
| 1199 | - | @test fn testResolveForIndexedLoopBindsIndex() throws (testing::TestError) { |
|
| 1199 | + | @test unsafe fn testResolveForIndexedLoopBindsIndex() throws (testing::TestError) { |
|
| 1200 | 1200 | let mut a = testResolver(); |
|
| 1201 | 1201 | let program = "let xs: [bool; 3] = [true; 3]; for value, idx in xs { value; idx; }"; |
|
| 1202 | 1202 | let result = try resolveProgramStr(&mut a, program); |
|
| 1203 | 1203 | try expectNoErrors(&result); |
|
| 1204 | 1204 |
| 1224 | 1224 | let indexTy = super::typeFor(&a, indexNode) |
|
| 1225 | 1225 | else throw testing::TestError::Failed; |
|
| 1226 | 1226 | try testing::expect(indexTy == super::Type::U32); |
|
| 1227 | 1227 | } |
|
| 1228 | 1228 | ||
| 1229 | - | @test fn testResolveForSliceIterable() throws (testing::TestError) { |
|
| 1229 | + | @test unsafe fn testResolveForSliceIterable() throws (testing::TestError) { |
|
| 1230 | 1230 | let mut a = testResolver(); |
|
| 1231 | 1231 | let program = "let xs: [i32; 3] = [1, 2, 3]; for x in &xs[..] { x; }"; |
|
| 1232 | 1232 | let result = try resolveProgramStr(&mut a, program); |
|
| 1233 | 1233 | try expectNoErrors(&result); |
|
| 1234 | 1234 |
| 1239 | 1239 | let bindingTy = super::typeFor(&a, loopNode.binding) |
|
| 1240 | 1240 | else throw testing::TestError::Failed; |
|
| 1241 | 1241 | try testing::expect(bindingTy == super::Type::I32); |
|
| 1242 | 1242 | } |
|
| 1243 | 1243 | ||
| 1244 | - | @test fn testResolveForRequiresIterable() throws (testing::TestError) { |
|
| 1244 | + | @test unsafe fn testResolveForRequiresIterable() throws (testing::TestError) { |
|
| 1245 | 1245 | let mut a = testResolver(); |
|
| 1246 | 1246 | let result = try resolveProgramStr(&mut a, "for x in true { x; }"); |
|
| 1247 | 1247 | try expectErrorKind(&result, super::ErrorKind::ExpectedIterable); |
|
| 1248 | 1248 | } |
|
| 1249 | 1249 | ||
| 1250 | - | @test fn testResolveForRangeBoundsMustNumeric() throws (testing::TestError) { |
|
| 1250 | + | @test unsafe fn testResolveForRangeBoundsMustNumeric() throws (testing::TestError) { |
|
| 1251 | 1251 | let mut a = testResolver(); |
|
| 1252 | 1252 | let result = try resolveBlockStr(&mut a, "for i in 0..true { i; }"); |
|
| 1253 | 1253 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 1254 | 1254 | } |
|
| 1255 | 1255 | ||
| 1256 | - | @test fn testResolveMatchPatternTypeMismatch() throws (testing::TestError) { |
|
| 1256 | + | @test unsafe fn testResolveMatchPatternTypeMismatch() throws (testing::TestError) { |
|
| 1257 | 1257 | let mut a = testResolver(); |
|
| 1258 | 1258 | let program = "let val: i32 = 0; match val { case true => {} }"; |
|
| 1259 | 1259 | let result = try resolveProgramStr(&mut a, program); |
|
| 1260 | 1260 | let err = try expectError(&result); |
|
| 1261 | 1261 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 1262 | 1262 | } |
|
| 1263 | 1263 | ||
| 1264 | - | @test fn testResolveMatchUnionVariantTypeMismatch() throws (testing::TestError) { |
|
| 1264 | + | @test unsafe fn testResolveMatchUnionVariantTypeMismatch() throws (testing::TestError) { |
|
| 1265 | 1265 | let mut a = testResolver(); |
|
| 1266 | 1266 | let program = "union First { A } union Second { B } fn run(val: First) { match val { case Second::B => {} } }"; |
|
| 1267 | 1267 | let result = try resolveProgramStr(&mut a, program); |
|
| 1268 | 1268 | let err = try expectError(&result); |
|
| 1269 | 1269 | ||
| 1270 | 1270 | let firstTy = try getTypeInScopeOf(&a, result.root, "First"); |
|
| 1271 | 1271 | let secondTy = try getTypeInScopeOf(&a, result.root, "Second"); |
|
| 1272 | 1272 | try expectTypeMismatch(err, super::Type::Nominal(firstTy), super::Type::Nominal(secondTy)); |
|
| 1273 | 1273 | } |
|
| 1274 | 1274 | ||
| 1275 | - | @test fn testResolveMatchUnionPayloadMissing() throws (testing::TestError) { |
|
| 1275 | + | @test unsafe fn testResolveMatchUnionPayloadMissing() throws (testing::TestError) { |
|
| 1276 | 1276 | let mut a = testResolver(); |
|
| 1277 | 1277 | let program = "union Opt { Some(i32) } fn run(val: Opt) { match val { case Opt::Some => {} } }"; |
|
| 1278 | 1278 | let result = try resolveProgramStr(&mut a, program); |
|
| 1279 | 1279 | try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadMissing("Some")); |
|
| 1280 | 1280 | } |
|
| 1281 | 1281 | ||
| 1282 | - | @test fn testResolveMatchUnionVoidVariantExplicitDiscriminant() throws (testing::TestError) { |
|
| 1282 | + | @test unsafe fn testResolveMatchUnionVoidVariantExplicitDiscriminant() throws (testing::TestError) { |
|
| 1283 | 1283 | let mut a = testResolver(); |
|
| 1284 | 1284 | let program = "union Opt { Some = 5 } fn run(val: Opt) { match val { case Opt::Some => {} } }"; |
|
| 1285 | 1285 | let result = try resolveProgramStr(&mut a, program); |
|
| 1286 | 1286 | try expectNoErrors(&result); |
|
| 1287 | 1287 | } |
|
| 1288 | 1288 | ||
| 1289 | - | @test fn testResolveMatchUnionPayloadUnexpected() throws (testing::TestError) { |
|
| 1289 | + | @test unsafe fn testResolveMatchUnionPayloadUnexpected() throws (testing::TestError) { |
|
| 1290 | 1290 | let mut a = testResolver(); |
|
| 1291 | 1291 | let program = "union Opt { None } fn run(val: Opt) { match val { case Opt::None(x) => {} } }"; |
|
| 1292 | 1292 | let result = try resolveProgramStr(&mut a, program); |
|
| 1293 | 1293 | try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadUnexpected("None")); |
|
| 1294 | 1294 | } |
|
| 1295 | 1295 | ||
| 1296 | - | @test fn testResolveMatchUnionUnknownVariant() throws (testing::TestError) { |
|
| 1296 | + | @test unsafe fn testResolveMatchUnionUnknownVariant() throws (testing::TestError) { |
|
| 1297 | 1297 | let mut a = testResolver(); |
|
| 1298 | 1298 | let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Unknown => {} } }"; |
|
| 1299 | 1299 | let result = try resolveProgramStr(&mut a, program); |
|
| 1300 | 1300 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 1301 | 1301 | } |
|
| 1302 | 1302 | ||
| 1303 | - | @test fn testResolveMatchUnionNonExhaustive() throws (testing::TestError) { |
|
| 1303 | + | @test unsafe fn testResolveMatchUnionNonExhaustive() throws (testing::TestError) { |
|
| 1304 | 1304 | { |
|
| 1305 | 1305 | let mut a = testResolver(); |
|
| 1306 | 1306 | let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Some => {} } }"; |
|
| 1307 | 1307 | let result = try resolveProgramStr(&mut a, program); |
|
| 1308 | 1308 | try expectErrorKind(&result, super::ErrorKind::UnionMatchNonExhaustive("None")); |
| 1312 | 1312 | let result = try resolveProgramStr(&mut a, program); |
|
| 1313 | 1313 | try expectNoErrors(&result); |
|
| 1314 | 1314 | } |
|
| 1315 | 1315 | } |
|
| 1316 | 1316 | ||
| 1317 | - | @test fn testResolveMatchUnionNonExhaustiveExplicitDiscriminants() throws (testing::TestError) { |
|
| 1317 | + | @test unsafe fn testResolveMatchUnionNonExhaustiveExplicitDiscriminants() throws (testing::TestError) { |
|
| 1318 | 1318 | let mut a = testResolver(); |
|
| 1319 | 1319 | let program = "union U { A = 3, B = 9 } fn run(value: U) { match value { case U::A => {}, case U::B => {} } }"; |
|
| 1320 | 1320 | let result = try resolveProgramStr(&mut a, program); |
|
| 1321 | 1321 | try expectNoErrors(&result); |
|
| 1322 | 1322 | } |
|
| 1323 | 1323 | ||
| 1324 | - | @test fn testResolveMatchUnionBindingScope() throws (testing::TestError) { |
|
| 1324 | + | @test unsafe fn testResolveMatchUnionBindingScope() throws (testing::TestError) { |
|
| 1325 | 1325 | let mut a = testResolver(); |
|
| 1326 | 1326 | let program = "union Opt { Some(i32), None } fn f(value: Opt) { match value { case Opt::Some(x) if x > 0 => { x; } else => {} } }"; |
|
| 1327 | 1327 | let result = try resolveProgramStr(&mut a, program); |
|
| 1328 | 1328 | try expectNoErrors(&result); |
|
| 1329 | 1329 |
| 1342 | 1342 | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 1343 | 1343 | else throw testing::TestError::Failed; |
|
| 1344 | 1344 | try testing::expect(payloadValType == super::Type::I32); |
|
| 1345 | 1345 | } |
|
| 1346 | 1346 | ||
| 1347 | - | @test fn testResolveMatchUnionPatternNonUnionType() throws (testing::TestError) { |
|
| 1347 | + | @test unsafe fn testResolveMatchUnionPatternNonUnionType() throws (testing::TestError) { |
|
| 1348 | 1348 | let mut a = testResolver(); |
|
| 1349 | 1349 | let program = "union Opt { Some, None } fn f(value: Opt) { match value { case true => {} } }"; |
|
| 1350 | 1350 | let result = try resolveProgramStr(&mut a, program); |
|
| 1351 | 1351 | let err = try expectError(&result); |
|
| 1352 | 1352 | let optionTy = try getTypeInScopeOf(&a, result.root, "Opt"); |
|
| 1353 | 1353 | try expectTypeMismatch(err, super::Type::Nominal(optionTy), super::Type::Bool); |
|
| 1354 | 1354 | } |
|
| 1355 | 1355 | ||
| 1356 | - | @test fn testResolveMatchGuardForms() throws (testing::TestError) { |
|
| 1356 | + | @test unsafe fn testResolveMatchGuardForms() throws (testing::TestError) { |
|
| 1357 | 1357 | let mut a = testResolver(); |
|
| 1358 | 1358 | let program = "fn first(value: i32) { match value { case _ if true => {}, else => {} } }"; |
|
| 1359 | 1359 | let result = try resolveProgramStr(&mut a, program); |
|
| 1360 | 1360 | try expectNoErrors(&result); |
|
| 1361 | 1361 | } |
|
| 1362 | 1362 | ||
| 1363 | 1363 | /// Test that a binding prong binds the subject to the identifier. |
|
| 1364 | - | @test fn testResolveMatchBindingProng() throws (testing::TestError) { |
|
| 1364 | + | @test unsafe fn testResolveMatchBindingProng() throws (testing::TestError) { |
|
| 1365 | 1365 | let mut a = testResolver(); |
|
| 1366 | 1366 | let program = "fn f(value: i32) -> i32 { match value { x => return x } }"; |
|
| 1367 | 1367 | let result = try resolveProgramStr(&mut a, program); |
|
| 1368 | 1368 | try expectNoErrors(&result); |
|
| 1369 | 1369 | } |
|
| 1370 | 1370 | ||
| 1371 | 1371 | /// Test that a binding prong with guard can use the bound variable. |
|
| 1372 | - | @test fn testResolveMatchBindingProngGuard() throws (testing::TestError) { |
|
| 1372 | + | @test unsafe fn testResolveMatchBindingProngGuard() throws (testing::TestError) { |
|
| 1373 | 1373 | let mut a = testResolver(); |
|
| 1374 | 1374 | let program = "fn f(value: i32) -> i32 { match value { x if x > 0 => return x, _ => return 0 } }"; |
|
| 1375 | 1375 | let result = try resolveProgramStr(&mut a, program); |
|
| 1376 | 1376 | try expectNoErrors(&result); |
|
| 1377 | 1377 | } |
|
| 1378 | 1378 | ||
| 1379 | 1379 | /// Test that a binding prong covers all union variants for exhaustiveness. |
|
| 1380 | - | @test fn testResolveMatchBindingProngExhaustive() throws (testing::TestError) { |
|
| 1380 | + | @test unsafe fn testResolveMatchBindingProngExhaustive() throws (testing::TestError) { |
|
| 1381 | 1381 | let mut a = testResolver(); |
|
| 1382 | 1382 | let program = "union U { A, B, C } fn f(u: U) -> i32 { match u { x => return 0 } }"; |
|
| 1383 | 1383 | let result = try resolveProgramStr(&mut a, program); |
|
| 1384 | 1384 | try expectNoErrors(&result); |
|
| 1385 | 1385 | } |
|
| 1386 | 1386 | ||
| 1387 | 1387 | /// Test that `case x =>` fails if `x` is not in scope, since bare identifiers |
|
| 1388 | 1388 | /// in case patterns are values to compare against, not bindings. |
|
| 1389 | - | @test fn testResolveMatchCaseUndefinedIdent() throws (testing::TestError) { |
|
| 1389 | + | @test unsafe fn testResolveMatchCaseUndefinedIdent() throws (testing::TestError) { |
|
| 1390 | 1390 | let mut a = testResolver(); |
|
| 1391 | 1391 | let program = "fn f(n: i32) -> i32 { match n { case x => return 0 } }"; |
|
| 1392 | 1392 | let result = try resolveProgramStr(&mut a, program); |
|
| 1393 | 1393 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 1394 | 1394 | } |
|
| 1395 | 1395 | ||
| 1396 | 1396 | /// Test matching on optionals: exhaustiveness and type unwrapping. |
|
| 1397 | - | @test fn testResolveMatchOptional() throws (testing::TestError) { |
|
| 1397 | + | @test unsafe fn testResolveMatchOptional() throws (testing::TestError) { |
|
| 1398 | 1398 | { |
|
| 1399 | 1399 | // Exhaustive: binding + nil case. |
|
| 1400 | 1400 | let mut a = testResolver(); |
|
| 1401 | 1401 | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {} } }"; |
|
| 1402 | 1402 | let result = try resolveProgramStr(&mut a, program); |
| 1427 | 1427 | try expectNoErrors(&result); |
|
| 1428 | 1428 | } |
|
| 1429 | 1429 | } |
|
| 1430 | 1430 | ||
| 1431 | 1431 | /// Test that match on non-union types requires exhaustiveness. |
|
| 1432 | - | @test fn testResolveMatchGenericExhaustive() throws (testing::TestError) { |
|
| 1432 | + | @test unsafe fn testResolveMatchGenericExhaustive() throws (testing::TestError) { |
|
| 1433 | 1433 | { |
|
| 1434 | 1434 | // Match on i32 without catch-all should error. |
|
| 1435 | 1435 | let mut a = testResolver(); |
|
| 1436 | 1436 | let program = "fn f(x: i32) { match x { case 1 => {} } }"; |
|
| 1437 | 1437 | let result = try resolveProgramStr(&mut a, program); |
| 1456 | 1456 | try expectNoErrors(&result); |
|
| 1457 | 1457 | } |
|
| 1458 | 1458 | } |
|
| 1459 | 1459 | ||
| 1460 | 1460 | /// Test that match on bool requires both true and false cases. |
|
| 1461 | - | @test fn testResolveMatchBoolExhaustive() throws (testing::TestError) { |
|
| 1461 | + | @test unsafe fn testResolveMatchBoolExhaustive() throws (testing::TestError) { |
|
| 1462 | 1462 | { |
|
| 1463 | 1463 | // Match on bool with both cases is fine. |
|
| 1464 | 1464 | let mut a = testResolver(); |
|
| 1465 | 1465 | let program = "fn f(x: bool) { match x { case true => {}, case false => {} } }"; |
|
| 1466 | 1466 | let result = try resolveProgramStr(&mut a, program); |
| 1490 | 1490 | let result = try resolveProgramStr(&mut a, program); |
|
| 1491 | 1491 | try expectNoErrors(&result); |
|
| 1492 | 1492 | } |
|
| 1493 | 1493 | } |
|
| 1494 | 1494 | ||
| 1495 | - | @test fn testResolveBreakRequiresLoop() throws (testing::TestError) { |
|
| 1495 | + | @test unsafe fn testResolveBreakRequiresLoop() throws (testing::TestError) { |
|
| 1496 | 1496 | { |
|
| 1497 | 1497 | let mut a = testResolver(); |
|
| 1498 | 1498 | let result = try resolveProgramStr(&mut a, "break;"); |
|
| 1499 | 1499 | try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl); |
|
| 1500 | 1500 | } { |
| 1502 | 1502 | let result = try resolveProgramStr(&mut a, "loop { break }"); |
|
| 1503 | 1503 | try expectNoErrors(&result); |
|
| 1504 | 1504 | } |
|
| 1505 | 1505 | } |
|
| 1506 | 1506 | ||
| 1507 | - | @test fn testResolveContinueRequiresLoop() throws (testing::TestError) { |
|
| 1507 | + | @test unsafe fn testResolveContinueRequiresLoop() throws (testing::TestError) { |
|
| 1508 | 1508 | { |
|
| 1509 | 1509 | let mut a = testResolver(); |
|
| 1510 | 1510 | let result = try resolveProgramStr(&mut a, "continue;"); |
|
| 1511 | 1511 | try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl); |
|
| 1512 | 1512 | } { |
| 1514 | 1514 | let result = try resolveProgramStr(&mut a, "while true { continue }"); |
|
| 1515 | 1515 | try expectNoErrors(&result); |
|
| 1516 | 1516 | } |
|
| 1517 | 1517 | } |
|
| 1518 | 1518 | ||
| 1519 | - | @test fn testResolveFnTypeVoidNoParams() throws (testing::TestError) { |
|
| 1519 | + | @test unsafe fn testResolveFnTypeVoidNoParams() throws (testing::TestError) { |
|
| 1520 | 1520 | let mut a = testResolver(); |
|
| 1521 | 1521 | let result = try resolveProgramStr(&mut a, "fn f() {} f();"); |
|
| 1522 | 1522 | try expectNoErrors(&result); |
|
| 1523 | 1523 | ||
| 1524 | 1524 | let blockNode = result.root; |
| 1544 | 1544 | else throw testing::TestError::Failed; |
|
| 1545 | 1545 | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1546 | 1546 | } |
|
| 1547 | 1547 | } |
|
| 1548 | 1548 | ||
| 1549 | - | @test fn testResolveFnTypeReturnsValue() throws (testing::TestError) { |
|
| 1549 | + | @test unsafe fn testResolveFnTypeReturnsValue() throws (testing::TestError) { |
|
| 1550 | 1550 | let mut a = testResolver(); |
|
| 1551 | 1551 | let program = "fn f() -> i32 { return 1; } f();"; |
|
| 1552 | 1552 | let result = try resolveProgramStr(&mut a, program); |
|
| 1553 | 1553 | try expectNoErrors(&result); |
|
| 1554 | 1554 |
| 1575 | 1575 | else throw testing::TestError::Failed; |
|
| 1576 | 1576 | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1577 | 1577 | } |
|
| 1578 | 1578 | } |
|
| 1579 | 1579 | ||
| 1580 | - | @test fn testResolveFnTypeSingleParam() throws (testing::TestError) { |
|
| 1580 | + | @test unsafe fn testResolveFnTypeSingleParam() throws (testing::TestError) { |
|
| 1581 | 1581 | let mut a = testResolver(); |
|
| 1582 | 1582 | let program = "fn f(x: i8) {} let x: i8 = 1; f(x);"; |
|
| 1583 | 1583 | let result = try resolveProgramStr(&mut a, program); |
|
| 1584 | 1584 | try expectNoErrors(&result); |
|
| 1585 | 1585 |
| 1613 | 1613 | else throw testing::TestError::Failed; |
|
| 1614 | 1614 | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1615 | 1615 | } |
|
| 1616 | 1616 | } |
|
| 1617 | 1617 | ||
| 1618 | - | @test fn testResolveFnTypeMultipleParams() throws (testing::TestError) { |
|
| 1618 | + | @test unsafe fn testResolveFnTypeMultipleParams() throws (testing::TestError) { |
|
| 1619 | 1619 | let mut a = testResolver(); |
|
| 1620 | 1620 | let program = "fn f(x: i8, y: i32) {} let x: i8 = 1; let y: i32 = 2; f(x, y);"; |
|
| 1621 | 1621 | let result = try resolveProgramStr(&mut a, program); |
|
| 1622 | 1622 | try expectNoErrors(&result); |
|
| 1623 | 1623 |
| 1654 | 1654 | else throw testing::TestError::Failed; |
|
| 1655 | 1655 | try expectType(&a, callExpr, *fnTy.returnType); |
|
| 1656 | 1656 | } |
|
| 1657 | 1657 | } |
|
| 1658 | 1658 | ||
| 1659 | - | @test fn testResolveFnRecursiveCall() throws (testing::TestError) { |
|
| 1659 | + | @test unsafe fn testResolveFnRecursiveCall() throws (testing::TestError) { |
|
| 1660 | 1660 | let mut a = testResolver(); |
|
| 1661 | 1661 | let program = "fn flip(b: bool) -> bool { if b { return false; } return flip(false); }"; |
|
| 1662 | 1662 | let result = try resolveProgramStr(&mut a, program); |
|
| 1663 | 1663 | try expectNoErrors(&result); |
|
| 1664 | 1664 |
| 1676 | 1676 | try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool); |
|
| 1677 | 1677 | try testing::expect(*fnTy.returnType == super::Type::Bool); |
|
| 1678 | 1678 | } |
|
| 1679 | 1679 | } |
|
| 1680 | 1680 | ||
| 1681 | - | @test fn testResolveFnCallMissingArgument() throws (testing::TestError) { |
|
| 1681 | + | @test unsafe fn testResolveFnCallMissingArgument() throws (testing::TestError) { |
|
| 1682 | 1682 | let mut a = testResolver(); |
|
| 1683 | 1683 | let program = "fn f(x: i8) {} f();"; |
|
| 1684 | 1684 | let result = try resolveProgramStr(&mut a, program); |
|
| 1685 | 1685 | // Expect an error when a required parameter is omitted. |
|
| 1686 | 1686 | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
|
| 1687 | 1687 | expected: 1, |
|
| 1688 | 1688 | actual: 0, |
|
| 1689 | 1689 | })); |
|
| 1690 | 1690 | } |
|
| 1691 | 1691 | ||
| 1692 | - | @test fn testResolveFnCallExtraArgument() throws (testing::TestError) { |
|
| 1692 | + | @test unsafe fn testResolveFnCallExtraArgument() throws (testing::TestError) { |
|
| 1693 | 1693 | let mut a = testResolver(); |
|
| 1694 | 1694 | let program = "fn f() {} f(1);"; |
|
| 1695 | 1695 | let result = try resolveProgramStr(&mut a, program); |
|
| 1696 | 1696 | // Passing more arguments than declared should fail. |
|
| 1697 | 1697 | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
|
| 1698 | 1698 | expected: 0, |
|
| 1699 | 1699 | actual: 1, |
|
| 1700 | 1700 | })); |
|
| 1701 | 1701 | } |
|
| 1702 | 1702 | ||
| 1703 | - | @test fn testResolveFnCallArgumentTypeMismatch() throws (testing::TestError) { |
|
| 1703 | + | @test unsafe fn testResolveFnCallArgumentTypeMismatch() throws (testing::TestError) { |
|
| 1704 | 1704 | let mut a = testResolver(); |
|
| 1705 | 1705 | let program = "fn f(x: i8) {} f(true);"; |
|
| 1706 | 1706 | let result = try resolveProgramStr(&mut a, program); |
|
| 1707 | 1707 | let err = try expectError(&result); |
|
| 1708 | 1708 | // The argument type (bool) should not match the parameter type (i8). |
|
| 1709 | 1709 | try expectTypeMismatch(err, super::Type::I8, super::Type::Bool); |
|
| 1710 | 1710 | } |
|
| 1711 | 1711 | ||
| 1712 | - | @test fn testResolveFnReturnTypeMismatch() throws (testing::TestError) { |
|
| 1712 | + | @test unsafe fn testResolveFnReturnTypeMismatch() throws (testing::TestError) { |
|
| 1713 | 1713 | let mut a = testResolver(); |
|
| 1714 | 1714 | let program = "fn f() -> i32 { return true; }"; |
|
| 1715 | 1715 | let result = try resolveProgramStr(&mut a, program); |
|
| 1716 | 1716 | let err = try expectError(&result); |
|
| 1717 | 1717 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
|
| 1718 | 1718 | } |
|
| 1719 | 1719 | ||
| 1720 | - | @test fn testResolveFnReturnVoid() throws (testing::TestError) { |
|
| 1720 | + | @test unsafe fn testResolveFnReturnVoid() throws (testing::TestError) { |
|
| 1721 | 1721 | { |
|
| 1722 | 1722 | let mut a = testResolver(); |
|
| 1723 | 1723 | let result = try resolveProgramStr(&mut a, "fn f() { return; }"); |
|
| 1724 | 1724 | try expectNoErrors(&result); |
|
| 1725 | 1725 | } { |
| 1728 | 1728 | let err = try expectError(&result); |
|
| 1729 | 1729 | try expectTypeMismatch(err, super::Type::I32, super::Type::Void); |
|
| 1730 | 1730 | } |
|
| 1731 | 1731 | } |
|
| 1732 | 1732 | ||
| 1733 | - | @test fn testResolveFnMissingReturn() throws (testing::TestError) { |
|
| 1733 | + | @test unsafe fn testResolveFnMissingReturn() throws (testing::TestError) { |
|
| 1734 | 1734 | { |
|
| 1735 | 1735 | let mut a = testResolver(); |
|
| 1736 | 1736 | let result = try resolveProgramStr(&mut a, "fn f() -> i32 {}"); |
|
| 1737 | 1737 | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 1738 | 1738 | } { |
| 1741 | 1741 | let result = try resolveProgramStr(&mut a, program); |
|
| 1742 | 1742 | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 1743 | 1743 | } |
|
| 1744 | 1744 | } |
|
| 1745 | 1745 | ||
| 1746 | - | @test fn testResolveFnAllPathsReturn() throws (testing::TestError) { |
|
| 1746 | + | @test unsafe fn testResolveFnAllPathsReturn() throws (testing::TestError) { |
|
| 1747 | 1747 | let mut a = testResolver(); |
|
| 1748 | 1748 | let program = "fn h(flag: bool) -> i32 { if flag { return 1; } else { return 2; } }"; |
|
| 1749 | 1749 | let result = try resolveProgramStr(&mut a, program); |
|
| 1750 | 1750 | try expectNoErrors(&result); |
|
| 1751 | 1751 | } |
|
| 1752 | 1752 | ||
| 1753 | 1753 | /// Test that match statements with returns in all branches don't require a |
|
| 1754 | 1754 | /// return at the end of the function. |
|
| 1755 | - | @test fn testResolveFnMatchAllPathsReturn() throws (testing::TestError) { |
|
| 1755 | + | @test unsafe fn testResolveFnMatchAllPathsReturn() throws (testing::TestError) { |
|
| 1756 | 1756 | { |
|
| 1757 | 1757 | // Union match with all variants returning. |
|
| 1758 | 1758 | let mut a = testResolver(); |
|
| 1759 | 1759 | let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => return 2 } }"; |
|
| 1760 | 1760 | let result = try resolveProgramStr(&mut a, program); |
| 1772 | 1772 | let result = try resolveProgramStr(&mut a, program); |
|
| 1773 | 1773 | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
|
| 1774 | 1774 | } |
|
| 1775 | 1775 | } |
|
| 1776 | 1776 | ||
| 1777 | - | @test fn testResolveAssign() throws (testing::TestError) { |
|
| 1777 | + | @test unsafe fn testResolveAssign() throws (testing::TestError) { |
|
| 1778 | 1778 | { |
|
| 1779 | 1779 | let mut a = testResolver(); |
|
| 1780 | 1780 | let result = try resolveProgramStr(&mut a, "let mut x: i32 = 0; set x = 1;"); |
|
| 1781 | 1781 | try expectNoErrors(&result); |
|
| 1782 | 1782 | } { |
| 1801 | 1801 | let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = nil;"); |
|
| 1802 | 1802 | try expectNoErrors(&result); |
|
| 1803 | 1803 | } |
|
| 1804 | 1804 | } |
|
| 1805 | 1805 | ||
| 1806 | - | @test fn testResolveAssignSubscript() throws (testing::TestError) { |
|
| 1806 | + | @test unsafe fn testResolveAssignSubscript() throws (testing::TestError) { |
|
| 1807 | 1807 | { |
|
| 1808 | 1808 | let mut a = testResolver(); |
|
| 1809 | 1809 | let program = "let mut xs: [u8; 2] = [0, 1]; set xs[0] = 9;"; |
|
| 1810 | 1810 | let result = try resolveProgramStr(&mut a, program); |
|
| 1811 | 1811 | try expectNoErrors(&result); |
|
| 1812 | 1812 | } |
|
| 1813 | 1813 | { |
|
| 1814 | 1814 | let mut a = testResolver(); |
|
| 1815 | - | let program = "let mut xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;"; |
|
| 1815 | + | let program = "static xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;"; |
|
| 1816 | 1816 | let result = try resolveProgramStr(&mut a, program); |
|
| 1817 | 1817 | try expectNoErrors(&result); |
|
| 1818 | 1818 | } |
|
| 1819 | 1819 | { |
|
| 1820 | 1820 | let mut a = testResolver(); |
|
| 1821 | - | let program = "let mut xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 1821 | + | let program = "static xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 1822 | 1822 | let result = try resolveProgramStr(&mut a, program); |
|
| 1823 | 1823 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1824 | 1824 | } |
|
| 1825 | 1825 | { |
|
| 1826 | 1826 | let mut a = testResolver(); |
| 1828 | 1828 | let result = try resolveProgramStr(&mut a, program); |
|
| 1829 | 1829 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1830 | 1830 | } |
|
| 1831 | 1831 | { |
|
| 1832 | 1832 | let mut a = testResolver(); |
|
| 1833 | - | let program = "let mut xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 1833 | + | let program = "static xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
|
| 1834 | 1834 | let result = try resolveProgramStr(&mut a, program); |
|
| 1835 | 1835 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 1836 | 1836 | } |
|
| 1837 | 1837 | } |
|
| 1838 | 1838 | ||
| 1839 | - | @test fn testResolveAssignIntegerLits() throws (testing::TestError) { |
|
| 1839 | + | @test unsafe fn testResolveAssignIntegerLits() throws (testing::TestError) { |
|
| 1840 | 1840 | try expectAnalyzeOk("let x: i8 = 127;"); |
|
| 1841 | 1841 | try expectAnalyzeOk("let x: i8 = 0x7F;"); |
|
| 1842 | 1842 | try expectAnalyzeOk("let x: i8 = -128;"); |
|
| 1843 | 1843 | try expectAnalyzeOk("let x: u8 = 255;"); |
|
| 1844 | 1844 | try expectAnalyzeOk("let x: u8 = 0b11111111;"); |
| 1873 | 1873 | try expectIntMismatch("let x: i64 = -9223372036854775809;", super::Type::I64); |
|
| 1874 | 1874 | try expectIntMismatch("constant LIMIT: u8 = 512;", super::Type::U8); |
|
| 1875 | 1875 | try expectIntMismatch("constant LIMIT: u8 = -5;", super::Type::U8); |
|
| 1876 | 1876 | } |
|
| 1877 | 1877 | ||
| 1878 | - | @test fn testNilCoercions() throws (testing::TestError) { |
|
| 1878 | + | @test unsafe fn testNilCoercions() throws (testing::TestError) { |
|
| 1879 | 1879 | { |
|
| 1880 | 1880 | let mut a = testResolver(); |
|
| 1881 | 1881 | let result = try resolveBlockStr(&mut a, "let opt: ?i32 = nil;"); |
|
| 1882 | 1882 | try expectNoErrors(&result); |
|
| 1883 | 1883 | } { |
| 1891 | 1891 | let result = try resolveProgramStr(&mut a, program); |
|
| 1892 | 1892 | try expectNoErrors(&result); |
|
| 1893 | 1893 | } |
|
| 1894 | 1894 | } |
|
| 1895 | 1895 | ||
| 1896 | - | @test fn testOptionalComparedWithNil() throws (testing::TestError) { |
|
| 1896 | + | @test unsafe fn testOptionalComparedWithNil() throws (testing::TestError) { |
|
| 1897 | 1897 | let mut a = testResolver(); |
|
| 1898 | 1898 | let program = "let opt: ?i32 = nil; opt == nil; nil == opt; opt == 1; 1 == opt; opt == opt; nil == nil;"; |
|
| 1899 | 1899 | let result = try resolveBlockStr(&mut a, program); |
|
| 1900 | 1900 | try expectNoErrors(&result); |
|
| 1901 | 1901 |
| 1903 | 1903 | let stmt = try getBlockStmt(result.root, i); |
|
| 1904 | 1904 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 1905 | 1905 | } |
|
| 1906 | 1906 | } |
|
| 1907 | 1907 | ||
| 1908 | - | @test fn testResolveRecordLiteralAllFieldsSet() throws (testing::TestError) { |
|
| 1908 | + | @test unsafe fn testResolveRecordLiteralAllFieldsSet() throws (testing::TestError) { |
|
| 1909 | 1909 | let mut a = testResolver(); |
|
| 1910 | 1910 | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, y: 2 };"; |
|
| 1911 | 1911 | let result = try resolveProgramStr(&mut a, program); |
|
| 1912 | 1912 | try expectNoErrors(&result); |
|
| 1913 | 1913 | } |
|
| 1914 | 1914 | ||
| 1915 | - | @test fn testResolveRecordLiteralMissingField() throws (testing::TestError) { |
|
| 1915 | + | @test unsafe fn testResolveRecordLiteralMissingField() throws (testing::TestError) { |
|
| 1916 | 1916 | let mut a = testResolver(); |
|
| 1917 | 1917 | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1 };"; |
|
| 1918 | 1918 | let result = try resolveProgramStr(&mut a, program); |
|
| 1919 | 1919 | try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y")); |
|
| 1920 | 1920 | } |
|
| 1921 | 1921 | ||
| 1922 | - | @test fn testResolveRecordLiteralFieldTypeMismatch() throws (testing::TestError) { |
|
| 1922 | + | @test unsafe fn testResolveRecordLiteralFieldTypeMismatch() throws (testing::TestError) { |
|
| 1923 | 1923 | let mut a = testResolver(); |
|
| 1924 | 1924 | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: true, y: 2 };"; |
|
| 1925 | 1925 | let result = try resolveProgramStr(&mut a, program); |
|
| 1926 | 1926 | let err = try expectError(&result); |
|
| 1927 | 1927 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
| 1930 | 1930 | else throw testing::TestError::Failed; |
|
| 1931 | 1931 | let case ast::NodeValue::Bool(_) = errNode.value |
|
| 1932 | 1932 | else throw testing::TestError::Failed; |
|
| 1933 | 1933 | } |
|
| 1934 | 1934 | ||
| 1935 | - | @test fn testResolveRecordLiteralExtraField() throws (testing::TestError) { |
|
| 1935 | + | @test unsafe fn testResolveRecordLiteralExtraField() throws (testing::TestError) { |
|
| 1936 | 1936 | let mut a = testResolver(); |
|
| 1937 | 1937 | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, z: 3, y: 2 };"; |
|
| 1938 | 1938 | let result = try resolveProgramStr(&mut a, program); |
|
| 1939 | 1939 | let err = try expectError(&result); |
|
| 1940 | 1940 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 1941 | 1941 | else throw testing::TestError::Failed; |
|
| 1942 | 1942 | } |
|
| 1943 | 1943 | ||
| 1944 | 1944 | /// Test that anonymous record literals with labels can be passed to functions expecting named records. |
|
| 1945 | - | @test fn testResolveAnonRecordLabeledToNamedRecord() throws (testing::TestError) { |
|
| 1945 | + | @test unsafe fn testResolveAnonRecordLabeledToNamedRecord() throws (testing::TestError) { |
|
| 1946 | 1946 | let mut a = testResolver(); |
|
| 1947 | 1947 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) -> i32 { return p.x; } foo({ x: 1, y: 2 });"; |
|
| 1948 | 1948 | let result = try resolveProgramStr(&mut a, program); |
|
| 1949 | 1949 | try expectNoErrors(&result); |
|
| 1950 | 1950 | } |
|
| 1951 | 1951 | ||
| 1952 | 1952 | /// Test that anonymous record with wrong field name causes out of order error. |
|
| 1953 | - | @test fn testResolveAnonRecordWrongFieldName() throws (testing::TestError) { |
|
| 1953 | + | @test unsafe fn testResolveAnonRecordWrongFieldName() throws (testing::TestError) { |
|
| 1954 | 1954 | let mut a = testResolver(); |
|
| 1955 | 1955 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, z: 2 });"; |
|
| 1956 | 1956 | let result = try resolveProgramStr(&mut a, program); |
|
| 1957 | 1957 | let err = try expectError(&result); |
|
| 1958 | 1958 | let case super::ErrorKind::RecordFieldOutOfOrder { field: _, prev: _ } = err.kind |
|
| 1959 | 1959 | else throw testing::TestError::Failed; |
|
| 1960 | 1960 | } |
|
| 1961 | 1961 | ||
| 1962 | 1962 | /// Test that anonymous record with wrong field type causes type mismatch. |
|
| 1963 | - | @test fn testResolveAnonRecordWrongFieldType() throws (testing::TestError) { |
|
| 1963 | + | @test unsafe fn testResolveAnonRecordWrongFieldType() throws (testing::TestError) { |
|
| 1964 | 1964 | let mut a = testResolver(); |
|
| 1965 | 1965 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: true, y: 2 });"; |
|
| 1966 | 1966 | let result = try resolveProgramStr(&mut a, program); |
|
| 1967 | 1967 | let err = try expectError(&result); |
|
| 1968 | 1968 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 1969 | 1969 | else throw testing::TestError::Failed; |
|
| 1970 | 1970 | } |
|
| 1971 | 1971 | ||
| 1972 | 1972 | /// Test that anonymous record with missing field causes a missing field error. |
|
| 1973 | - | @test fn testResolveAnonRecordMissingField() throws (testing::TestError) { |
|
| 1973 | + | @test unsafe fn testResolveAnonRecordMissingField() throws (testing::TestError) { |
|
| 1974 | 1974 | let mut a = testResolver(); |
|
| 1975 | 1975 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1 });"; |
|
| 1976 | 1976 | let result = try resolveProgramStr(&mut a, program); |
|
| 1977 | 1977 | try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y")); |
|
| 1978 | 1978 | } |
|
| 1979 | 1979 | ||
| 1980 | 1980 | /// Test that anonymous record with extra field causes a count mismatch error. |
|
| 1981 | - | @test fn testResolveAnonRecordExtraField() throws (testing::TestError) { |
|
| 1981 | + | @test unsafe fn testResolveAnonRecordExtraField() throws (testing::TestError) { |
|
| 1982 | 1982 | let mut a = testResolver(); |
|
| 1983 | 1983 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, y: 2, z: 3 });"; |
|
| 1984 | 1984 | let result = try resolveProgramStr(&mut a, program); |
|
| 1985 | 1985 | let err = try expectError(&result); |
|
| 1986 | 1986 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 1987 | 1987 | else throw testing::TestError::Failed; |
|
| 1988 | 1988 | } |
|
| 1989 | 1989 | ||
| 1990 | 1990 | /// Test that anonymous record fields can be coerced (e.g., i32 to optional). |
|
| 1991 | - | @test fn testResolveAnonRecordFieldCoercion() throws (testing::TestError) { |
|
| 1991 | + | @test unsafe fn testResolveAnonRecordFieldCoercion() throws (testing::TestError) { |
|
| 1992 | 1992 | let mut a = testResolver(); |
|
| 1993 | 1993 | let program = "record Opt { x: ?i32 } fn foo(p: Opt) {} foo({ x: 42 });"; |
|
| 1994 | 1994 | let result = try resolveProgramStr(&mut a, program); |
|
| 1995 | 1995 | try expectNoErrors(&result); |
|
| 1996 | 1996 | } |
|
| 1997 | 1997 | ||
| 1998 | 1998 | /// Test that arrays of anonymous records with labeled fields are allowed. |
|
| 1999 | - | @test fn testResolveAnonRecordArray() throws (testing::TestError) { |
|
| 1999 | + | @test unsafe fn testResolveAnonRecordArray() throws (testing::TestError) { |
|
| 2000 | 2000 | let mut a = testResolver(); |
|
| 2001 | 2001 | let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4 }];"; |
|
| 2002 | 2002 | let result = try resolveProgramStr(&mut a, program); |
|
| 2003 | 2003 | try expectNoErrors(&result); |
|
| 2004 | 2004 | } |
|
| 2005 | 2005 | ||
| 2006 | 2006 | /// Test that arrays of anonymous records with extra fields cause count mismatch. |
|
| 2007 | - | @test fn testResolveAnonRecordArrayMismatch() throws (testing::TestError) { |
|
| 2007 | + | @test unsafe fn testResolveAnonRecordArrayMismatch() throws (testing::TestError) { |
|
| 2008 | 2008 | let mut a = testResolver(); |
|
| 2009 | 2009 | let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4, z: 5 }];"; |
|
| 2010 | 2010 | let result = try resolveProgramStr(&mut a, program); |
|
| 2011 | 2011 | let err = try expectError(&result); |
|
| 2012 | 2012 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 2013 | 2013 | else throw testing::TestError::Failed; |
|
| 2014 | 2014 | } |
|
| 2015 | 2015 | ||
| 2016 | 2016 | /// Test that unlabeled record declarations are analyzed correctly. |
|
| 2017 | - | @test fn testResolveUnlabeledRecordDecl() throws (testing::TestError) { |
|
| 2017 | + | @test unsafe fn testResolveUnlabeledRecordDecl() throws (testing::TestError) { |
|
| 2018 | 2018 | let mut a = testResolver(); |
|
| 2019 | 2019 | let program = "record R(i32, bool);"; |
|
| 2020 | 2020 | let result = try resolveProgramStr(&mut a, program); |
|
| 2021 | 2021 | try expectNoErrors(&result); |
|
| 2022 | 2022 |
| 2028 | 2028 | try testing::expect(recordType.fields.len == 2); |
|
| 2029 | 2029 | try testing::expect(recordType.fields[0].name == nil); |
|
| 2030 | 2030 | try testing::expect(recordType.fields[1].name == nil); |
|
| 2031 | 2031 | } |
|
| 2032 | 2032 | ||
| 2033 | - | @test fn testResolveLabeledRecordDecl() throws (testing::TestError) { |
|
| 2033 | + | @test unsafe fn testResolveLabeledRecordDecl() throws (testing::TestError) { |
|
| 2034 | 2034 | let mut a = testResolver(); |
|
| 2035 | 2035 | let program = "record R { x: i32, y: i32 }"; |
|
| 2036 | 2036 | let result = try resolveProgramStr(&mut a, program); |
|
| 2037 | 2037 | try expectNoErrors(&result); |
|
| 2038 | 2038 |
| 2043 | 2043 | try testing::expect(recordType.fields.len == 2); |
|
| 2044 | 2044 | try testing::expect(recordType.fields[0].name <> nil); |
|
| 2045 | 2045 | try testing::expect(recordType.fields[1].name <> nil); |
|
| 2046 | 2046 | } |
|
| 2047 | 2047 | ||
| 2048 | - | @test fn testResolveRecordFieldAccessValid() throws (testing::TestError) { |
|
| 2048 | + | @test unsafe fn testResolveRecordFieldAccessValid() throws (testing::TestError) { |
|
| 2049 | 2049 | let mut a = testResolver(); |
|
| 2050 | 2050 | let program = "record Pt { x: i32, y: u8 } let p = Pt { x: 1, y: 2 }; p.y;"; |
|
| 2051 | 2051 | let result = try resolveProgramStr(&mut a, program); |
|
| 2052 | 2052 | try expectNoErrors(&result); |
|
| 2053 | 2053 | ||
| 2054 | 2054 | let fieldStmt = try getBlockStmt(result.root, 2); |
|
| 2055 | 2055 | try expectExprStmtType(&a, fieldStmt, super::Type::U8); |
|
| 2056 | 2056 | } |
|
| 2057 | 2057 | ||
| 2058 | - | @test fn testResolveRecordFieldAccessUnknownField() throws (testing::TestError) { |
|
| 2058 | + | @test unsafe fn testResolveRecordFieldAccessUnknownField() throws (testing::TestError) { |
|
| 2059 | 2059 | let mut a = testResolver(); |
|
| 2060 | 2060 | let program = "record Pt { x: i32 } let p = Pt { x: 1 }; p.y;"; |
|
| 2061 | 2061 | let result = try resolveProgramStr(&mut a, program); |
|
| 2062 | 2062 | try expectErrorKind(&result, super::ErrorKind::RecordFieldUnknown("y")); |
|
| 2063 | 2063 | } |
|
| 2064 | 2064 | ||
| 2065 | - | @test fn testResolveRecordFieldAccessOnFunctionReturn() throws (testing::TestError) { |
|
| 2065 | + | @test unsafe fn testResolveRecordFieldAccessOnFunctionReturn() throws (testing::TestError) { |
|
| 2066 | 2066 | let mut a = testResolver(); |
|
| 2067 | 2067 | let program = "record Pt { x: i32, y: i32 } fn make() -> Pt { return Pt { x: 5, y: 10 }; } make().x;"; |
|
| 2068 | 2068 | let result = try resolveProgramStr(&mut a, program); |
|
| 2069 | 2069 | try expectNoErrors(&result); |
|
| 2070 | 2070 | ||
| 2071 | 2071 | let stmt = try getBlockStmt(result.root, 2); |
|
| 2072 | 2072 | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2073 | 2073 | } |
|
| 2074 | 2074 | ||
| 2075 | - | @test fn testResolveRecordFieldAccessChained() throws (testing::TestError) { |
|
| 2075 | + | @test unsafe fn testResolveRecordFieldAccessChained() throws (testing::TestError) { |
|
| 2076 | 2076 | let mut a = testResolver(); |
|
| 2077 | 2077 | let program = "record C { value: i32 } record B { c: C } record A { b: B } let a = A { b: B { c: C { value: 100 } } }; a.b.c.value;"; |
|
| 2078 | 2078 | let result = try resolveProgramStr(&mut a, program); |
|
| 2079 | 2079 | try expectNoErrors(&result); |
|
| 2080 | 2080 | ||
| 2081 | 2081 | let stmt = try getBlockStmt(result.root, 4); |
|
| 2082 | 2082 | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2083 | 2083 | } |
|
| 2084 | 2084 | ||
| 2085 | - | @test fn testResolveRecordFieldAccessOnInteger() throws (testing::TestError) { |
|
| 2085 | + | @test unsafe fn testResolveRecordFieldAccessOnInteger() throws (testing::TestError) { |
|
| 2086 | 2086 | let mut a = testResolver(); |
|
| 2087 | 2087 | let program = "let x: i32 = 42; x.field;"; |
|
| 2088 | 2088 | let result = try resolveBlockStr(&mut a, program); |
|
| 2089 | 2089 | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2090 | 2090 | } |
|
| 2091 | 2091 | ||
| 2092 | - | @test fn testResolveRecordFieldAccessOnArray() throws (testing::TestError) { |
|
| 2092 | + | @test unsafe fn testResolveRecordFieldAccessOnArray() throws (testing::TestError) { |
|
| 2093 | 2093 | let mut a = testResolver(); |
|
| 2094 | 2094 | let program = "let arr: [i32; 3] = [1, 2, 3]; arr.field;"; |
|
| 2095 | 2095 | let result = try resolveProgramStr(&mut a, program); |
|
| 2096 | 2096 | try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field")); |
|
| 2097 | 2097 | } |
|
| 2098 | 2098 | ||
| 2099 | - | @test fn testResolveRecordFieldAccessOnBool() throws (testing::TestError) { |
|
| 2099 | + | @test unsafe fn testResolveRecordFieldAccessOnBool() throws (testing::TestError) { |
|
| 2100 | 2100 | let mut a = testResolver(); |
|
| 2101 | 2101 | let program = "let b: bool = true; b.field;"; |
|
| 2102 | 2102 | let result = try resolveProgramStr(&mut a, program); |
|
| 2103 | 2103 | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2104 | 2104 | } |
|
| 2105 | 2105 | ||
| 2106 | - | @test fn testResolveRecordFieldAccessOnOptional() throws (testing::TestError) { |
|
| 2106 | + | @test unsafe fn testResolveRecordFieldAccessOnOptional() throws (testing::TestError) { |
|
| 2107 | 2107 | let mut a = testResolver(); |
|
| 2108 | 2108 | let program = "record Pt { x: i32 } let opt: ?Pt = Pt { x: 5 }; opt.x;"; |
|
| 2109 | 2109 | let result = try resolveProgramStr(&mut a, program); |
|
| 2110 | 2110 | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
|
| 2111 | 2111 | } |
|
| 2112 | 2112 | ||
| 2113 | 2113 | /// Records may reference themselves through pointers without causing resolution errors. |
|
| 2114 | - | @test fn testResolveRecordSelfReferentialPointer() throws (testing::TestError) { |
|
| 2114 | + | @test unsafe fn testResolveRecordSelfReferentialPointer() throws (testing::TestError) { |
|
| 2115 | 2115 | let mut a = testResolver(); |
|
| 2116 | 2116 | let program = "record A { next: *A }"; |
|
| 2117 | 2117 | let result = try resolveProgramStr(&mut a, program); |
|
| 2118 | 2118 | try expectNoErrors(&result); |
|
| 2119 | 2119 | } |
|
| 2120 | 2120 | ||
| 2121 | 2121 | /// Mutually recursive records should resolve without infinite loops. |
|
| 2122 | - | @test fn testResolveRecordMutuallyRecursive() throws (testing::TestError) { |
|
| 2122 | + | @test unsafe fn testResolveRecordMutuallyRecursive() throws (testing::TestError) { |
|
| 2123 | 2123 | let mut a = testResolver(); |
|
| 2124 | 2124 | let program = "record A { b: *B } record B { a: *A }"; |
|
| 2125 | 2125 | let result = try resolveProgramStr(&mut a, program); |
|
| 2126 | 2126 | try expectNoErrors(&result); |
|
| 2127 | 2127 | } |
|
| 2128 | 2128 | ||
| 2129 | 2129 | /// Unions may reference themselves through pointers without causing resolution errors. |
|
| 2130 | - | @test fn testResolveUnionSelfReferentialPointerAllowed() throws (testing::TestError) { |
|
| 2130 | + | @test unsafe fn testResolveUnionSelfReferentialPointerAllowed() throws (testing::TestError) { |
|
| 2131 | 2131 | let mut a = testResolver(); |
|
| 2132 | 2132 | let program = "union List { Cons(*List), Nil }"; |
|
| 2133 | 2133 | let result = try resolveProgramStr(&mut a, program); |
|
| 2134 | 2134 | try expectNoErrors(&result); |
|
| 2135 | 2135 | } |
|
| 2136 | 2136 | ||
| 2137 | 2137 | /// Mutually recursive unions should resolve without infinite loops. |
|
| 2138 | - | @test fn testResolveUnionMutuallyRecursive() throws (testing::TestError) { |
|
| 2138 | + | @test unsafe fn testResolveUnionMutuallyRecursive() throws (testing::TestError) { |
|
| 2139 | 2139 | let mut a = testResolver(); |
|
| 2140 | 2140 | let program = "union A { HasB(*B), None } union B { HasA(*A), None }"; |
|
| 2141 | 2141 | let result = try resolveProgramStr(&mut a, program); |
|
| 2142 | 2142 | try expectNoErrors(&result); |
|
| 2143 | 2143 | } |
|
| 2144 | 2144 | ||
| 2145 | 2145 | /// Unions with record payloads containing slice references to self should resolve. |
|
| 2146 | 2146 | /// This matches the pattern in sexpr.rad: `List { tail: *[Expr] }`. |
|
| 2147 | - | @test fn testResolveUnionRecordPayloadWithSliceSelfRef() throws (testing::TestError) { |
|
| 2147 | + | @test unsafe fn testResolveUnionRecordPayloadWithSliceSelfRef() throws (testing::TestError) { |
|
| 2148 | 2148 | let mut a = testResolver(); |
|
| 2149 | 2149 | let program = "union Expr { Null, List { head: *[u8], tail: *[Expr] } }"; |
|
| 2150 | 2150 | let result = try resolveProgramStr(&mut a, program); |
|
| 2151 | 2151 | try expectNoErrors(&result); |
|
| 2152 | 2152 | } |
|
| 2153 | 2153 | ||
| 2154 | - | @test fn testUndefinedCoercions() throws (testing::TestError) { |
|
| 2154 | + | @test unsafe fn testUndefinedCoercions() throws (testing::TestError) { |
|
| 2155 | 2155 | { |
|
| 2156 | 2156 | let mut a = testResolver(); |
|
| 2157 | 2157 | let result = try resolveBlockStr(&mut a, "let count: i32 = undefined;"); |
|
| 2158 | 2158 | try expectNoErrors(&result); |
|
| 2159 | 2159 | } { |
| 2172 | 2172 | let result = try resolveProgramStr(&mut a, program); |
|
| 2173 | 2173 | try expectNoErrors(&result); |
|
| 2174 | 2174 | } |
|
| 2175 | 2175 | } |
|
| 2176 | 2176 | ||
| 2177 | - | @test fn testResolveBlockVoid() throws (testing::TestError) { |
|
| 2177 | + | @test unsafe fn testResolveBlockVoid() throws (testing::TestError) { |
|
| 2178 | 2178 | let mut a = testResolver(); |
|
| 2179 | 2179 | let result = try resolveProgramStr(&mut a, "{ 42; }"); |
|
| 2180 | 2180 | try expectNoErrors(&result); |
|
| 2181 | 2181 | ||
| 2182 | 2182 | let block = try getBlockStmt(result.root, 0); |
|
| 2183 | 2183 | try expectType(&a, block, super::Type::Void); |
|
| 2184 | 2184 | } |
|
| 2185 | 2185 | ||
| 2186 | - | @test fn testResolveBlockNever() throws (testing::TestError) { |
|
| 2186 | + | @test unsafe fn testResolveBlockNever() throws (testing::TestError) { |
|
| 2187 | 2187 | let mut a = testResolver(); |
|
| 2188 | 2188 | let result = try resolveProgramStr(&mut a, "{ panic; }"); |
|
| 2189 | 2189 | try expectNoErrors(&result); |
|
| 2190 | 2190 | ||
| 2191 | 2191 | let block = try getBlockStmt(result.root, 0); |
|
| 2192 | 2192 | try expectType(&a, block, super::Type::Never); |
|
| 2193 | 2193 | } |
|
| 2194 | 2194 | ||
| 2195 | - | @test fn testResolveIfAllBranchesNever() throws (testing::TestError) { |
|
| 2195 | + | @test unsafe fn testResolveIfAllBranchesNever() throws (testing::TestError) { |
|
| 2196 | 2196 | let mut a = testResolver(); |
|
| 2197 | 2197 | let program = "if true { panic; } else { panic; }"; |
|
| 2198 | 2198 | let result = try resolveProgramStr(&mut a, program); |
|
| 2199 | 2199 | try expectNoErrors(&result); |
|
| 2200 | 2200 | ||
| 2201 | 2201 | let stmt = try getBlockStmt(result.root, 0); |
|
| 2202 | 2202 | try expectType(&a, stmt, super::Type::Never); |
|
| 2203 | 2203 | } |
|
| 2204 | 2204 | ||
| 2205 | - | @test fn testResolveIfMixedBranchesNotNever() throws (testing::TestError) { |
|
| 2205 | + | @test unsafe fn testResolveIfMixedBranchesNotNever() throws (testing::TestError) { |
|
| 2206 | 2206 | let mut a = testResolver(); |
|
| 2207 | 2207 | let program = "if true { panic; } else {}"; |
|
| 2208 | 2208 | let result = try resolveProgramStr(&mut a, program); |
|
| 2209 | 2209 | try expectNoErrors(&result); |
|
| 2210 | 2210 | ||
| 2211 | 2211 | let stmt = try getBlockStmt(result.root, 0); |
|
| 2212 | 2212 | try expectType(&a, stmt, super::Type::Void); |
|
| 2213 | 2213 | } |
|
| 2214 | 2214 | ||
| 2215 | - | @test fn testResolveLetElse() throws (testing::TestError) { |
|
| 2215 | + | @test unsafe fn testResolveLetElse() throws (testing::TestError) { |
|
| 2216 | 2216 | let mut a = testResolver(); |
|
| 2217 | 2217 | let program = "let opt: ?i32 = 42; let value = opt else panic; value;"; |
|
| 2218 | 2218 | let result = try resolveProgramStr(&mut a, program); |
|
| 2219 | 2219 | try expectNoErrors(&result); |
|
| 2220 | 2220 |
| 2235 | 2235 | } |
|
| 2236 | 2236 | // The let-else statement itself should be typed as void. |
|
| 2237 | 2237 | try expectType(&a, letElseNode, super::Type::Void); |
|
| 2238 | 2238 | } |
|
| 2239 | 2239 | ||
| 2240 | - | @test fn testResolveLetElseDefaultValue() throws (testing::TestError) { |
|
| 2240 | + | @test unsafe fn testResolveLetElseDefaultValue() throws (testing::TestError) { |
|
| 2241 | 2241 | let mut a = testResolver(); |
|
| 2242 | 2242 | let program = "let opt: ?i32 = nil; let value = opt else 42; value;"; |
|
| 2243 | 2243 | let result = try resolveProgramStr(&mut a, program); |
|
| 2244 | 2244 | try expectNoErrors(&result); |
|
| 2245 | 2245 | } |
|
| 2246 | 2246 | ||
| 2247 | - | @test fn testResolveLetElseRequiresDivergentElse() throws (testing::TestError) { |
|
| 2247 | + | @test unsafe fn testResolveLetElseRequiresDivergentElse() throws (testing::TestError) { |
|
| 2248 | 2248 | let mut a = testResolver(); |
|
| 2249 | 2249 | let program = "let opt: ?i32 = nil; let value = opt else {}; value;"; |
|
| 2250 | 2250 | let result = try resolveProgramStr(&mut a, program); |
|
| 2251 | 2251 | let err = try expectError(&result); |
|
| 2252 | 2252 | try expectTypeMismatch(err, super::Type::I32, super::Type::Void); |
|
| 2253 | 2253 | } |
|
| 2254 | 2254 | ||
| 2255 | - | @test fn testResolveLetElseRequiresOptional() throws (testing::TestError) { |
|
| 2255 | + | @test unsafe fn testResolveLetElseRequiresOptional() throws (testing::TestError) { |
|
| 2256 | 2256 | let mut a = testResolver(); |
|
| 2257 | 2257 | let program = "let x: i32 = 42; let value = x else panic;"; |
|
| 2258 | 2258 | let result = try resolveProgramStr(&mut a, program); |
|
| 2259 | 2259 | try expectErrorKind(&result, super::ErrorKind::ExpectedOptional); |
|
| 2260 | 2260 | } |
|
| 2261 | 2261 | ||
| 2262 | 2262 | /// Test that `if let mut` produces a mutable binding. |
|
| 2263 | - | @test fn testResolveIfLetMut() throws (testing::TestError) { |
|
| 2263 | + | @test unsafe fn testResolveIfLetMut() throws (testing::TestError) { |
|
| 2264 | 2264 | let mut a = testResolver(); |
|
| 2265 | 2265 | let program = "let opt: ?i32 = 42; if let mut v = opt { set v = v + 1; }"; |
|
| 2266 | 2266 | let result = try resolveProgramStr(&mut a, program); |
|
| 2267 | 2267 | try expectNoErrors(&result); |
|
| 2268 | 2268 | } |
|
| 2269 | 2269 | ||
| 2270 | 2270 | /// Test that `if let` (without mut) rejects assignment. |
|
| 2271 | - | @test fn testResolveIfLetImmutable() throws (testing::TestError) { |
|
| 2271 | + | @test unsafe fn testResolveIfLetImmutable() throws (testing::TestError) { |
|
| 2272 | 2272 | let mut a = testResolver(); |
|
| 2273 | 2273 | let program = "let opt: ?i32 = 42; if let v = opt { set v = 1; }"; |
|
| 2274 | 2274 | let result = try resolveProgramStr(&mut a, program); |
|
| 2275 | 2275 | let err = try expectError(&result); |
|
| 2276 | 2276 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2277 | 2277 | } |
|
| 2278 | 2278 | ||
| 2279 | 2279 | /// Test that `let mut ... else` produces a mutable binding. |
|
| 2280 | - | @test fn testResolveLetMutElse() throws (testing::TestError) { |
|
| 2280 | + | @test unsafe fn testResolveLetMutElse() throws (testing::TestError) { |
|
| 2281 | 2281 | let mut a = testResolver(); |
|
| 2282 | 2282 | let program = "let opt: ?i32 = 42; let mut v = opt else panic; set v = v + 1;"; |
|
| 2283 | 2283 | let result = try resolveProgramStr(&mut a, program); |
|
| 2284 | 2284 | try expectNoErrors(&result); |
|
| 2285 | 2285 | } |
|
| 2286 | 2286 | ||
| 2287 | 2287 | /// Test that `let ... else` (without mut) rejects assignment. |
|
| 2288 | - | @test fn testResolveLetElseImmutable() throws (testing::TestError) { |
|
| 2288 | + | @test unsafe fn testResolveLetElseImmutable() throws (testing::TestError) { |
|
| 2289 | 2289 | let mut a = testResolver(); |
|
| 2290 | 2290 | let program = "let opt: ?i32 = 42; let v = opt else panic; set v = 1;"; |
|
| 2291 | 2291 | let result = try resolveProgramStr(&mut a, program); |
|
| 2292 | 2292 | let err = try expectError(&result); |
|
| 2293 | 2293 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2294 | 2294 | } |
|
| 2295 | 2295 | ||
| 2296 | - | @test fn testResolveLetCaseElse() throws (testing::TestError) { |
|
| 2296 | + | @test unsafe fn testResolveLetCaseElse() throws (testing::TestError) { |
|
| 2297 | 2297 | { |
|
| 2298 | 2298 | let mut a = testResolver(); |
|
| 2299 | 2299 | let program = "let case _ = 1 else panic;"; |
|
| 2300 | 2300 | let result = try resolveProgramStr(&mut a, program); |
|
| 2301 | 2301 | try expectNoErrors(&result); |
| 2305 | 2305 | let result = try resolveProgramStr(&mut a, program); |
|
| 2306 | 2306 | try expectNoErrors(&result); |
|
| 2307 | 2307 | } |
|
| 2308 | 2308 | } |
|
| 2309 | 2309 | ||
| 2310 | - | @test fn testResolveLetCaseElseRequiresDivergentElse() throws (testing::TestError) { |
|
| 2310 | + | @test unsafe fn testResolveLetCaseElseRequiresDivergentElse() throws (testing::TestError) { |
|
| 2311 | 2311 | let mut a = testResolver(); |
|
| 2312 | 2312 | let program = "let case _ = 1 else {};"; |
|
| 2313 | 2313 | let result = try resolveProgramStr(&mut a, program); |
|
| 2314 | 2314 | let err = try expectError(&result); |
|
| 2315 | 2315 | try expectTypeMismatch(err, super::Type::Int, super::Type::Void); |
|
| 2316 | 2316 | } |
|
| 2317 | 2317 | ||
| 2318 | - | @test fn testResolveTryValidPropagation() throws (testing::TestError) { |
|
| 2318 | + | @test unsafe fn testResolveTryValidPropagation() throws (testing::TestError) { |
|
| 2319 | 2319 | let mut a = testResolver(); |
|
| 2320 | 2320 | let program = "fn fallible() throws (i32) {} fn caller() throws (i32) { try fallible() }"; |
|
| 2321 | 2321 | let result = try resolveProgramStr(&mut a, program); |
|
| 2322 | 2322 | try expectNoErrors(&result); |
|
| 2323 | 2323 | } |
|
| 2324 | 2324 | ||
| 2325 | - | @test fn testResolveTryRequiresThrowsClause() throws (testing::TestError) { |
|
| 2325 | + | @test unsafe fn testResolveTryRequiresThrowsClause() throws (testing::TestError) { |
|
| 2326 | 2326 | let mut a = testResolver(); |
|
| 2327 | 2327 | let program = "fn fallible() throws (i32) {} fn caller() { try fallible() }"; |
|
| 2328 | 2328 | let result = try resolveProgramStr(&mut a, program); |
|
| 2329 | 2329 | try expectErrorKind(&result, super::ErrorKind::TryRequiresThrows); |
|
| 2330 | 2330 | } |
|
| 2331 | 2331 | ||
| 2332 | - | @test fn testResolveTryIncompatibleError() throws (testing::TestError) { |
|
| 2332 | + | @test unsafe fn testResolveTryIncompatibleError() throws (testing::TestError) { |
|
| 2333 | 2333 | let mut a = testResolver(); |
|
| 2334 | 2334 | let program = "fn fallible() throws (i32) {} fn caller() throws (i8) { try fallible() }"; |
|
| 2335 | 2335 | let result = try resolveProgramStr(&mut a, program); |
|
| 2336 | 2336 | try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError); |
|
| 2337 | 2337 | } |
|
| 2338 | 2338 | ||
| 2339 | - | @test fn testResolveTryNonThrowing() throws (testing::TestError) { |
|
| 2339 | + | @test unsafe fn testResolveTryNonThrowing() throws (testing::TestError) { |
|
| 2340 | 2340 | let mut a = testResolver(); |
|
| 2341 | 2341 | let program = "fn safe() {} fn caller() throws (i32) { try safe() }"; |
|
| 2342 | 2342 | let result = try resolveProgramStr(&mut a, program); |
|
| 2343 | 2343 | try expectErrorKind(&result, super::ErrorKind::TryNonThrowing); |
|
| 2344 | 2344 | } |
|
| 2345 | 2345 | ||
| 2346 | - | @test fn testResolveTryCatchBlockMatchesResult() throws (testing::TestError) { |
|
| 2346 | + | @test unsafe fn testResolveTryCatchBlockMatchesResult() throws (testing::TestError) { |
|
| 2347 | 2347 | let mut a = testResolver(); |
|
| 2348 | 2348 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 42; }; }"; |
|
| 2349 | 2349 | let result = try resolveProgramStr(&mut a, program); |
|
| 2350 | 2350 | try expectNoErrors(&result); |
|
| 2351 | 2351 | } |
|
| 2352 | 2352 | ||
| 2353 | - | @test fn testResolveTryCatchBlockDiverges() throws (testing::TestError) { |
|
| 2353 | + | @test unsafe fn testResolveTryCatchBlockDiverges() throws (testing::TestError) { |
|
| 2354 | 2354 | let mut a = testResolver(); |
|
| 2355 | 2355 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 7; }; }"; |
|
| 2356 | 2356 | let result = try resolveProgramStr(&mut a, program); |
|
| 2357 | 2357 | try expectNoErrors(&result); |
|
| 2358 | 2358 | } |
|
| 2359 | 2359 | ||
| 2360 | - | @test fn testResolveTryCatchBlockMustDiverge() throws (testing::TestError) { |
|
| 2360 | + | @test unsafe fn testResolveTryCatchBlockMustDiverge() throws (testing::TestError) { |
|
| 2361 | 2361 | let mut a = testResolver(); |
|
| 2362 | 2362 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { 7; }; }"; |
|
| 2363 | 2363 | let result = try resolveProgramStr(&mut a, program); |
|
| 2364 | 2364 | let err = try expectError(&result); |
|
| 2365 | 2365 | try expectTypeMismatch(err, super::Type::U32, super::Type::Void); |
|
| 2366 | 2366 | } |
|
| 2367 | 2367 | ||
| 2368 | - | @test fn testResolveCallMissingTry() throws (testing::TestError) { |
|
| 2368 | + | @test unsafe fn testResolveCallMissingTry() throws (testing::TestError) { |
|
| 2369 | 2369 | let mut a = testResolver(); |
|
| 2370 | 2370 | let program = "fn fallible() throws (i32) {} fn caller() { fallible() }"; |
|
| 2371 | 2371 | let result = try resolveProgramStr(&mut a, program); |
|
| 2372 | 2372 | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
|
| 2373 | 2373 | } |
|
| 2374 | 2374 | ||
| 2375 | 2375 | /// Test that `try?` converts errors to optionals without requiring caller to throw. |
|
| 2376 | - | @test fn testResolveTryOptionalConvertsToOptional() throws (testing::TestError) { |
|
| 2376 | + | @test unsafe fn testResolveTryOptionalConvertsToOptional() throws (testing::TestError) { |
|
| 2377 | 2377 | // `try?` should wrap the return type in optional and not require caller to throw. |
|
| 2378 | 2378 | { |
|
| 2379 | 2379 | let mut a = testResolver(); |
|
| 2380 | 2380 | let program = "record S {} fn fallible() -> *S throws (i32) { panic; } fn caller() -> ?*S { return try? fallible(); }"; |
|
| 2381 | 2381 | let result = try resolveProgramStr(&mut a, program); |
| 2395 | 2395 | let result = try resolveProgramStr(&mut a, program); |
|
| 2396 | 2396 | try expectNoErrors(&result); |
|
| 2397 | 2397 | } |
|
| 2398 | 2398 | } |
|
| 2399 | 2399 | ||
| 2400 | - | @test fn testResolveThrowValid() throws (testing::TestError) { |
|
| 2400 | + | @test unsafe fn testResolveThrowValid() throws (testing::TestError) { |
|
| 2401 | 2401 | let mut a = testResolver(); |
|
| 2402 | 2402 | let program = "fn fail() throws (i32) { throw 1; }"; |
|
| 2403 | 2403 | let result = try resolveProgramStr(&mut a, program); |
|
| 2404 | 2404 | try expectNoErrors(&result); |
|
| 2405 | 2405 | } |
|
| 2406 | 2406 | ||
| 2407 | - | @test fn testResolveThrowRequiresThrowsClause() throws (testing::TestError) { |
|
| 2407 | + | @test unsafe fn testResolveThrowRequiresThrowsClause() throws (testing::TestError) { |
|
| 2408 | 2408 | let mut a = testResolver(); |
|
| 2409 | 2409 | let program = "fn fail() { throw 1; }"; |
|
| 2410 | 2410 | let result = try resolveProgramStr(&mut a, program); |
|
| 2411 | 2411 | try expectErrorKind(&result, super::ErrorKind::ThrowRequiresThrows); |
|
| 2412 | 2412 | } |
|
| 2413 | 2413 | ||
| 2414 | - | @test fn testResolveThrowIncompatibleError() throws (testing::TestError) { |
|
| 2414 | + | @test unsafe fn testResolveThrowIncompatibleError() throws (testing::TestError) { |
|
| 2415 | 2415 | let mut a = testResolver(); |
|
| 2416 | 2416 | let program = "fn fail() throws (i32) { throw true; }"; |
|
| 2417 | 2417 | let result = try resolveProgramStr(&mut a, program); |
|
| 2418 | 2418 | try expectErrorKind(&result, super::ErrorKind::ThrowIncompatibleError); |
|
| 2419 | 2419 | } |
|
| 2420 | 2420 | ||
| 2421 | 2421 | // Binary operation tests ////////////////////////////////////////////////////// |
|
| 2422 | 2422 | ||
| 2423 | - | @test fn testResolveBinaryOpArithmetic() throws (testing::TestError) { |
|
| 2423 | + | @test unsafe fn testResolveBinaryOpArithmetic() throws (testing::TestError) { |
|
| 2424 | 2424 | { |
|
| 2425 | 2425 | let mut a = testResolver(); |
|
| 2426 | 2426 | let result = try resolveExprStr(&mut a, "4 + 4"); |
|
| 2427 | 2427 | try expectNoErrors(&result); |
|
| 2428 | 2428 | try expectType(&a, result.root, super::Type::Int); |
| 2476 | 2476 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2477 | 2477 | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2478 | 2478 | } |
|
| 2479 | 2479 | } |
|
| 2480 | 2480 | ||
| 2481 | - | @test fn testResolveBinaryOpComparison() throws (testing::TestError) { |
|
| 2481 | + | @test unsafe fn testResolveBinaryOpComparison() throws (testing::TestError) { |
|
| 2482 | 2482 | { |
|
| 2483 | 2483 | let mut a = testResolver(); |
|
| 2484 | 2484 | let result = try resolveExprStr(&mut a, "5 == 5"); |
|
| 2485 | 2485 | try expectNoErrors(&result); |
|
| 2486 | 2486 | try expectType(&a, result.root, super::Type::Bool); |
| 2532 | 2532 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2533 | 2533 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
|
| 2534 | 2534 | } |
|
| 2535 | 2535 | } |
|
| 2536 | 2536 | ||
| 2537 | - | @test fn testResolveBinaryOpLogical() throws (testing::TestError) { |
|
| 2537 | + | @test unsafe fn testResolveBinaryOpLogical() throws (testing::TestError) { |
|
| 2538 | 2538 | { |
|
| 2539 | 2539 | let mut a = testResolver(); |
|
| 2540 | 2540 | let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x and y;"); |
|
| 2541 | 2541 | try expectNoErrors(&result); |
|
| 2542 | 2542 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 2553 | 2553 | try expectNoErrors(&result); |
|
| 2554 | 2554 | try expectType(&a, result.root, super::Type::Bool); |
|
| 2555 | 2555 | } |
|
| 2556 | 2556 | } |
|
| 2557 | 2557 | ||
| 2558 | - | @test fn testResolveBinaryOpArithmeticTypeMismatch() throws (testing::TestError) { |
|
| 2558 | + | @test unsafe fn testResolveBinaryOpArithmeticTypeMismatch() throws (testing::TestError) { |
|
| 2559 | 2559 | { |
|
| 2560 | 2560 | let mut a = testResolver(); |
|
| 2561 | 2561 | let result = try resolveProgramStr(&mut a, "4 + true"); |
|
| 2562 | 2562 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 2563 | 2563 | } { |
| 2585 | 2585 | let result = try resolveProgramStr(&mut a, "1 + (true * 3)"); |
|
| 2586 | 2586 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 2587 | 2587 | } |
|
| 2588 | 2588 | } |
|
| 2589 | 2589 | ||
| 2590 | - | @test fn testResolveBinaryOpLogicalTypeMismatch() throws (testing::TestError) { |
|
| 2590 | + | @test unsafe fn testResolveBinaryOpLogicalTypeMismatch() throws (testing::TestError) { |
|
| 2591 | 2591 | { |
|
| 2592 | 2592 | let mut a = testResolver(); |
|
| 2593 | 2593 | let result = try resolveProgramStr(&mut a, "42 and true"); |
|
| 2594 | 2594 | let err = try expectError(&result); |
|
| 2595 | 2595 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 2604 | 2604 | let err = try expectError(&result); |
|
| 2605 | 2605 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
|
| 2606 | 2606 | } |
|
| 2607 | 2607 | } |
|
| 2608 | 2608 | ||
| 2609 | - | @test fn testResolveBinaryOpComparisonTypeMismatch() throws (testing::TestError) { |
|
| 2609 | + | @test unsafe fn testResolveBinaryOpComparisonTypeMismatch() throws (testing::TestError) { |
|
| 2610 | 2610 | let mut a = testResolver(); |
|
| 2611 | 2611 | let result = try resolveProgramStr(&mut a, "true < false"); |
|
| 2612 | 2612 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 2613 | 2613 | } |
|
| 2614 | 2614 | ||
| 2615 | 2615 | // Unary operation tests /////////////////////////////////////////////////////// |
|
| 2616 | 2616 | ||
| 2617 | - | @test fn testResolveUnaryOpNot() throws (testing::TestError) { |
|
| 2617 | + | @test unsafe fn testResolveUnaryOpNot() throws (testing::TestError) { |
|
| 2618 | 2618 | { |
|
| 2619 | 2619 | let mut a = testResolver(); |
|
| 2620 | 2620 | let result = try resolveExprStr(&mut a, "not true"); |
|
| 2621 | 2621 | try expectNoErrors(&result); |
|
| 2622 | 2622 | try expectType(&a, result.root, super::Type::Bool); |
| 2642 | 2642 | let err = try expectError(&result); |
|
| 2643 | 2643 | try expectTypeMismatch(err, super::Type::Bool, super::Type::I32); |
|
| 2644 | 2644 | } |
|
| 2645 | 2645 | } |
|
| 2646 | 2646 | ||
| 2647 | - | @test fn testResolveUnaryOpNeg() throws (testing::TestError) { |
|
| 2647 | + | @test unsafe fn testResolveUnaryOpNeg() throws (testing::TestError) { |
|
| 2648 | 2648 | { |
|
| 2649 | 2649 | let mut a = testResolver(); |
|
| 2650 | 2650 | let result = try resolveExprStr(&mut a, "-42"); |
|
| 2651 | 2651 | try expectNoErrors(&result); |
|
| 2652 | 2652 | try expectType(&a, result.root, super::Type::Int); |
| 2676 | 2676 | let result = try resolveBlockStr(&mut a, "let x: bool = false; -x;"); |
|
| 2677 | 2677 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 2678 | 2678 | } |
|
| 2679 | 2679 | } |
|
| 2680 | 2680 | ||
| 2681 | - | @test fn testResolveUnaryOpBitNot() throws (testing::TestError) { |
|
| 2681 | + | @test unsafe fn testResolveUnaryOpBitNot() throws (testing::TestError) { |
|
| 2682 | 2682 | { |
|
| 2683 | 2683 | let mut a = testResolver(); |
|
| 2684 | 2684 | let result = try resolveExprStr(&mut a, "~42"); |
|
| 2685 | 2685 | try expectNoErrors(&result); |
|
| 2686 | 2686 | try expectType(&a, result.root, super::Type::Int); |
| 2710 | 2710 | let result = try resolveBlockStr(&mut a, "let x: bool = false; ~x;"); |
|
| 2711 | 2711 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
|
| 2712 | 2712 | } |
|
| 2713 | 2713 | } |
|
| 2714 | 2714 | ||
| 2715 | - | @test fn testResolveUnaryOpNested() throws (testing::TestError) { |
|
| 2715 | + | @test unsafe fn testResolveUnaryOpNested() throws (testing::TestError) { |
|
| 2716 | 2716 | { |
|
| 2717 | 2717 | let mut a = testResolver(); |
|
| 2718 | 2718 | let result = try resolveExprStr(&mut a, "not not true"); |
|
| 2719 | 2719 | try expectNoErrors(&result); |
|
| 2720 | 2720 | try expectType(&a, result.root, super::Type::Bool); |
| 2742 | 2742 | // try expectNoErrors(&result); |
|
| 2743 | 2743 | // } |
|
| 2744 | 2744 | ||
| 2745 | 2745 | // Dereference tests ////////////////////////////////////////////////////////// |
|
| 2746 | 2746 | ||
| 2747 | - | @test fn testResolveDeref() throws (testing::TestError) { |
|
| 2747 | + | @test unsafe fn testResolveDeref() throws (testing::TestError) { |
|
| 2748 | 2748 | { |
|
| 2749 | 2749 | let mut a = testResolver(); |
|
| 2750 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 42; let ptr: *i32 = &x; *ptr;"); |
|
| 2750 | + | let result = try resolveBlockStr(&mut a, "static x: i32 = 42; let ptr: *i32 = &x; *ptr;"); |
|
| 2751 | 2751 | try expectNoErrors(&result); |
|
| 2752 | 2752 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
|
| 2753 | 2753 | try expectExprStmtType(&a, stmt, super::Type::I32); |
|
| 2754 | 2754 | } { |
|
| 2755 | 2755 | let mut a = testResolver(); |
| 2760 | 2760 | let result = try resolveBlockStr(&mut a, "let x: i32 = 5; *x;"); |
|
| 2761 | 2761 | try expectErrorKind(&result, super::ErrorKind::ExpectedPointer); |
|
| 2762 | 2762 | } |
|
| 2763 | 2763 | } |
|
| 2764 | 2764 | ||
| 2765 | - | @test fn testResolveAssignDeref() throws (testing::TestError) { |
|
| 2765 | + | @test unsafe fn testResolveAssignDeref() throws (testing::TestError) { |
|
| 2766 | 2766 | { |
|
| 2767 | 2767 | let mut a = testResolver(); |
|
| 2768 | - | let program = "let mut x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;"; |
|
| 2768 | + | let program = "static x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;"; |
|
| 2769 | 2769 | let result = try resolveProgramStr(&mut a, program); |
|
| 2770 | 2770 | try expectNoErrors(&result); |
|
| 2771 | 2771 | } { |
|
| 2772 | 2772 | let mut a = testResolver(); |
|
| 2773 | - | let program = "let mut x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;"; |
|
| 2773 | + | let program = "static x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;"; |
|
| 2774 | 2774 | let result = try resolveProgramStr(&mut a, program); |
|
| 2775 | 2775 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2776 | 2776 | } { |
|
| 2777 | 2777 | let mut a = testResolver(); |
|
| 2778 | - | let program = "let mut x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;"; |
|
| 2778 | + | let program = "static x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;"; |
|
| 2779 | 2779 | let result = try resolveProgramStr(&mut a, program); |
|
| 2780 | 2780 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 2781 | 2781 | } { |
|
| 2782 | 2782 | let mut a = testResolver(); |
|
| 2783 | - | let program = "let mut x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;"; |
|
| 2783 | + | let program = "static x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;"; |
|
| 2784 | 2784 | let result = try resolveProgramStr(&mut a, program); |
|
| 2785 | 2785 | try expectNoErrors(&result); |
|
| 2786 | 2786 | } |
|
| 2787 | 2787 | } |
|
| 2788 | 2788 | ||
| 2789 | 2789 | // Type inference tests /////////////////////////////////////////////////////// |
|
| 2790 | 2790 | ||
| 2791 | - | @test fn testResolveBasicTypeInference() throws (testing::TestError) { |
|
| 2791 | + | @test unsafe fn testResolveBasicTypeInference() throws (testing::TestError) { |
|
| 2792 | 2792 | { |
|
| 2793 | 2793 | // Boolean literals are unambiguous. |
|
| 2794 | 2794 | let mut a = testResolver(); |
|
| 2795 | 2795 | let result = try resolveProgramStr(&mut a, "let x = true; x;"); |
|
| 2796 | 2796 | try expectNoErrors(&result); |
| 2805 | 2805 | } |
|
| 2806 | 2806 | } |
|
| 2807 | 2807 | ||
| 2808 | 2808 | // Union tests ///////////////////////////////////////////////////////////////// |
|
| 2809 | 2809 | ||
| 2810 | - | @test fn testResolveUnionVariantWithoutPayload() throws (testing::TestError) { |
|
| 2810 | + | @test unsafe fn testResolveUnionVariantWithoutPayload() throws (testing::TestError) { |
|
| 2811 | 2811 | let mut a = testResolver(); |
|
| 2812 | 2812 | let program = "union Status { Ok, Error } Status::Ok;"; |
|
| 2813 | 2813 | let result = try resolveProgramStr(&mut a, program); |
|
| 2814 | 2814 | ||
| 2815 | 2815 | let ty = try getTypeInScopeOf(&a, result.root, "Status"); |
| 2824 | 2824 | let stmt = try getBlockStmt(result.root, 1); |
|
| 2825 | 2825 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 2826 | 2826 | try expectNoErrors(&result); |
|
| 2827 | 2827 | } |
|
| 2828 | 2828 | ||
| 2829 | - | @test fn testResolveUnionVariantWithPayload() throws (testing::TestError) { |
|
| 2829 | + | @test unsafe fn testResolveUnionVariantWithPayload() throws (testing::TestError) { |
|
| 2830 | 2830 | let mut a = testResolver(); |
|
| 2831 | 2831 | let program = "union R { Ok(i32), Err(bool) } R::Ok(42);"; |
|
| 2832 | 2832 | let result = try resolveProgramStr(&mut a, program); |
|
| 2833 | 2833 | try expectNoErrors(&result); |
|
| 2834 | 2834 |
| 2844 | 2844 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 2845 | 2845 | ||
| 2846 | 2846 | // TODO: Test payload type. |
|
| 2847 | 2847 | } |
|
| 2848 | 2848 | ||
| 2849 | - | @test fn testResolveUnionVariantWithoutPayloadExplicitDiscriminant() throws (testing::TestError) { |
|
| 2849 | + | @test unsafe fn testResolveUnionVariantWithoutPayloadExplicitDiscriminant() throws (testing::TestError) { |
|
| 2850 | 2850 | let mut a = testResolver(); |
|
| 2851 | 2851 | let program = "union R { Ok = 7, Err = 11 } R::Ok;"; |
|
| 2852 | 2852 | let result = try resolveProgramStr(&mut a, program); |
|
| 2853 | 2853 | try expectNoErrors(&result); |
|
| 2854 | 2854 | ||
| 2855 | 2855 | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
|
| 2856 | 2856 | let stmt = try getBlockStmt(result.root, 1); |
|
| 2857 | 2857 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 2858 | 2858 | } |
|
| 2859 | 2859 | ||
| 2860 | - | @test fn testResolveUnionVariantPayloadTypeMismatch() throws (testing::TestError) { |
|
| 2860 | + | @test unsafe fn testResolveUnionVariantPayloadTypeMismatch() throws (testing::TestError) { |
|
| 2861 | 2861 | let mut a = testResolver(); |
|
| 2862 | 2862 | let program = "union R { Ok(i32), Error(bool) } R::Ok(true);"; |
|
| 2863 | 2863 | let result = try resolveProgramStr(&mut a, program); |
|
| 2864 | 2864 | let err = try expectError(&result); |
|
| 2865 | 2865 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
| 2872 | 2872 | else throw testing::TestError::Failed; |
|
| 2873 | 2873 | let case ast::NodeValue::Bool(_) = errNode.value |
|
| 2874 | 2874 | else throw testing::TestError::Failed; |
|
| 2875 | 2875 | } |
|
| 2876 | 2876 | ||
| 2877 | - | @test fn testResolveUnionVariantUnexpectedPayload() throws (testing::TestError) { |
|
| 2877 | + | @test unsafe fn testResolveUnionVariantUnexpectedPayload() throws (testing::TestError) { |
|
| 2878 | 2878 | let mut a = testResolver(); |
|
| 2879 | 2879 | let program = "union Status { Ok, Error } Status::Ok(42);"; |
|
| 2880 | 2880 | let result = try resolveProgramStr(&mut a, program); |
|
| 2881 | 2881 | let err = try expectError(&result); |
|
| 2882 | 2882 |
| 2886 | 2886 | else throw testing::TestError::Failed; |
|
| 2887 | 2887 | let case ast::NodeValue::Call(_) = node.value |
|
| 2888 | 2888 | else throw testing::TestError::Failed; |
|
| 2889 | 2889 | } |
|
| 2890 | 2890 | ||
| 2891 | - | @test fn testResolveUnionVariantUnknown() throws (testing::TestError) { |
|
| 2891 | + | @test unsafe fn testResolveUnionVariantUnknown() throws (testing::TestError) { |
|
| 2892 | 2892 | let mut a = testResolver(); |
|
| 2893 | 2893 | let program = "union Status { Ok, Error } Status::Unknown;"; |
|
| 2894 | 2894 | let result = try resolveProgramStr(&mut a, program); |
|
| 2895 | 2895 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 2896 | 2896 | } |
|
| 2897 | 2897 | ||
| 2898 | - | @test fn testResolveScopeAccessUndefinedType() throws (testing::TestError) { |
|
| 2898 | + | @test unsafe fn testResolveScopeAccessUndefinedType() throws (testing::TestError) { |
|
| 2899 | 2899 | let mut a = testResolver(); |
|
| 2900 | 2900 | let result = try resolveProgramStr(&mut a, "Unknown::X;"); |
|
| 2901 | 2901 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
|
| 2902 | 2902 | } |
|
| 2903 | 2903 | ||
| 2904 | - | @test fn testResolveUnionVariantVoidPayload() throws (testing::TestError) { |
|
| 2904 | + | @test unsafe fn testResolveUnionVariantVoidPayload() throws (testing::TestError) { |
|
| 2905 | 2905 | let mut a = testResolver(); |
|
| 2906 | 2906 | let program = "union R { Success(i32), Pending } R::Pending;"; |
|
| 2907 | 2907 | let result = try resolveProgramStr(&mut a, program); |
|
| 2908 | 2908 | try expectNoErrors(&result); |
|
| 2909 | 2909 |
| 2913 | 2913 | ||
| 2914 | 2914 | let stmt = try getBlockStmt(result.root, 1); |
|
| 2915 | 2915 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 2916 | 2916 | } |
|
| 2917 | 2917 | ||
| 2918 | - | @test fn testResolveUnionVariantRecordPayload() throws (testing::TestError) { |
|
| 2918 | + | @test unsafe fn testResolveUnionVariantRecordPayload() throws (testing::TestError) { |
|
| 2919 | 2919 | let mut a = testResolver(); |
|
| 2920 | 2920 | let program = "record P { x: i32, y: i32 } union S { Point(P), Num(u32) } S::Point(P { x: 10, y: 20 });"; |
|
| 2921 | 2921 | let result = try resolveProgramStr(&mut a, program); |
|
| 2922 | 2922 | try expectNoErrors(&result); |
|
| 2923 | 2923 | ||
| 2924 | 2924 | let ty = try getTypeInScopeOf(&a, result.root, "S"); |
|
| 2925 | 2925 | let stmt = try getBlockStmt(result.root, 2); |
|
| 2926 | 2926 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
|
| 2927 | 2927 | } |
|
| 2928 | 2928 | ||
| 2929 | - | @test fn testResolveBuiltinSizeOf() throws (testing::TestError) { |
|
| 2929 | + | @test unsafe fn testResolveBuiltinSizeOf() throws (testing::TestError) { |
|
| 2930 | 2930 | try resolveAndExpectConstExpr("@sizeOf(u8)", 1); |
|
| 2931 | 2931 | try resolveAndExpectConstExpr("@sizeOf(u16)", 2); |
|
| 2932 | 2932 | try resolveAndExpectConstExpr("@sizeOf(u32)", 4); |
|
| 2933 | 2933 | try resolveAndExpectConstExpr("@sizeOf(i32)", 4); |
|
| 2934 | 2934 | try resolveAndExpectConstExpr("@sizeOf(bool)", 1); |
| 2951 | 2951 | try resolveAndExpectConstStmt("union T { A, B(u16), C }; @sizeOf(T);", 4); |
|
| 2952 | 2952 | try resolveAndExpectConstStmt("union T { A(u32), B(u16), C(u16) }; @sizeOf(T);", 8); |
|
| 2953 | 2953 | try resolveAndExpectConstStmt("union T { A(u32), B(u16), C([u8; 16]) }; @sizeOf(T);", 20); |
|
| 2954 | 2954 | } |
|
| 2955 | 2955 | ||
| 2956 | - | @test fn testResolveBuiltinAlignOf() throws (testing::TestError) { |
|
| 2956 | + | @test unsafe fn testResolveBuiltinAlignOf() throws (testing::TestError) { |
|
| 2957 | 2957 | try resolveAndExpectConstExpr("@alignOf(u8)", 1); |
|
| 2958 | 2958 | try resolveAndExpectConstExpr("@alignOf(u16)", 2); |
|
| 2959 | 2959 | try resolveAndExpectConstExpr("@alignOf(u32)", 4); |
|
| 2960 | 2960 | try resolveAndExpectConstExpr("@alignOf(i32)", 4); |
|
| 2961 | 2961 | try resolveAndExpectConstExpr("@alignOf(bool)", 1); |
| 2975 | 2975 | try resolveAndExpectConstStmt("record T { x: u32, y: u8, z: u8 }; @alignOf(T);", 4); |
|
| 2976 | 2976 | try resolveAndExpectConstStmt("union T { A, B, C }; @alignOf(T);", 1); |
|
| 2977 | 2977 | try resolveAndExpectConstStmt("union T { A, B(u32), C }; @alignOf(T);", 4); |
|
| 2978 | 2978 | } |
|
| 2979 | 2979 | ||
| 2980 | - | @test fn testResolveBuiltinSizeOfRecord() throws (testing::TestError) { |
|
| 2980 | + | @test unsafe fn testResolveBuiltinSizeOfRecord() throws (testing::TestError) { |
|
| 2981 | 2981 | let mut a = testResolver(); |
|
| 2982 | 2982 | let program = "record T { x: u8, y: u32 } @sizeOf(T);"; |
|
| 2983 | 2983 | let result = try resolveProgramStr(&mut a, program); |
|
| 2984 | 2984 | try expectNoErrors(&result); |
|
| 2985 | 2985 | ||
| 2986 | 2986 | let stmt = try getBlockStmt(result.root, 1); |
|
| 2987 | 2987 | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 2988 | 2988 | try expectConstInt(&a, expr, 8); |
|
| 2989 | 2989 | } |
|
| 2990 | 2990 | ||
| 2991 | - | @test fn testResolveBuiltinSizeOfUnion() throws (testing::TestError) { |
|
| 2991 | + | @test unsafe fn testResolveBuiltinSizeOfUnion() throws (testing::TestError) { |
|
| 2992 | 2992 | let mut a = testResolver(); |
|
| 2993 | 2993 | let program = "union Result { Ok(u32), Err(u8) } @sizeOf(Result);"; |
|
| 2994 | 2994 | let result = try resolveProgramStr(&mut a, program); |
|
| 2995 | 2995 | try expectNoErrors(&result); |
|
| 2996 | 2996 | ||
| 2997 | 2997 | let stmt = try getBlockStmt(result.root, 1); |
|
| 2998 | 2998 | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
|
| 2999 | 2999 | try expectConstInt(&a, expr, 8); |
|
| 3000 | 3000 | } |
|
| 3001 | 3001 | ||
| 3002 | - | @test fn testResolveAlignAnnotation() throws (testing::TestError) { |
|
| 3002 | + | @test unsafe fn testResolveAlignAnnotation() throws (testing::TestError) { |
|
| 3003 | 3003 | { |
|
| 3004 | 3004 | let mut a = testResolver(); |
|
| 3005 | 3005 | let result = try resolveBlockStr(&mut a, "let x: u8 align(8) = 0;"); |
|
| 3006 | 3006 | try expectNoErrors(&result); |
|
| 3007 | 3007 |
| 3027 | 3027 | else throw testing::TestError::Failed; |
|
| 3028 | 3028 | try testing::expect(val == 7); |
|
| 3029 | 3029 | } |
|
| 3030 | 3030 | } |
|
| 3031 | 3031 | ||
| 3032 | - | @test fn testResolveVoidAssignmentError() throws (testing::TestError) { |
|
| 3032 | + | @test unsafe fn testResolveVoidAssignmentError() throws (testing::TestError) { |
|
| 3033 | 3033 | { |
|
| 3034 | 3034 | let mut a = testResolver(); |
|
| 3035 | 3035 | let program = "fn voidFn() {} let _ = voidFn();"; |
|
| 3036 | 3036 | let result = try resolveProgramStr(&mut a, program); |
|
| 3037 | 3037 | try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid); |
| 3045 | 3045 | ||
| 3046 | 3046 | // |
|
| 3047 | 3047 | // Module Declaration Tests |
|
| 3048 | 3048 | // |
|
| 3049 | 3049 | ||
| 3050 | - | @test fn testResolveEmptyMod() throws (testing::TestError) { |
|
| 3050 | + | @test unsafe fn testResolveEmptyMod() throws (testing::TestError) { |
|
| 3051 | 3051 | let mut a = testResolver(); |
|
| 3052 | 3052 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3053 | 3053 | let mut graph = &mut MODULE_GRAPH; |
|
| 3054 | 3054 | ||
| 3055 | 3055 | let rootId = try registerModule(graph, nil, "root", "mod child;", &mut arena); |
|
| 3056 | 3056 | let childId = try registerModule(graph, rootId, "child", "{}", &mut arena); |
|
| 3057 | 3057 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3058 | 3058 | try expectNoErrors(&result); |
|
| 3059 | 3059 | } |
|
| 3060 | 3060 | ||
| 3061 | - | @test fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) { |
|
| 3061 | + | @test unsafe fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) { |
|
| 3062 | 3062 | let mut a = testResolver(); |
|
| 3063 | 3063 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3064 | 3064 | ||
| 3065 | 3065 | // Register root and util modules. |
|
| 3066 | 3066 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena); |
| 3072 | 3072 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 3073 | 3073 | else throw testing::TestError::Failed; |
|
| 3074 | 3074 | try testing::expect(mem::eq(name, "helper")); |
|
| 3075 | 3075 | } |
|
| 3076 | 3076 | ||
| 3077 | - | @test fn testResolveModuleAccessPrivateSubModule() throws (testing::TestError) { |
|
| 3077 | + | @test unsafe fn testResolveModuleAccessPrivateSubModule() throws (testing::TestError) { |
|
| 3078 | 3078 | let mut a = testResolver(); |
|
| 3079 | 3079 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3080 | 3080 | ||
| 3081 | 3081 | // Register root and util modules. |
|
| 3082 | 3082 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena); |
| 3085 | 3085 | // Resolve should succeed: parent can access child. |
|
| 3086 | 3086 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3087 | 3087 | try expectNoErrors(&result); |
|
| 3088 | 3088 | } |
|
| 3089 | 3089 | ||
| 3090 | - | @test fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) { |
|
| 3090 | + | @test unsafe fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) { |
|
| 3091 | 3091 | let mut a = testResolver(); |
|
| 3092 | 3092 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3093 | 3093 | ||
| 3094 | 3094 | // Register root with two sibling modules. |
|
| 3095 | 3095 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
| 3102 | 3102 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 3103 | 3103 | else throw testing::TestError::Failed; |
|
| 3104 | 3104 | try testing::expect(mem::eq(name, "patrick")); |
|
| 3105 | 3105 | } |
|
| 3106 | 3106 | ||
| 3107 | - | @test fn testResolveSiblingModulesViaRoot() throws (testing::TestError) { |
|
| 3107 | + | @test unsafe fn testResolveSiblingModulesViaRoot() throws (testing::TestError) { |
|
| 3108 | 3108 | let mut a = testResolver(); |
|
| 3109 | 3109 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3110 | 3110 | ||
| 3111 | 3111 | // Register root with two sibling modules. |
|
| 3112 | 3112 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
| 3116 | 3116 | // Resolve should succeed: siblings can access each other via root. |
|
| 3117 | 3117 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3118 | 3118 | try expectNoErrors(&result); |
|
| 3119 | 3119 | } |
|
| 3120 | 3120 | ||
| 3121 | - | @test fn testResolveModuleMutualRecursion() throws (testing::TestError) { |
|
| 3121 | + | @test unsafe fn testResolveModuleMutualRecursion() throws (testing::TestError) { |
|
| 3122 | 3122 | let mut a = testResolver(); |
|
| 3123 | 3123 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3124 | 3124 | ||
| 3125 | 3125 | // Register root with two sibling modules that call each other. |
|
| 3126 | 3126 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena); |
| 3130 | 3130 | // Resolve should succeed: cyclic use is allowed. |
|
| 3131 | 3131 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3132 | 3132 | try expectNoErrors(&result); |
|
| 3133 | 3133 | } |
|
| 3134 | 3134 | ||
| 3135 | - | @test fn testResolveAccessModuleType() throws (testing::TestError) { |
|
| 3135 | + | @test unsafe fn testResolveAccessModuleType() throws (testing::TestError) { |
|
| 3136 | 3136 | let mut a = testResolver(); |
|
| 3137 | 3137 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3138 | 3138 | ||
| 3139 | 3139 | // Register root with types module containing a record. |
|
| 3140 | 3140 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
| 3144 | 3144 | // Resolve should succeed: types can be accessed. |
|
| 3145 | 3145 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3146 | 3146 | try expectNoErrors(&result); |
|
| 3147 | 3147 | } |
|
| 3148 | 3148 | ||
| 3149 | - | @test fn testResolveAccessModuleConstant() throws (testing::TestError) { |
|
| 3149 | + | @test unsafe fn testResolveAccessModuleConstant() throws (testing::TestError) { |
|
| 3150 | 3150 | let mut a = testResolver(); |
|
| 3151 | 3151 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3152 | 3152 | ||
| 3153 | 3153 | // Register root with constants module. |
|
| 3154 | 3154 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
| 3158 | 3158 | // Resolve should succeed: constants can be accessed. |
|
| 3159 | 3159 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3160 | 3160 | try expectNoErrors(&result); |
|
| 3161 | 3161 | } |
|
| 3162 | 3162 | ||
| 3163 | - | @test fn testResolveRootSymbolMustBeImported() throws (testing::TestError) { |
|
| 3163 | + | @test unsafe fn testResolveRootSymbolMustBeImported() throws (testing::TestError) { |
|
| 3164 | 3164 | let mut a = testResolver(); |
|
| 3165 | 3165 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3166 | 3166 | ||
| 3167 | 3167 | // Register deeply nested modules: `root::app::services::auth`. |
|
| 3168 | 3168 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena); |
| 3171 | 3171 | // Resolve should fail: the `root` module must be imported. |
|
| 3172 | 3172 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3173 | 3173 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root")); |
|
| 3174 | 3174 | } |
|
| 3175 | 3175 | ||
| 3176 | - | @test fn testResolveUseImportsNestedSymbol() throws (testing::TestError) { |
|
| 3176 | + | @test unsafe fn testResolveUseImportsNestedSymbol() throws (testing::TestError) { |
|
| 3177 | 3177 | let mut a = testResolver(); |
|
| 3178 | 3178 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3179 | 3179 | ||
| 3180 | 3180 | // Register deeply nested modules: `root::app::services::auth`. |
|
| 3181 | 3181 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena); |
| 3188 | 3188 | // Resolve should succeed: use imports the module symbol. |
|
| 3189 | 3189 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3190 | 3190 | try expectNoErrors(&result); |
|
| 3191 | 3191 | } |
|
| 3192 | 3192 | ||
| 3193 | - | @test fn testResolveUseNonExistentModule() throws (testing::TestError) { |
|
| 3193 | + | @test unsafe fn testResolveUseNonExistentModule() throws (testing::TestError) { |
|
| 3194 | 3194 | let mut a = testResolver(); |
|
| 3195 | 3195 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3196 | 3196 | ||
| 3197 | 3197 | // Register root with app trying to use a non-existent module. |
|
| 3198 | 3198 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod app;", &mut arena); |
| 3201 | 3201 | // Resolve should fail: module doesn't exist. |
|
| 3202 | 3202 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3203 | 3203 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("unknown")); |
|
| 3204 | 3204 | } |
|
| 3205 | 3205 | ||
| 3206 | - | @test fn testResolveUsePrivateFn() throws (testing::TestError) { |
|
| 3206 | + | @test unsafe fn testResolveUsePrivateFn() throws (testing::TestError) { |
|
| 3207 | 3207 | let mut a = testResolver(); |
|
| 3208 | 3208 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3209 | 3209 | ||
| 3210 | 3210 | // Register root with util module containing a private function. |
|
| 3211 | 3211 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena); |
| 3215 | 3215 | // Resolve should fail: function is not public. |
|
| 3216 | 3216 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3217 | 3217 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 3218 | 3218 | } |
|
| 3219 | 3219 | ||
| 3220 | - | @test fn testResolveUsePrivateMod() throws (testing::TestError) { |
|
| 3220 | + | @test unsafe fn testResolveUsePrivateMod() throws (testing::TestError) { |
|
| 3221 | 3221 | let mut a = testResolver(); |
|
| 3222 | 3222 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3223 | 3223 | ||
| 3224 | 3224 | // Register root with public and private child modules. |
|
| 3225 | 3225 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; mod private;", &mut arena); |
| 3229 | 3229 | // Resolve should fail: module is not public. |
|
| 3230 | 3230 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3231 | 3231 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 3232 | 3232 | } |
|
| 3233 | 3233 | ||
| 3234 | - | @test fn testResolveUsePublicMod() throws (testing::TestError) { |
|
| 3234 | + | @test unsafe fn testResolveUsePublicMod() throws (testing::TestError) { |
|
| 3235 | 3235 | let mut a = testResolver(); |
|
| 3236 | 3236 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3237 | 3237 | ||
| 3238 | 3238 | // Register root with public and private child modules. |
|
| 3239 | 3239 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena); |
| 3243 | 3243 | // Resolve should succeed: module is public. |
|
| 3244 | 3244 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3245 | 3245 | try expectNoErrors(&result); |
|
| 3246 | 3246 | } |
|
| 3247 | 3247 | ||
| 3248 | - | @test fn testResolveUseNonPublicType() throws (testing::TestError) { |
|
| 3248 | + | @test unsafe fn testResolveUseNonPublicType() throws (testing::TestError) { |
|
| 3249 | 3249 | let mut a = testResolver(); |
|
| 3250 | 3250 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3251 | 3251 | ||
| 3252 | 3252 | // Register root with types module containing a private record. |
|
| 3253 | 3253 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
| 3257 | 3257 | // Resolve should fail: record is not public. |
|
| 3258 | 3258 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3259 | 3259 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Priv")); |
|
| 3260 | 3260 | } |
|
| 3261 | 3261 | ||
| 3262 | - | @test fn testResolveImportPublicType() throws (testing::TestError) { |
|
| 3262 | + | @test unsafe fn testResolveImportPublicType() throws (testing::TestError) { |
|
| 3263 | 3263 | let mut a = testResolver(); |
|
| 3264 | 3264 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3265 | 3265 | ||
| 3266 | 3266 | // Register root with types module containing a public record. |
|
| 3267 | 3267 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
| 3271 | 3271 | // Resolve should succeed: record is public. |
|
| 3272 | 3272 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3273 | 3273 | try expectNoErrors(&result); |
|
| 3274 | 3274 | } |
|
| 3275 | 3275 | ||
| 3276 | - | @test fn testResolveUseNonPublicStatic() throws (testing::TestError) { |
|
| 3276 | + | @test unsafe fn testResolveUseNonPublicStatic() throws (testing::TestError) { |
|
| 3277 | 3277 | let mut a = testResolver(); |
|
| 3278 | 3278 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3279 | 3279 | ||
| 3280 | 3280 | // Register root with statics module containing a private static. |
|
| 3281 | 3281 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
| 3285 | 3285 | // Resolve should fail: static is not public. |
|
| 3286 | 3286 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3287 | 3287 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("PRIVATE")); |
|
| 3288 | 3288 | } |
|
| 3289 | 3289 | ||
| 3290 | - | @test fn testResolveImportPublicStatic() throws (testing::TestError) { |
|
| 3290 | + | @test unsafe fn testResolveImportPublicStatic() throws (testing::TestError) { |
|
| 3291 | 3291 | let mut a = testResolver(); |
|
| 3292 | 3292 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3293 | 3293 | ||
| 3294 | 3294 | // Register root with statics module containing a public static. |
|
| 3295 | 3295 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
| 3299 | 3299 | // Resolve should succeed: static is public. |
|
| 3300 | 3300 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3301 | 3301 | try expectNoErrors(&result); |
|
| 3302 | 3302 | } |
|
| 3303 | 3303 | ||
| 3304 | - | @test fn testResolveAccessSuper() throws (testing::TestError) { |
|
| 3304 | + | @test unsafe fn testResolveAccessSuper() throws (testing::TestError) { |
|
| 3305 | 3305 | { |
|
| 3306 | 3306 | let mut a = testResolver(); |
|
| 3307 | 3307 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3308 | 3308 | ||
| 3309 | 3309 | // Test function access. |
| 3322 | 3322 | try expectNoErrors(&result); |
|
| 3323 | 3323 | } |
|
| 3324 | 3324 | } |
|
| 3325 | 3325 | ||
| 3326 | 3326 | /// Test nested super access to union variants (e.g. `super::E::A`). |
|
| 3327 | - | @test fn testResolveSuperUnionVariant() throws (testing::TestError) { |
|
| 3327 | + | @test unsafe fn testResolveSuperUnionVariant() throws (testing::TestError) { |
|
| 3328 | 3328 | let mut a = testResolver(); |
|
| 3329 | 3329 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3330 | 3330 | ||
| 3331 | 3331 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena); |
|
| 3332 | 3332 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c", |
| 3334 | 3334 | &mut arena); |
|
| 3335 | 3335 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3336 | 3336 | try expectNoErrors(&result); |
|
| 3337 | 3337 | } |
|
| 3338 | 3338 | ||
| 3339 | - | @test fn testResolveUseSuper() throws (testing::TestError) { |
|
| 3339 | + | @test unsafe fn testResolveUseSuper() throws (testing::TestError) { |
|
| 3340 | 3340 | let mut a = testResolver(); |
|
| 3341 | 3341 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3342 | 3342 | ||
| 3343 | 3343 | // Register root with a function, and a child module that uses super to access it. |
|
| 3344 | 3344 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena); |
| 3348 | 3348 | // Resolve should succeed - super allows accessing parent module. |
|
| 3349 | 3349 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3350 | 3350 | try expectNoErrors(&result); |
|
| 3351 | 3351 | } |
|
| 3352 | 3352 | ||
| 3353 | - | @test fn testResolveModNotFound() throws (testing::TestError) { |
|
| 3353 | + | @test unsafe fn testResolveModNotFound() throws (testing::TestError) { |
|
| 3354 | 3354 | let mut a = testResolver(); |
|
| 3355 | 3355 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3356 | 3356 | ||
| 3357 | 3357 | // Register root that declares a module that doesn't exist. |
|
| 3358 | 3358 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod unknown;", &mut arena); |
| 3363 | 3363 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
|
| 3364 | 3364 | else throw testing::TestError::Failed; |
|
| 3365 | 3365 | try testing::expect(mem::eq(name, "unknown")); |
|
| 3366 | 3366 | } |
|
| 3367 | 3367 | ||
| 3368 | - | @test fn testResolveDuplicateSubModule() throws (testing::TestError) { |
|
| 3368 | + | @test unsafe fn testResolveDuplicateSubModule() throws (testing::TestError) { |
|
| 3369 | 3369 | let mut a = testResolver(); |
|
| 3370 | 3370 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3371 | 3371 | ||
| 3372 | 3372 | // Register root that declares a module twice. |
|
| 3373 | 3373 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; mod child;", &mut arena); |
| 3377 | 3377 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3378 | 3378 | let err = try expectError(&result); |
|
| 3379 | 3379 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child")); |
|
| 3380 | 3380 | } |
|
| 3381 | 3381 | ||
| 3382 | - | @test fn testResolveUseSubModule() throws (testing::TestError) { |
|
| 3382 | + | @test unsafe fn testResolveUseSubModule() throws (testing::TestError) { |
|
| 3383 | 3383 | let mut a = testResolver(); |
|
| 3384 | 3384 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3385 | 3385 | ||
| 3386 | 3386 | // Register root that declares and imports the same module. |
|
| 3387 | 3387 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; use child;", &mut arena); |
| 3391 | 3391 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3392 | 3392 | let err = try expectError(&result); |
|
| 3393 | 3393 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child")); |
|
| 3394 | 3394 | } |
|
| 3395 | 3395 | ||
| 3396 | - | @test fn testResolveDuplicateUse() throws (testing::TestError) { |
|
| 3396 | + | @test unsafe fn testResolveDuplicateUse() throws (testing::TestError) { |
|
| 3397 | 3397 | let mut a = testResolver(); |
|
| 3398 | 3398 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3399 | 3399 | ||
| 3400 | 3400 | // Register a module that imports the same module twice. |
|
| 3401 | 3401 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child", &mut arena); |
| 3406 | 3406 | let err = try expectError(&result); |
|
| 3407 | 3407 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("root")); |
|
| 3408 | 3408 | } |
|
| 3409 | 3409 | ||
| 3410 | 3410 | /// Test that opaque pointers are allowed in record fields. |
|
| 3411 | - | @test fn testOpaquePointerInRecordField() throws (testing::TestError) { |
|
| 3411 | + | @test unsafe fn testOpaquePointerInRecordField() throws (testing::TestError) { |
|
| 3412 | 3412 | let mut a = testResolver(); |
|
| 3413 | 3413 | let result = try resolveProgramStr(&mut a, "record T { x: *opaque }"); |
|
| 3414 | 3414 | try expectNoErrors(&result); |
|
| 3415 | 3415 | } |
|
| 3416 | 3416 | ||
| 3417 | 3417 | /// You cannot use `@sizeOf` or `@alignOf` on opaque type. |
|
| 3418 | - | @test fn testOpaqueTypeNoSizeOfAlignOf() throws (testing::TestError) { |
|
| 3418 | + | @test unsafe fn testOpaqueTypeNoSizeOfAlignOf() throws (testing::TestError) { |
|
| 3419 | 3419 | let mut a = testResolver(); |
|
| 3420 | 3420 | ||
| 3421 | 3421 | let result1 = try resolveExprStr(&mut a, "@sizeOf(opaque)"); |
|
| 3422 | 3422 | let err1 = try expectError(&result1); |
|
| 3423 | 3423 | try expectErrorKind(&result1, super::ErrorKind::OpaqueTypeNotAllowed); |
| 3426 | 3426 | let err2 = try expectError(&result2); |
|
| 3427 | 3427 | try expectErrorKind(&result2, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 3428 | 3428 | } |
|
| 3429 | 3429 | ||
| 3430 | 3430 | /// Test that immutable slice/pointer parameters cannot be borrowed mutably. |
|
| 3431 | - | @test fn testMutableBorrowFromImmutablePointer() throws (testing::TestError) { |
|
| 3431 | + | @test unsafe fn testMutableBorrowFromImmutablePointer() throws (testing::TestError) { |
|
| 3432 | 3432 | let mut a = testResolver(); |
|
| 3433 | 3433 | let program = "fn f(p: *i32) { let x = &mut *p; }"; |
|
| 3434 | 3434 | let result = try resolveProgramStr(&mut a, program); |
|
| 3435 | 3435 | let err = try expectError(&result); |
|
| 3436 | 3436 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3437 | 3437 | } |
|
| 3438 | 3438 | ||
| 3439 | 3439 | /// Test that immutable slice parameters cannot be borrowed mutably. |
|
| 3440 | - | @test fn testMutableBorrowFromImmutableSlice() throws (testing::TestError) { |
|
| 3440 | + | @test unsafe fn testMutableBorrowFromImmutableSlice() throws (testing::TestError) { |
|
| 3441 | 3441 | let mut a = testResolver(); |
|
| 3442 | 3442 | let program = "fn f(s: *[i32]) { let x: *mut i32 = &mut s[0]; }"; |
|
| 3443 | 3443 | let result = try resolveProgramStr(&mut a, program); |
|
| 3444 | 3444 | let err = try expectError(&result); |
|
| 3445 | 3445 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3446 | 3446 | } |
|
| 3447 | 3447 | ||
| 3448 | 3448 | /// Test that mutable pointer parameters can be borrowed mutably. |
|
| 3449 | - | @test fn testMutableBorrowFromMutablePointer() throws (testing::TestError) { |
|
| 3449 | + | @test unsafe fn testMutableBorrowFromMutablePointer() throws (testing::TestError) { |
|
| 3450 | 3450 | let mut a = testResolver(); |
|
| 3451 | 3451 | let program = "fn f(p: *mut i32) { let x: *mut i32 = &mut *p; }"; |
|
| 3452 | 3452 | let result = try resolveProgramStr(&mut a, program); |
|
| 3453 | 3453 | try expectNoErrors(&result); |
|
| 3454 | 3454 | } |
|
| 3455 | 3455 | ||
| 3456 | 3456 | /// Test that mutable slice parameters can be borrowed mutably. |
|
| 3457 | - | @test fn testMutableBorrowFromMutableSlice() throws (testing::TestError) { |
|
| 3457 | + | @test unsafe fn testMutableBorrowFromMutableSlice() throws (testing::TestError) { |
|
| 3458 | 3458 | let mut a = testResolver(); |
|
| 3459 | 3459 | let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }"; |
|
| 3460 | 3460 | let result = try resolveProgramStr(&mut a, program); |
|
| 3461 | 3461 | try expectNoErrors(&result); |
|
| 3462 | 3462 | } |
|
| 3463 | 3463 | ||
| 3464 | 3464 | /// Test borrowing mutably from a field access on a call returning `*mut`. |
|
| 3465 | - | @test fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) { |
|
| 3465 | + | @test unsafe fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) { |
|
| 3466 | 3466 | let mut a = testResolver(); |
|
| 3467 | - | let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { let mut b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }"; |
|
| 3467 | + | let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { static b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }"; |
|
| 3468 | 3468 | let result = try resolveProgramStr(&mut a, program); |
|
| 3469 | 3469 | try expectNoErrors(&result); |
|
| 3470 | 3470 | } |
|
| 3471 | 3471 | ||
| 3472 | 3472 | /// Test that calls returning immutable pointers cannot be mutably borrowed. |
|
| 3473 | - | @test fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) { |
|
| 3473 | + | @test unsafe fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) { |
|
| 3474 | 3474 | let mut a = testResolver(); |
|
| 3475 | - | let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { let b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }"; |
|
| 3475 | + | let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { constant b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }"; |
|
| 3476 | 3476 | let result = try resolveProgramStr(&mut a, program); |
|
| 3477 | 3477 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3478 | 3478 | } |
|
| 3479 | 3479 | ||
| 3480 | 3480 | /// Test borrowing mutably from a public static through scope access. |
|
| 3481 | - | @test fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) { |
|
| 3481 | + | @test unsafe fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) { |
|
| 3482 | 3482 | let mut a = testResolver(); |
|
| 3483 | 3483 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3484 | 3484 | ||
| 3485 | 3485 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 3486 | 3486 | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena); |
| 3489 | 3489 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3490 | 3490 | try expectNoErrors(&result); |
|
| 3491 | 3491 | } |
|
| 3492 | 3492 | ||
| 3493 | 3493 | /// Test that constants through scope access cannot be mutably borrowed. |
|
| 3494 | - | @test fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) { |
|
| 3494 | + | @test unsafe fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) { |
|
| 3495 | 3495 | let mut a = testResolver(); |
|
| 3496 | 3496 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3497 | 3497 | ||
| 3498 | 3498 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3499 | 3499 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena); |
| 3502 | 3502 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3503 | 3503 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3504 | 3504 | } |
|
| 3505 | 3505 | ||
| 3506 | 3506 | /// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer. |
|
| 3507 | - | @test fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) { |
|
| 3507 | + | @test unsafe fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) { |
|
| 3508 | 3508 | let mut a = testResolver(); |
|
| 3509 | - | let program = "fn f() { let mut x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }"; |
|
| 3509 | + | let program = "fn f() { static x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }"; |
|
| 3510 | 3510 | let result = try resolveProgramStr(&mut a, program); |
|
| 3511 | 3511 | let err = try expectError(&result); |
|
| 3512 | 3512 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3513 | 3513 | } |
|
| 3514 | 3514 | ||
| 3515 | 3515 | /// Test that mutable pointer to immutable slice cannot be assigned through index. |
|
| 3516 | 3516 | /// This tests the case where we have `*mut *[T]`; the outer pointer is mutable but |
|
| 3517 | 3517 | /// the inner slice is immutable, so we shouldn't be able to mutate the elements. |
|
| 3518 | - | @test fn testAssignThroughMutablePointerToImmutableSlice() throws (testing::TestError) { |
|
| 3518 | + | @test unsafe fn testAssignThroughMutablePointerToImmutableSlice() throws (testing::TestError) { |
|
| 3519 | 3519 | let mut a = testResolver(); |
|
| 3520 | 3520 | let program = "fn f(slice: *[i32]) { let p: *mut *[i32] = &mut slice; set p[0] = 1; }"; |
|
| 3521 | 3521 | let result = try resolveProgramStr(&mut a, program); |
|
| 3522 | 3522 | ||
| 3523 | 3523 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3524 | 3524 | } |
|
| 3525 | 3525 | ||
| 3526 | 3526 | /// Test that mutable slice parameters can be assigned through index. |
|
| 3527 | - | @test fn testAssignThroughMutableSliceParam() throws (testing::TestError) { |
|
| 3527 | + | @test unsafe fn testAssignThroughMutableSliceParam() throws (testing::TestError) { |
|
| 3528 | 3528 | { |
|
| 3529 | 3529 | // Mutable slice param: direct assignment should work |
|
| 3530 | 3530 | let mut a = testResolver(); |
|
| 3531 | 3531 | let program = "fn f(slice: *mut [i32]) { set slice[0] = 1; }"; |
|
| 3532 | 3532 | let result = try resolveProgramStr(&mut a, program); |
| 3539 | 3539 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3540 | 3540 | } |
|
| 3541 | 3541 | } |
|
| 3542 | 3542 | ||
| 3543 | 3543 | /// Test range end type coercion with assignable types. |
|
| 3544 | - | @test fn testRangeEndTypeCoercion() throws (testing::TestError) { |
|
| 3544 | + | @test unsafe fn testRangeEndTypeCoercion() throws (testing::TestError) { |
|
| 3545 | 3545 | { |
|
| 3546 | 3546 | let mut a = testResolver(); |
|
| 3547 | 3547 | let program = "fn f(end: u32) { for i in 0..end {} }"; |
|
| 3548 | 3548 | let result = try resolveProgramStr(&mut a, program); |
|
| 3549 | 3549 | try expectNoErrors(&result); |
| 3554 | 3554 | try expectNoErrors(&result); |
|
| 3555 | 3555 | } |
|
| 3556 | 3556 | } |
|
| 3557 | 3557 | ||
| 3558 | 3558 | /// Mixed-width range bounds require an explicit cast. |
|
| 3559 | - | @test fn testRangeEndTypeSubType() throws (testing::TestError) { |
|
| 3559 | + | @test unsafe fn testRangeEndTypeSubType() throws (testing::TestError) { |
|
| 3560 | 3560 | { |
|
| 3561 | 3561 | let mut a = testResolver(); |
|
| 3562 | 3562 | let program = "fn f(start: i8, end: u32) { for i in start..end {} }"; |
|
| 3563 | 3563 | let result = try resolveProgramStr(&mut a, program); |
|
| 3564 | 3564 | let err = try expectError(&result); |
| 3570 | 3570 | try expectNoErrors(&result); |
|
| 3571 | 3571 | } |
|
| 3572 | 3572 | } |
|
| 3573 | 3573 | ||
| 3574 | 3574 | /// Test that try-catch expressions in statement context accept mismatched types. |
|
| 3575 | - | @test fn testTryCatchInStatementContextTypeMismatchOk() throws (testing::TestError) { |
|
| 3575 | + | @test unsafe fn testTryCatchInStatementContextTypeMismatchOk() throws (testing::TestError) { |
|
| 3576 | 3576 | let mut a = testResolver(); |
|
| 3577 | 3577 | let program = "fn f() { try g() catch {}; } fn g() -> bool throws (i32) { panic; }"; |
|
| 3578 | 3578 | let result = try resolveProgramStr(&mut a, program); |
|
| 3579 | 3579 | try expectNoErrors(&result); |
|
| 3580 | 3580 | } |
|
| 3581 | 3581 | ||
| 3582 | 3582 | /// Test that try-catch blocks in value context require divergence or void. |
|
| 3583 | - | @test fn testTryCatchInValueContextTypeMismatch() throws (testing::TestError) { |
|
| 3583 | + | @test unsafe fn testTryCatchInValueContextTypeMismatch() throws (testing::TestError) { |
|
| 3584 | 3584 | let mut a = testResolver(); |
|
| 3585 | 3585 | let program = "fn f() -> bool { return try g() catch {}; } fn g() -> bool throws (i32) { panic; }"; |
|
| 3586 | 3586 | let result = try resolveProgramStr(&mut a, program); |
|
| 3587 | 3587 | let err = try expectError(&result); |
|
| 3588 | 3588 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Void); |
|
| 3589 | 3589 | } |
|
| 3590 | 3590 | ||
| 3591 | 3591 | /// Test that try-catch blocks in value context work when they diverge. |
|
| 3592 | - | @test fn testTryCatchInValueContextDiverges() throws (testing::TestError) { |
|
| 3592 | + | @test unsafe fn testTryCatchInValueContextDiverges() throws (testing::TestError) { |
|
| 3593 | 3593 | let mut a = testResolver(); |
|
| 3594 | 3594 | let program = "fn f() -> bool { return try g() catch { return false; }; } fn g() -> bool throws (i32) { panic; }"; |
|
| 3595 | 3595 | let result = try resolveProgramStr(&mut a, program); |
|
| 3596 | 3596 | try expectNoErrors(&result); |
|
| 3597 | 3597 | } |
|
| 3598 | 3598 | ||
| 3599 | 3599 | /// Test that `try?` lifts result type to optional. |
|
| 3600 | - | @test fn testTryOptionalLiftsToOptional() throws (testing::TestError) { |
|
| 3600 | + | @test unsafe fn testTryOptionalLiftsToOptional() throws (testing::TestError) { |
|
| 3601 | 3601 | let mut a = testResolver(); |
|
| 3602 | 3602 | let program = "record S {} fn f() -> ?*S { return try? g(); } fn g() -> *S throws (i32) { panic; }"; |
|
| 3603 | 3603 | let result = try resolveProgramStr(&mut a, program); |
|
| 3604 | 3604 | try expectNoErrors(&result); |
|
| 3605 | 3605 | } |
|
| 3606 | 3606 | ||
| 3607 | 3607 | /// Test that record fields can be assigned if the record binding is mutable. |
|
| 3608 | - | @test fn testMutableAssignToMutableRecordBinding() throws (testing::TestError) { |
|
| 3608 | + | @test unsafe fn testMutableAssignToMutableRecordBinding() throws (testing::TestError) { |
|
| 3609 | 3609 | let mut a = testResolver(); |
|
| 3610 | 3610 | let program = "record S { x: i32 } fn f() { let mut s = S { x: 1 }; set s.x = 2; }"; |
|
| 3611 | 3611 | let result = try resolveProgramStr(&mut a, program); |
|
| 3612 | 3612 | try expectNoErrors(&result); |
|
| 3613 | 3613 | } |
|
| 3614 | 3614 | ||
| 3615 | 3615 | /// Test that record fields cannot be assigned if the record binding is immutable. |
|
| 3616 | - | @test fn testMutableAssignToImmutableRecordBinding() throws (testing::TestError) { |
|
| 3616 | + | @test unsafe fn testMutableAssignToImmutableRecordBinding() throws (testing::TestError) { |
|
| 3617 | 3617 | let mut a = testResolver(); |
|
| 3618 | 3618 | let program = "record S { x: i32 } fn f() { let s = S { x: 1 }; set s.x = 2; }"; |
|
| 3619 | 3619 | let result = try resolveProgramStr(&mut a, program); |
|
| 3620 | 3620 | let err = try expectError(&result); |
|
| 3621 | 3621 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3622 | 3622 | } |
|
| 3623 | 3623 | ||
| 3624 | 3624 | /// Test that record fields can be assigned through a mutable pointer. |
|
| 3625 | - | @test fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) { |
|
| 3625 | + | @test unsafe fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) { |
|
| 3626 | 3626 | let mut a = testResolver(); |
|
| 3627 | 3627 | let program = "record S { x: i32 } fn f(p: *mut S) { set p.x = 2; }"; |
|
| 3628 | 3628 | let result = try resolveProgramStr(&mut a, program); |
|
| 3629 | 3629 | try expectNoErrors(&result); |
|
| 3630 | 3630 | } |
|
| 3631 | 3631 | ||
| 3632 | 3632 | /// Test that record fields cannot be assigned through an immutable pointer. |
|
| 3633 | - | @test fn testMutableAssignToImmutablePointerToRecord() throws (testing::TestError) { |
|
| 3633 | + | @test unsafe fn testMutableAssignToImmutablePointerToRecord() throws (testing::TestError) { |
|
| 3634 | 3634 | let mut a = testResolver(); |
|
| 3635 | 3635 | let program = "record S { x: i32 } fn f(p: *S) { set p.x = 2; }"; |
|
| 3636 | 3636 | let result = try resolveProgramStr(&mut a, program); |
|
| 3637 | 3637 | let err = try expectError(&result); |
|
| 3638 | 3638 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 3639 | 3639 | } |
|
| 3640 | 3640 | ||
| 3641 | 3641 | // Opaque pointer tests. |
|
| 3642 | 3642 | ||
| 3643 | 3643 | /// You can assign any pointer (*T) to an opaque pointer (*opaque) without a cast. |
|
| 3644 | - | @test fn testOpaquePointerAutoCoercion() throws (testing::TestError) { |
|
| 3644 | + | @test unsafe fn testOpaquePointerAutoCoercion() throws (testing::TestError) { |
|
| 3645 | 3645 | let mut a = testResolver(); |
|
| 3646 | - | let result = try resolveProgramStr(&mut a, "fn f(x: i32) { let mut ptr: *i32 = &x; let o: *opaque = ptr; set ptr = o as *i32; }"); |
|
| 3646 | + | let result = try resolveProgramStr(&mut a, "fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }"); |
|
| 3647 | 3647 | try expectNoErrors(&result); |
|
| 3648 | 3648 | } |
|
| 3649 | 3649 | ||
| 3650 | 3650 | /// You cannot assign an opaque pointer to a non-opaque pointer without a cast. |
|
| 3651 | - | @test fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) { |
|
| 3651 | + | @test unsafe fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) { |
|
| 3652 | 3652 | let mut a = testResolver(); |
|
| 3653 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let ptr: *i32 = o; }"); |
|
| 3653 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let ptr: *i32 = o; }"); |
|
| 3654 | 3654 | let err = try expectError(&result); |
|
| 3655 | 3655 | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
|
| 3656 | 3656 | else throw testing::TestError::Failed; |
|
| 3657 | 3657 | let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected |
|
| 3658 | 3658 | else throw testing::TestError::Failed; |
| 3662 | 3662 | try testing::expect(*expectedTarget == super::Type::I32); |
|
| 3663 | 3663 | try testing::expect(*actualTarget == super::Type::Opaque); |
|
| 3664 | 3664 | } |
|
| 3665 | 3665 | ||
| 3666 | 3666 | /// You cannot have a value of type `opaque` (function parameter). |
|
| 3667 | - | @test fn testOpaqueValue() throws (testing::TestError) { |
|
| 3667 | + | @test unsafe fn testOpaqueValue() throws (testing::TestError) { |
|
| 3668 | 3668 | { |
|
| 3669 | 3669 | let mut a = testResolver(); |
|
| 3670 | 3670 | let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}"); |
|
| 3671 | 3671 | let err = try expectError(&result); |
|
| 3672 | 3672 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
| 3682 | 3682 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 3683 | 3683 | } |
|
| 3684 | 3684 | } |
|
| 3685 | 3685 | ||
| 3686 | 3686 | /// You cannot dereference an opaque pointer, you have to cast it first. |
|
| 3687 | - | @test fn testOpaquePointerNoDereference() throws (testing::TestError) { |
|
| 3687 | + | @test unsafe fn testOpaquePointerNoDereference() throws (testing::TestError) { |
|
| 3688 | 3688 | let mut a = testResolver(); |
|
| 3689 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = *o; }"); |
|
| 3689 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = *o; }"); |
|
| 3690 | 3690 | let err = try expectError(&result); |
|
| 3691 | 3691 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref); |
|
| 3692 | 3692 | } |
|
| 3693 | 3693 | ||
| 3694 | 3694 | /// Test that you can dereference after casting. |
|
| 3695 | - | @test fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) { |
|
| 3695 | + | @test unsafe fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) { |
|
| 3696 | 3696 | let mut a = testResolver(); |
|
| 3697 | 3697 | let result = try resolveProgramStr(&mut a, "fn f() { let o: *opaque = undefined; let x = *(o as *i32); }"); |
|
| 3698 | 3698 | try expectNoErrors(&result); |
|
| 3699 | 3699 | } |
|
| 3700 | 3700 | ||
| 3701 | 3701 | /// You cannot do pointer arithmetic with an opaque pointer. |
|
| 3702 | - | @test fn testOpaquePointerNoArithmetic() throws (testing::TestError) { |
|
| 3702 | + | @test unsafe fn testOpaquePointerNoArithmetic() throws (testing::TestError) { |
|
| 3703 | 3703 | { |
|
| 3704 | 3704 | let mut a = testResolver(); |
|
| 3705 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = o + 1; }"); |
|
| 3705 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o + 1; }"); |
|
| 3706 | 3706 | let err = try expectError(&result); |
|
| 3707 | 3707 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 3708 | 3708 | } { |
|
| 3709 | 3709 | let mut a = testResolver(); |
|
| 3710 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = 1 + o; }"); |
|
| 3710 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 + o; }"); |
|
| 3711 | 3711 | let err = try expectError(&result); |
|
| 3712 | 3712 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 3713 | 3713 | } { |
|
| 3714 | 3714 | let mut a = testResolver(); |
|
| 3715 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = o - 1; }"); |
|
| 3715 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o - 1; }"); |
|
| 3716 | 3716 | let err = try expectError(&result); |
|
| 3717 | 3717 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 3718 | 3718 | } { |
|
| 3719 | 3719 | let mut a = testResolver(); |
|
| 3720 | - | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let x = 1 - o; }"); |
|
| 3720 | + | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 - o; }"); |
|
| 3721 | 3721 | let err = try expectError(&result); |
|
| 3722 | 3722 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
|
| 3723 | 3723 | } |
|
| 3724 | 3724 | } |
|
| 3725 | 3725 | ||
| 3726 | 3726 | // Wildcard import/reexport tests. |
|
| 3727 | 3727 | ||
| 3728 | 3728 | /// Test transitive re-export. |
|
| 3729 | - | @test fn testWildcardReexportTransitive() throws (testing::TestError) { |
|
| 3729 | + | @test unsafe fn testWildcardReexportTransitive() throws (testing::TestError) { |
|
| 3730 | 3730 | let mut a = testResolver(); |
|
| 3731 | 3731 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3732 | 3732 | ||
| 3733 | 3733 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena); |
|
| 3734 | 3734 | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena); |
| 3739 | 3739 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3740 | 3740 | try expectNoErrors(&result); |
|
| 3741 | 3741 | } |
|
| 3742 | 3742 | ||
| 3743 | 3743 | /// Test that wildcard import can access public symbols. |
|
| 3744 | - | @test fn testWildcardImportPublicOnly() throws (testing::TestError) { |
|
| 3744 | + | @test unsafe fn testWildcardImportPublicOnly() throws (testing::TestError) { |
|
| 3745 | 3745 | let mut a = testResolver(); |
|
| 3746 | 3746 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3747 | 3747 | ||
| 3748 | 3748 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
|
| 3749 | 3749 | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export record Value { number: i32 } export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
| 3752 | 3752 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3753 | 3753 | try expectNoErrors(&result); |
|
| 3754 | 3754 | } |
|
| 3755 | 3755 | ||
| 3756 | 3756 | /// Test that wildcard import cannot access private symbols. |
|
| 3757 | - | @test fn testWildcardImportSkipsPrivate() throws (testing::TestError) { |
|
| 3757 | + | @test unsafe fn testWildcardImportSkipsPrivate() throws (testing::TestError) { |
|
| 3758 | 3758 | let mut a = testResolver(); |
|
| 3759 | 3759 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3760 | 3760 | ||
| 3761 | 3761 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
|
| 3762 | 3762 | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
| 3765 | 3765 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3766 | 3766 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 3767 | 3767 | } |
|
| 3768 | 3768 | ||
| 3769 | 3769 | /// Test that a constant array can use another constant as its length. |
|
| 3770 | - | @test fn testConstArrayWithConstLength() throws (testing::TestError) { |
|
| 3770 | + | @test unsafe fn testConstArrayWithConstLength() throws (testing::TestError) { |
|
| 3771 | 3771 | let mut a = testResolver(); |
|
| 3772 | 3772 | let program = "constant LEN: u32 = 3; constant ARR: [i32; LEN] = [1, 2, 3];"; |
|
| 3773 | 3773 | let result = try resolveProgramStr(&mut a, program); |
|
| 3774 | 3774 | try expectNoErrors(&result); |
|
| 3775 | 3775 |
| 3781 | 3781 | else throw testing::TestError::Failed; |
|
| 3782 | 3782 | try testing::expect(arrType.length == 3); |
|
| 3783 | 3783 | } |
|
| 3784 | 3784 | ||
| 3785 | 3785 | /// Test that a record field can use a constant as its array length. |
|
| 3786 | - | @test fn testRecordFieldWithConstArrayLength() throws (testing::TestError) { |
|
| 3786 | + | @test unsafe fn testRecordFieldWithConstArrayLength() throws (testing::TestError) { |
|
| 3787 | 3787 | let mut a = testResolver(); |
|
| 3788 | 3788 | let program = "constant SIZE: u32 = 4; record Buffer { data: [i32; SIZE], }"; |
|
| 3789 | 3789 | let result = try resolveProgramStr(&mut a, program); |
|
| 3790 | 3790 | try expectNoErrors(&result); |
|
| 3791 | 3791 | } |
|
| 3792 | 3792 | ||
| 3793 | 3793 | /// Test that a constant can have a record literal value (lazy record body resolution). |
|
| 3794 | - | @test fn testConstWithRecordLiteral() throws (testing::TestError) { |
|
| 3794 | + | @test unsafe fn testConstWithRecordLiteral() throws (testing::TestError) { |
|
| 3795 | 3795 | let mut a = testResolver(); |
|
| 3796 | 3796 | let program = "record Point { x: i32, y: i32 } constant ORIGIN: Point = Point { x: 0, y: 0 };"; |
|
| 3797 | 3797 | let result = try resolveProgramStr(&mut a, program); |
|
| 3798 | 3798 | try expectNoErrors(&result); |
|
| 3799 | 3799 | } |
|
| 3800 | 3800 | ||
| 3801 | 3801 | /// Test that a constant can have a union variant value (lazy union body resolution). |
|
| 3802 | - | @test fn testConstWithUnionVariant() throws (testing::TestError) { |
|
| 3802 | + | @test unsafe fn testConstWithUnionVariant() throws (testing::TestError) { |
|
| 3803 | 3803 | let mut a = testResolver(); |
|
| 3804 | 3804 | let program = "union Color { Red, Green, Blue } constant DEFAULT: Color = Color::Red;"; |
|
| 3805 | 3805 | let result = try resolveProgramStr(&mut a, program); |
|
| 3806 | 3806 | try expectNoErrors(&result); |
|
| 3807 | 3807 | } |
|
| 3808 | 3808 | ||
| 3809 | 3809 | /// Test that record field types can reference imported types. |
|
| 3810 | 3810 | /// |
|
| 3811 | 3811 | /// This tests that `use` statements are processed before record body resolution, |
|
| 3812 | 3812 | /// allowing record fields to use types from imported modules. |
|
| 3813 | - | @test fn testRecordFieldUsesImportedType() throws (testing::TestError) { |
|
| 3813 | + | @test unsafe fn testRecordFieldUsesImportedType() throws (testing::TestError) { |
|
| 3814 | 3814 | let mut a = testResolver(); |
|
| 3815 | 3815 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3816 | 3816 | ||
| 3817 | 3817 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena); |
|
| 3818 | 3818 | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena); |
| 3824 | 3824 | ||
| 3825 | 3825 | /// Test that imported constants can be used in array size expressions. |
|
| 3826 | 3826 | /// |
|
| 3827 | 3827 | /// This tests that constant values are propagated through scope access expressions, |
|
| 3828 | 3828 | /// enabling compile-time evaluation of array sizes using imported constants. |
|
| 3829 | - | @test fn testImportedConstantInArraySize() throws (testing::TestError) { |
|
| 3829 | + | @test unsafe fn testImportedConstantInArraySize() throws (testing::TestError) { |
|
| 3830 | 3830 | let mut a = testResolver(); |
|
| 3831 | 3831 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3832 | 3832 | ||
| 3833 | 3833 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3834 | 3834 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena); |
| 3840 | 3840 | ||
| 3841 | 3841 | /// Test that `if let case` binds payload variables in the then branch. |
|
| 3842 | 3842 | /// |
|
| 3843 | 3843 | /// When using `if let case Union::Variant(x) = expr { ... }`, the variable `x` should |
|
| 3844 | 3844 | /// be bound to the payload value within the then branch scope. |
|
| 3845 | - | @test fn testResolveIfCaseBindsPayload() throws (testing::TestError) { |
|
| 3845 | + | @test unsafe fn testResolveIfCaseBindsPayload() throws (testing::TestError) { |
|
| 3846 | 3846 | let mut a = testResolver(); |
|
| 3847 | 3847 | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value { return x; } return 0; }"; |
|
| 3848 | 3848 | let result = try resolveProgramStr(&mut a, program); |
|
| 3849 | 3849 | try expectNoErrors(&result); |
|
| 3850 | 3850 | } |
|
| 3851 | 3851 | ||
| 3852 | 3852 | /// Test that `if let case` payload binding is scoped to the then branch. |
|
| 3853 | 3853 | /// |
|
| 3854 | 3854 | /// The payload variable should not be accessible outside the then branch. |
|
| 3855 | - | @test fn testResolveIfCasePayloadScopeError() throws (testing::TestError) { |
|
| 3855 | + | @test unsafe fn testResolveIfCasePayloadScopeError() throws (testing::TestError) { |
|
| 3856 | 3856 | let mut a = testResolver(); |
|
| 3857 | 3857 | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value {} return x; }"; |
|
| 3858 | 3858 | let result = try resolveProgramStr(&mut a, program); |
|
| 3859 | 3859 | let err = try expectError(&result); |
|
| 3860 | 3860 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
| 3864 | 3864 | ||
| 3865 | 3865 | /// Test that `let case` binds payload variables in the current scope. |
|
| 3866 | 3866 | /// |
|
| 3867 | 3867 | /// When using `let case Union::Variant(x) = expr else { ... }`, the variable `x` |
|
| 3868 | 3868 | /// should be bound in the scope after the statement. |
|
| 3869 | - | @test fn testResolveLetCaseElseBindsPayload() throws (testing::TestError) { |
|
| 3869 | + | @test unsafe fn testResolveLetCaseElseBindsPayload() throws (testing::TestError) { |
|
| 3870 | 3870 | let mut a = testResolver(); |
|
| 3871 | 3871 | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { let case Opt::Some(x) = value else panic; return x; }"; |
|
| 3872 | 3872 | let result = try resolveProgramStr(&mut a, program); |
|
| 3873 | 3873 | try expectNoErrors(&result); |
|
| 3874 | 3874 | } |
|
| 3875 | 3875 | ||
| 3876 | 3876 | /// Test that function pointers with identical signatures are assignable. |
|
| 3877 | 3877 | /// |
|
| 3878 | 3878 | /// Two function types with the same parameters, return type, and throw list |
|
| 3879 | 3879 | /// should be considered structurally equal, even if they are separate allocations. |
|
| 3880 | - | @test fn testFnPointerAssignability() throws (testing::TestError) { |
|
| 3880 | + | @test unsafe fn testFnPointerAssignability() throws (testing::TestError) { |
|
| 3881 | 3881 | let mut a = testResolver(); |
|
| 3882 | 3882 | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn double(n: i32) -> i32 { return n * 2; } apply(double, 5);"; |
|
| 3883 | 3883 | let result = try resolveProgramStr(&mut a, program); |
|
| 3884 | 3884 | try expectNoErrors(&result); |
|
| 3885 | 3885 | } |
|
| 3886 | 3886 | ||
| 3887 | 3887 | /// Test that function pointers with different parameter types are not assignable. |
|
| 3888 | - | @test fn testFnPointerParamMismatch() throws (testing::TestError) { |
|
| 3888 | + | @test unsafe fn testFnPointerParamMismatch() throws (testing::TestError) { |
|
| 3889 | 3889 | let mut a = testResolver(); |
|
| 3890 | 3890 | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i8) -> i32 { return n as i32; } apply(other, 5);"; |
|
| 3891 | 3891 | let result = try resolveProgramStr(&mut a, program); |
|
| 3892 | 3892 | let err = try expectError(&result); |
|
| 3893 | 3893 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 3894 | 3894 | else throw testing::TestError::Failed; |
|
| 3895 | 3895 | } |
|
| 3896 | 3896 | ||
| 3897 | 3897 | /// Test that function pointers with different return types are not assignable. |
|
| 3898 | - | @test fn testFnPointerReturnMismatch() throws (testing::TestError) { |
|
| 3898 | + | @test unsafe fn testFnPointerReturnMismatch() throws (testing::TestError) { |
|
| 3899 | 3899 | let mut a = testResolver(); |
|
| 3900 | 3900 | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i32) -> i8 { return n as i8; } apply(other, 5);"; |
|
| 3901 | 3901 | let result = try resolveProgramStr(&mut a, program); |
|
| 3902 | 3902 | let err = try expectError(&result); |
|
| 3903 | 3903 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 3906 | 3906 | ||
| 3907 | 3907 | /// Test that named records use nominal typing, not structural. |
|
| 3908 | 3908 | /// |
|
| 3909 | 3909 | /// Two different named record types with identical fields should NOT be |
|
| 3910 | 3910 | /// assignable to each other, because they are distinct nominal types. |
|
| 3911 | - | @test fn testNamedRecordNominalTyping() throws (testing::TestError) { |
|
| 3911 | + | @test unsafe fn testNamedRecordNominalTyping() throws (testing::TestError) { |
|
| 3912 | 3912 | let mut a = testResolver(); |
|
| 3913 | 3913 | let program = "record Point { x: i32, y: i32 } record Vec2 { x: i32, y: i32 } fn take(p: Point) -> i32 { return p.x; } let v = Vec2 { x: 1, y: 2 }; take(v);"; |
|
| 3914 | 3914 | let result = try resolveProgramStr(&mut a, program); |
|
| 3915 | 3915 | let err = try expectError(&result); |
|
| 3916 | 3916 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 3917 | 3917 | else throw testing::TestError::Failed; |
|
| 3918 | 3918 | } |
|
| 3919 | 3919 | ||
| 3920 | 3920 | /// Test that union variants with labeled record payloads can be constructed. |
|
| 3921 | - | @test fn testUnionVariantAnonRecordPayload() throws (testing::TestError) { |
|
| 3921 | + | @test unsafe fn testUnionVariantAnonRecordPayload() throws (testing::TestError) { |
|
| 3922 | 3922 | let mut a = testResolver(); |
|
| 3923 | 3923 | let program = "union Event { Click { x: i32, y: i32 }, Key { code: u32 } } let e = Event::Click { x: 10, y: 20 };"; |
|
| 3924 | 3924 | let result = try resolveProgramStr(&mut a, program); |
|
| 3925 | 3925 | try expectNoErrors(&result); |
|
| 3926 | 3926 | } |
|
| 3927 | 3927 | ||
| 3928 | 3928 | /// Test that unlabeled record literals with positional fields work correctly. |
|
| 3929 | 3929 | /// |
|
| 3930 | 3930 | /// When a record is declared with positional fields (e.g., `record R(i32, bool)`), |
|
| 3931 | 3931 | /// the literal must use constructor call syntax with positional arguments. |
|
| 3932 | - | @test fn testResolveUnlabeledRecordLitValid() throws (testing::TestError) { |
|
| 3932 | + | @test unsafe fn testResolveUnlabeledRecordLitValid() throws (testing::TestError) { |
|
| 3933 | 3933 | let mut a = testResolver(); |
|
| 3934 | 3934 | let program = "record R(i32, bool); let r: R = R(1, true);"; |
|
| 3935 | 3935 | let result = try resolveProgramStr(&mut a, program); |
|
| 3936 | 3936 | try expectNoErrors(&result); |
|
| 3937 | 3937 | } |
|
| 3938 | 3938 | ||
| 3939 | 3939 | /// Test that using brace syntax for an unlabeled record causes an error. |
|
| 3940 | - | @test fn testResolveUnlabeledRecordLitStyleMismatch() throws (testing::TestError) { |
|
| 3940 | + | @test unsafe fn testResolveUnlabeledRecordLitStyleMismatch() throws (testing::TestError) { |
|
| 3941 | 3941 | let mut a = testResolver(); |
|
| 3942 | 3942 | let program = "record R(i32); let r = R { x: 1 };"; |
|
| 3943 | 3943 | let result = try resolveProgramStr(&mut a, program); |
|
| 3944 | 3944 | try expectErrorKind(&result, super::ErrorKind::RecordFieldStyleMismatch); |
|
| 3945 | 3945 | } |
|
| 3946 | 3946 | ||
| 3947 | 3947 | /// Test that providing too many fields for an unlabeled record causes count mismatch. |
|
| 3948 | - | @test fn testResolveUnlabeledRecordLitTooManyFields() throws (testing::TestError) { |
|
| 3948 | + | @test unsafe fn testResolveUnlabeledRecordLitTooManyFields() throws (testing::TestError) { |
|
| 3949 | 3949 | let mut a = testResolver(); |
|
| 3950 | 3950 | let program = "record R(i32, bool); let r = R(1, true, 3);"; |
|
| 3951 | 3951 | let result = try resolveProgramStr(&mut a, program); |
|
| 3952 | 3952 | let err = try expectError(&result); |
|
| 3953 | 3953 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 3954 | 3954 | else throw testing::TestError::Failed; |
|
| 3955 | 3955 | } |
|
| 3956 | 3956 | ||
| 3957 | 3957 | /// Test that match pattern with wrong number of bindings causes count mismatch. |
|
| 3958 | - | @test fn testResolveMatchPatternWrongBindingCount() throws (testing::TestError) { |
|
| 3958 | + | @test unsafe fn testResolveMatchPatternWrongBindingCount() throws (testing::TestError) { |
|
| 3959 | 3959 | let mut a = testResolver(); |
|
| 3960 | 3960 | let program = "union Event { Click { x: i32, y: i32 } } fn f(e: Event) { match e { case Event::Click(a) => {} } }"; |
|
| 3961 | 3961 | let result = try resolveProgramStr(&mut a, program); |
|
| 3962 | 3962 | let err = try expectError(&result); |
|
| 3963 | 3963 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 3964 | 3964 | else throw testing::TestError::Failed; |
|
| 3965 | 3965 | } |
|
| 3966 | 3966 | ||
| 3967 | 3967 | /// Test that shorthand field syntax works in record literals. |
|
| 3968 | 3968 | /// `Point { x, y }` should be equivalent to `Point { x: x, y: y }`. |
|
| 3969 | - | @test fn testResolveRecordLiteralShorthand() throws (testing::TestError) { |
|
| 3969 | + | @test unsafe fn testResolveRecordLiteralShorthand() throws (testing::TestError) { |
|
| 3970 | 3970 | let mut a = testResolver(); |
|
| 3971 | 3971 | let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 1; let y: i32 = 2; let p = Point { x, y }; }"; |
|
| 3972 | 3972 | let result = try resolveProgramStr(&mut a, program); |
|
| 3973 | 3973 | try expectNoErrors(&result); |
|
| 3974 | 3974 | } |
|
| 3975 | 3975 | ||
| 3976 | 3976 | /// Test shorthand field syntax with mixed explicit and shorthand fields. |
|
| 3977 | - | @test fn testResolveRecordLiteralMixedShorthand() throws (testing::TestError) { |
|
| 3977 | + | @test unsafe fn testResolveRecordLiteralMixedShorthand() throws (testing::TestError) { |
|
| 3978 | 3978 | let mut a = testResolver(); |
|
| 3979 | 3979 | let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 5; let p = Point { x, y: 10 }; }"; |
|
| 3980 | 3980 | let result = try resolveProgramStr(&mut a, program); |
|
| 3981 | 3981 | try expectNoErrors(&result); |
|
| 3982 | 3982 | } |
|
| 3983 | 3983 | ||
| 3984 | 3984 | /// Test record-style union variant patterns with shorthand syntax. |
|
| 3985 | - | @test fn testResolveMatchRecordPatternShorthand() throws (testing::TestError) { |
|
| 3985 | + | @test unsafe fn testResolveMatchRecordPatternShorthand() throws (testing::TestError) { |
|
| 3986 | 3986 | let mut a = testResolver(); |
|
| 3987 | 3987 | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height } => return width + height } }"; |
|
| 3988 | 3988 | let result = try resolveProgramStr(&mut a, program); |
|
| 3989 | 3989 | try expectNoErrors(&result); |
|
| 3990 | 3990 | } |
|
| 3991 | 3991 | ||
| 3992 | 3992 | /// Test record pattern with mixed shorthand and explicit labels. |
|
| 3993 | - | @test fn testResolveMatchRecordPatternMixed() throws (testing::TestError) { |
|
| 3993 | + | @test unsafe fn testResolveMatchRecordPatternMixed() throws (testing::TestError) { |
|
| 3994 | 3994 | let mut a = testResolver(); |
|
| 3995 | 3995 | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height: h } => return width + h } }"; |
|
| 3996 | 3996 | let result = try resolveProgramStr(&mut a, program); |
|
| 3997 | 3997 | try expectNoErrors(&result); |
|
| 3998 | 3998 | } |
|
| 3999 | 3999 | ||
| 4000 | 4000 | /// Test record pattern with fields in reverse order. |
|
| 4001 | - | @test fn testResolveMatchRecordPatternReversed() throws (testing::TestError) { |
|
| 4001 | + | @test unsafe fn testResolveMatchRecordPatternReversed() throws (testing::TestError) { |
|
| 4002 | 4002 | let mut a = testResolver(); |
|
| 4003 | 4003 | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height: h, width: w } => return w + h } }"; |
|
| 4004 | 4004 | let result = try resolveProgramStr(&mut a, program); |
|
| 4005 | 4005 | try expectNoErrors(&result); |
|
| 4006 | 4006 | } |
|
| 4007 | 4007 | ||
| 4008 | 4008 | /// Test record pattern with shorthand syntax in reverse order. |
|
| 4009 | 4009 | /// Pattern `{ height, width }` binds all fields using shorthand, but not in definition order. |
|
| 4010 | - | @test fn testResolveMatchRecordPatternShorthandReversed() throws (testing::TestError) { |
|
| 4010 | + | @test unsafe fn testResolveMatchRecordPatternShorthandReversed() throws (testing::TestError) { |
|
| 4011 | 4011 | let mut a = testResolver(); |
|
| 4012 | 4012 | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height, width } => return width + height } }"; |
|
| 4013 | 4013 | let result = try resolveProgramStr(&mut a, program); |
|
| 4014 | 4014 | try expectNoErrors(&result); |
|
| 4015 | 4015 | } |
|
| 4016 | 4016 | ||
| 4017 | 4017 | /// Test record pattern with `..` ignoring fields. |
|
| 4018 | - | @test fn testResolveMatchRecordPatternIgnoreRest() throws (testing::TestError) { |
|
| 4018 | + | @test unsafe fn testResolveMatchRecordPatternIgnoreRest() throws (testing::TestError) { |
|
| 4019 | 4019 | { |
|
| 4020 | 4020 | let mut a = testResolver(); |
|
| 4021 | 4021 | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x, .. } => return x } }"; |
|
| 4022 | 4022 | let result = try resolveProgramStr(&mut a, program); |
|
| 4023 | 4023 | try expectNoErrors(&result); |
| 4043 | 4043 | try expectNoErrors(&result); |
|
| 4044 | 4044 | } |
|
| 4045 | 4045 | } |
|
| 4046 | 4046 | ||
| 4047 | 4047 | /// Test standalone record pattern matching with unlabeled patterns. |
|
| 4048 | - | @test fn testResolveMatchStandaloneRecordUnlabeledPattern() throws (testing::TestError) { |
|
| 4048 | + | @test unsafe fn testResolveMatchStandaloneRecordUnlabeledPattern() throws (testing::TestError) { |
|
| 4049 | 4049 | let mut a = testResolver(); |
|
| 4050 | 4050 | let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x) => return x, else => return 0 } }"; |
|
| 4051 | 4051 | let result = try resolveProgramStr(&mut a, program); |
|
| 4052 | 4052 | try expectNoErrors(&result); |
|
| 4053 | 4053 | } |
|
| 4054 | 4054 | ||
| 4055 | 4055 | /// Test standalone record pattern matching with labeled patterns. |
|
| 4056 | 4056 | /// Pattern syntax: `T { x }` matches a named record and binds x to the field. |
|
| 4057 | - | @test fn testResolveMatchStandaloneRecordLabeledPattern() throws (testing::TestError) { |
|
| 4057 | + | @test unsafe fn testResolveMatchStandaloneRecordLabeledPattern() throws (testing::TestError) { |
|
| 4058 | 4058 | let mut a = testResolver(); |
|
| 4059 | 4059 | let program = "record T { x: i32 } fn f(t: T) -> i32 { match t { case T { x } => return x, else => return 0 } }"; |
|
| 4060 | 4060 | let result = try resolveProgramStr(&mut a, program); |
|
| 4061 | 4061 | try expectNoErrors(&result); |
|
| 4062 | 4062 | } |
|
| 4063 | 4063 | ||
| 4064 | 4064 | /// Test standalone record pattern with multiple fields. |
|
| 4065 | 4065 | /// Pattern syntax: `R(a, b)` matches an unlabeled record with multiple fields. |
|
| 4066 | - | @test fn testResolveMatchStandaloneRecordMultipleFields() throws (testing::TestError) { |
|
| 4066 | + | @test unsafe fn testResolveMatchStandaloneRecordMultipleFields() throws (testing::TestError) { |
|
| 4067 | 4067 | let mut a = testResolver(); |
|
| 4068 | 4068 | let program = "record R(bool, u8); fn f(r: R) -> u8 { match r { case R(_, x) => return x, else => return 0 } }"; |
|
| 4069 | 4069 | let result = try resolveProgramStr(&mut a, program); |
|
| 4070 | 4070 | try expectNoErrors(&result); |
|
| 4071 | 4071 | } |
|
| 4072 | 4072 | ||
| 4073 | 4073 | /// Test standalone record pattern with wrong field count. |
|
| 4074 | 4074 | /// Pattern `S(x, y)` should fail for a single-field record. |
|
| 4075 | - | @test fn testResolveMatchStandaloneRecordWrongFieldCount() throws (testing::TestError) { |
|
| 4075 | + | @test unsafe fn testResolveMatchStandaloneRecordWrongFieldCount() throws (testing::TestError) { |
|
| 4076 | 4076 | let mut a = testResolver(); |
|
| 4077 | 4077 | let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x, y) => return x + y, else => return 0 } }"; |
|
| 4078 | 4078 | let result = try resolveProgramStr(&mut a, program); |
|
| 4079 | 4079 | let err = try expectError(&result); |
|
| 4080 | 4080 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
|
| 4081 | 4081 | else throw testing::TestError::Failed; |
|
| 4082 | 4082 | } |
|
| 4083 | 4083 | ||
| 4084 | 4084 | /// Test array pattern matching with element bindings. |
|
| 4085 | 4085 | /// Pattern syntax: `[x, y]` matches an array and binds elements. |
|
| 4086 | - | @test fn testResolveMatchArrayPattern() throws (testing::TestError) { |
|
| 4086 | + | @test unsafe fn testResolveMatchArrayPattern() throws (testing::TestError) { |
|
| 4087 | 4087 | let mut a = testResolver(); |
|
| 4088 | 4088 | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [x, y] => return x + y } }"; |
|
| 4089 | 4089 | let result = try resolveProgramStr(&mut a, program); |
|
| 4090 | 4090 | try expectNoErrors(&result); |
|
| 4091 | 4091 | } |
|
| 4092 | 4092 | ||
| 4093 | 4093 | /// Test array pattern with placeholder elements. |
|
| 4094 | 4094 | /// Pattern syntax: `[_, y]` ignores first element. |
|
| 4095 | - | @test fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) { |
|
| 4095 | + | @test unsafe fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) { |
|
| 4096 | 4096 | let mut a = testResolver(); |
|
| 4097 | 4097 | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }"; |
|
| 4098 | 4098 | let result = try resolveProgramStr(&mut a, program); |
|
| 4099 | 4099 | try expectNoErrors(&result); |
|
| 4100 | 4100 | } |
|
| 4101 | 4101 | ||
| 4102 | 4102 | /// Test identifier pattern that binds the whole value. |
|
| 4103 | 4103 | /// Pattern syntax: `x` matches any value and binds it. |
|
| 4104 | - | @test fn testResolveMatchIdentPattern() throws (testing::TestError) { |
|
| 4104 | + | @test unsafe fn testResolveMatchIdentPattern() throws (testing::TestError) { |
|
| 4105 | 4105 | let mut a = testResolver(); |
|
| 4106 | 4106 | let program = "fn f(val: i32) -> i32 { match val { x => return x } }"; |
|
| 4107 | 4107 | let result = try resolveProgramStr(&mut a, program); |
|
| 4108 | 4108 | try expectNoErrors(&result); |
|
| 4109 | 4109 | } |
|
| 4110 | 4110 | ||
| 4111 | 4111 | /// Test numeric literal pattern matching. |
|
| 4112 | - | @test fn testResolveMatchNumericLiteralPattern() throws (testing::TestError) { |
|
| 4112 | + | @test unsafe fn testResolveMatchNumericLiteralPattern() throws (testing::TestError) { |
|
| 4113 | 4113 | let mut a = testResolver(); |
|
| 4114 | 4114 | let program = "fn f(val: i32) -> i32 { match val { case 42 => return 1, else => return 0 } }"; |
|
| 4115 | 4115 | let result = try resolveProgramStr(&mut a, program); |
|
| 4116 | 4116 | try expectNoErrors(&result); |
|
| 4117 | 4117 | } |
|
| 4118 | 4118 | ||
| 4119 | 4119 | /// Test string literal pattern matching. |
|
| 4120 | - | @test fn testResolveMatchStringLiteralPattern() throws (testing::TestError) { |
|
| 4120 | + | @test unsafe fn testResolveMatchStringLiteralPattern() throws (testing::TestError) { |
|
| 4121 | 4121 | let mut a = testResolver(); |
|
| 4122 | 4122 | let program = "fn f(val: *[u8]) -> i32 { match val { case \"hello\" => return 1, else => return 0 } }"; |
|
| 4123 | 4123 | let result = try resolveProgramStr(&mut a, program); |
|
| 4124 | 4124 | try expectNoErrors(&result); |
|
| 4125 | 4125 | } |
|
| 4126 | 4126 | ||
| 4127 | 4127 | /// Test boolean literal pattern matching. |
|
| 4128 | - | @test fn testResolveMatchBoolLiteralPattern() throws (testing::TestError) { |
|
| 4128 | + | @test unsafe fn testResolveMatchBoolLiteralPattern() throws (testing::TestError) { |
|
| 4129 | 4129 | let mut a = testResolver(); |
|
| 4130 | 4130 | let program = "fn f(val: bool) -> i32 { match val { case true => return 1, case false => return 0 } }"; |
|
| 4131 | 4131 | let result = try resolveProgramStr(&mut a, program); |
|
| 4132 | 4132 | try expectNoErrors(&result); |
|
| 4133 | 4133 | } |
|
| 4134 | 4134 | ||
| 4135 | 4135 | /// Test @sliceOf with correct arguments succeeds. |
|
| 4136 | - | @test fn testResolveSliceOfCorrect() throws (testing::TestError) { |
|
| 4136 | + | @test unsafe fn testResolveSliceOfCorrect() throws (testing::TestError) { |
|
| 4137 | 4137 | // Immutable pointer. |
|
| 4138 | 4138 | { |
|
| 4139 | 4139 | let mut a = testResolver(); |
|
| 4140 | 4140 | let program = "fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4141 | 4141 | let result = try resolveProgramStr(&mut a, program); |
| 4149 | 4149 | try expectNoErrors(&result); |
|
| 4150 | 4150 | } |
|
| 4151 | 4151 | } |
|
| 4152 | 4152 | ||
| 4153 | 4153 | /// Test @sliceOf with wrong argument count produces an error. |
|
| 4154 | - | @test fn testResolveSliceOfWrongArgCount() throws (testing::TestError) { |
|
| 4154 | + | @test unsafe fn testResolveSliceOfWrongArgCount() throws (testing::TestError) { |
|
| 4155 | 4155 | // No arguments. |
|
| 4156 | 4156 | { |
|
| 4157 | 4157 | let mut a = testResolver(); |
|
| 4158 | 4158 | let program = "fn f() -> *[u8] { return @sliceOf(); }"; |
|
| 4159 | 4159 | let result = try resolveProgramStr(&mut a, program); |
| 4186 | 4186 | try testing::expect(mismatch.actual == 4); |
|
| 4187 | 4187 | } |
|
| 4188 | 4188 | } |
|
| 4189 | 4189 | ||
| 4190 | 4190 | /// Test @sliceOf with wrong argument types produces errors. |
|
| 4191 | - | @test fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) { |
|
| 4191 | + | @test unsafe fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) { |
|
| 4192 | 4192 | // Non-pointer first argument. |
|
| 4193 | 4193 | { |
|
| 4194 | 4194 | let mut a = testResolver(); |
|
| 4195 | 4195 | let program = "fn f(val: u32, len: u32) -> *[u8] { return @sliceOf(val, len); }"; |
|
| 4196 | 4196 | let result = try resolveProgramStr(&mut a, program); |
| 4226 | 4226 | else throw testing::TestError::Failed; |
|
| 4227 | 4227 | } |
|
| 4228 | 4228 | } |
|
| 4229 | 4229 | ||
| 4230 | 4230 | /// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds. |
|
| 4231 | - | @test fn testResolveSliceOfWithCap() throws (testing::TestError) { |
|
| 4231 | + | @test unsafe fn testResolveSliceOfWithCap() throws (testing::TestError) { |
|
| 4232 | 4232 | { |
|
| 4233 | 4233 | let mut a = testResolver(); |
|
| 4234 | 4234 | let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4235 | 4235 | let result = try resolveProgramStr(&mut a, program); |
|
| 4236 | 4236 | try expectNoErrors(&result); |
| 4243 | 4243 | try expectNoErrors(&result); |
|
| 4244 | 4244 | } |
|
| 4245 | 4245 | } |
|
| 4246 | 4246 | ||
| 4247 | 4247 | /// Test @sliceOf with 3 arguments but wrong cap type. |
|
| 4248 | - | @test fn testResolveSliceOfCapWrongType() throws (testing::TestError) { |
|
| 4248 | + | @test unsafe fn testResolveSliceOfCapWrongType() throws (testing::TestError) { |
|
| 4249 | 4249 | let mut a = testResolver(); |
|
| 4250 | 4250 | let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4251 | 4251 | let result = try resolveProgramStr(&mut a, program); |
|
| 4252 | 4252 | let err = try expectError(&result); |
|
| 4253 | 4253 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4254 | 4254 | else throw testing::TestError::Failed; |
|
| 4255 | 4255 | } |
|
| 4256 | 4256 | ||
| 4257 | 4257 | /// Test .cap field access on slices resolves to u32. |
|
| 4258 | - | @test fn testResolveSliceCapField() throws (testing::TestError) { |
|
| 4258 | + | @test unsafe fn testResolveSliceCapField() throws (testing::TestError) { |
|
| 4259 | 4259 | let mut a = testResolver(); |
|
| 4260 | 4260 | let program = "fn f(s: *[u8]) -> u32 { return s.cap; }"; |
|
| 4261 | 4261 | let result = try resolveProgramStr(&mut a, program); |
|
| 4262 | 4262 | try expectNoErrors(&result); |
|
| 4263 | 4263 | } |
|
| 4264 | 4264 | ||
| 4265 | 4265 | /// Test `.append()` on immutable slice produces an error. |
|
| 4266 | - | @test fn testResolveSliceAppendImmutable() throws (testing::TestError) { |
|
| 4266 | + | @test unsafe fn testResolveSliceAppendImmutable() throws (testing::TestError) { |
|
| 4267 | 4267 | let mut a = testResolver(); |
|
| 4268 | 4268 | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *[i32], a: A) { s.append(1, a); }"; |
|
| 4269 | 4269 | let result = try resolveProgramStr(&mut a, program); |
|
| 4270 | 4270 | let err = try expectError(&result); |
|
| 4271 | 4271 | let case super::ErrorKind::ImmutableBinding = err.kind |
|
| 4272 | 4272 | else throw testing::TestError::Failed; |
|
| 4273 | 4273 | } |
|
| 4274 | 4274 | ||
| 4275 | 4275 | /// Test `.append()` with wrong argument count produces an error. |
|
| 4276 | - | @test fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) { |
|
| 4276 | + | @test unsafe fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) { |
|
| 4277 | 4277 | // Too few arguments. |
|
| 4278 | 4278 | { |
|
| 4279 | 4279 | let mut a = testResolver(); |
|
| 4280 | 4280 | let program = "fn f(s: *mut [i32]) { s.append(1); }"; |
|
| 4281 | 4281 | let result = try resolveProgramStr(&mut a, program); |
| 4297 | 4297 | try testing::expect(m.actual == 3); |
|
| 4298 | 4298 | } |
|
| 4299 | 4299 | } |
|
| 4300 | 4300 | ||
| 4301 | 4301 | /// Test `.append()` with correct arguments succeeds. |
|
| 4302 | - | @test fn testResolveSliceAppendCorrect() throws (testing::TestError) { |
|
| 4302 | + | @test unsafe fn testResolveSliceAppendCorrect() throws (testing::TestError) { |
|
| 4303 | 4303 | let mut a = testResolver(); |
|
| 4304 | 4304 | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a); }"; |
|
| 4305 | 4305 | let result = try resolveProgramStr(&mut a, program); |
|
| 4306 | 4306 | try expectNoErrors(&result); |
|
| 4307 | 4307 | } |
|
| 4308 | 4308 | ||
| 4309 | 4309 | /// Test `.append()` with wrong element type produces an error. |
|
| 4310 | - | @test fn testResolveSliceAppendWrongElemType() throws (testing::TestError) { |
|
| 4310 | + | @test unsafe fn testResolveSliceAppendWrongElemType() throws (testing::TestError) { |
|
| 4311 | 4311 | let mut a = testResolver(); |
|
| 4312 | 4312 | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(true, a); }"; |
|
| 4313 | 4313 | let result = try resolveProgramStr(&mut a, program); |
|
| 4314 | 4314 | let err = try expectError(&result); |
|
| 4315 | 4315 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4316 | 4316 | else throw testing::TestError::Failed; |
|
| 4317 | 4317 | } |
|
| 4318 | 4318 | ||
| 4319 | 4319 | /// Test `.delete()` on immutable slice produces an error. |
|
| 4320 | - | @test fn testResolveSliceDeleteImmutable() throws (testing::TestError) { |
|
| 4320 | + | @test unsafe fn testResolveSliceDeleteImmutable() throws (testing::TestError) { |
|
| 4321 | 4321 | let mut a = testResolver(); |
|
| 4322 | 4322 | let program = "fn f(s: *[i32]) { s.delete(0); }"; |
|
| 4323 | 4323 | let result = try resolveProgramStr(&mut a, program); |
|
| 4324 | 4324 | let err = try expectError(&result); |
|
| 4325 | 4325 | let case super::ErrorKind::ImmutableBinding = err.kind |
|
| 4326 | 4326 | else throw testing::TestError::Failed; |
|
| 4327 | 4327 | } |
|
| 4328 | 4328 | ||
| 4329 | 4329 | /// Test `.delete()` with wrong argument count produces an error. |
|
| 4330 | - | @test fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) { |
|
| 4330 | + | @test unsafe fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) { |
|
| 4331 | 4331 | // No arguments. |
|
| 4332 | 4332 | { |
|
| 4333 | 4333 | let mut a = testResolver(); |
|
| 4334 | 4334 | let program = "fn f(s: *mut [i32]) { s.delete(); }"; |
|
| 4335 | 4335 | let result = try resolveProgramStr(&mut a, program); |
| 4351 | 4351 | try testing::expect(m.actual == 2); |
|
| 4352 | 4352 | } |
|
| 4353 | 4353 | } |
|
| 4354 | 4354 | ||
| 4355 | 4355 | /// Test `.delete()` with correct arguments succeeds. |
|
| 4356 | - | @test fn testResolveSliceDeleteCorrect() throws (testing::TestError) { |
|
| 4356 | + | @test unsafe fn testResolveSliceDeleteCorrect() throws (testing::TestError) { |
|
| 4357 | 4357 | let mut a = testResolver(); |
|
| 4358 | 4358 | let program = "fn f(s: *mut [i32]) { s.delete(0); }"; |
|
| 4359 | 4359 | let result = try resolveProgramStr(&mut a, program); |
|
| 4360 | 4360 | try expectNoErrors(&result); |
|
| 4361 | 4361 | } |
|
| 4362 | 4362 | ||
| 4363 | 4363 | /// Test `.delete()` with wrong argument type produces an error. |
|
| 4364 | - | @test fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) { |
|
| 4364 | + | @test unsafe fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) { |
|
| 4365 | 4365 | let mut a = testResolver(); |
|
| 4366 | 4366 | let program = "fn f(s: *mut [i32]) { s.delete(true); }"; |
|
| 4367 | 4367 | let result = try resolveProgramStr(&mut a, program); |
|
| 4368 | 4368 | let err = try expectError(&result); |
|
| 4369 | 4369 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4370 | 4370 | else throw testing::TestError::Failed; |
|
| 4371 | 4371 | } |
|
| 4372 | 4372 | ||
| 4373 | 4373 | /// Test `match &opt` produces immutable pointer bindings. |
|
| 4374 | - | @test fn testResolveMatchRefUnionBinding() throws (testing::TestError) { |
|
| 4374 | + | @test unsafe fn testResolveMatchRefUnionBinding() throws (testing::TestError) { |
|
| 4375 | 4375 | let mut a = testResolver(); |
|
| 4376 | 4376 | let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4377 | 4377 | let result = try resolveProgramStr(&mut a, program); |
|
| 4378 | 4378 | try expectNoErrors(&result); |
|
| 4379 | 4379 |
| 4394 | 4394 | try testing::expect(not mutable); |
|
| 4395 | 4395 | try testing::expect(*target == super::Type::I32); |
|
| 4396 | 4396 | } |
|
| 4397 | 4397 | ||
| 4398 | 4398 | /// Test `match &mut opt` produces mutable pointer bindings. |
|
| 4399 | - | @test fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) { |
|
| 4399 | + | @test unsafe fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) { |
|
| 4400 | 4400 | let mut a = testResolver(); |
|
| 4401 | 4401 | let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4402 | 4402 | let result = try resolveProgramStr(&mut a, program); |
|
| 4403 | 4403 | try expectNoErrors(&result); |
|
| 4404 | 4404 |
| 4419 | 4419 | try testing::expect(mutable); |
|
| 4420 | 4420 | try testing::expect(*target == super::Type::I32); |
|
| 4421 | 4421 | } |
|
| 4422 | 4422 | ||
| 4423 | 4423 | /// Non-constant integer widening must use an explicit cast. |
|
| 4424 | - | @test fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) { |
|
| 4424 | + | @test unsafe fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) { |
|
| 4425 | 4425 | { |
|
| 4426 | 4426 | let mut a = testResolver(); |
|
| 4427 | 4427 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x;"); |
|
| 4428 | 4428 | let err = try expectError(&result); |
|
| 4429 | 4429 | try expectTypeMismatch(err, super::Type::U32, super::Type::U8); |
| 4452 | 4452 | try expectNoErrors(&result); |
|
| 4453 | 4453 | } |
|
| 4454 | 4454 | } |
|
| 4455 | 4455 | ||
| 4456 | 4456 | /// Mixed-width integer binary ops require an explicit cast. |
|
| 4457 | - | @test fn testResolveIntegerWideningBinOpRequiresCast() throws (testing::TestError) { |
|
| 4457 | + | @test unsafe fn testResolveIntegerWideningBinOpRequiresCast() throws (testing::TestError) { |
|
| 4458 | 4458 | { |
|
| 4459 | 4459 | let mut a = testResolver(); |
|
| 4460 | 4460 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x | y;"); |
|
| 4461 | 4461 | let err = try expectError(&result); |
|
| 4462 | 4462 | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
| 4484 | 4484 | try expectNoErrors(&result); |
|
| 4485 | 4485 | } |
|
| 4486 | 4486 | } |
|
| 4487 | 4487 | ||
| 4488 | 4488 | /// A mutable slice pointer should be assignable to an immutable slice pointer. |
|
| 4489 | - | @test fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) { |
|
| 4489 | + | @test unsafe fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) { |
|
| 4490 | 4490 | let mut a = testResolver(); |
|
| 4491 | - | let result = try resolveBlockStr(&mut a, "let mut arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;"); |
|
| 4491 | + | let result = try resolveBlockStr(&mut a, "static arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;"); |
|
| 4492 | 4492 | try expectNoErrors(&result); |
|
| 4493 | 4493 | } |
|
| 4494 | 4494 | ||
| 4495 | 4495 | /// Comprehensive tests for `as` cast expressions. |
|
| 4496 | - | @test fn testResolveAsCasts() throws (testing::TestError) { |
|
| 4496 | + | @test unsafe fn testResolveAsCasts() throws (testing::TestError) { |
|
| 4497 | 4497 | { // Pointer to numeric. |
|
| 4498 | 4498 | let mut a = testResolver(); |
|
| 4499 | - | let result = try resolveBlockStr(&mut a, "let x: i32 = 0; let p = &x; p as u32;"); |
|
| 4499 | + | let result = try resolveBlockStr(&mut a, "static x: i32 = 0; let p = &x; p as u32;"); |
|
| 4500 | 4500 | try expectNoErrors(&result); |
|
| 4501 | 4501 | } { // Function pointer to numeric. |
|
| 4502 | 4502 | let mut a = testResolver(); |
|
| 4503 | 4503 | let result = try resolveBlockStr(&mut a, "let f: fn() = undefined; f as u32;"); |
|
| 4504 | 4504 | try expectNoErrors(&result); |
| 4556 | 4556 | try expectNoErrors(&result); |
|
| 4557 | 4557 | } |
|
| 4558 | 4558 | } |
|
| 4559 | 4559 | ||
| 4560 | 4560 | /// Tests for invalid `as` casts that should be rejected. |
|
| 4561 | - | @test fn testResolveAsCastsInvalid() throws (testing::TestError) { |
|
| 4561 | + | @test unsafe fn testResolveAsCastsInvalid() throws (testing::TestError) { |
|
| 4562 | 4562 | { // Pointer to slice is invalid. |
|
| 4563 | 4563 | let mut a = testResolver(); |
|
| 4564 | 4564 | let result = try resolveBlockStr(&mut a, "let p: *i32 = undefined; p as *[i32];"); |
|
| 4565 | 4565 | let err = try expectError(&result); |
|
| 4566 | 4566 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 4597 | 4597 | else throw testing::TestError::Failed; |
|
| 4598 | 4598 | } |
|
| 4599 | 4599 | } |
|
| 4600 | 4600 | ||
| 4601 | 4601 | /// Test that catch binding is available in catch block scope. |
|
| 4602 | - | @test fn testResolveTryCatchBinding() throws (testing::TestError) { |
|
| 4602 | + | @test unsafe fn testResolveTryCatchBinding() throws (testing::TestError) { |
|
| 4603 | 4603 | { |
|
| 4604 | 4604 | let mut a = testResolver(); |
|
| 4605 | 4605 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { return 0; }; }"; |
|
| 4606 | 4606 | let result = try resolveProgramStr(&mut a, program); |
|
| 4607 | 4607 | try expectNoErrors(&result); |
| 4622 | 4622 | try expectNoErrors(&result); |
|
| 4623 | 4623 | } |
|
| 4624 | 4624 | } |
|
| 4625 | 4625 | ||
| 4626 | 4626 | /// Test that duplicate union variant patterns are detected. |
|
| 4627 | - | @test fn testResolveMatchDuplicateUnionPattern() throws (testing::TestError) { |
|
| 4627 | + | @test unsafe fn testResolveMatchDuplicateUnionPattern() throws (testing::TestError) { |
|
| 4628 | 4628 | { |
|
| 4629 | 4629 | let mut a = testResolver(); |
|
| 4630 | 4630 | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::A => {}, else => {} } }"; |
|
| 4631 | 4631 | let result = try resolveProgramStr(&mut a, program); |
|
| 4632 | 4632 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
| 4638 | 4638 | try expectNoErrors(&result); |
|
| 4639 | 4639 | } |
|
| 4640 | 4640 | } |
|
| 4641 | 4641 | ||
| 4642 | 4642 | /// Test that duplicate bool patterns are detected. |
|
| 4643 | - | @test fn testResolveMatchDuplicateBoolPattern() throws (testing::TestError) { |
|
| 4643 | + | @test unsafe fn testResolveMatchDuplicateBoolPattern() throws (testing::TestError) { |
|
| 4644 | 4644 | { |
|
| 4645 | 4645 | let mut a = testResolver(); |
|
| 4646 | 4646 | let program = "fn f(x: bool) { match x { case true => {}, case true => {}, else => {} } }"; |
|
| 4647 | 4647 | let result = try resolveProgramStr(&mut a, program); |
|
| 4648 | 4648 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
| 4653 | 4653 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 4654 | 4654 | } |
|
| 4655 | 4655 | } |
|
| 4656 | 4656 | ||
| 4657 | 4657 | /// Test that duplicate nil patterns in optional match are detected. |
|
| 4658 | - | @test fn testResolveMatchDuplicateOptionalPattern() throws (testing::TestError) { |
|
| 4658 | + | @test unsafe fn testResolveMatchDuplicateOptionalPattern() throws (testing::TestError) { |
|
| 4659 | 4659 | { |
|
| 4660 | 4660 | let mut a = testResolver(); |
|
| 4661 | 4661 | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, case nil => {} } }"; |
|
| 4662 | 4662 | let result = try resolveProgramStr(&mut a, program); |
|
| 4663 | 4663 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
| 4669 | 4669 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
|
| 4670 | 4670 | } |
|
| 4671 | 4671 | } |
|
| 4672 | 4672 | ||
| 4673 | 4673 | /// Test that guarded match arms are not considered duplicates. |
|
| 4674 | - | @test fn testResolveMatchGuardedNotDuplicate() throws (testing::TestError) { |
|
| 4674 | + | @test unsafe fn testResolveMatchGuardedNotDuplicate() throws (testing::TestError) { |
|
| 4675 | 4675 | { |
|
| 4676 | 4676 | // Guarded union variant followed by same variant is fine. |
|
| 4677 | 4677 | let mut a = testResolver(); |
|
| 4678 | 4678 | let program = "union U { A, B } fn f(u: U) { match u { case U::A if true => {}, case U::A => {}, case U::B => {} } }"; |
|
| 4679 | 4679 | let result = try resolveProgramStr(&mut a, program); |
| 4698 | 4698 | try expectNoErrors(&result); |
|
| 4699 | 4699 | } |
|
| 4700 | 4700 | } |
|
| 4701 | 4701 | ||
| 4702 | 4702 | /// Test that unreachable else is detected when all union variants are covered. |
|
| 4703 | - | @test fn testResolveMatchUnreachableElseUnion() throws (testing::TestError) { |
|
| 4703 | + | @test unsafe fn testResolveMatchUnreachableElseUnion() throws (testing::TestError) { |
|
| 4704 | 4704 | { |
|
| 4705 | 4705 | let mut a = testResolver(); |
|
| 4706 | 4706 | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {}, else => {} } }"; |
|
| 4707 | 4707 | let result = try resolveProgramStr(&mut a, program); |
|
| 4708 | 4708 | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
| 4714 | 4714 | try expectNoErrors(&result); |
|
| 4715 | 4715 | } |
|
| 4716 | 4716 | } |
|
| 4717 | 4717 | ||
| 4718 | 4718 | /// Test that unreachable else is detected when both bool cases are covered. |
|
| 4719 | - | @test fn testResolveMatchUnreachableElseBool() throws (testing::TestError) { |
|
| 4719 | + | @test unsafe fn testResolveMatchUnreachableElseBool() throws (testing::TestError) { |
|
| 4720 | 4720 | { |
|
| 4721 | 4721 | let mut a = testResolver(); |
|
| 4722 | 4722 | let program = "fn f(x: bool) { match x { case true => {}, case false => {}, else => {} } }"; |
|
| 4723 | 4723 | let result = try resolveProgramStr(&mut a, program); |
|
| 4724 | 4724 | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
| 4730 | 4730 | try expectNoErrors(&result); |
|
| 4731 | 4731 | } |
|
| 4732 | 4732 | } |
|
| 4733 | 4733 | ||
| 4734 | 4734 | /// Test that unreachable else is detected when both optional cases are covered. |
|
| 4735 | - | @test fn testResolveMatchUnreachableElseOptional() throws (testing::TestError) { |
|
| 4735 | + | @test unsafe fn testResolveMatchUnreachableElseOptional() throws (testing::TestError) { |
|
| 4736 | 4736 | { |
|
| 4737 | 4737 | let mut a = testResolver(); |
|
| 4738 | 4738 | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, else => {} } }"; |
|
| 4739 | 4739 | let result = try resolveProgramStr(&mut a, program); |
|
| 4740 | 4740 | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
| 4747 | 4747 | } |
|
| 4748 | 4748 | } |
|
| 4749 | 4749 | ||
| 4750 | 4750 | // --- Multi-error typed catch tests --- |
|
| 4751 | 4751 | ||
| 4752 | - | @test fn testTypedCatchExhaustive() throws (testing::TestError) { |
|
| 4752 | + | @test unsafe fn testTypedCatchExhaustive() throws (testing::TestError) { |
|
| 4753 | 4753 | let mut a = testResolver(); |
|
| 4754 | 4754 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrB { return 1; }; }"; |
|
| 4755 | 4755 | let result = try resolveProgramStr(&mut a, program); |
|
| 4756 | 4756 | try expectNoErrors(&result); |
|
| 4757 | 4757 | } |
|
| 4758 | 4758 | ||
| 4759 | - | @test fn testTypedCatchNonExhaustive() throws (testing::TestError) { |
|
| 4759 | + | @test unsafe fn testTypedCatchNonExhaustive() throws (testing::TestError) { |
|
| 4760 | 4760 | let mut a = testResolver(); |
|
| 4761 | 4761 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; }; }"; |
|
| 4762 | 4762 | let result = try resolveProgramStr(&mut a, program); |
|
| 4763 | 4763 | try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive); |
|
| 4764 | 4764 | } |
|
| 4765 | 4765 | ||
| 4766 | - | @test fn testTypedCatchDuplicate() throws (testing::TestError) { |
|
| 4766 | + | @test unsafe fn testTypedCatchDuplicate() throws (testing::TestError) { |
|
| 4767 | 4767 | let mut a = testResolver(); |
|
| 4768 | 4768 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrA { return 1; }; }"; |
|
| 4769 | 4769 | let result = try resolveProgramStr(&mut a, program); |
|
| 4770 | 4770 | try expectErrorKind(&result, super::ErrorKind::TryCatchDuplicateType); |
|
| 4771 | 4771 | } |
|
| 4772 | 4772 | ||
| 4773 | - | @test fn testTypedCatchWithCatchAll() throws (testing::TestError) { |
|
| 4773 | + | @test unsafe fn testTypedCatchWithCatchAll() throws (testing::TestError) { |
|
| 4774 | 4774 | let mut a = testResolver(); |
|
| 4775 | 4775 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch { return 1; }; }"; |
|
| 4776 | 4776 | let result = try resolveProgramStr(&mut a, program); |
|
| 4777 | 4777 | try expectNoErrors(&result); |
|
| 4778 | 4778 | } |
|
| 4779 | 4779 | ||
| 4780 | - | @test fn testTypedCatchWrongType() throws (testing::TestError) { |
|
| 4780 | + | @test unsafe fn testTypedCatchWrongType() throws (testing::TestError) { |
|
| 4781 | 4781 | let mut a = testResolver(); |
|
| 4782 | 4782 | let program = "union ErrA { A } union ErrB { B } union ErrC { C } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrC { return 0; } catch e as ErrA { return 1; }; }"; |
|
| 4783 | 4783 | let result = try resolveProgramStr(&mut a, program); |
|
| 4784 | 4784 | try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError); |
|
| 4785 | 4785 | } |
|
| 4786 | 4786 | ||
| 4787 | - | @test fn testInferredCatchMultiError() throws (testing::TestError) { |
|
| 4787 | + | @test unsafe fn testInferredCatchMultiError() throws (testing::TestError) { |
|
| 4788 | 4788 | let mut a = testResolver(); |
|
| 4789 | 4789 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e { return 0; }; }"; |
|
| 4790 | 4790 | let result = try resolveProgramStr(&mut a, program); |
|
| 4791 | 4791 | try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError); |
|
| 4792 | 4792 | } |
|
| 4793 | 4793 | ||
| 4794 | - | @test fn testResolveInstanceMissingMethod() throws (testing::TestError) { |
|
| 4794 | + | @test unsafe fn testResolveInstanceMissingMethod() throws (testing::TestError) { |
|
| 4795 | 4795 | let mut a = testResolver(); |
|
| 4796 | 4796 | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}"; |
|
| 4797 | 4797 | let result = try resolveProgramStr(&mut a, program); |
|
| 4798 | 4798 | try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f")); |
|
| 4799 | 4799 | } |
|
| 4800 | 4800 | ||
| 4801 | - | @test fn testResolveInstanceUnknownMethod() throws (testing::TestError) { |
|
| 4801 | + | @test unsafe fn testResolveInstanceUnknownMethod() throws (testing::TestError) { |
|
| 4802 | 4802 | let mut a = testResolver(); |
|
| 4803 | 4803 | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }"; |
|
| 4804 | 4804 | let result = try resolveProgramStr(&mut a, program); |
|
| 4805 | 4805 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 4806 | 4806 | } |
|
| 4807 | 4807 | ||
| 4808 | - | @test fn testResolveTraitDuplicateMethodRejected() throws (testing::TestError) { |
|
| 4808 | + | @test unsafe fn testResolveTraitDuplicateMethodRejected() throws (testing::TestError) { |
|
| 4809 | 4809 | let mut a = testResolver(); |
|
| 4810 | 4810 | let program = "trait Adder { fn (*mut Adder) add(n: i32) -> i32; fn (*mut Adder) add(n: i32) -> i32; }"; |
|
| 4811 | 4811 | let result = try resolveProgramStr(&mut a, program); |
|
| 4812 | 4812 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("add")); |
|
| 4813 | 4813 | } |
|
| 4814 | 4814 | ||
| 4815 | - | @test fn testResolveInstanceReceiverTypeMustMatchTarget() throws (testing::TestError) { |
|
| 4815 | + | @test unsafe fn testResolveInstanceReceiverTypeMustMatchTarget() throws (testing::TestError) { |
|
| 4816 | 4816 | let mut a = testResolver(); |
|
| 4817 | 4817 | let program = "record Counter { value: i32 } record Wrong { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Wrong) add(n: i32) -> i32 { return n; } }"; |
|
| 4818 | 4818 | let result = try resolveProgramStr(&mut a, program); |
|
| 4819 | 4819 | let err = try expectError(&result); |
|
| 4820 | 4820 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4821 | 4821 | else throw testing::TestError::Failed; |
|
| 4822 | 4822 | } |
|
| 4823 | 4823 | ||
| 4824 | - | @test fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) { |
|
| 4824 | + | @test unsafe fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) { |
|
| 4825 | 4825 | let mut a = testResolver(); |
|
| 4826 | 4826 | let program = "union Error { Fail } record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32 throws (Error); } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 throws (Error) { throw Error::Fail; return n; } } fn caller(a: *mut opaque Adder) -> i32 { return a.add(1); }"; |
|
| 4827 | 4827 | let result = try resolveProgramStr(&mut a, program); |
|
| 4828 | 4828 | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
|
| 4829 | 4829 | } |
|
| 4830 | 4830 | ||
| 4831 | 4831 | /// Trait declares immutable receiver (*Trait) but instance uses mutable (*mut Type). |
|
| 4832 | 4832 | /// The instance method could mutate through what was originally an immutable pointer. |
|
| 4833 | - | @test fn testResolveInstanceMutReceiverOnImmutableTrait() throws (testing::TestError) { |
|
| 4833 | + | @test unsafe fn testResolveInstanceMutReceiverOnImmutableTrait() throws (testing::TestError) { |
|
| 4834 | 4834 | let mut a = testResolver(); |
|
| 4835 | 4835 | let program = "record Counter { value: i32 } trait Reader { fn (*Reader) read() -> i32; } instance Reader for Counter { fn (c: *mut Counter) read() -> i32 { set c.value = c.value + 1; return c.value; } }"; |
|
| 4836 | 4836 | let result = try resolveProgramStr(&mut a, program); |
|
| 4837 | 4837 | // Should reject: instance declares *mut receiver but trait only requires immutable. |
|
| 4838 | 4838 | try expectErrorKind(&result, super::ErrorKind::ReceiverMutabilityMismatch); |
|
| 4839 | 4839 | } |
|
| 4840 | 4840 | ||
| 4841 | 4841 | /// Instance method declares different parameter types than the trait. |
|
| 4842 | 4842 | /// The resolver should reject the mismatch rather than silently using the trait's types. |
|
| 4843 | - | @test fn testResolveInstanceParamTypeMismatch() throws (testing::TestError) { |
|
| 4843 | + | @test unsafe fn testResolveInstanceParamTypeMismatch() throws (testing::TestError) { |
|
| 4844 | 4844 | let mut a = testResolver(); |
|
| 4845 | 4845 | let program = "record Acc { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Acc { fn (a: *mut Acc) add(n: u8) -> i32 { set a.value = a.value + n as i32; return a.value; } }"; |
|
| 4846 | 4846 | let result = try resolveProgramStr(&mut a, program); |
|
| 4847 | 4847 | // Should reject: instance param type u8 doesn't match trait param type i32. |
|
| 4848 | 4848 | let err = try expectError(&result); |
|
| 4849 | 4849 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4850 | 4850 | else throw testing::TestError::Failed; |
|
| 4851 | 4851 | } |
|
| 4852 | 4852 | ||
| 4853 | 4853 | /// Duplicate instance declarations for the same (trait, type) pair should be rejected. |
|
| 4854 | - | @test fn testResolveInstanceDuplicateRejected() throws (testing::TestError) { |
|
| 4854 | + | @test unsafe fn testResolveInstanceDuplicateRejected() throws (testing::TestError) { |
|
| 4855 | 4855 | let mut a = testResolver(); |
|
| 4856 | 4856 | let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n + 100; return c.value; } }"; |
|
| 4857 | 4857 | let result = try resolveProgramStr(&mut a, program); |
|
| 4858 | 4858 | // Should reject: duplicate instance for (Adder, Counter). |
|
| 4859 | 4859 | try expectErrorKind(&result, super::ErrorKind::DuplicateInstance); |
|
| 4860 | 4860 | } |
|
| 4861 | 4861 | ||
| 4862 | 4862 | /// Trait method receiver must point to the declaring trait type. |
|
| 4863 | - | @test fn testResolveTraitReceiverMismatch() throws (testing::TestError) { |
|
| 4863 | + | @test unsafe fn testResolveTraitReceiverMismatch() throws (testing::TestError) { |
|
| 4864 | 4864 | let mut a = testResolver(); |
|
| 4865 | 4865 | let program = "record Other { x: i32 } trait Foo { fn (*mut Other) bar() -> i32; }"; |
|
| 4866 | 4866 | let result = try resolveProgramStr(&mut a, program); |
|
| 4867 | 4867 | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
|
| 4868 | 4868 | } |
|
| 4869 | 4869 | ||
| 4870 | 4870 | /// Using a trait name as a value expression should be rejected. |
|
| 4871 | - | @test fn testResolveTraitNameAsValueRejected() throws (testing::TestError) { |
|
| 4871 | + | @test unsafe fn testResolveTraitNameAsValueRejected() throws (testing::TestError) { |
|
| 4872 | 4872 | let mut a = testResolver(); |
|
| 4873 | 4873 | let program = "trait Foo { fn (*Foo) bar() -> i32; } fn test() -> i32 { let x = Foo; return 0; }"; |
|
| 4874 | 4874 | let result = try resolveProgramStr(&mut a, program); |
|
| 4875 | 4875 | try expectErrorKind(&result, super::ErrorKind::UnexpectedTraitName); |
|
| 4876 | 4876 | } |
|
| 4877 | 4877 | ||
| 4878 | 4878 | /// Cross-module trait: coerce to trait object and dispatch from a different module. |
|
| 4879 | - | @test fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) { |
|
| 4879 | + | @test unsafe fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) { |
|
| 4880 | 4880 | let mut a = testResolver(); |
|
| 4881 | 4881 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4882 | 4882 | ||
| 4883 | 4883 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena); |
|
| 4884 | 4884 | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 4885 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { let mut c = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 4885 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 4886 | 4886 | ||
| 4887 | 4887 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4888 | 4888 | try expectNoErrors(&result); |
|
| 4889 | 4889 | } |
|
| 4890 | 4890 | ||
| 4891 | 4891 | /// Instance in a different module from trait and type. |
|
| 4892 | - | @test fn testResolveInstanceCrossModule() throws (testing::TestError) { |
|
| 4892 | + | @test unsafe fn testResolveInstanceCrossModule() throws (testing::TestError) { |
|
| 4893 | 4893 | let mut a = testResolver(); |
|
| 4894 | 4894 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4895 | 4895 | ||
| 4896 | 4896 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena); |
|
| 4897 | 4897 | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena); |
|
| 4898 | 4898 | let implsId = try registerModule(&mut MODULE_GRAPH, rootId, "impls", "use root::defs; instance defs::Adder for defs::Counter { fn (c: *mut defs::Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 4899 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { let mut c = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 4899 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 4900 | 4900 | ||
| 4901 | 4901 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4902 | 4902 | try expectNoErrors(&result); |
|
| 4903 | 4903 | } |
|
| 4904 | 4904 | ||
| 4905 | 4905 | /// Calling a mutable-receiver trait method on an immutable trait object |
|
| 4906 | 4906 | /// must be rejected. |
|
| 4907 | - | @test fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) { |
|
| 4907 | + | @test unsafe fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) { |
|
| 4908 | 4908 | let mut a = testResolver(); |
|
| 4909 | 4909 | let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } fn caller(a: *opaque Adder) -> i32 { return a.add(1); }"; |
|
| 4910 | 4910 | let result = try resolveProgramStr(&mut a, program); |
|
| 4911 | 4911 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
|
| 4912 | 4912 | } |
|
| 4913 | 4913 | ||
| 4914 | 4914 | /// Immutable methods on an immutable trait object should be accepted. |
|
| 4915 | - | @test fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) { |
|
| 4915 | + | @test unsafe fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) { |
|
| 4916 | 4916 | let mut a = testResolver(); |
|
| 4917 | 4917 | let program = "record Counter { value: i32 } trait Reader { fn (*Reader) get() -> i32; } instance Reader for Counter { fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(r: *opaque Reader) -> i32 { return r.get(); }"; |
|
| 4918 | 4918 | let result = try resolveProgramStr(&mut a, program); |
|
| 4919 | 4919 | try expectNoErrors(&result); |
|
| 4920 | 4920 | } |
|
| 4921 | 4921 | ||
| 4922 | 4922 | /// Both mutable and immutable methods on a mutable trait object should work. |
|
| 4923 | - | @test fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) { |
|
| 4923 | + | @test unsafe fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) { |
|
| 4924 | 4924 | let mut a = testResolver(); |
|
| 4925 | 4925 | let program = "record Counter { value: i32 } trait Ops { fn (*mut Ops) inc(); fn (*Ops) get() -> i32; } instance Ops for Counter { fn (c: *mut Counter) inc() { set c.value = c.value + 1; } fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(o: *mut opaque Ops) -> i32 { o.inc(); return o.get(); }"; |
|
| 4926 | 4926 | let result = try resolveProgramStr(&mut a, program); |
|
| 4927 | 4927 | try expectNoErrors(&result); |
|
| 4928 | 4928 | } |
|
| 4929 | 4929 | ||
| 4930 | 4930 | /// Instance method body type must match the trait return type. |
|
| 4931 | 4931 | /// The trait declares `-> i32` but the body returns `bool`. |
|
| 4932 | - | @test fn testResolveInstanceReturnTypeMismatch() throws (testing::TestError) { |
|
| 4932 | + | @test unsafe fn testResolveInstanceReturnTypeMismatch() throws (testing::TestError) { |
|
| 4933 | 4933 | let mut a = testResolver(); |
|
| 4934 | 4934 | let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> bool { return true; } }"; |
|
| 4935 | 4935 | let result = try resolveProgramStr(&mut a, program); |
|
| 4936 | 4936 | let err = try expectError(&result); |
|
| 4937 | 4937 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4938 | 4938 | else throw testing::TestError::Failed; |
|
| 4939 | 4939 | } |
|
| 4940 | 4940 | ||
| 4941 | 4941 | /// Diamond supertrait inheritance: traits B and C both extend A. |
|
| 4942 | 4942 | /// Declaring them independently should work fine. |
|
| 4943 | - | @test fn testResolveTraitDiamondSupertrait() throws (testing::TestError) { |
|
| 4943 | + | @test unsafe fn testResolveTraitDiamondSupertrait() throws (testing::TestError) { |
|
| 4944 | 4944 | let mut a = testResolver(); |
|
| 4945 | 4945 | let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; }"; |
|
| 4946 | 4946 | let result = try resolveProgramStr(&mut a, program); |
|
| 4947 | 4947 | try expectNoErrors(&result); |
|
| 4948 | 4948 | } |
|
| 4949 | 4949 | ||
| 4950 | 4950 | /// Diamond supertrait with a combined trait that would cause duplicate |
|
| 4951 | 4951 | /// method names should be detected. |
|
| 4952 | - | @test fn testResolveTraitDiamondDuplicateMethod() throws (testing::TestError) { |
|
| 4952 | + | @test unsafe fn testResolveTraitDiamondDuplicateMethod() throws (testing::TestError) { |
|
| 4953 | 4953 | let mut a = testResolver(); |
|
| 4954 | 4954 | let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; } trait D: B + C { fn (*D) i() -> i32; }"; |
|
| 4955 | 4955 | let result = try resolveProgramStr(&mut a, program); |
|
| 4956 | 4956 | // B inherits `f` from A, C inherits `f` from A. D: B + C sees duplicate `f`. |
|
| 4957 | 4957 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("f")); |
|
| 4958 | 4958 | } |
|
| 4959 | 4959 | ||
| 4960 | 4960 | /// Supertrait instance must exist when declaring a combined trait instance. |
|
| 4961 | - | @test fn testResolveInstanceMissingSupertraitInstance() throws (testing::TestError) { |
|
| 4961 | + | @test unsafe fn testResolveInstanceMissingSupertraitInstance() throws (testing::TestError) { |
|
| 4962 | 4962 | let mut a = testResolver(); |
|
| 4963 | 4963 | let program = "trait Base { fn (*Base) f() -> i32; } trait Child: Base { fn (*Child) g() -> i32; } record R { x: i32 } instance Child for R { fn (r: *R) g() -> i32 { return r.x; } }"; |
|
| 4964 | 4964 | let result = try resolveProgramStr(&mut a, program); |
|
| 4965 | 4965 | try expectErrorKind(&result, super::ErrorKind::MissingSupertraitInstance("Base")); |
|
| 4966 | 4966 | } |
|
| 4967 | 4967 | ||
| 4968 | 4968 | /// Instance method omits return type when the trait declares `-> i32`. |
|
| 4969 | 4969 | /// This is rejected -- the return type must be stated explicitly. |
|
| 4970 | - | @test fn testResolveInstanceReturnTypeOmitted() throws (testing::TestError) { |
|
| 4970 | + | @test unsafe fn testResolveInstanceReturnTypeOmitted() throws (testing::TestError) { |
|
| 4971 | 4971 | let mut a = testResolver(); |
|
| 4972 | 4972 | let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() { } }"; |
|
| 4973 | 4973 | let result = try resolveProgramStr(&mut a, program); |
|
| 4974 | 4974 | let err = try expectError(&result); |
|
| 4975 | 4975 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4976 | 4976 | else throw testing::TestError::Failed; |
|
| 4977 | 4977 | } |
|
| 4978 | 4978 | ||
| 4979 | 4979 | /// Instance method declares throws but the trait method does not throw. |
|
| 4980 | - | @test fn testResolveInstanceThrowsMismatchExtra() throws (testing::TestError) { |
|
| 4980 | + | @test unsafe fn testResolveInstanceThrowsMismatchExtra() throws (testing::TestError) { |
|
| 4981 | 4981 | let mut a = testResolver(); |
|
| 4982 | 4982 | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> i32 throws (E) { return r.x; } }"; |
|
| 4983 | 4983 | let result = try resolveProgramStr(&mut a, program); |
|
| 4984 | 4984 | let err = try expectError(&result); |
|
| 4985 | 4985 | let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind |
|
| 4986 | 4986 | else throw testing::TestError::Failed; |
|
| 4987 | 4987 | } |
|
| 4988 | 4988 | ||
| 4989 | 4989 | /// Instance method declares a different throws type than the trait. |
|
| 4990 | - | @test fn testResolveInstanceThrowsMismatchWrongType() throws (testing::TestError) { |
|
| 4990 | + | @test unsafe fn testResolveInstanceThrowsMismatchWrongType() throws (testing::TestError) { |
|
| 4991 | 4991 | let mut a = testResolver(); |
|
| 4992 | 4992 | let program = "union E1 { Fail } union E2 { Oops } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E1); } instance T for R { fn (r: *R) get() -> i32 throws (E2) { return r.x; } }"; |
|
| 4993 | 4993 | let result = try resolveProgramStr(&mut a, program); |
|
| 4994 | 4994 | let err = try expectError(&result); |
|
| 4995 | 4995 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4996 | 4996 | else throw testing::TestError::Failed; |
|
| 4997 | 4997 | } |
|
| 4998 | 4998 | ||
| 4999 | 4999 | /// Instance method omits throws clause when trait declares throws. |
|
| 5000 | 5000 | /// This is rejected -- the throws clause must match exactly. |
|
| 5001 | - | @test fn testResolveInstanceThrowsOmitted() throws (testing::TestError) { |
|
| 5001 | + | @test unsafe fn testResolveInstanceThrowsOmitted() throws (testing::TestError) { |
|
| 5002 | 5002 | let mut a = testResolver(); |
|
| 5003 | 5003 | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 { throw E::Fail; return r.x; } }"; |
|
| 5004 | 5004 | let result = try resolveProgramStr(&mut a, program); |
|
| 5005 | 5005 | let err = try expectError(&result); |
|
| 5006 | 5006 | let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind |
|
| 5007 | 5007 | else throw testing::TestError::Failed; |
|
| 5008 | 5008 | } |
|
| 5009 | 5009 | ||
| 5010 | 5010 | /// Instance method correctly matches the trait's throws clause. |
|
| 5011 | - | @test fn testResolveInstanceThrowsMatch() throws (testing::TestError) { |
|
| 5011 | + | @test unsafe fn testResolveInstanceThrowsMatch() throws (testing::TestError) { |
|
| 5012 | 5012 | let mut a = testResolver(); |
|
| 5013 | 5013 | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 throws (E) { throw E::Fail; return r.x; } }"; |
|
| 5014 | 5014 | let result = try resolveProgramStr(&mut a, program); |
|
| 5015 | 5015 | try expectNoErrors(&result); |
|
| 5016 | 5016 | } |
|
| 5017 | 5017 | ||
| 5018 | 5018 | // Constant expression folding tests ////////////////////////////////////////// |
|
| 5019 | 5019 | ||
| 5020 | 5020 | /// Resolve a program and verify that the constant at the given statement index |
|
| 5021 | 5021 | /// has the expected integer magnitude. |
|
| 5022 | - | fn expectConstFold(program: *[u8], stmtIdx: u32, expected: u64) |
|
| 5022 | + | unsafe fn expectConstFold(program: *[u8], stmtIdx: u32, expected: u64) |
|
| 5023 | 5023 | throws (testing::TestError) |
|
| 5024 | 5024 | { |
|
| 5025 | 5025 | let mut a = testResolver(); |
|
| 5026 | 5026 | let result = try resolveProgramStr(&mut a, program); |
|
| 5027 | 5027 | try expectNoErrors(&result); |
| 5036 | 5036 | else throw testing::TestError::Failed; |
|
| 5037 | 5037 | try testing::expect(intVal.magnitude == expected); |
|
| 5038 | 5038 | } |
|
| 5039 | 5039 | ||
| 5040 | 5040 | /// Test arithmetic constant folding: add, sub, mul, div. |
|
| 5041 | - | @test fn testConstExprArithmetic() throws (testing::TestError) { |
|
| 5041 | + | @test unsafe fn testConstExprArithmetic() throws (testing::TestError) { |
|
| 5042 | 5042 | try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B;", 2, 30); |
|
| 5043 | 5043 | try expectConstFold("constant A: i32 = 50; constant B: i32 = 20; constant C: i32 = A - B;", 2, 30); |
|
| 5044 | 5044 | try expectConstFold("constant A: i32 = 6; constant B: i32 = 7; constant C: i32 = A * B;", 2, 42); |
|
| 5045 | 5045 | try expectConstFold("constant A: i32 = 100; constant B: i32 = 5; constant C: i32 = A / B;", 2, 20); |
|
| 5046 | 5046 | } |
|
| 5047 | 5047 | ||
| 5048 | 5048 | /// Test bitwise constant folding: and, or, xor. |
|
| 5049 | - | @test fn testConstExprBitwise() throws (testing::TestError) { |
|
| 5049 | + | @test unsafe fn testConstExprBitwise() throws (testing::TestError) { |
|
| 5050 | 5050 | try expectConstFold("constant A: i32 = 0xFF; constant B: i32 = 0x0F; constant C: i32 = A & B;", 2, 0x0F); |
|
| 5051 | 5051 | try expectConstFold("constant A: i32 = 0xF0; constant B: i32 = 0x0F; constant C: i32 = A | B;", 2, 0xFF); |
|
| 5052 | 5052 | try expectConstFold("constant A: i32 = 0xFF; constant B: i32 = 0x0F; constant C: i32 = A ^ B;", 2, 0xF0); |
|
| 5053 | 5053 | } |
|
| 5054 | 5054 | ||
| 5055 | 5055 | /// Test shift constant folding. |
|
| 5056 | - | @test fn testConstExprShift() throws (testing::TestError) { |
|
| 5056 | + | @test unsafe fn testConstExprShift() throws (testing::TestError) { |
|
| 5057 | 5057 | try expectConstFold("constant A: i32 = 1; constant B: i32 = A << 4;", 1, 16); |
|
| 5058 | 5058 | try expectConstFold("constant A: i32 = 32; constant B: i32 = A >> 2;", 1, 8); |
|
| 5059 | 5059 | } |
|
| 5060 | 5060 | ||
| 5061 | 5061 | /// Test chained constant expressions (C depends on A + B, D depends on C). |
|
| 5062 | - | @test fn testConstExprChained() throws (testing::TestError) { |
|
| 5062 | + | @test unsafe fn testConstExprChained() throws (testing::TestError) { |
|
| 5063 | 5063 | try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B; constant D: i32 = C * 2;", 3, 60); |
|
| 5064 | 5064 | } |
|
| 5065 | 5065 | ||
| 5066 | 5066 | /// Test constant expression used as array size. |
|
| 5067 | - | @test fn testConstExprAsArraySize() throws (testing::TestError) { |
|
| 5067 | + | @test unsafe fn testConstExprAsArraySize() throws (testing::TestError) { |
|
| 5068 | 5068 | let mut a = testResolver(); |
|
| 5069 | 5069 | let program = "constant A: u32 = 2; constant B: u32 = 3; constant SIZE: u32 = A + B; constant ARR: [i32; SIZE] = [1, 2, 3, 4, 5];"; |
|
| 5070 | 5070 | let result = try resolveProgramStr(&mut a, program); |
|
| 5071 | 5071 | try expectNoErrors(&result); |
|
| 5072 | 5072 |
| 5078 | 5078 | try testing::expect(arrType.length == 5); |
|
| 5079 | 5079 | } |
|
| 5080 | 5080 | ||
| 5081 | 5081 | /// Test cross-module constant expression: a constant in one module references |
|
| 5082 | 5082 | /// a constant from another module via scope access. |
|
| 5083 | - | @test fn testCrossModuleConstExpr() throws (testing::TestError) { |
|
| 5083 | + | @test unsafe fn testCrossModuleConstExpr() throws (testing::TestError) { |
|
| 5084 | 5084 | let mut a = testResolver(); |
|
| 5085 | 5085 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5086 | 5086 | ||
| 5087 | 5087 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 5088 | 5088 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant BASE: i32 = 100;", &mut arena); |
| 5091 | 5091 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5092 | 5092 | try expectNoErrors(&result); |
|
| 5093 | 5093 | } |
|
| 5094 | 5094 | ||
| 5095 | 5095 | /// Test cross-module constant expression used as array size. |
|
| 5096 | - | @test fn testCrossModuleConstExprArraySize() throws (testing::TestError) { |
|
| 5096 | + | @test unsafe fn testCrossModuleConstExprArraySize() throws (testing::TestError) { |
|
| 5097 | 5097 | let mut a = testResolver(); |
|
| 5098 | 5098 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 5099 | 5099 | ||
| 5100 | 5100 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 5101 | 5101 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena); |
| 5104 | 5104 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 5105 | 5105 | try expectNoErrors(&result); |
|
| 5106 | 5106 | } |
|
| 5107 | 5107 | ||
| 5108 | 5108 | /// Test that non-constant expressions in constant declarations are still rejected. |
|
| 5109 | - | @test fn testConstExprNonConstRejected() throws (testing::TestError) { |
|
| 5109 | + | @test unsafe fn testConstExprNonConstRejected() throws (testing::TestError) { |
|
| 5110 | 5110 | let mut a = testResolver(); |
|
| 5111 | 5111 | let program = "fn value() -> i32 { return 1; } constant BAD: i32 = value() + 1;"; |
|
| 5112 | 5112 | let result = try resolveProgramStr(&mut a, program); |
|
| 5113 | 5113 | let err = try expectError(&result); |
|
| 5114 | 5114 | let case super::ErrorKind::ConstExprRequired = err.kind |
|
| 5115 | 5115 | else throw testing::TestError::Failed; |
|
| 5116 | 5116 | } |
|
| 5117 | 5117 | ||
| 5118 | 5118 | /// Test unary negation in constant expressions. |
|
| 5119 | - | @test fn testConstExprUnaryNeg() throws (testing::TestError) { |
|
| 5119 | + | @test unsafe fn testConstExprUnaryNeg() throws (testing::TestError) { |
|
| 5120 | 5120 | let mut a = testResolver(); |
|
| 5121 | 5121 | let program = "constant A: i32 = 10; constant B: i32 = -A;"; |
|
| 5122 | 5122 | let result = try resolveProgramStr(&mut a, program); |
|
| 5123 | 5123 | try expectNoErrors(&result); |
|
| 5124 | 5124 | } |
|
| 5125 | 5125 | ||
| 5126 | 5126 | /// Test unary not in constant expressions. |
|
| 5127 | - | @test fn testConstExprUnaryNot() throws (testing::TestError) { |
|
| 5127 | + | @test unsafe fn testConstExprUnaryNot() throws (testing::TestError) { |
|
| 5128 | 5128 | let mut a = testResolver(); |
|
| 5129 | 5129 | let program = "constant A: bool = true; constant B: bool = not A;"; |
|
| 5130 | 5130 | let result = try resolveProgramStr(&mut a, program); |
|
| 5131 | 5131 | try expectNoErrors(&result); |
|
| 5132 | 5132 | } |
|
| 5133 | 5133 | ||
| 5134 | 5134 | /// Test `as` casts in constant expressions: widening, narrowing, sign changes, chaining. |
|
| 5135 | - | @test fn testConstExprCast() throws (testing::TestError) { |
|
| 5135 | + | @test unsafe fn testConstExprCast() throws (testing::TestError) { |
|
| 5136 | 5136 | try expectConstFold("constant A: i32 = 42; constant B: u64 = A as u64;", 1, 42); |
|
| 5137 | 5137 | try expectConstFold("constant A: u64 = 10; constant B: u8 = A as u8;", 1, 10); |
|
| 5138 | 5138 | try expectConstFold("constant A: i32 = 7; constant B: u32 = A as u32;", 1, 7); |
|
| 5139 | 5139 | try expectConstFold("constant A: u32 = 100; constant B: i32 = A as i32;", 1, 100); |
|
| 5140 | 5140 | try expectConstFold("constant A: u8 = 5; constant B: u64 = (A as u32) as u64;", 1, 5); |
| 5145 | 5145 | try expectConstFold("constant A: u32 = (3 + 4) as u32 + 1;", 0, 8); |
|
| 5146 | 5146 | try expectConstFold("constant A: i32 = (2 as i32) * (3 + 4);", 0, 14); |
|
| 5147 | 5147 | } |
|
| 5148 | 5148 | ||
| 5149 | 5149 | /// Test `as` cast in constant expressions used as array size. |
|
| 5150 | - | @test fn testConstExprCastAsArraySize() throws (testing::TestError) { |
|
| 5150 | + | @test unsafe fn testConstExprCastAsArraySize() throws (testing::TestError) { |
|
| 5151 | 5151 | let mut a = testResolver(); |
|
| 5152 | 5152 | let program = "constant LEN: u64 = 4; constant SIZE: u32 = LEN as u32; constant ARR: [i32; SIZE] = [1, 2, 3, 4];"; |
|
| 5153 | 5153 | let result = try resolveProgramStr(&mut a, program); |
|
| 5154 | 5154 | try expectNoErrors(&result); |
|
| 5155 | 5155 |
| 5160 | 5160 | else throw testing::TestError::Failed; |
|
| 5161 | 5161 | try testing::expect(arrType.length == 4); |
|
| 5162 | 5162 | } |
|
| 5163 | 5163 | ||
| 5164 | 5164 | /// Test unsuffixed integer literals in constant expressions. |
|
| 5165 | - | @test fn testConstExprUnsuffixedLiterals() throws (testing::TestError) { |
|
| 5165 | + | @test unsafe fn testConstExprUnsuffixedLiterals() throws (testing::TestError) { |
|
| 5166 | 5166 | try expectConstFold("constant A: u32 = 4 * 4;", 0, 16); |
|
| 5167 | 5167 | try expectConstFold("constant B: u32 = 10; constant C: u32 = B * 2;", 1, 20); |
|
| 5168 | 5168 | try expectConstFold("constant D: u32 = 3 + 7;", 0, 10); |
|
| 5169 | 5169 | try expectConstFold("constant E: u32 = 2 * 3 + 4;", 0, 10); |
|
| 5170 | 5170 | try expectConstFold("constant F: i32 = -(3 + 4);", 0, 7); |
|
| 5171 | 5171 | } |
|
| 5172 | 5172 | ||
| 5173 | 5173 | /// References cannot escape through return types. |
|
| 5174 | - | @test fn testRefReturnRejected() throws (testing::TestError) { |
|
| 5174 | + | @test unsafe fn testRefReturnRejected() throws (testing::TestError) { |
|
| 5175 | 5175 | let mut a = testResolver(); |
|
| 5176 | 5176 | let program = "record Marker: Once {} fn bad(value: &u32) -> &u32 { return value; }"; |
|
| 5177 | 5177 | let result = try resolveProgramStr(&mut a, program); |
|
| 5178 | 5178 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 5179 | 5179 | } |
|
| 5180 | 5180 | ||
| 5181 | 5181 | /// Case-pattern fallbacks must terminate instead of synthesizing bindings. |
|
| 5182 | - | @test fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) { |
|
| 5182 | + | @test unsafe fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) { |
|
| 5183 | 5183 | let mut a = testResolver(); |
|
| 5184 | 5184 | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }"; |
|
| 5185 | 5185 | let result = try resolveProgramStr(&mut a, program); |
|
| 5186 | 5186 | try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate); |
|
| 5187 | 5187 | } |
|
| 5188 | 5188 | ||
| 5189 | 5189 | /// Case bindings are unavailable on the pattern-failure path. |
|
| 5190 | - | @test fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) { |
|
| 5190 | + | @test unsafe fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) { |
|
| 5191 | 5191 | let mut a = testResolver(); |
|
| 5192 | 5192 | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }"; |
|
| 5193 | 5193 | let result = try resolveProgramStr(&mut a, program); |
|
| 5194 | 5194 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item")); |
|
| 5195 | 5195 | } |
|
| 5196 | 5196 | ||
| 5197 | 5197 | /// Unsafe pointer dereference requires an unsafe declaration. |
|
| 5198 | - | @test fn testUnsafePointerOperationRejected() throws (testing::TestError) { |
|
| 5198 | + | @test unsafe fn testUnsafePointerOperationRejected() throws (testing::TestError) { |
|
| 5199 | 5199 | let mut a = testResolver(); |
|
| 5200 | 5200 | let program = "record Marker: Once {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }"; |
|
| 5201 | 5201 | let result = try resolveProgramStr(&mut a, program); |
|
| 5202 | 5202 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5203 | 5203 | } |
|
| 5204 | 5204 | ||
| 5205 | 5205 | /// Unsafe pointers remain freely copyable inside an unsafe declaration. |
|
| 5206 | - | @test fn testUnsafePointerOperationAllowed() throws (testing::TestError) { |
|
| 5206 | + | @test unsafe fn testUnsafePointerOperationAllowed() throws (testing::TestError) { |
|
| 5207 | 5207 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; }"; |
|
| 5208 | 5208 | try expectAnalyzeOk(program); |
|
| 5209 | 5209 | } |
|
| 5210 | 5210 | ||
| 5211 | 5211 | /// Safe code cannot call a function that accepts unsafe operations. |
|
| 5212 | - | @test fn testUnsafeFunctionCallRejected() throws (testing::TestError) { |
|
| 5212 | + | @test unsafe fn testUnsafeFunctionCallRejected() throws (testing::TestError) { |
|
| 5213 | 5213 | let mut a = testResolver(); |
|
| 5214 | 5214 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }"; |
|
| 5215 | 5215 | let result = try resolveProgramStr(&mut a, program); |
|
| 5216 | 5216 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 5217 | 5217 | } |
|
| 5218 | 5218 | ||
| 5219 | 5219 | /// Unsafe function values retain their call-site safety requirement. |
|
| 5220 | - | @test fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) { |
|
| 5220 | + | @test unsafe fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) { |
|
| 5221 | 5221 | let mut a = testResolver(); |
|
| 5222 | 5222 | let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }"; |
|
| 5223 | 5223 | let result = try resolveProgramStr(&mut a, program); |
|
| 5224 | 5224 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 5225 | 5225 | } |
|
| 5226 | 5226 | ||
| 5227 | 5227 | /// References cannot be embedded in aggregate fields. |
|
| 5228 | - | @test fn testRefFieldRejected() throws (testing::TestError) { |
|
| 5228 | + | @test unsafe fn testRefFieldRejected() throws (testing::TestError) { |
|
| 5229 | 5229 | let mut a = testResolver(); |
|
| 5230 | 5230 | let program = "record Marker: Once {} record Bad { value: &u32 }"; |
|
| 5231 | 5231 | let result = try resolveProgramStr(&mut a, program); |
|
| 5232 | 5232 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 5233 | 5233 | } |
|
| 5234 | 5234 | ||
| 5235 | 5235 | /// Trait methods may use reference receivers. |
|
| 5236 | - | @test fn testTraitRefReceiver() throws (testing::TestError) { |
|
| 5236 | + | @test unsafe fn testTraitRefReceiver() throws (testing::TestError) { |
|
| 5237 | 5237 | let program = "record Marker: Once {} record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: &Value) get() -> i32 { return value.number; } } fn inspect(object: &opaque Read) -> i32 { return object.get(); } fn call(value: &Value) -> i32 { return inspect(value); }"; |
|
| 5238 | 5238 | try expectAnalyzeOk(program); |
|
| 5239 | 5239 | } |
|
| 5240 | 5240 | ||
| 5241 | 5241 | /// Trait implementations must preserve the receiver pointer class. |
|
| 5242 | - | @test fn testTraitReceiverClassMismatch() throws (testing::TestError) { |
|
| 5242 | + | @test unsafe fn testTraitReceiverClassMismatch() throws (testing::TestError) { |
|
| 5243 | 5243 | let mut a = testResolver(); |
|
| 5244 | 5244 | let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }"; |
|
| 5245 | 5245 | let result = try resolveProgramStr(&mut a, program); |
|
| 5246 | 5246 | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
|
| 5247 | 5247 | } |
|
| 5248 | 5248 | ||
| 5249 | 5249 | /// Unmarked composite values may be discarded. |
|
| 5250 | - | @test fn testAffineCompositeMayBeDiscarded() throws (testing::TestError) { |
|
| 5250 | + | @test unsafe fn testAffineCompositeMayBeDiscarded() throws (testing::TestError) { |
|
| 5251 | 5251 | let program = "record Value { number: u32 } fn run() { let value = Value { number: 1 }; }"; |
|
| 5252 | 5252 | try expectAnalyzeOk(program); |
|
| 5253 | 5253 | } |
|
| 5254 | 5254 | ||
| 5255 | 5255 | /// A by-value use moves an unmarked composite value. |
|
| 5256 | - | @test fn testAffineCompositeUseAfterMoveRejected() throws (testing::TestError) { |
|
| 5256 | + | @test unsafe fn testAffineCompositeUseAfterMoveRejected() throws (testing::TestError) { |
|
| 5257 | 5257 | let mut a = testResolver(); |
|
| 5258 | 5258 | let program = "record Value { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }"; |
|
| 5259 | 5259 | let result = try resolveProgramStr(&mut a, program); |
|
| 5260 | 5260 | try expectErrorKind(&result, super::ErrorKind::AffineUseAfterMove("value")); |
|
| 5261 | 5261 | } |
|
| 5262 | 5262 | ||
| 5263 | 5263 | /// Affine values may move on only one branch when not used later. |
|
| 5264 | - | @test fn testAffineConditionalMoveMayBeDiscarded() throws (testing::TestError) { |
|
| 5264 | + | @test unsafe fn testAffineConditionalMoveMayBeDiscarded() throws (testing::TestError) { |
|
| 5265 | 5265 | let program = "record Value { number: u32 } fn take(value: Value) {} fn run(condition: bool) { let value = Value { number: 1 }; if condition { take(value); } }"; |
|
| 5266 | 5266 | try expectAnalyzeOk(program); |
|
| 5267 | 5267 | } |
|
| 5268 | 5268 | ||
| 5269 | 5269 | /// A `Copy` composite remains available after a by-value use. |
|
| 5270 | - | @test fn testCopyCompositeMayBeReused() throws (testing::TestError) { |
|
| 5270 | + | @test unsafe fn testCopyCompositeMayBeReused() throws (testing::TestError) { |
|
| 5271 | 5271 | let program = "record Value: Copy { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }"; |
|
| 5272 | 5272 | try expectAnalyzeOk(program); |
|
| 5273 | 5273 | } |
|
| 5274 | 5274 | ||
| 5275 | 5275 | /// A `Copy` composite may contain only copy values. |
|
| 5276 | - | @test fn testCopyCompositeRejectsAffineField() throws (testing::TestError) { |
|
| 5276 | + | @test unsafe fn testCopyCompositeRejectsAffineField() throws (testing::TestError) { |
|
| 5277 | 5277 | let mut a = testResolver(); |
|
| 5278 | 5278 | let program = "record Inner { number: u32 } record Outer: Copy { inner: Inner }"; |
|
| 5279 | 5279 | let result = try resolveProgramStr(&mut a, program); |
|
| 5280 | 5280 | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
|
| 5281 | 5281 | } |
|
| 5282 | 5282 | ||
| 5283 | 5283 | /// A composite cannot carry conflicting ownership markers. |
|
| 5284 | - | @test fn testConflictingOwnershipMarkersRejected() throws (testing::TestError) { |
|
| 5284 | + | @test unsafe fn testConflictingOwnershipMarkersRejected() throws (testing::TestError) { |
|
| 5285 | 5285 | let mut a = testResolver(); |
|
| 5286 | 5286 | let program = "record Value: Copy + Once { number: u32 }"; |
|
| 5287 | 5287 | let result = try resolveProgramStr(&mut a, program); |
|
| 5288 | 5288 | try expectErrorKind(&result, super::ErrorKind::ConflictingOwnershipMarkers); |
|
| 5289 | 5289 | } |
|
| 5290 | 5290 | ||
| 5291 | 5291 | /// Linear composites still require one consuming use. |
|
| 5292 | - | @test fn testLinearCompositeMustBeConsumed() throws (testing::TestError) { |
|
| 5292 | + | @test unsafe fn testLinearCompositeMustBeConsumed() throws (testing::TestError) { |
|
| 5293 | 5293 | let mut a = testResolver(); |
|
| 5294 | 5294 | let program = "record Token: Once { number: u32 } fn run() { let token = Token { number: 1 }; }"; |
|
| 5295 | 5295 | let result = try resolveProgramStr(&mut a, program); |
|
| 5296 | 5296 | try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token")); |
|
| 5297 | 5297 | } |
|
| 5298 | 5298 | ||
| 5299 | 5299 | /// The compiler-known marker cannot be derived more than once. |
|
| 5300 | - | @test fn testDuplicateOnceMarkerRejected() throws (testing::TestError) { |
|
| 5300 | + | @test unsafe fn testDuplicateOnceMarkerRejected() throws (testing::TestError) { |
|
| 5301 | 5301 | let mut a = testResolver(); |
|
| 5302 | 5302 | let program = "record Token: Once + Once { value: u32 }"; |
|
| 5303 | 5303 | let result = try resolveProgramStr(&mut a, program); |
|
| 5304 | 5304 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Once")); |
|
| 5305 | 5305 | } |
|
| 5306 | 5306 | ||
| 5307 | - | /// A `Once` marker does not change legacy pointer inference. |
|
| 5308 | - | @test fn testOnceMarkerKeepsLegacyPointerInference() throws (testing::TestError) { |
|
| 5309 | - | let program = "record Marker: Once {} fn run() { let value: u32 = 0; let pointer: *u32 = &value; pointer; }"; |
|
| 5310 | - | try expectAnalyzeOk(program); |
|
| 5307 | + | /// Stack storage cannot produce a safe stored pointer or slice. |
|
| 5308 | + | @test unsafe fn testStackPointerRejected() throws (testing::TestError) { |
|
| 5309 | + | let programs = &[ |
|
| 5310 | + | "fn run() { let value: u32 = 0; let pointer: *u32 = &value; }", |
|
| 5311 | + | "fn run() -> *u32 { let value: u32 = 0; return &value; }", |
|
| 5312 | + | "fn run(value: u32) -> *u32 { return &value; }", |
|
| 5313 | + | "record Cell { value: u32 } fn run() { let cell = Cell { value: 1 }; let pointer: *u32 = &cell.value; }", |
|
| 5314 | + | "fn run() { let values = [1, 2]; let pointer: *i32 = &values[0]; }", |
|
| 5315 | + | "fn run() { let values = [1, 2]; let slice: *[i32] = &values[..]; }", |
|
| 5316 | + | "fn run(value: i32) { let slice: *[i32] = &[value]; }", |
|
| 5317 | + | "fn run(value: &u32) -> *u32 { return value; }", |
|
| 5318 | + | "fn run(value: &u32) -> *u32 { return &*value; }", |
|
| 5319 | + | "fn run(values: &[u32]) -> *[u32] { return values; }", |
|
| 5320 | + | "fn run(values: &[u32]) -> *[u32] { return &values[..]; }", |
|
| 5321 | + | "fn run(values: &[u32]) -> *u32 { return values.ptr; }", |
|
| 5322 | + | "fn run() -> *i32 { return &[1, 2][0]; }", |
|
| 5323 | + | "fn run() -> *[i32] { return &[1, 2][..]; }", |
|
| 5324 | + | "fn run(value: *u32) -> **u32 { return &value; }", |
|
| 5325 | + | "fn run(values: *[u32]) -> *u32 { return &values.len; }", |
|
| 5326 | + | "unsafe fn run(value: *unsafe u32) -> *u32 { return &*value; }", |
|
| 5327 | + | "fn run() { let value: u32 = 7; let pointer: *unsafe u32 = &value; }", |
|
| 5328 | + | "record Cell { value: u32 } fn run(value: &Cell) -> *u32 { return &value.value; }", |
|
| 5329 | + | "record Cell { value: *u32 } fn run(value: &u32) -> Cell { return Cell { value }; }", |
|
| 5330 | + | "fn run(value: &u32) -> *[u32] { return @sliceOf(value, 1); }", |
|
| 5331 | + | ]; |
|
| 5332 | + | for program in programs { |
|
| 5333 | + | let mut resolver = testResolver(); |
|
| 5334 | + | let result = try resolveProgramStr(&mut resolver, program); |
|
| 5335 | + | let _ = try expectError(&result); |
|
| 5336 | + | } |
|
| 5337 | + | } |
|
| 5338 | + | ||
| 5339 | + | /// Stack values can be borrowed for a call. |
|
| 5340 | + | @test unsafe fn testStackBorrowAllowed() throws (testing::TestError) { |
|
| 5341 | + | try expectAnalyzeOk("fn read(value: &u32) -> u32 { return *value; } fn run() -> u32 { let value: u32 = 7; return read(&value); }"); |
|
| 5342 | + | try expectAnalyzeOk("fn write(values: &mut [u32]) { set values[0] = 7; } fn run() { let mut values: [u32; 2] = [1, 2]; write(&mut values[..]); }"); |
|
| 5343 | + | } |
|
| 5344 | + | ||
| 5345 | + | /// Unsafe declarations can store raw pointers to stack values. |
|
| 5346 | + | @test unsafe fn testUnsafeStackPointerAllowed() throws (testing::TestError) { |
|
| 5347 | + | try expectAnalyzeOk("unsafe fn run() { let mut value: u32 = 0; let pointer: *unsafe mut u32 = &mut value as *unsafe mut u32; set *pointer = 7; }"); |
|
| 5348 | + | try expectAnalyzeOk("unsafe fn run() { let mut values: [u32; 2] = [1, 2]; let slice: *unsafe mut [u32] = &mut values[..] as *unsafe mut [u32]; set slice[0] = 7; }"); |
|
| 5349 | + | try expectAnalyzeOk("unsafe fn run() { let value: u32 = 7; let pointer: *unsafe u32 = &value; }"); |
|
| 5350 | + | try expectAnalyzeOk("unsafe fn run() { let values: [u32; 2] = [1, 2]; let slice: *unsafe [u32] = &values[..]; }"); |
|
| 5351 | + | } |
|
| 5352 | + | ||
| 5353 | + | /// Permanent storage can produce safe stored pointers and slices. |
|
| 5354 | + | @test unsafe fn testPermanentPointerAllowed() throws (testing::TestError) { |
|
| 5355 | + | try expectAnalyzeOk("static VALUE: u32 = 7; fn run() -> *u32 { return &VALUE; }"); |
|
| 5356 | + | try expectAnalyzeOk("static VALUES: [u32; 2] = [1, 2]; fn run() -> *[u32] { return &VALUES[..]; }"); |
|
| 5357 | + | try expectAnalyzeOk("fn run() -> *[u32] { return &[1, 2]; }"); |
|
| 5358 | + | try expectAnalyzeOk("fn run(value: *u32) -> *u32 { return &*value; }"); |
|
| 5359 | + | try expectAnalyzeOk("fn run(values: *[u32]) -> *[u32] { return &values[..]; }"); |
|
| 5360 | + | try expectAnalyzeOk("record Cell { value: u32 } fn run(value: *Cell) -> *u32 { return &value.value; }"); |
|
| 5361 | + | try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return &values[0]; }"); |
|
| 5362 | + | try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return values.ptr; }"); |
|
| 5363 | + | try expectAnalyzeOk("fn run(value: *u32) -> *[u32] { return @sliceOf(value, 1); }"); |
|
| 5311 | 5364 | } |
|
| 5312 | 5365 | ||
| 5313 | 5366 | /// References are rejected from every nested or storable type position. |
|
| 5314 | - | @test fn testNestedRefPositionsRejected() throws (testing::TestError) { |
|
| 5367 | + | @test unsafe fn testNestedRefPositionsRejected() throws (testing::TestError) { |
|
| 5315 | 5368 | { |
|
| 5316 | 5369 | let mut a = testResolver(); |
|
| 5317 | 5370 | let program = "record Marker: Once {} union Bad { Value(&u32) }"; |
|
| 5318 | 5371 | let result = try resolveProgramStr(&mut a, program); |
|
| 5319 | 5372 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 5344 | 5397 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 5345 | 5398 | } |
|
| 5346 | 5399 | } |
|
| 5347 | 5400 | ||
| 5348 | 5401 | /// Function pointer parameter references remain call-scoped and valid. |
|
| 5349 | - | @test fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) { |
|
| 5402 | + | @test unsafe fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) { |
|
| 5350 | 5403 | let program = "record Marker: Once {} fn invoke(callback: fn(&u32), value: &u32) { callback(value); }"; |
|
| 5351 | 5404 | try expectAnalyzeOk(program); |
|
| 5352 | 5405 | } |
|
| 5353 | 5406 | ||
| 5354 | 5407 | /// Pointer and slice casts cannot change reference ownership. |
|
| 5355 | - | @test fn testRefCastClassPreserved() throws (testing::TestError) { |
|
| 5408 | + | @test unsafe fn testRefCastClassPreserved() throws (testing::TestError) { |
|
| 5356 | 5409 | { |
|
| 5357 | 5410 | let mut a = testResolver(); |
|
| 5358 | 5411 | let program = "record Marker: Once {} fn cast(value: &u32) { value as *u32; }"; |
|
| 5359 | 5412 | let result = try resolveProgramStr(&mut a, program); |
|
| 5360 | 5413 | let err = try expectError(&result); |
| 5369 | 5422 | else throw testing::TestError::Failed; |
|
| 5370 | 5423 | } |
|
| 5371 | 5424 | } |
|
| 5372 | 5425 | ||
| 5373 | 5426 | /// Every operation that interprets an unsafe address requires an unsafe declaration. |
|
| 5374 | - | @test fn testUnsafePointerOperationsRejected() throws (testing::TestError) { |
|
| 5427 | + | @test unsafe fn testUnsafePointerOperationsRejected() throws (testing::TestError) { |
|
| 5375 | 5428 | { |
|
| 5376 | 5429 | let mut a = testResolver(); |
|
| 5377 | 5430 | let program = "record Marker: Once {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }"; |
|
| 5378 | 5431 | let result = try resolveProgramStr(&mut a, program); |
|
| 5379 | 5432 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 5409 | 5462 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5410 | 5463 | } |
|
| 5411 | 5464 | } |
|
| 5412 | 5465 | ||
| 5413 | 5466 | /// Unsafe declarations may compose unsafe operations and calls. |
|
| 5414 | - | @test fn testUnsafePointerOperationsAllowed() throws (testing::TestError) { |
|
| 5467 | + | @test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) { |
|
| 5415 | 5468 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }"; |
|
| 5416 | 5469 | try expectAnalyzeOk(program); |
|
| 5417 | 5470 | } |
|
| 5418 | 5471 | ||
| 5419 | 5472 | /// Unsafe code may drop a checked reference to an unsafe pointer. |
|
| 5420 | - | @test fn testUnsafePointerFromReference() throws (testing::TestError) { |
|
| 5473 | + | @test unsafe fn testUnsafePointerFromReference() throws (testing::TestError) { |
|
| 5421 | 5474 | let program = "record Marker: Once {} unsafe fn store(pointer: *unsafe mut u32) { set *pointer = 42; } unsafe fn run() { let mut value: u32 = 0; store(&mut value as *unsafe mut u32); }"; |
|
| 5422 | 5475 | try expectAnalyzeOk(program); |
|
| 5423 | 5476 | } |
|
| 5424 | 5477 | ||
| 5425 | 5478 | /// Dropping a reference to an unsafe pointer cannot add mutability. |
|
| 5426 | - | @test fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) { |
|
| 5479 | + | @test unsafe fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) { |
|
| 5427 | 5480 | let mut a = testResolver(); |
|
| 5428 | 5481 | let program = "record Marker: Once {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }"; |
|
| 5429 | 5482 | let result = try resolveProgramStr(&mut a, program); |
|
| 5430 | 5483 | let err = try expectError(&result); |
|
| 5431 | 5484 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5432 | 5485 | else throw testing::TestError::Failed; |
|
| 5433 | 5486 | } |
|
| 5434 | 5487 | ||
| 5435 | 5488 | /// Recursive cast validation cannot hide a checked-to-unsafe transition. |
|
| 5436 | - | @test fn testNestedUnsafePointerCastRejected() throws (testing::TestError) { |
|
| 5489 | + | @test unsafe fn testNestedUnsafePointerCastRejected() throws (testing::TestError) { |
|
| 5437 | 5490 | let mut a = testResolver(); |
|
| 5438 | 5491 | let program = "record Marker: Once {} fn run(value: **u32) { value as **unsafe u32; }"; |
|
| 5439 | 5492 | let result = try resolveProgramStr(&mut a, program); |
|
| 5440 | 5493 | let err = try expectError(&result); |
|
| 5441 | 5494 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5442 | 5495 | else throw testing::TestError::Failed; |
|
| 5443 | 5496 | } |
|
| 5444 | 5497 | ||
| 5445 | 5498 | /// Unsafe code may drop a checked slice reference to an unsafe slice. |
|
| 5446 | - | @test fn testUnsafeSliceFromReference() throws (testing::TestError) { |
|
| 5499 | + | @test unsafe fn testUnsafeSliceFromReference() throws (testing::TestError) { |
|
| 5447 | 5500 | let program = "record Marker: Once {} unsafe fn run(values: &[u32]) { let raw: *unsafe [u32] = values as *unsafe [u32]; }"; |
|
| 5448 | 5501 | try expectAnalyzeOk(program); |
|
| 5449 | 5502 | } |
|
| 5450 | 5503 | ||
| 5451 | 5504 | /// Slice casts cannot add mutability. |
|
| 5452 | - | @test fn testSliceCastCannotAddMutability() throws (testing::TestError) { |
|
| 5505 | + | @test unsafe fn testSliceCastCannotAddMutability() throws (testing::TestError) { |
|
| 5453 | 5506 | let mut a = testResolver(); |
|
| 5454 | 5507 | let program = "record Marker: Once {} fn run(values: &[u32]) { values as &mut [u32]; }"; |
|
| 5455 | 5508 | let result = try resolveProgramStr(&mut a, program); |
|
| 5456 | 5509 | let err = try expectError(&result); |
|
| 5457 | 5510 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
|
| 5458 | 5511 | else throw testing::TestError::Failed; |
|
| 5459 | 5512 | } |
|
| 5460 | 5513 | ||
| 5461 | 5514 | /// Mutable unsafe receivers do not create checked exclusive loans. |
|
| 5462 | - | @test fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) { |
|
| 5515 | + | @test unsafe fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) { |
|
| 5463 | 5516 | let program = "record Marker: Once {} record Value { number: u32 } unsafe fn (value: *unsafe mut Value) update(other: *unsafe mut Value) {} unsafe fn run(value: *unsafe mut Value) { value.update(value); }"; |
|
| 5464 | 5517 | try expectAnalyzeOk(program); |
|
| 5465 | 5518 | } |
|
| 5466 | 5519 | ||
| 5467 | 5520 | /// Unsafe instance-method attributes enable unsafe operations in the body. |
|
| 5468 | - | @test fn testUnsafeInstanceMethodBody() throws (testing::TestError) { |
|
| 5521 | + | @test unsafe fn testUnsafeInstanceMethodBody() throws (testing::TestError) { |
|
| 5469 | 5522 | let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (*unsafe Read) get() -> u32; } instance Read for Value { unsafe fn (value: *unsafe Value) get() -> u32 { return value.number; } }"; |
|
| 5470 | 5523 | try expectAnalyzeOk(program); |
|
| 5471 | 5524 | } |
|
| 5472 | 5525 | ||
| 5473 | 5526 | /// Unsafe instance methods cannot implement safe trait contracts. |
|
| 5474 | - | @test fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) { |
|
| 5527 | + | @test unsafe fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) { |
|
| 5475 | 5528 | let mut a = testResolver(); |
|
| 5476 | 5529 | let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }"; |
|
| 5477 | 5530 | let result = try resolveProgramStr(&mut a, program); |
|
| 5478 | 5531 | try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch); |
|
| 5479 | 5532 | } |
|
| 5480 | 5533 | ||
| 5481 | 5534 | /// Unsafe trait methods retain their call-site requirement through dispatch. |
|
| 5482 | - | @test fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) { |
|
| 5535 | + | @test unsafe fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) { |
|
| 5483 | 5536 | let mut a = testResolver(); |
|
| 5484 | 5537 | let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (&Read) get() -> u32; } instance Read for Value { unsafe fn (value: &Value) get() -> u32 { return value.number; } } fn inspect(object: &opaque Read) -> u32 { return object.get(); }"; |
|
| 5485 | 5538 | let result = try resolveProgramStr(&mut a, program); |
|
| 5486 | 5539 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 5487 | 5540 | } |
|
| 5541 | + | ||
| 5542 | + | /// An unsafe callback retains its requirement at an indirect call. |
|
| 5543 | + | @test unsafe fn testUnsafeCallbackCallRejected() throws (testing::TestError) { |
|
| 5544 | + | let mut a = testResolver(); |
|
| 5545 | + | let result = try resolveProgramStr(&mut a, |
|
| 5546 | + | "fn run(callback: unsafe fn() -> u32) -> u32 { return callback(); }"); |
|
| 5547 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 5548 | + | } |
|
| 5549 | + | ||
| 5550 | + | /// An unsafe function cannot enter a safe callback slot. |
|
| 5551 | + | @test unsafe fn testUnsafeCallbackAssignmentRejected() throws (testing::TestError) { |
|
| 5552 | + | let mut a = testResolver(); |
|
| 5553 | + | let result = try resolveProgramStr(&mut a, |
|
| 5554 | + | "unsafe fn load() -> u32 { return 1; } fn run() { let callback: fn() -> u32 = load; }"); |
|
| 5555 | + | let err = try expectError(&result); |
|
| 5556 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5557 | + | else throw testing::TestError::Failed; |
|
| 5558 | + | } |
|
| 5559 | + | ||
| 5560 | + | /// A safe function can enter an unsafe callback slot. |
|
| 5561 | + | @test unsafe fn testSafeCallbackIntoUnsafeSlot() throws (testing::TestError) { |
|
| 5562 | + | try expectAnalyzeOk( |
|
| 5563 | + | "fn load() -> u32 { return 1; } unsafe fn run() -> u32 { let callback: unsafe fn() -> u32 = load; return callback(); }"); |
|
| 5564 | + | } |
|
| 5565 | + | ||
| 5566 | + | /// Borrowed callback storage must preserve its function safety type. |
|
| 5567 | + | @test unsafe fn testBorrowedCallbackSafetyInvariant() throws (testing::TestError) { |
|
| 5568 | + | let mut a = testResolver(); |
|
| 5569 | + | let result = try resolveProgramStr(&mut a, |
|
| 5570 | + | "fn replace(slot: &mut unsafe fn()) {} fn load() {} fn run() { let mut callback: fn() = load; replace(&mut callback); }"); |
|
| 5571 | + | let err = try expectError(&result); |
|
| 5572 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 5573 | + | else throw testing::TestError::Failed; |
|
| 5574 | + | } |
lib/std/lang/scanner.rad
+13 -13
| 236 | 236 | ||
| 237 | 237 | return Scanner { sourceLoc, source, token: 0, cursor: 0, pool }; |
|
| 238 | 238 | } |
|
| 239 | 239 | ||
| 240 | 240 | /// Check if we've reached the end of input. |
|
| 241 | - | export fn isEof(s: *Scanner) -> bool { |
|
| 241 | + | export fn isEof(s: &Scanner) -> bool { |
|
| 242 | 242 | return s.cursor >= s.source.len; |
|
| 243 | 243 | } |
|
| 244 | 244 | ||
| 245 | 245 | /// Get the current character, if any. |
|
| 246 | - | export fn current(s: *Scanner) -> ?u8 { |
|
| 246 | + | export fn current(s: &Scanner) -> ?u8 { |
|
| 247 | 247 | if isEof(s) { |
|
| 248 | 248 | return nil; |
|
| 249 | 249 | } |
|
| 250 | 250 | return s.source[s.cursor]; |
|
| 251 | 251 | } |
|
| 252 | 252 | ||
| 253 | 253 | /// Peek at the next character without advancing the scanner. |
|
| 254 | - | fn peek(s: *Scanner) -> ?u8 { |
|
| 254 | + | fn peek(s: &Scanner) -> ?u8 { |
|
| 255 | 255 | if s.cursor + 1 >= s.source.len { |
|
| 256 | 256 | return nil; |
|
| 257 | 257 | } |
|
| 258 | 258 | return s.source[s.cursor + 1]; |
|
| 259 | 259 | } |
|
| 260 | 260 | ||
| 261 | 261 | /// Advance scanner and return the character that was consumed. |
|
| 262 | - | fn advance(s: *mut Scanner) -> u8 { |
|
| 262 | + | fn advance(s: &mut Scanner) -> u8 { |
|
| 263 | 263 | let ch = s.source[s.cursor]; |
|
| 264 | 264 | set s.cursor += 1; |
|
| 265 | 265 | return ch; |
|
| 266 | 266 | } |
|
| 267 | 267 | ||
| 268 | 268 | /// Consume the expected character if it matches the current position. |
|
| 269 | - | fn consume(s: *mut Scanner, expected: u8) -> bool { |
|
| 269 | + | fn consume(s: &mut Scanner, expected: u8) -> bool { |
|
| 270 | 270 | if let c = current(s); c == expected { |
|
| 271 | 271 | advance(s); |
|
| 272 | 272 | return true; |
|
| 273 | 273 | } |
|
| 274 | 274 | return false; |
|
| 275 | 275 | } |
|
| 276 | 276 | ||
| 277 | 277 | /// Create a token from the current scanner state. |
|
| 278 | - | fn tok(s: *Scanner, kind: TokenKind) -> Token { |
|
| 278 | + | fn tok(s: &Scanner, kind: TokenKind) -> Token { |
|
| 279 | 279 | return Token { kind, source: &s.source[s.token..s.cursor], offset: s.token }; |
|
| 280 | 280 | } |
|
| 281 | 281 | ||
| 282 | 282 | /// Create an invalid token with the given message. |
|
| 283 | 283 | export fn invalid(offset: u32, message: *[u8]) -> Token { |
|
| 284 | 284 | return Token { kind: TokenKind::Invalid, source: message, offset }; |
|
| 285 | 285 | } |
|
| 286 | 286 | ||
| 287 | 287 | /// Skip whitespace characters and line comments. |
|
| 288 | - | fn skipWhitespace(s: *mut Scanner) { |
|
| 288 | + | fn skipWhitespace(s: &mut Scanner) { |
|
| 289 | 289 | while let ch = current(s) { |
|
| 290 | 290 | match ch { |
|
| 291 | 291 | case ' ', '\n', '\r', '\t' => advance(s), |
|
| 292 | 292 | case '/' => { |
|
| 293 | 293 | if let c = peek(s); c == '/' { |
| 302 | 302 | } |
|
| 303 | 303 | } |
|
| 304 | 304 | } |
|
| 305 | 305 | ||
| 306 | 306 | /// Scan numeric literal (decimal, hex, or binary). |
|
| 307 | - | fn scanNumber(s: *mut Scanner) -> Token { |
|
| 307 | + | fn scanNumber(s: &mut Scanner) -> Token { |
|
| 308 | 308 | // Check for hex literal (`0x` or `0X` prefix). |
|
| 309 | 309 | if s.source[s.cursor - 1] == '0' { |
|
| 310 | 310 | if let ch = current(s); ch == 'x' or ch == 'X' { |
|
| 311 | 311 | advance(s); |
|
| 312 | 312 | // Must have at least one hex digit after `0x`. |
| 345 | 345 | } |
|
| 346 | 346 | } |
|
| 347 | 347 | return tok(s, TokenKind::Number); |
|
| 348 | 348 | } |
|
| 349 | 349 | ||
| 350 | - | fn scanDelimited(s: *mut Scanner, delim: u8, kind: TokenKind) -> ?Token { |
|
| 350 | + | fn scanDelimited(s: &mut Scanner, delim: u8, kind: TokenKind) -> ?Token { |
|
| 351 | 351 | while let ch = current(s); ch <> delim { |
|
| 352 | 352 | if not char::isPrint(ch) { |
|
| 353 | 353 | return invalid(s.token, "invalid character"); |
|
| 354 | 354 | } |
|
| 355 | 355 | if consume(s, '\\') { // Consume escapes |
| 364 | 364 | } |
|
| 365 | 365 | return tok(s, kind); |
|
| 366 | 366 | } |
|
| 367 | 367 | ||
| 368 | 368 | /// Scan string literal enclosed in double quotes. |
|
| 369 | - | fn scanString(s: *mut Scanner) -> Token { |
|
| 369 | + | fn scanString(s: &mut Scanner) -> Token { |
|
| 370 | 370 | if let tok = scanDelimited(s, '"', TokenKind::String) { |
|
| 371 | 371 | return tok; |
|
| 372 | 372 | } |
|
| 373 | 373 | return invalid(s.token, "unterminated string"); |
|
| 374 | 374 | } |
|
| 375 | 375 | ||
| 376 | 376 | /// Scan character literal enclosed in single quotes. |
|
| 377 | - | fn scanChar(s: *mut Scanner) -> Token { |
|
| 377 | + | fn scanChar(s: &mut Scanner) -> Token { |
|
| 378 | 378 | if let tok = scanDelimited(s, '\'', TokenKind::Char) { |
|
| 379 | 379 | return tok; |
|
| 380 | 380 | } |
|
| 381 | 381 | return invalid(s.token, "unterminated character"); |
|
| 382 | 382 | } |
| 399 | 399 | } |
|
| 400 | 400 | return TokenKind::Ident; |
|
| 401 | 401 | } |
|
| 402 | 402 | ||
| 403 | 403 | /// Scan an identifier, keyword, or label. |
|
| 404 | - | fn scanIdentifier(s: *mut Scanner) -> Token { |
|
| 404 | + | fn scanIdentifier(s: &mut Scanner) -> Token { |
|
| 405 | 405 | while let ch = current(s); char::isAlpha(ch) or ch == '_' or char::isDigit(ch) { |
|
| 406 | 406 | advance(s); |
|
| 407 | 407 | } |
|
| 408 | 408 | let ident = &s.source[s.token..s.cursor]; |
|
| 409 | 409 | let kind = keywordOrIdent(ident); |
| 414 | 414 | } |
|
| 415 | 415 | return tok(s, kind); |
|
| 416 | 416 | } |
|
| 417 | 417 | ||
| 418 | 418 | /// Scan the next token. |
|
| 419 | - | export fn next(s: *mut Scanner) -> Token { |
|
| 419 | + | export fn next(s: &mut Scanner) -> Token { |
|
| 420 | 420 | skipWhitespace(s); // Skip any whitespace between tokens. |
|
| 421 | 421 | set s.token = s.cursor; // Token starts at current position. |
|
| 422 | 422 | ||
| 423 | 423 | if isEof(s) { |
|
| 424 | 424 | return tok(s, TokenKind::Eof); |
lib/std/lang/sexpr.rad
+14 -14
| 9 | 9 | /// Output target for S-expression printing. |
|
| 10 | 10 | export union Output: Copy { |
|
| 11 | 11 | /// Print to stdout. |
|
| 12 | 12 | Stdout, |
|
| 13 | 13 | /// Write to a buffer, tracking position. |
|
| 14 | - | Buffer { buf: *mut [u8], pos: *mut u32 }, |
|
| 14 | + | Buffer { buf: *mut [u8], pos: *unsafe mut u32 }, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// An S-expression element. |
|
| 18 | 18 | export union Expr: Copy { |
|
| 19 | 19 | /// An empty expression. |
| 31 | 31 | /// A block with a name, inline items, and child statements on separate lines. |
|
| 32 | 32 | Block { name: *[u8], items: *[Expr], children: *[Expr] }, |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Allocate an array of Expr in the arena. |
|
| 36 | - | export fn allocExprs(arena: *mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) { |
|
| 36 | + | export fn allocExprs(arena: &mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) { |
|
| 37 | 37 | if len == 0 { |
|
| 38 | 38 | throw alloc::AllocError::OutOfMemory; |
|
| 39 | 39 | } |
|
| 40 | 40 | let ptr = try alloc::allocSlice(arena, @sizeOf(Expr), @alignOf(Expr), len); |
|
| 41 | 41 | return ptr as *mut [Expr]; |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Allocate and copy items into the arena. |
|
| 45 | - | export fn allocItems(a: *mut alloc::Arena, items: *[Expr]) -> *[Expr] { |
|
| 45 | + | export fn allocItems(a: &mut alloc::Arena, items: &[Expr]) -> *[Expr] { |
|
| 46 | 46 | if items.len == 0 { |
|
| 47 | 47 | return &[]; |
|
| 48 | 48 | } |
|
| 49 | 49 | let buf = try! allocExprs(a, items.len); |
|
| 50 | 50 | for item, i in items { |
| 62 | 62 | export fn str(s: *[u8]) -> Expr { |
|
| 63 | 63 | return Expr::Str(s); |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Shorthand for creating a list. |
|
| 67 | - | export fn list(a: *mut alloc::Arena, head: *[u8], tail: *[Expr]) -> Expr { |
|
| 67 | + | export fn list(a: &mut alloc::Arena, head: *[u8], tail: &[Expr]) -> Expr { |
|
| 68 | 68 | return Expr::List { head, tail: allocItems(a, tail), multiline: false }; |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | 71 | /// Shorthand for creating a bracket-delimited vector. |
|
| 72 | - | export fn vec(a: *mut alloc::Arena, items: *[Expr]) -> Expr { |
|
| 72 | + | export fn vec(a: &mut alloc::Arena, items: &[Expr]) -> Expr { |
|
| 73 | 73 | return Expr::Vec { items: allocItems(a, items) }; |
|
| 74 | 74 | } |
|
| 75 | 75 | ||
| 76 | 76 | /// Shorthand for creating a block with inline items and child expressions. |
|
| 77 | - | export fn block(a: *mut alloc::Arena, name: *[u8], items: *[Expr], children: *[Expr]) -> Expr { |
|
| 77 | + | export fn block(a: &mut alloc::Arena, name: *[u8], items: &[Expr], children: &[Expr]) -> Expr { |
|
| 78 | 78 | return Expr::Block { name, items: allocItems(a, items), children: allocItems(a, children) }; |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Write a string to the output target. |
|
| 82 | - | export fn write(out: *mut Output, s: *[u8]) { |
|
| 82 | + | export unsafe fn write(out: &mut Output, s: &[u8]) { |
|
| 83 | 83 | match *out { |
|
| 84 | 84 | case Output::Stdout => io::print(s), |
|
| 85 | 85 | case Output::Buffer { buf, pos } => { |
|
| 86 | 86 | let remaining = buf.len - *pos; |
|
| 87 | 87 | let toWrite = remaining if s.len > remaining else s.len; |
| 92 | 92 | } |
|
| 93 | 93 | } |
|
| 94 | 94 | } |
|
| 95 | 95 | ||
| 96 | 96 | /// Emit indentation to the output target. |
|
| 97 | - | fn indentTo(out: *mut Output, depth: u32) { |
|
| 97 | + | unsafe fn indentTo(out: &mut Output, depth: u32) { |
|
| 98 | 98 | for _ in 0..depth { |
|
| 99 | 99 | write(out, " "); |
|
| 100 | 100 | } |
|
| 101 | 101 | } |
|
| 102 | 102 | ||
| 103 | 103 | /// Print a single character with escaping to the output target. |
|
| 104 | - | export fn printEscapedTo(out: *mut Output, c: u8) { |
|
| 104 | + | export unsafe fn printEscapedTo(out: &mut Output, c: u8) { |
|
| 105 | 105 | match c { |
|
| 106 | 106 | case '\n' => write(out, "\\n"), |
|
| 107 | 107 | case '\r' => write(out, "\\r"), |
|
| 108 | 108 | case '\t' => write(out, "\\t"), |
|
| 109 | 109 | case '\\' => write(out, "\\\\"), |
| 111 | 111 | else => write(out, &[c]), |
|
| 112 | 112 | } |
|
| 113 | 113 | } |
|
| 114 | 114 | ||
| 115 | 115 | /// Print a quoted string with escape sequences to the output target. |
|
| 116 | - | export fn printStringTo(out: *mut Output, s: *[u8]) { |
|
| 116 | + | export unsafe fn printStringTo(out: &mut Output, s: &[u8]) { |
|
| 117 | 117 | write(out, "\""); |
|
| 118 | 118 | for i in 0..s.len { |
|
| 119 | 119 | printEscapedTo(out, s[i]); |
|
| 120 | 120 | } |
|
| 121 | 121 | write(out, "\""); |
|
| 122 | 122 | } |
|
| 123 | 123 | ||
| 124 | 124 | /// Print a character literal with escape sequences to the output target. |
|
| 125 | - | export fn printCharTo(out: *mut Output, c: u8) { |
|
| 125 | + | export unsafe fn printCharTo(out: &mut Output, c: u8) { |
|
| 126 | 126 | write(out, "'"); |
|
| 127 | 127 | printEscapedTo(out, c); |
|
| 128 | 128 | write(out, "'"); |
|
| 129 | 129 | } |
|
| 130 | 130 | ||
| 131 | 131 | /// Print an S-expression to the given output target at the given depth. |
|
| 132 | - | export fn printTo(expr: Expr, depth: u32, out: *mut Output) { |
|
| 132 | + | export unsafe fn printTo(expr: Expr, depth: u32, out: &mut Output) { |
|
| 133 | 133 | match expr { |
|
| 134 | 134 | case Expr::Null => {}, |
|
| 135 | 135 | case Expr::Sym(s) => write(out, s), |
|
| 136 | 136 | case Expr::Str(s) => printStringTo(out, s), |
|
| 137 | 137 | case Expr::Char(c) => printCharTo(out, c), |
| 193 | 193 | } |
|
| 194 | 194 | } |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// Print an S-expression to stdout at the given indentation depth. |
|
| 198 | - | export fn print(expr: Expr, depth: u32) { |
|
| 198 | + | export unsafe fn print(expr: Expr, depth: u32) { |
|
| 199 | 199 | let mut out = Output::Stdout; |
|
| 200 | 200 | printTo(expr, depth, &mut out); |
|
| 201 | 201 | } |
|
| 202 | 202 | ||
| 203 | 203 | /// Emit indentation for `depth` levels to stdout. |
|
| 204 | - | export fn indent(depth: u32) { |
|
| 204 | + | export unsafe fn indent(depth: u32) { |
|
| 205 | 205 | let mut out = Output::Stdout; |
|
| 206 | 206 | indentTo(&mut out, depth); |
|
| 207 | 207 | } |
|
| 208 | 208 |
lib/std/mem.rad
+4 -4
| 3 | 3 | /// Buffer is too small. |
|
| 4 | 4 | BufferTooSmall, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | /// Copy bytes between two slices. Returns the number of bytes copied. |
|
| 8 | - | export fn copy(into: *mut [u8], from: *[u8]) -> u32 throws (MemoryError) { |
|
| 8 | + | export fn copy(into: &mut [u8], from: &[u8]) -> u32 throws (MemoryError) { |
|
| 9 | 9 | if into.len < from.len { |
|
| 10 | 10 | throw MemoryError::BufferTooSmall; |
|
| 11 | 11 | } |
|
| 12 | 12 | for x, i in from { |
|
| 13 | 13 | set into[i] = x; |
| 15 | 15 | return from.len; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | /// Strip a byte-level prefix from the input, and return the suffix. |
|
| 19 | 19 | /// Returns `nil` if the prefix wasn't found. |
|
| 20 | - | export fn stripPrefix(prefix: *[u8], input: *[u8]) -> ?*[u8] { |
|
| 20 | + | export fn stripPrefix(prefix: &[u8], input: *[u8]) -> ?*[u8] { |
|
| 21 | 21 | if prefix.len == 0 { |
|
| 22 | 22 | return input; |
|
| 23 | 23 | } |
|
| 24 | 24 | if prefix.len > input.len { |
|
| 25 | 25 | return nil; |
| 52 | 52 | } |
|
| 53 | 53 | return count; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Check whether two byte slices have the same length and contents. |
|
| 57 | - | export fn eq(a: *[u8], b: *[u8]) -> bool { |
|
| 57 | + | export fn eq(a: &[u8], b: &[u8]) -> bool { |
|
| 58 | 58 | if a.len <> b.len { |
|
| 59 | 59 | return false; |
|
| 60 | 60 | } |
|
| 61 | 61 | if a.ptr == b.ptr { |
|
| 62 | 62 | return true; |
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Compare two byte slices lexicographically. |
|
| 73 | 73 | /// |
|
| 74 | 74 | /// Returns `-1` when `a < b`, `1` when `a > b`, and `0` when equal. |
|
| 75 | - | export fn cmp(a: *[u8], b: *[u8]) -> i32 { |
|
| 75 | + | export fn cmp(a: &[u8], b: &[u8]) -> i32 { |
|
| 76 | 76 | let aLen = a.len; |
|
| 77 | 77 | let bLen = b.len; |
|
| 78 | 78 | ||
| 79 | 79 | let common = bLen if bLen < aLen else aLen; |
|
| 80 | 80 | for i in 0..common { |
lib/std/sys/unix.rad
+18 -14
| 31 | 31 | /// Special value representing current working directory for `openat()`. |
|
| 32 | 32 | constant AT_FDCWD: i64 = -100; |
|
| 33 | 33 | ||
| 34 | 34 | /// Opens a file at the given path and returns a file descriptor. |
|
| 35 | 35 | /// Returns a negative value on error. |
|
| 36 | - | export fn open(path: *[u8], flags: OpenFlags) -> i64 { |
|
| 36 | + | export fn open(path: &[u8], flags: OpenFlags) -> i64 { |
|
| 37 | 37 | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, 0); |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Opens a file at the given path with mode, returns a file descriptor. |
|
| 41 | - | export fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 { |
|
| 41 | + | export fn openOpts(path: &[u8], flags: OpenFlags, mode: i64) -> i64 { |
|
| 42 | 42 | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, mode); |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | 45 | /// Reads from a file descriptor into the provided buffer. |
|
| 46 | 46 | /// Returns the number of bytes read, or a negative value on error. |
|
| 47 | - | export fn read(fd: i64, buf: *mut [u8]) -> i64 { |
|
| 47 | + | export fn read(fd: i64, buf: &mut [u8]) -> i64 { |
|
| 48 | 48 | return intrinsics::ecall(63, fd, buf.ptr as i64, buf.len as i64, 0); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Reads from a file descriptor until EOF or buffer is full. |
|
| 52 | 52 | /// Returns the total number of bytes read, or a negative value on error. |
|
| 53 | - | export fn readToEnd(fd: i64, buf: *mut [u8]) -> i64 { |
|
| 53 | + | export fn readToEnd(fd: i64, buf: &mut [u8]) -> i64 { |
|
| 54 | 54 | let mut total: u32 = 0; |
|
| 55 | 55 | while total < buf.len { |
|
| 56 | - | let chunk = &mut buf[total..]; |
|
| 57 | - | let n = read(fd, chunk); |
|
| 56 | + | let n = read(fd, &mut buf[total..]); |
|
| 58 | 57 | ||
| 59 | 58 | if n < 0 { |
|
| 60 | 59 | return n; |
|
| 61 | 60 | } |
|
| 62 | 61 | if n == 0 { |
| 67 | 66 | return total as i64; |
|
| 68 | 67 | } |
|
| 69 | 68 | ||
| 70 | 69 | /// Writes to a file descriptor from the provided buffer. |
|
| 71 | 70 | /// Returns the number of bytes written, or a negative value on error. |
|
| 72 | - | export fn write(fd: i64, buf: *[u8]) -> i64 { |
|
| 71 | + | export fn write(fd: i64, buf: &[u8]) -> i64 { |
|
| 73 | 72 | return intrinsics::ecall(64, fd, buf.ptr as i64, buf.len as i64, 0); |
|
| 74 | 73 | } |
|
| 75 | 74 | ||
| 76 | 75 | /// Writes the entire contents of a buffer to a file descriptor. |
|
| 77 | 76 | /// Returns `false` when the descriptor cannot accept the full buffer. |
|
| 78 | - | export fn writeAll(fd: i64, data: *[u8]) -> bool { |
|
| 77 | + | export fn writeAll(fd: i64, data: &[u8]) -> bool { |
|
| 79 | 78 | let mut written: u32 = 0; |
|
| 80 | 79 | while written < data.len { |
|
| 81 | 80 | let n = write(fd, &data[written..]); |
|
| 82 | 81 | if n <= 0 { |
|
| 83 | 82 | return false; |
| 92 | 91 | export fn close(fd: i64) -> i64 { |
|
| 93 | 92 | return intrinsics::ecall(57, fd, 0, 0, 0); |
|
| 94 | 93 | } |
|
| 95 | 94 | ||
| 96 | 95 | /// Reads the entire contents of a file at the given path into the provided buffer. |
|
| 97 | - | /// Returns a slice containing the data read, or `nil` on error. |
|
| 98 | - | export fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] { |
|
| 96 | + | /// Return the number of bytes read, or `nil` on error. |
|
| 97 | + | export fn readFile(path: &[u8], buf: &mut [u8]) -> ?u32 { |
|
| 99 | 98 | let fd = open(path, O_RDONLY); |
|
| 100 | 99 | if fd < 0 { |
|
| 101 | 100 | return nil; |
|
| 102 | 101 | } |
|
| 103 | 102 | let n = readToEnd(fd, buf); |
| 106 | 105 | return nil; |
|
| 107 | 106 | } |
|
| 108 | 107 | if n < 0 { |
|
| 109 | 108 | return nil; |
|
| 110 | 109 | } |
|
| 111 | - | return &buf[..n as u32]; |
|
| 110 | + | return n as u32; |
|
| 112 | 111 | } |
|
| 113 | 112 | ||
| 114 | 113 | /// Exit the current process with the given status code. |
|
| 115 | 114 | export fn exit(status: i64) { |
|
| 116 | 115 | intrinsics::ecall(93, status, 0, 0, 0); |
|
| 117 | 116 | } |
|
| 118 | 117 | ||
| 119 | 118 | /// Writes the entire contents of a buffer to a file at the given path. |
|
| 120 | 119 | /// Creates the file if it doesn't exist, truncates if it does. |
|
| 121 | 120 | /// Returns `true` on success. |
|
| 122 | - | export fn writeFile(path: *[u8], data: *[u8]) -> bool { |
|
| 123 | - | return writeFileParts(path, &[data]); |
|
| 121 | + | export fn writeFile(path: &[u8], data: &[u8]) -> bool { |
|
| 122 | + | let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC); |
|
| 123 | + | let fd = openOpts(path, flags, 420); |
|
| 124 | + | if fd < 0 { return false; } |
|
| 125 | + | let written = writeAll(fd, data); |
|
| 126 | + | let closed = close(fd) == 0; |
|
| 127 | + | return written and closed; |
|
| 124 | 128 | } |
|
| 125 | 129 | ||
| 126 | 130 | /// Writes each buffer to a file at the given path. |
|
| 127 | 131 | /// Creates the file if it doesn't exist, truncates if it does. |
|
| 128 | 132 | /// Returns `true` only when every part and the final close succeed. |
|
| 129 | - | export fn writeFileParts(path: *[u8], parts: *[*[u8]]) -> bool { |
|
| 133 | + | export fn writeFileParts(path: &[u8], parts: &[*[u8]]) -> bool { |
|
| 130 | 134 | let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC); |
|
| 131 | 135 | let fd = openOpts(path, flags, 420); // 0644 in octal. |
|
| 132 | 136 | if fd < 0 { |
|
| 133 | 137 | return false; |
|
| 134 | 138 | } |
lib/std/testing.rad
+8 -8
| 16 | 16 | ||
| 17 | 17 | /// Descriptor for a single test case, holding its module path, name, and entry point. |
|
| 18 | 18 | export record TestInfo: Copy { |
|
| 19 | 19 | module: *[u8], |
|
| 20 | 20 | name: *[u8], |
|
| 21 | - | func: fn() throws (TestError), |
|
| 21 | + | func: unsafe fn() throws (TestError), |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | /// Construct a [`TestInfo`]. Used by the compiler's synthetic test harness. |
|
| 25 | - | export fn test(module: *[u8], name: *[u8], func: fn() throws (TestError)) -> TestInfo { |
|
| 25 | + | export fn test(module: *[u8], name: *[u8], func: unsafe fn() throws (TestError)) -> TestInfo { |
|
| 26 | 26 | return TestInfo { module, name, func }; |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// Run all tests and return `0` on success or `1` if any test failed. |
|
| 30 | - | export fn runAllTests(tests: *[TestInfo]) -> i32 { |
|
| 30 | + | export unsafe fn runAllTests(tests: &[TestInfo]) -> i32 { |
|
| 31 | 31 | let mut ctx: Ctx = Ctx { passed: 0, failed: 0 }; |
|
| 32 | 32 | ||
| 33 | 33 | io::print("Running "); |
|
| 34 | 34 | io::printU32(tests.len); |
|
| 35 | 35 | io::print(" test(s)...\n\n"); |
| 43 | 43 | return 1; |
|
| 44 | 44 | } |
|
| 45 | 45 | return 0; |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | - | fn runTest( |
|
| 49 | - | ctx: *mut Ctx, |
|
| 48 | + | unsafe fn runTest( |
|
| 49 | + | ctx: &mut Ctx, |
|
| 50 | 50 | module: *[u8], |
|
| 51 | 51 | name: *[u8], |
|
| 52 | - | testFn: fn () throws (TestError) |
|
| 52 | + | testFn: unsafe fn() throws (TestError) |
|
| 53 | 53 | ) { |
|
| 54 | 54 | io::print("test "); |
|
| 55 | 55 | io::print(module); |
|
| 56 | 56 | io::print("::"); |
|
| 57 | 57 | io::print(name); |
| 68 | 68 | io::printLn("FAILED"); |
|
| 69 | 69 | set ctx.failed += 1; |
|
| 70 | 70 | } |
|
| 71 | 71 | } |
|
| 72 | 72 | ||
| 73 | - | fn printTestSummary(ctx: *Ctx) { |
|
| 73 | + | fn printTestSummary(ctx: &Ctx) { |
|
| 74 | 74 | if (ctx.failed > 0) { |
|
| 75 | 75 | io::print("\ntest result: FAILED. "); |
|
| 76 | 76 | io::printU32(ctx.passed); |
|
| 77 | 77 | io::print(" passed; "); |
|
| 78 | 78 | io::printU32(ctx.failed); |
| 83 | 83 | io::printLn(" passed; 0 failed"); |
|
| 84 | 84 | } |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Assert that two byte slices are equal. |
|
| 88 | - | export fn expectBytesEq(left: *[u8], right: *[u8]) |
|
| 88 | + | export fn expectBytesEq(left: &[u8], right: &[u8]) |
|
| 89 | 89 | throws (TestError) |
|
| 90 | 90 | { |
|
| 91 | 91 | try expect(mem::eq(left, right)); |
|
| 92 | 92 | } |
|
| 93 | 93 |
lib/std/tests.rad
+53 -53
| 15 | 15 | ||
| 16 | 16 | // fmt ///////////////////////////////////////////////////////////////////////// |
|
| 17 | 17 | ||
| 18 | 18 | @test fn testFormatU32Zero() throws (testing::TestError) { |
|
| 19 | 19 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 20 | - | let result: *[u8] = fmt::formatU32(0, &mut buffer[..]); |
|
| 21 | - | try testing::expect(result.len == 1); |
|
| 22 | - | try testing::expectBytesEq(result, "0"); |
|
| 20 | + | let start = fmt::formatU32(0, &mut buffer[..]); |
|
| 21 | + | try testing::expect(buffer.len - start == 1); |
|
| 22 | + | try testing::expectBytesEq(&buffer[start..], "0"); |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | @test fn testFormatU32Basic() throws (testing::TestError) { |
|
| 26 | 26 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 27 | - | let result: *[u8] = fmt::formatU32(123, &mut buffer[..]); |
|
| 28 | - | try testing::expect(result.len == 3); |
|
| 29 | - | try testing::expectBytesEq(result, "123"); |
|
| 27 | + | let start = fmt::formatU32(123, &mut buffer[..]); |
|
| 28 | + | try testing::expect(buffer.len - start == 3); |
|
| 29 | + | try testing::expectBytesEq(&buffer[start..], "123"); |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | @test fn testFormatU32Large() throws (testing::TestError) { |
|
| 33 | 33 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 34 | - | let result: *[u8] = fmt::formatU32(90000, &mut buffer[..]); |
|
| 35 | - | try testing::expect(result.len == 5); |
|
| 36 | - | try testing::expectBytesEq(result, "90000"); |
|
| 34 | + | let start = fmt::formatU32(90000, &mut buffer[..]); |
|
| 35 | + | try testing::expect(buffer.len - start == 5); |
|
| 36 | + | try testing::expectBytesEq(&buffer[start..], "90000"); |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | @test fn testFormatU32Max() throws (testing::TestError) { |
|
| 40 | 40 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 41 | - | let result: *[u8] = fmt::formatU32(4294967295, &mut buffer[..]); |
|
| 42 | - | try testing::expect(result.len == 10); |
|
| 43 | - | try testing::expectBytesEq(result, "4294967295"); |
|
| 41 | + | let start = fmt::formatU32(4294967295, &mut buffer[..]); |
|
| 42 | + | try testing::expect(buffer.len - start == 10); |
|
| 43 | + | try testing::expectBytesEq(&buffer[start..], "4294967295"); |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | @test fn testFormatI32Positive() throws (testing::TestError) { |
|
| 47 | 47 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 48 | - | let result: *[u8] = fmt::formatI32(456, &mut buffer[..]); |
|
| 49 | - | try testing::expect(result.len == 3); |
|
| 50 | - | try testing::expectBytesEq(result, "456"); |
|
| 48 | + | let start = fmt::formatI32(456, &mut buffer[..]); |
|
| 49 | + | try testing::expect(buffer.len - start == 3); |
|
| 50 | + | try testing::expectBytesEq(&buffer[start..], "456"); |
|
| 51 | 51 | } |
|
| 52 | 52 | ||
| 53 | 53 | @test fn testFormatI32Negative() throws (testing::TestError) { |
|
| 54 | 54 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 55 | - | let result: *[u8] = fmt::formatI32(-789, &mut buffer[..]); |
|
| 56 | - | try testing::expect(result.len == 4); |
|
| 57 | - | try testing::expectBytesEq(result, "-789"); |
|
| 55 | + | let start = fmt::formatI32(-789, &mut buffer[..]); |
|
| 56 | + | try testing::expect(buffer.len - start == 4); |
|
| 57 | + | try testing::expectBytesEq(&buffer[start..], "-789"); |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | @test fn testFormatI32Zero() throws (testing::TestError) { |
|
| 61 | 61 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 62 | - | let result: *[u8] = fmt::formatI32(0, &mut buffer[..]); |
|
| 63 | - | try testing::expect(result.len == 1); |
|
| 64 | - | try testing::expectBytesEq(result, "0"); |
|
| 62 | + | let start = fmt::formatI32(0, &mut buffer[..]); |
|
| 63 | + | try testing::expect(buffer.len - start == 1); |
|
| 64 | + | try testing::expectBytesEq(&buffer[start..], "0"); |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | @test fn testFormatI32Max() throws (testing::TestError) { |
|
| 68 | 68 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 69 | - | let result: *[u8] = fmt::formatI32(2147483647, &mut buffer[..]); |
|
| 70 | - | try testing::expect(result.len == 10); |
|
| 71 | - | try testing::expectBytesEq(result, "2147483647"); |
|
| 69 | + | let start = fmt::formatI32(2147483647, &mut buffer[..]); |
|
| 70 | + | try testing::expect(buffer.len - start == 10); |
|
| 71 | + | try testing::expectBytesEq(&buffer[start..], "2147483647"); |
|
| 72 | 72 | } |
|
| 73 | 73 | ||
| 74 | 74 | @test fn testFormatI32Min() throws (testing::TestError) { |
|
| 75 | 75 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 76 | - | let result: *[u8] = fmt::formatI32(-2147483648, &mut buffer[..]); |
|
| 77 | - | try testing::expect(result.len == 11); |
|
| 78 | - | try testing::expectBytesEq(result, "-2147483648"); |
|
| 76 | + | let start = fmt::formatI32(-2147483648, &mut buffer[..]); |
|
| 77 | + | try testing::expect(buffer.len - start == 11); |
|
| 78 | + | try testing::expectBytesEq(&buffer[start..], "-2147483648"); |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | @test fn testFormatU64Zero() throws (testing::TestError) { |
|
| 82 | 82 | let mut buffer: [u8; 20] = [0; 20]; |
|
| 83 | - | let result: *[u8] = fmt::formatU64(0, &mut buffer[..]); |
|
| 84 | - | try testing::expect(result.len == 1); |
|
| 85 | - | try testing::expectBytesEq(result, "0"); |
|
| 83 | + | let start = fmt::formatU64(0, &mut buffer[..]); |
|
| 84 | + | try testing::expect(buffer.len - start == 1); |
|
| 85 | + | try testing::expectBytesEq(&buffer[start..], "0"); |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | 88 | @test fn testFormatU64Max() throws (testing::TestError) { |
|
| 89 | 89 | let mut buffer: [u8; 20] = [0; 20]; |
|
| 90 | - | let result: *[u8] = fmt::formatU64(18446744073709551615, &mut buffer[..]); |
|
| 91 | - | try testing::expect(result.len == 20); |
|
| 92 | - | try testing::expectBytesEq(result, "18446744073709551615"); |
|
| 90 | + | let start = fmt::formatU64(18446744073709551615, &mut buffer[..]); |
|
| 91 | + | try testing::expect(buffer.len - start == 20); |
|
| 92 | + | try testing::expectBytesEq(&buffer[start..], "18446744073709551615"); |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | 95 | @test fn testFormatI64Negative() throws (testing::TestError) { |
|
| 96 | 96 | let mut buffer: [u8; 20] = [0; 20]; |
|
| 97 | - | let result: *[u8] = fmt::formatI64(-9876543210, &mut buffer[..]); |
|
| 98 | - | try testing::expect(result.len == 11); |
|
| 99 | - | try testing::expectBytesEq(result, "-9876543210"); |
|
| 97 | + | let start = fmt::formatI64(-9876543210, &mut buffer[..]); |
|
| 98 | + | try testing::expect(buffer.len - start == 11); |
|
| 99 | + | try testing::expectBytesEq(&buffer[start..], "-9876543210"); |
|
| 100 | 100 | } |
|
| 101 | 101 | ||
| 102 | 102 | @test fn testFormatI64Min() throws (testing::TestError) { |
|
| 103 | 103 | let mut buffer: [u8; 20] = [0; 20]; |
|
| 104 | - | let result: *[u8] = fmt::formatI64(-9223372036854775808, &mut buffer[..]); |
|
| 105 | - | try testing::expect(result.len == 20); |
|
| 106 | - | try testing::expectBytesEq(result, "-9223372036854775808"); |
|
| 104 | + | let start = fmt::formatI64(-9223372036854775808, &mut buffer[..]); |
|
| 105 | + | try testing::expect(buffer.len - start == 20); |
|
| 106 | + | try testing::expectBytesEq(&buffer[start..], "-9223372036854775808"); |
|
| 107 | 107 | } |
|
| 108 | 108 | ||
| 109 | 109 | @test fn testParseIntLiteralText() throws (testing::TestError) { |
|
| 110 | 110 | let dec = try fmt::parseInt("123") catch { |
|
| 111 | 111 | throw testing::TestError::Failed; |
| 252 | 252 | throw testing::TestError::Failed; |
|
| 253 | 253 | } |
|
| 254 | 254 | let data = unix::readFile(path, &mut buffer[..]) else { |
|
| 255 | 255 | throw testing::TestError::Failed; |
|
| 256 | 256 | }; |
|
| 257 | - | try testing::expectBytesEq(data, "hello world"); |
|
| 257 | + | try testing::expectBytesEq(&buffer[..data], "hello world"); |
|
| 258 | 258 | } |
|
| 259 | 259 | ||
| 260 | 260 | @test fn testStripPrefixMatch() throws (testing::TestError) { |
|
| 261 | 261 | let input: *[u8] = "hello world"; |
|
| 262 | 262 | let prefix: *[u8] = "hello"; |
| 338 | 338 | try testing::expect(mem::cmp("abcd", "abc") > 0); |
|
| 339 | 339 | } |
|
| 340 | 340 | ||
| 341 | 341 | // vec ///////////////////////////////////////////////////////////////////////// |
|
| 342 | 342 | ||
| 343 | - | @test fn testVecInitialState() throws (testing::TestError) { |
|
| 343 | + | @test unsafe fn testVecInitialState() throws (testing::TestError) { |
|
| 344 | 344 | let mut arena: [u8; 16] align(4) = undefined; |
|
| 345 | 345 | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 346 | 346 | ||
| 347 | 347 | try testing::expect(vec::len(&v) == 0); |
|
| 348 | 348 | try testing::expect(vec::capacity(&v) == 4); |
|
| 349 | 349 | } |
|
| 350 | 350 | ||
| 351 | - | @test fn testVecPushAndGet() throws (testing::TestError) { |
|
| 351 | + | @test unsafe fn testVecPushAndGet() throws (testing::TestError) { |
|
| 352 | 352 | let mut arena: [u8; 16] align(4) = undefined; |
|
| 353 | 353 | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 354 | 354 | ||
| 355 | 355 | let val1: i32 = 42; |
|
| 356 | 356 | try testing::expect(vec::push(&mut v, &val1)); |
|
| 357 | 357 | try testing::expect(vec::len(&v) == 1); |
|
| 358 | 358 | ||
| 359 | 359 | let ptr = vec::get(&v, 0) else { |
|
| 360 | 360 | throw testing::TestError::Failed; |
|
| 361 | 361 | }; |
|
| 362 | - | let i32ptr: *i32 = ptr as *i32; |
|
| 362 | + | let i32ptr: *unsafe i32 = ptr as *unsafe i32; |
|
| 363 | 363 | let value: i32 = *i32ptr; |
|
| 364 | 364 | try testing::expect(value == 42); |
|
| 365 | 365 | } |
|
| 366 | 366 | ||
| 367 | - | @test fn testVecPushPop() throws (testing::TestError) { |
|
| 367 | + | @test unsafe fn testVecPushPop() throws (testing::TestError) { |
|
| 368 | 368 | let mut arena: [u8; 16] align(4) = undefined; |
|
| 369 | 369 | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 370 | 370 | ||
| 371 | 371 | let val1: i32 = 42; |
|
| 372 | 372 | let val2: i32 = 100; |
| 396 | 396 | try testing::expect(vec::len(&v) == 0); |
|
| 397 | 397 | ||
| 398 | 398 | try testing::expectNot(vec::pop(&mut v, &mut popped)); |
|
| 399 | 399 | } |
|
| 400 | 400 | ||
| 401 | - | @test fn testVecGetSet() throws (testing::TestError) { |
|
| 401 | + | @test unsafe fn testVecGetSet() throws (testing::TestError) { |
|
| 402 | 402 | let mut arena: [u8; 32] align(4) = undefined; |
|
| 403 | 403 | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 404 | 404 | ||
| 405 | 405 | let val1: i32 = 10; |
|
| 406 | 406 | let val2: i32 = 20; |
| 409 | 409 | vec::push(&mut v, &val1); |
|
| 410 | 410 | vec::push(&mut v, &val2); |
|
| 411 | 411 | vec::push(&mut v, &val3); |
|
| 412 | 412 | ||
| 413 | 413 | if let ptr = vec::get(&v, 0) { |
|
| 414 | - | let value: i32 = *(ptr as *i32); |
|
| 414 | + | let value: i32 = *(ptr as *unsafe i32); |
|
| 415 | 415 | try testing::expect(value == 10); |
|
| 416 | 416 | } else { |
|
| 417 | 417 | throw testing::TestError::Failed; |
|
| 418 | 418 | } |
|
| 419 | 419 | ||
| 420 | 420 | if let ptr = vec::get(&v, 1) { |
|
| 421 | - | let value: i32 = *(ptr as *i32); |
|
| 421 | + | let value: i32 = *(ptr as *unsafe i32); |
|
| 422 | 422 | try testing::expect(value == 20); |
|
| 423 | 423 | } else { |
|
| 424 | 424 | throw testing::TestError::Failed; |
|
| 425 | 425 | } |
|
| 426 | 426 | ||
| 427 | 427 | if let ptr = vec::get(&v, 2) { |
|
| 428 | - | let value: i32 = *(ptr as *i32); |
|
| 428 | + | let value: i32 = *(ptr as *unsafe i32); |
|
| 429 | 429 | try testing::expect(value == 30); |
|
| 430 | 430 | } else { |
|
| 431 | 431 | throw testing::TestError::Failed; |
|
| 432 | 432 | } |
|
| 433 | 433 | try testing::expect(vec::get(&v, 3) == nil); |
|
| 434 | 434 | ||
| 435 | 435 | let new: i32 = 999; |
|
| 436 | 436 | try testing::expect(vec::put(&mut v, 1, &new)); |
|
| 437 | 437 | ||
| 438 | 438 | if let ptr = vec::get(&v, 1) { |
|
| 439 | - | let value: i32 = *(ptr as *i32); |
|
| 439 | + | let value: i32 = *(ptr as *unsafe i32); |
|
| 440 | 440 | try testing::expect(value == 999); |
|
| 441 | 441 | } else { |
|
| 442 | 442 | throw testing::TestError::Failed; |
|
| 443 | 443 | } |
|
| 444 | 444 | try testing::expectNot(vec::put(&mut v, 5, &new)); |
|
| 445 | 445 | } |
|
| 446 | 446 | ||
| 447 | - | @test fn testVecCapacity() throws (testing::TestError) { |
|
| 447 | + | @test unsafe fn testVecCapacity() throws (testing::TestError) { |
|
| 448 | 448 | let mut arena: [u8; 16] align(4) = undefined; |
|
| 449 | 449 | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 450 | 450 | ||
| 451 | 451 | let val: i32 = 7; |
|
| 452 | 452 | try testing::expect(vec::push(&mut v, &val)); |
| 457 | 457 | ||
| 458 | 458 | try testing::expectNot(vec::push(&mut v, &val)); |
|
| 459 | 459 | try testing::expect(vec::len(&v) == 4); |
|
| 460 | 460 | } |
|
| 461 | 461 | ||
| 462 | - | @test fn testVecReset() throws (testing::TestError) { |
|
| 462 | + | @test unsafe fn testVecReset() throws (testing::TestError) { |
|
| 463 | 463 | let mut arena: [u8; 16] align(4) = undefined; |
|
| 464 | 464 | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 465 | 465 | ||
| 466 | 466 | let val: i32 = 123; |
|
| 467 | 467 | vec::push(&mut v, &val); |
| 473 | 473 | ||
| 474 | 474 | try testing::expect(vec::push(&mut v, &val)); |
|
| 475 | 475 | try testing::expect(vec::len(&v) == 1); |
|
| 476 | 476 | } |
|
| 477 | 477 | ||
| 478 | - | @test fn testVecStruct() throws (testing::TestError) { |
|
| 478 | + | @test unsafe fn testVecStruct() throws (testing::TestError) { |
|
| 479 | 479 | let mut arena: [u8; 64] align(4) = undefined; |
|
| 480 | 480 | let mut v = vec::new(&mut arena[..], @sizeOf(Point), @alignOf(Point)); |
|
| 481 | 481 | ||
| 482 | 482 | let p1: Point = Point { x: 1, y: 2 }; |
|
| 483 | 483 | let p2: Point = Point { x: 3, y: 4 }; |
|
| 484 | 484 | ||
| 485 | 485 | try testing::expect(vec::push(&mut v, &p1)); |
|
| 486 | 486 | try testing::expect(vec::push(&mut v, &p2)); |
|
| 487 | 487 | ||
| 488 | 488 | if let ptr = vec::get(&v, 0) { |
|
| 489 | - | let p: *Point = ptr as *Point; |
|
| 489 | + | let p: *unsafe Point = ptr as *unsafe Point; |
|
| 490 | 490 | try testing::expect(p.x == 1); |
|
| 491 | 491 | try testing::expect(p.y == 2); |
|
| 492 | 492 | } else { |
|
| 493 | 493 | throw testing::TestError::Failed; |
|
| 494 | 494 | } |
lib/std/vec.rad
+22 -30
| 1 | - | //! Raw vector: type-unsafe dynamic array backed by static storage. |
|
| 1 | + | //! Raw vector backed by caller-managed storage. |
|
| 2 | 2 | //! |
|
| 3 | - | //! Users provide their own arena (static array) and the vector manages |
|
| 3 | + | //! Users provide their own storage and the vector manages |
|
| 4 | 4 | //! element count within that arena. The arena should be aligned according |
|
| 5 | 5 | //! to the element type's requirements. |
|
| 6 | 6 | ||
| 7 | 7 | /// Raw vector metadata structure. |
|
| 8 | 8 | /// |
|
| 9 | 9 | /// Does not own storage, points to user-provided arena. |
|
| 10 | 10 | export record RawVec: Copy { |
|
| 11 | 11 | /// Pointer to user-provided byte arena. |
|
| 12 | - | data: *mut [u8], |
|
| 12 | + | data: *unsafe mut [u8], |
|
| 13 | 13 | /// Current number of elements stored. |
|
| 14 | 14 | len: u32, |
|
| 15 | 15 | /// Size of each element in bytes (stride between elements). |
|
| 16 | 16 | stride: u32, |
|
| 17 | 17 | /// Alignment in bytes required by element type (>= 1). |
|
| 18 | 18 | alignment: u32, |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | /// Create a new raw vector with external arena. |
|
| 22 | 22 | /// |
|
| 23 | - | /// * `arena` is a pointer to static array backing storage. |
|
| 23 | + | /// * `arena` must outlive the vector and all returned element pointers. |
|
| 24 | 24 | /// * `stride` is the size of each element. |
|
| 25 | 25 | /// * `alignment` is the required alignment for elements. |
|
| 26 | - | export fn new(arena: *mut [u8], stride: u32, alignment: u32) -> RawVec { |
|
| 26 | + | export unsafe fn new(arena: &mut [u8], stride: u32, alignment: u32) -> RawVec { |
|
| 27 | 27 | assert stride > 0; |
|
| 28 | 28 | assert alignment > 0; |
|
| 29 | 29 | assert (arena.ptr as u32) % alignment == 0; |
|
| 30 | 30 | assert (arena.len % stride) == 0; |
|
| 31 | 31 | ||
| 32 | - | return RawVec { data: arena, len: 0, stride, alignment }; |
|
| 32 | + | return RawVec { data: arena as *unsafe mut [u8], len: 0, stride, alignment }; |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Get the current number of elements in the vector. |
|
| 36 | - | export fn len(vec: *RawVec) -> u32 { |
|
| 36 | + | export fn len(vec: &RawVec) -> u32 { |
|
| 37 | 37 | return vec.len; |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Get the maximum capacity of the vector. |
|
| 41 | - | export fn capacity(vec: *RawVec) -> u32 { |
|
| 41 | + | export unsafe fn capacity(vec: &RawVec) -> u32 { |
|
| 42 | 42 | return vec.data.len / vec.stride; |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | 45 | /// Reset the vector to empty (does not clear memory). |
|
| 46 | - | export fn reset(vec: *mut RawVec) { |
|
| 46 | + | export fn reset(vec: &mut RawVec) { |
|
| 47 | 47 | set vec.len = 0; |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | /// Get a pointer to the element at the given index. |
|
| 51 | 51 | /// |
|
| 52 | 52 | /// Returns nil if index is out of bounds. |
|
| 53 | - | export fn get(vec: *RawVec, index: u32) -> ?*opaque { |
|
| 53 | + | export unsafe fn get(vec: &RawVec, index: u32) -> ?*unsafe opaque { |
|
| 54 | 54 | if index >= vec.len { |
|
| 55 | 55 | return nil; |
|
| 56 | 56 | } |
|
| 57 | 57 | let offset: u32 = index * vec.stride; |
|
| 58 | - | let ptr: *u8 = &vec.data[offset]; |
|
| 58 | + | let ptr: *unsafe u8 = &vec.data[offset]; |
|
| 59 | 59 | ||
| 60 | - | return ptr as *opaque; |
|
| 60 | + | return ptr as *unsafe opaque; |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Push an element onto the end of the vector. |
|
| 64 | 64 | /// |
|
| 65 | 65 | /// Returns false if the vector is at capacity. |
|
| 66 | - | export fn push(vec: *mut RawVec, elem: *opaque) -> bool { |
|
| 66 | + | export unsafe fn push(vec: &mut RawVec, elem: &opaque) -> bool { |
|
| 67 | 67 | if vec.len >= capacity(vec) { |
|
| 68 | 68 | return false; |
|
| 69 | 69 | } |
|
| 70 | 70 | let off: u32 = vec.len * vec.stride; |
|
| 71 | - | let dst: *mut u8 = &mut vec.data[off]; |
|
| 72 | - | let src: *u8 = elem as *u8; |
|
| 73 | - | ||
| 74 | - | copyBytes(dst, src, vec.stride); |
|
| 71 | + | copyBytes(&mut vec.data[off..off + vec.stride], @sliceOf(elem as &u8, vec.stride)); |
|
| 75 | 72 | set vec.len += 1; |
|
| 76 | 73 | ||
| 77 | 74 | return true; |
|
| 78 | 75 | } |
|
| 79 | 76 | ||
| 80 | 77 | /// Pop an element from the end of the vector. |
|
| 81 | 78 | /// |
|
| 82 | 79 | /// Copies the element into the provided output pointer. |
|
| 83 | 80 | /// Returns false if the vector is empty. |
|
| 84 | - | export fn pop(vec: *mut RawVec, out: *mut opaque) -> bool { |
|
| 81 | + | export fn pop(vec: &mut RawVec, out: &mut opaque) -> bool { |
|
| 85 | 82 | if vec.len == 0 { |
|
| 86 | 83 | return false; |
|
| 87 | 84 | } |
|
| 88 | 85 | set vec.len -= 1; |
|
| 89 | 86 | ||
| 90 | 87 | let off: u32 = vec.len * vec.stride; |
|
| 91 | - | let src: *u8 = &vec.data[off]; |
|
| 92 | - | let dst: *mut u8 = out as *mut u8; |
|
| 93 | - | ||
| 94 | - | copyBytes(dst, src, vec.stride); |
|
| 88 | + | copyBytes(@sliceOf(out as &mut u8, vec.stride), &vec.data[off..off + vec.stride]); |
|
| 95 | 89 | ||
| 96 | 90 | return true; |
|
| 97 | 91 | } |
|
| 98 | 92 | ||
| 99 | 93 | /// Set the element at the given index. |
|
| 100 | 94 | /// |
|
| 101 | 95 | /// Returns false if index is out of bounds. |
|
| 102 | - | export fn put(vec: *mut RawVec, index: u32, elem: *opaque) -> bool { |
|
| 96 | + | export fn put(vec: &mut RawVec, index: u32, elem: &opaque) -> bool { |
|
| 103 | 97 | if index >= vec.len { |
|
| 104 | 98 | return false; |
|
| 105 | 99 | } |
|
| 106 | 100 | let off: u32 = index * vec.stride; |
|
| 107 | - | let dst: *mut u8 = &mut vec.data[off]; |
|
| 108 | - | let src: *u8 = elem as *u8; |
|
| 109 | - | ||
| 110 | - | copyBytes(dst, src, vec.stride); |
|
| 101 | + | copyBytes(&mut vec.data[off..off + vec.stride], @sliceOf(elem as &u8, vec.stride)); |
|
| 111 | 102 | ||
| 112 | 103 | return true; |
|
| 113 | 104 | } |
|
| 114 | 105 | ||
| 115 | 106 | /// Copy bytes from source to destination. |
|
| 116 | - | fn copyBytes(dst: *mut u8, src: *u8, count: u32) { |
|
| 117 | - | for i in 0..count { |
|
| 118 | - | set *(dst + i) = *(src + i); |
|
| 107 | + | fn copyBytes(dst: &mut [u8], src: &[u8]) { |
|
| 108 | + | assert dst.len == src.len; |
|
| 109 | + | for i in 0..src.len { |
|
| 110 | + | set dst[i] = src[i]; |
|
| 119 | 111 | } |
|
| 120 | 112 | } |
seed/radiance.rv64
+0 -0
Binary file changed.
seed/radiance.rv64.git
+1 -1
| 1 | - | 1e2fdea952350036a4172f567442e608555094b9e18fa69f1fb615453d5352b6 |
|
| 1 | + | 55e5d92edb60a10e44c67ba6bb5c2cc19f89ce22fd9c3c1c38c23fb2df718814 |
seed/update
+1 -1
| 39 | 39 | # --------------------------------------------------------------------------- |
|
| 40 | 40 | # Command line flags for Radiance compiler |
|
| 41 | 41 | # --------------------------------------------------------------------------- |
|
| 42 | 42 | ||
| 43 | 43 | STD_MODS="$(sed 's/^/-mod /' std.lib | tr '\n' ' ')" |
|
| 44 | - | OPTS="-pkg std ${STD_MODS} -pkg radiance -mod compiler/radiance.rad -entry radiance" |
|
| 44 | + | OPTS="-pkg std ${STD_MODS} -pkg radiance -mod compiler/radiance.rad -mod compiler/radiance/codegen.rad -entry radiance" |
|
| 45 | 45 | ||
| 46 | 46 | # --------------------------------------------------------------------------- |
|
| 47 | 47 | # Emulator settings |
|
| 48 | 48 | # --------------------------------------------------------------------------- |
|
| 49 | 49 |
test/runner.rad
+29 -16
| 92 | 92 | } |
|
| 93 | 93 | return &line[..end]; |
|
| 94 | 94 | } |
|
| 95 | 95 | ||
| 96 | 96 | /// Get next line from string at offset. Returns the line and updates offset past newline. |
|
| 97 | - | fn nextLine(s: *[u8], offset: *mut u32) -> *[u8] { |
|
| 97 | + | fn nextLine(s: *[u8], offset: &mut u32) -> *[u8] { |
|
| 98 | 98 | let start = *offset; |
|
| 99 | 99 | let mut i = start; |
|
| 100 | 100 | ||
| 101 | 101 | while i < s.len and s[i] <> '\n' { |
|
| 102 | 102 | set i += 1; |
| 143 | 143 | } |
|
| 144 | 144 | ||
| 145 | 145 | /// Derive the `.ril` path from a `.rad` source path. Returns nil if the path |
|
| 146 | 146 | /// does not end in `.rad` or the buffer is too small. The result is |
|
| 147 | 147 | /// null-terminated for use with syscalls. |
|
| 148 | - | fn deriveRilPath(sourcePath: *[u8], buf: *mut [u8]) -> ?*[u8] { |
|
| 148 | + | fn deriveRilPath(sourcePath: *[u8], buf: &mut [u8]) -> ?u32 { |
|
| 149 | 149 | let len = sourcePath.len; |
|
| 150 | 150 | if len < SOURCE_EXT.len { |
|
| 151 | 151 | return nil; |
|
| 152 | 152 | } |
|
| 153 | 153 | let extStart = len - SOURCE_EXT.len; |
| 165 | 165 | try mem::copy(&mut buf[extStart..len], SNAPSHOT_EXT) catch { |
|
| 166 | 166 | return nil; |
|
| 167 | 167 | }; |
|
| 168 | 168 | set buf[len] = 0; |
|
| 169 | 169 | ||
| 170 | - | return &buf[..len]; |
|
| 170 | + | return len; |
|
| 171 | 171 | } |
|
| 172 | 172 | ||
| 173 | 173 | /// Write a self-contained RV64 image containing text and data sections. |
|
| 174 | - | fn writeImage(code: *[u32], roData: *[u8], rwData: *[u8], path: *[u8]) -> bool { |
|
| 174 | + | unsafe fn writeImage( |
|
| 175 | + | code: *[u32], |
|
| 176 | + | roData: *[u8], |
|
| 177 | + | rwData: *[u8], |
|
| 178 | + | path: *[u8] |
|
| 179 | + | ) -> bool { |
|
| 175 | 180 | let mut header = rv64::imageHeader(code.len * rv64::INSTR_SIZE as u32, roData.len, rwData.len); |
|
| 176 | - | let headerWords = &header[..]; |
|
| 177 | - | let headerBytes = @sliceOf(headerWords.ptr as *u8, headerWords.len * rv64::WORD_SIZE as u32); |
|
| 181 | + | let headerBytes: *unsafe [u8] = @sliceOf(&header[0] as *unsafe u8, header.len * rv64::WORD_SIZE as u32); |
|
| 178 | 182 | let codeBytes = @sliceOf(code.ptr as *u8, code.len * rv64::INSTR_SIZE as u32); |
|
| 179 | 183 | ||
| 180 | - | return unix::writeFileParts(path, &[headerBytes, codeBytes, roData, rwData]); |
|
| 184 | + | let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420); |
|
| 185 | + | if fd < 0 { return false; } |
|
| 186 | + | let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, codeBytes) |
|
| 187 | + | and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData); |
|
| 188 | + | let closed = unix::close(fd) == 0; |
|
| 189 | + | return written and closed; |
|
| 181 | 190 | } |
|
| 182 | 191 | ||
| 183 | - | fn assembleBinary(sourcePath: *[u8], outputPath: *[u8]) -> bool { |
|
| 184 | - | let source = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else { |
|
| 192 | + | unsafe fn assembleBinary(sourcePath: *[u8], outputPath: *[u8]) -> bool { |
|
| 193 | + | let sourceLen = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else { |
|
| 185 | 194 | io::printError("error: could not read source: "); |
|
| 186 | 195 | io::printError(sourcePath); |
|
| 187 | 196 | io::printError("\n"); |
|
| 188 | 197 | return false; |
|
| 189 | 198 | }; |
|
| 190 | 199 | ||
| 200 | + | let source = &SOURCE_BUF[..sourceLen]; |
|
| 191 | 201 | let mut arena = alloc::new(&mut AST_ARENA_STORAGE[..]); |
|
| 192 | 202 | let program = try asm::assemble( |
|
| 193 | 203 | asm::scanner::SourceKind::File { path: sourcePath }, |
|
| 194 | 204 | source, |
|
| 195 | 205 | &mut ASM_TEXT_STORAGE[..], |
| 212 | 222 | } |
|
| 213 | 223 | return true; |
|
| 214 | 224 | } |
|
| 215 | 225 | ||
| 216 | 226 | /// Run a single IL snapshot test case. Returns `true` on success. |
|
| 217 | - | fn runTest(sourcePath: *[u8]) -> bool { |
|
| 227 | + | unsafe fn runTest(sourcePath: *[u8]) -> bool { |
|
| 218 | 228 | // Path buffer. |
|
| 219 | 229 | let mut rilPathBuf: [u8; MAX_PATH_LEN] = undefined; |
|
| 220 | - | let mut pkgScope: resolver::Scope = undefined; |
|
| 230 | + | static pkgScope: resolver::Scope = undefined; |
|
| 221 | 231 | ||
| 222 | 232 | // Derive .ril path from source path. |
|
| 223 | - | let rilPath = deriveRilPath(sourcePath, &mut rilPathBuf[..]) else { |
|
| 233 | + | let rilPathLen = deriveRilPath(sourcePath, &mut rilPathBuf[..]) else { |
|
| 224 | 234 | io::print("error: invalid source path (must end in .rad): "); |
|
| 225 | 235 | io::printLn(sourcePath); |
|
| 226 | 236 | return false; |
|
| 227 | 237 | }; |
|
| 228 | 238 | ||
| 229 | 239 | // Read expected IL. |
|
| 230 | - | let expected = unix::readFile(rilPath, &mut EXPECTED_BUF[..]) else { |
|
| 240 | + | let expectedLen = unix::readFile(&rilPathBuf[..rilPathLen], &mut EXPECTED_BUF[..]) else { |
|
| 231 | 241 | io::print("error: could not read expected IL: "); |
|
| 232 | - | io::printLn(rilPath); |
|
| 242 | + | io::printLn(&rilPathBuf[..rilPathLen]); |
|
| 233 | 243 | return false; |
|
| 234 | 244 | }; |
|
| 235 | 245 | ||
| 236 | 246 | // Read source file. |
|
| 237 | - | let source = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else { |
|
| 247 | + | let sourceLen = unix::readFile(sourcePath, &mut SOURCE_BUF[..]) else { |
|
| 238 | 248 | io::print("error: could not read source: "); |
|
| 239 | 249 | io::printLn(sourcePath); |
|
| 240 | 250 | return false; |
|
| 241 | 251 | }; |
|
| 242 | 252 | ||
| 253 | + | let expected = &EXPECTED_BUF[..expectedLen]; |
|
| 254 | + | let source = &SOURCE_BUF[..sourceLen]; |
|
| 255 | + | ||
| 243 | 256 | // Parse source. |
|
| 244 | 257 | let mut astArena = ast::nodeArena(&mut AST_ARENA_STORAGE[..]); |
|
| 245 | 258 | let root = try parser::parse(scanner::SourceLoc::String, source, &mut astArena, &mut STRING_POOL) catch { |
|
| 246 | 259 | io::printLn("error: parsing failed"); |
|
| 247 | 260 | return false; |
| 287 | 300 | ||
| 288 | 301 | return true; |
|
| 289 | 302 | } |
|
| 290 | 303 | ||
| 291 | 304 | /// Run a single test specified as an argument. |
|
| 292 | - | @default fn main(env: *sys::Env) -> i32 { |
|
| 305 | + | @default unsafe fn main(env: *sys::Env) -> i32 { |
|
| 293 | 306 | let args = env.args; |
|
| 294 | 307 | ||
| 295 | 308 | if args.len == 4 and mem::eq(args[1], "assemble") { |
|
| 296 | 309 | if assembleBinary(args[2], args[3]) { |
|
| 297 | 310 | return 0; |
test/tests/array.aggregate.stride.rad
+1 -1
| 8 | 8 | fn arrayOfRecords(arr: [Point; 3], idx: u32) -> i32 { |
|
| 9 | 9 | return arr[idx].x; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// Take address of record in array. |
|
| 13 | - | fn addressOfRecord(arr: [Point; 3], idx: u32) -> *Point { |
|
| 13 | + | unsafe fn addressOfRecord(arr: [Point; 3], idx: u32) -> *unsafe Point { |
|
| 14 | 14 | return &arr[idx]; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Array of arrays (16 bytes per element). |
|
| 18 | 18 | fn arrayOfArrays(arr: [[i32; 4]; 3], idx: u32) -> i32 { |
test/tests/array.slice.full.rad
+1 -1
| 1 | 1 | /// Creates a slice from an array with full range. |
|
| 2 | - | fn sliceFull(a: [i32; 4]) -> *[i32] { |
|
| 2 | + | unsafe fn sliceFull(a: [i32; 4]) -> *unsafe [i32] { |
|
| 3 | 3 | return &a[..]; |
|
| 4 | 4 | } |
test/tests/array.slice.gen.end.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[..2]; |
|
| 4 | + | let mut slice: *unsafe [i32] = &arr[..2]; |
|
| 5 | 5 | ||
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/array.slice.gen.index.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[1..]; |
|
| 4 | + | let mut slice: *unsafe [i32] = &arr[1..]; |
|
| 5 | 5 | ||
| 6 | 6 | return (slice[1]) - 3; |
|
| 7 | 7 | } |
test/tests/array.slice.gen.open.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[..]; |
|
| 4 | + | let mut slice: *unsafe [i32] = &arr[..]; |
|
| 5 | 5 | ||
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/array.slice.gen.start.end.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[1..3]; |
|
| 4 | + | let mut slice: *unsafe [i32] = &arr[1..3]; |
|
| 5 | 5 | ||
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/array.slice.gen.start.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 4 | - | let mut slice: *[i32] = &arr[2..]; |
|
| 4 | + | let mut slice: *unsafe [i32] = &arr[2..]; |
|
| 5 | 5 | ||
| 6 | 6 | return 0; |
|
| 7 | 7 | } |
test/tests/array.slice.openend.rad
+1 -1
| 1 | 1 | /// Creates a slice from an array with open end bound. |
|
| 2 | - | fn sliceOpenEnd(a: [i32; 4], start: u32) -> *[i32] { |
|
| 2 | + | unsafe fn sliceOpenEnd(a: [i32; 4], start: u32) -> *unsafe [i32] { |
|
| 3 | 3 | return &a[start..]; |
|
| 4 | 4 | } |
test/tests/array.slice.openstart.rad
+1 -1
| 1 | 1 | /// Creates a slice from an array with open start bound. |
|
| 2 | - | fn sliceOpenStart(a: [i32; 4], end: u32) -> *[i32] { |
|
| 2 | + | unsafe fn sliceOpenStart(a: [i32; 4], end: u32) -> *unsafe [i32] { |
|
| 3 | 3 | return &a[..end]; |
|
| 4 | 4 | } |
test/tests/array.slice.rad
+6 -6
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test array slicing with various ranges. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | let arr: [i32; 5] = [1, 2, 3, 4, 5]; |
|
| 6 | 6 | ||
| 7 | - | let slice1: *[i32] = &arr[..]; |
|
| 8 | - | let slice2: *[i32] = &arr[1..4]; |
|
| 9 | - | let slice3: *[i32] = &arr[..3]; |
|
| 10 | - | let slice4: *[i32] = &arr[2..]; |
|
| 7 | + | let slice1: *unsafe [i32] = &arr[..]; |
|
| 8 | + | let slice2: *unsafe [i32] = &arr[1..4]; |
|
| 9 | + | let slice3: *unsafe [i32] = &arr[..3]; |
|
| 10 | + | let slice4: *unsafe [i32] = &arr[2..]; |
|
| 11 | 11 | ||
| 12 | 12 | assert slice1.len == 5; |
|
| 13 | 13 | assert slice2.len == 3; |
|
| 14 | 14 | assert slice3.len == 3; |
|
| 15 | 15 | assert slice4.len == 3; |
|
| 16 | 16 | ||
| 17 | - | let slice5: *[i32] = slice3; |
|
| 17 | + | let slice5: *unsafe [i32] = slice3; |
|
| 18 | 18 | ||
| 19 | 19 | assert slice5.len == 3; |
|
| 20 | 20 | assert slice5.ptr == slice3.ptr; |
|
| 21 | 21 | ||
| 22 | 22 | let sum1: i32 = slice1[0] + slice1[4]; // 6 |
test/tests/bool.comparison.slice.rad
+45 -45
| 1 | 1 | //! returns: 0 |
|
| 2 | - | fn memEq(a: *[u8], b: *[u8]) -> bool { |
|
| 2 | + | fn memEq(a: &[u8], b: &[u8]) -> bool { |
|
| 3 | 3 | if a.len <> b.len { |
|
| 4 | 4 | return false; |
|
| 5 | 5 | } |
|
| 6 | 6 | for i in 0..a.len { |
|
| 7 | 7 | if a[i] <> b[i] { |
| 13 | 13 | ||
| 14 | 14 | fn sliceU8(input: *[u8]) -> *[u8] { |
|
| 15 | 15 | return input; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | fn sliceI32(input: *[i32]) -> *[i32] { |
|
| 18 | + | fn sliceI32(input: *unsafe [i32]) -> *unsafe [i32] { |
|
| 19 | 19 | return input; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | fn sliceEqualU32(a: *[u32], b: *[u32]) -> bool { |
|
| 22 | + | fn sliceEqualU32(a: &[u32], b: &[u32]) -> bool { |
|
| 23 | 23 | if a.len <> b.len { |
|
| 24 | 24 | return false; |
|
| 25 | 25 | } |
|
| 26 | 26 | for i in 0..a.len { |
|
| 27 | 27 | if a[i] <> b[i] { |
| 29 | 29 | } |
|
| 30 | 30 | } |
|
| 31 | 31 | return true; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | - | fn sliceEqualI32(a: *[i32], b: *[i32]) -> bool { |
|
| 34 | + | fn sliceEqualI32(a: &[i32], b: &[i32]) -> bool { |
|
| 35 | 35 | if a.len <> b.len { |
|
| 36 | 36 | return false; |
|
| 37 | 37 | } |
|
| 38 | 38 | for i in 0..a.len { |
|
| 39 | 39 | if a[i] <> b[i] { |
| 41 | 41 | } |
|
| 42 | 42 | } |
|
| 43 | 43 | return true; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | - | fn sliceEqual1() -> bool { |
|
| 46 | + | unsafe fn sliceEqual1() -> bool { |
|
| 47 | 47 | let a1: [u32; 3] = [9, 42, 3]; |
|
| 48 | 48 | ||
| 49 | - | let s1: *[u32] = &a1[..]; |
|
| 50 | - | let s2: *[u32] = &a1[..]; |
|
| 49 | + | let s1: *unsafe [u32] = &a1[..]; |
|
| 50 | + | let s2: *unsafe [u32] = &a1[..]; |
|
| 51 | 51 | ||
| 52 | - | return s1 == s2 and sliceEqualU32(s1, s2); |
|
| 52 | + | return s1 == s2 and sliceEqualU32(&s1[..], &s2[..]); |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | - | fn sliceEqual2() -> bool { |
|
| 55 | + | unsafe fn sliceEqual2() -> bool { |
|
| 56 | 56 | let a1: [u32; 3] = [9, 42, 3]; |
|
| 57 | 57 | ||
| 58 | - | let s1: *[u32] = &a1[1..]; |
|
| 59 | - | let s2: *[u32] = &a1[1..]; |
|
| 58 | + | let s1: *unsafe [u32] = &a1[1..]; |
|
| 59 | + | let s2: *unsafe [u32] = &a1[1..]; |
|
| 60 | 60 | ||
| 61 | - | return s1 == s2 and sliceEqualU32(s1, s2); |
|
| 61 | + | return s1 == s2 and sliceEqualU32(&s1[..], &s2[..]); |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn sliceEqual3() -> bool { |
|
| 64 | + | unsafe fn sliceEqual3() -> bool { |
|
| 65 | 65 | let a1: [u32; 3] = [9, 42, 3]; |
|
| 66 | 66 | ||
| 67 | - | let s1: *[u32] = &a1[..1]; |
|
| 68 | - | let s2: *[u32] = &a1[..1]; |
|
| 67 | + | let s1: *unsafe [u32] = &a1[..1]; |
|
| 68 | + | let s2: *unsafe [u32] = &a1[..1]; |
|
| 69 | 69 | ||
| 70 | - | return s1 == s2 and sliceEqualU32(s1, s2); |
|
| 70 | + | return s1 == s2 and sliceEqualU32(&s1[..], &s2[..]); |
|
| 71 | 71 | } |
|
| 72 | 72 | ||
| 73 | - | fn sliceEqual4() -> bool { |
|
| 73 | + | unsafe fn sliceEqual4() -> bool { |
|
| 74 | 74 | let a1: [u32; 3] = [9, 42, 3]; |
|
| 75 | 75 | ||
| 76 | - | let s1: *[u32] = &a1[2..]; |
|
| 77 | - | let s2: *[u32] = &a1[2..]; |
|
| 76 | + | let s1: *unsafe [u32] = &a1[2..]; |
|
| 77 | + | let s2: *unsafe [u32] = &a1[2..]; |
|
| 78 | 78 | ||
| 79 | - | return s1 == s2 and sliceEqualU32(s1, s2); |
|
| 79 | + | return s1 == s2 and sliceEqualU32(&s1[..], &s2[..]); |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | - | fn sliceNotEqualSameArray1() -> bool { |
|
| 82 | + | unsafe fn sliceNotEqualSameArray1() -> bool { |
|
| 83 | 83 | let a1: [u32; 3] = [42, 8, 3]; |
|
| 84 | 84 | ||
| 85 | - | let s1: *[u32] = &a1[..2]; |
|
| 86 | - | let s2: *[u32] = &a1[1..3]; |
|
| 85 | + | let s1: *unsafe [u32] = &a1[..2]; |
|
| 86 | + | let s2: *unsafe [u32] = &a1[1..3]; |
|
| 87 | 87 | ||
| 88 | - | return s1 <> s2 and not sliceEqualU32(s1, s2); |
|
| 88 | + | return s1 <> s2 and not sliceEqualU32(&s1[..], &s2[..]); |
|
| 89 | 89 | } |
|
| 90 | 90 | ||
| 91 | - | fn sliceNotEqualSameArray2() -> bool { |
|
| 91 | + | unsafe fn sliceNotEqualSameArray2() -> bool { |
|
| 92 | 92 | let a1: [u32; 3] = [42, 8, 3]; |
|
| 93 | 93 | ||
| 94 | - | let s1: *[u32] = &a1[..2]; |
|
| 95 | - | let s2: *[u32] = &a1[..3]; |
|
| 94 | + | let s1: *unsafe [u32] = &a1[..2]; |
|
| 95 | + | let s2: *unsafe [u32] = &a1[..3]; |
|
| 96 | 96 | ||
| 97 | - | return s1 <> s2 and not sliceEqualU32(s1, s2); |
|
| 97 | + | return s1 <> s2 and not sliceEqualU32(&s1[..], &s2[..]); |
|
| 98 | 98 | } |
|
| 99 | 99 | ||
| 100 | - | fn sliceEqualDifferentArray() -> bool { |
|
| 100 | + | unsafe fn sliceEqualDifferentArray() -> bool { |
|
| 101 | 101 | let a1: [u32; 3] = [42, 8, 3]; |
|
| 102 | 102 | let a2: [u32; 3] = [42, 8, 3]; |
|
| 103 | 103 | ||
| 104 | - | let s1: *[u32] = &a1[..]; |
|
| 105 | - | let s2: *[u32] = &a2[..]; |
|
| 104 | + | let s1: *unsafe [u32] = &a1[..]; |
|
| 105 | + | let s2: *unsafe [u32] = &a2[..]; |
|
| 106 | 106 | ||
| 107 | - | return sliceEqualU32(s1, s2) and s1 <> s2; |
|
| 107 | + | return sliceEqualU32(&s1[..], &s2[..]) and s1 <> s2; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | 110 | fn sliceEqualString1() -> bool { |
|
| 111 | 111 | let s1: *[u8] = "ABC"; |
|
| 112 | 112 | let s2: *[u8] = "ABC"; |
|
| 113 | 113 | ||
| 114 | - | return memEq(s1, s2); |
|
| 114 | + | return memEq(&s1[..], s2); |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | 117 | fn sliceEqualString2() -> bool { |
|
| 118 | 118 | let s1: *[u8] = "ABC"; |
|
| 119 | 119 | let s2: *[u8] = "DEF"; |
|
| 120 | 120 | ||
| 121 | - | return not memEq(s1, s2); |
|
| 121 | + | return not memEq(&s1[..], s2); |
|
| 122 | 122 | } |
|
| 123 | 123 | ||
| 124 | - | fn sliceEqualString3() -> bool { |
|
| 124 | + | unsafe fn sliceEqualString3() -> bool { |
|
| 125 | 125 | let a1: [u8; 3] = ['A', 'B', 'C']; |
|
| 126 | - | let s1: *[u8] = &a1[..]; |
|
| 126 | + | let s1: *unsafe [u8] = &a1[..]; |
|
| 127 | 127 | let s2: *[u8] = "ABC"; |
|
| 128 | 128 | ||
| 129 | - | return memEq(s1, s2) |
|
| 130 | - | and memEq(s1, "ABC") |
|
| 129 | + | return memEq(&s1[..], s2) |
|
| 130 | + | and memEq(&s1[..], "ABC") |
|
| 131 | 131 | and memEq(&a1[..], "ABC"); |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | 134 | fn sliceEqualString4() -> bool { |
|
| 135 | 135 | let s1: *[u8] = "ABC"; |
|
| 136 | 136 | let s2: *[u8] = &['A', 'B', 'C']; |
|
| 137 | 137 | ||
| 138 | - | return memEq(s1, s2); |
|
| 138 | + | return memEq(&s1[..], s2); |
|
| 139 | 139 | } |
|
| 140 | 140 | ||
| 141 | 141 | fn sliceEqualString5() -> bool { |
|
| 142 | 142 | let s1: *[u8] = "ABC"; |
|
| 143 | 143 | ||
| 144 | 144 | // Sub-slicing a slice currently trips a separate compiler bug. |
|
| 145 | 145 | // Keep this check focused on slice equality behavior itself. |
|
| 146 | - | return memEq(s1, "ABC"); |
|
| 146 | + | return memEq(&s1[..], "ABC"); |
|
| 147 | 147 | } |
|
| 148 | 148 | ||
| 149 | 149 | fn sliceNotEqualU8() -> bool { |
|
| 150 | 150 | let a: [u8; 3] = [1, 2, 3]; |
|
| 151 | 151 | let b: [u8; 3] = [1, 2, 4]; |
| 158 | 158 | and not memEq(&a[..], &a[1..]) |
|
| 159 | 159 | and not memEq(&a[..], &b[..]) |
|
| 160 | 160 | and not memEq(&a[..], &[1, 3, 3]); |
|
| 161 | 161 | } |
|
| 162 | 162 | ||
| 163 | - | fn sliceNotEqualU16() -> bool { |
|
| 163 | + | unsafe fn sliceNotEqualU16() -> bool { |
|
| 164 | 164 | let a: [u16; 3] = [1, 2, 3]; |
|
| 165 | 165 | let b: [u16; 3] = [1, 2, 4]; |
|
| 166 | - | let s: *[u16] = &[1, 2, 3]; |
|
| 166 | + | let s: *unsafe [u16] = &[1, 2, 3]; |
|
| 167 | 167 | ||
| 168 | 168 | return s <> &[1, 2, 4] |
|
| 169 | 169 | and s <> &[1, 0, 3] |
|
| 170 | 170 | and &a[..] <> &a[1..] |
|
| 171 | 171 | and &a[..] <> &b[..] |
|
| 172 | 172 | and &a[..] <> &[1, 3, 3]; |
|
| 173 | 173 | } |
|
| 174 | 174 | ||
| 175 | - | fn sliceReturnEqual() -> bool { |
|
| 175 | + | unsafe fn sliceReturnEqual() -> bool { |
|
| 176 | 176 | return memEq(sliceU8("ABC"), "ABC") |
|
| 177 | - | and sliceEqualI32(sliceI32(&[1, 2, 3]), &[1, 2, 3]); |
|
| 177 | + | and sliceEqualI32(&sliceI32(&[1, 2, 3])[..], &[1, 2, 3]); |
|
| 178 | 178 | } |
|
| 179 | 179 | ||
| 180 | - | @default fn main() -> i32 { |
|
| 180 | + | @default unsafe fn main() -> i32 { |
|
| 181 | 181 | assert sliceEqual1(); |
|
| 182 | 182 | assert sliceEqual2(); |
|
| 183 | 183 | assert sliceEqual3(); |
|
| 184 | 184 | assert sliceEqual4(); |
|
| 185 | 185 | assert sliceEqualString1(); |
test/tests/bool.comparison.slice.record.gen.rad
+10 -10
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | record Line: Copy { |
|
| 9 | - | points: *[Point], |
|
| 9 | + | points: *unsafe [Point], |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | fn pointsEqual(a: *[Point], b: *[Point]) -> bool { |
|
| 12 | + | fn pointsEqual(a: &[Point], b: &[Point]) -> bool { |
|
| 13 | 13 | if a.len <> b.len { |
|
| 14 | 14 | return false; |
|
| 15 | 15 | } |
|
| 16 | 16 | for i in 0..a.len { |
|
| 17 | 17 | if a[i].x <> b[i].x or a[i].y <> b[i].y { |
| 19 | 19 | } |
|
| 20 | 20 | } |
|
| 21 | 21 | return true; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | fn lineEqual(a: Line, b: Line) -> bool { |
|
| 25 | - | return pointsEqual(a.points, b.points); |
|
| 24 | + | unsafe fn lineEqual(a: Line, b: Line) -> bool { |
|
| 25 | + | return pointsEqual(&a.points[..], &b.points[..]); |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn testSliceStructEqual() -> bool { |
|
| 28 | + | unsafe fn testSliceStructEqual() -> bool { |
|
| 29 | 29 | let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 30 | 30 | let b: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 31 | 31 | ||
| 32 | 32 | return pointsEqual(&a[..], &b[..]); |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | - | fn testSliceStructNotEqualContent() -> bool { |
|
| 35 | + | unsafe fn testSliceStructNotEqualContent() -> bool { |
|
| 36 | 36 | let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 37 | 37 | let b: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }]; |
|
| 38 | 38 | ||
| 39 | 39 | return not pointsEqual(&a[..], &b[..]); |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | - | fn testSliceStructNotEqualLength() -> bool { |
|
| 42 | + | unsafe fn testSliceStructNotEqualLength() -> bool { |
|
| 43 | 43 | let a: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 44 | 44 | let b: [Point; 1] = [Point { x: 1, y: 2 }]; |
|
| 45 | 45 | ||
| 46 | 46 | return not pointsEqual(&a[..], &b[..]); |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | - | fn testStructWithSliceFieldEqual() -> bool { |
|
| 49 | + | unsafe fn testStructWithSliceFieldEqual() -> bool { |
|
| 50 | 50 | let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 51 | 51 | let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 52 | 52 | ||
| 53 | 53 | let line1: Line = Line { points: &pts1[..] }; |
|
| 54 | 54 | let line2: Line = Line { points: &pts2[..] }; |
|
| 55 | 55 | ||
| 56 | 56 | return lineEqual(line1, line2); |
|
| 57 | 57 | } |
|
| 58 | 58 | ||
| 59 | - | fn testStructWithSliceFieldNotEqual() -> bool { |
|
| 59 | + | unsafe fn testStructWithSliceFieldNotEqual() -> bool { |
|
| 60 | 60 | let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 61 | 61 | let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }]; |
|
| 62 | 62 | ||
| 63 | 63 | let line1: Line = Line { points: &pts1[..] }; |
|
| 64 | 64 | let line2: Line = Line { points: &pts2[..] }; |
|
| 65 | 65 | ||
| 66 | 66 | return not lineEqual(line1, line2); |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | - | @default fn main() -> i32 { |
|
| 69 | + | @default unsafe fn main() -> i32 { |
|
| 70 | 70 | assert testSliceStructEqual(); |
|
| 71 | 71 | assert testSliceStructNotEqualContent(); |
|
| 72 | 72 | assert testSliceStructNotEqualLength(); |
|
| 73 | 73 | assert testStructWithSliceFieldEqual(); |
|
| 74 | 74 | assert testStructWithSliceFieldNotEqual(); |
test/tests/bool.comparison.slice.union.gen.rad
+12 -12
| 9 | 9 | width: u32, |
|
| 10 | 10 | height: u32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | union Shape: Copy { |
|
| 14 | - | points(*[Point]), |
|
| 15 | - | sizes(*[Size]), |
|
| 14 | + | points(*unsafe [Point]), |
|
| 15 | + | sizes(*unsafe [Size]), |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | fn pointsEqual(a: *[Point], b: *[Point]) -> bool { |
|
| 18 | + | fn pointsEqual(a: &[Point], b: &[Point]) -> bool { |
|
| 19 | 19 | if a.len <> b.len { |
|
| 20 | 20 | return false; |
|
| 21 | 21 | } |
|
| 22 | 22 | for i in 0..a.len { |
|
| 23 | 23 | if a[i].x <> b[i].x or a[i].y <> b[i].y { |
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | return true; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | - | fn sizesEqual(a: *[Size], b: *[Size]) -> bool { |
|
| 30 | + | fn sizesEqual(a: &[Size], b: &[Size]) -> bool { |
|
| 31 | 31 | if a.len <> b.len { |
|
| 32 | 32 | return false; |
|
| 33 | 33 | } |
|
| 34 | 34 | for i in 0..a.len { |
|
| 35 | 35 | if a[i].width <> b[i].width or a[i].height <> b[i].height { |
| 37 | 37 | } |
|
| 38 | 38 | } |
|
| 39 | 39 | return true; |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | - | fn shapeEqual(a: Shape, b: Shape) -> bool { |
|
| 42 | + | unsafe fn shapeEqual(a: Shape, b: Shape) -> bool { |
|
| 43 | 43 | if let case Shape::points(pointsA) = a { |
|
| 44 | 44 | if let case Shape::points(pointsB) = b { |
|
| 45 | - | return pointsEqual(pointsA, pointsB); |
|
| 45 | + | return pointsEqual(&pointsA[..], &pointsB[..]); |
|
| 46 | 46 | } |
|
| 47 | 47 | return false; |
|
| 48 | 48 | } |
|
| 49 | 49 | if let case Shape::sizes(sizesA) = a { |
|
| 50 | 50 | if let case Shape::sizes(sizesB) = b { |
|
| 51 | - | return sizesEqual(sizesA, sizesB); |
|
| 51 | + | return sizesEqual(&sizesA[..], &sizesB[..]); |
|
| 52 | 52 | } |
|
| 53 | 53 | return false; |
|
| 54 | 54 | } |
|
| 55 | 55 | return false; |
|
| 56 | 56 | } |
|
| 57 | 57 | ||
| 58 | - | fn testEnumSliceEqual() -> bool { |
|
| 58 | + | unsafe fn testEnumSliceEqual() -> bool { |
|
| 59 | 59 | let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 60 | 60 | let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 61 | 61 | ||
| 62 | 62 | let s1: Shape = Shape::points(&pts1[..]); |
|
| 63 | 63 | let s2: Shape = Shape::points(&pts2[..]); |
|
| 64 | 64 | ||
| 65 | 65 | return shapeEqual(s1, s2); |
|
| 66 | 66 | } |
|
| 67 | 67 | ||
| 68 | - | fn testEnumSliceNotEqual() -> bool { |
|
| 68 | + | unsafe fn testEnumSliceNotEqual() -> bool { |
|
| 69 | 69 | let pts1: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 70 | 70 | let pts2: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 5 }]; |
|
| 71 | 71 | ||
| 72 | 72 | let s1: Shape = Shape::points(&pts1[..]); |
|
| 73 | 73 | let s2: Shape = Shape::points(&pts2[..]); |
|
| 74 | 74 | ||
| 75 | 75 | return not shapeEqual(s1, s2); |
|
| 76 | 76 | } |
|
| 77 | 77 | ||
| 78 | - | fn testEnumSizesSliceEqual() -> bool { |
|
| 78 | + | unsafe fn testEnumSizesSliceEqual() -> bool { |
|
| 79 | 79 | let sizes1: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }]; |
|
| 80 | 80 | let sizes2: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }]; |
|
| 81 | 81 | ||
| 82 | 82 | let s1: Shape = Shape::sizes(&sizes1[..]); |
|
| 83 | 83 | let s2: Shape = Shape::sizes(&sizes2[..]); |
|
| 84 | 84 | ||
| 85 | 85 | return shapeEqual(s1, s2); |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn testEnumDifferentVariants() -> bool { |
|
| 88 | + | unsafe fn testEnumDifferentVariants() -> bool { |
|
| 89 | 89 | let pts: [Point; 2] = [Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; |
|
| 90 | 90 | let sizes: [Size; 2] = [Size { width: 10, height: 20 }, Size { width: 30, height: 40 }]; |
|
| 91 | 91 | ||
| 92 | 92 | let s1: Shape = Shape::points(&pts[..]); |
|
| 93 | 93 | let s2: Shape = Shape::sizes(&sizes[..]); |
|
| 94 | 94 | ||
| 95 | 95 | return not shapeEqual(s1, s2); |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | - | @default fn main() -> i32 { |
|
| 98 | + | @default unsafe fn main() -> i32 { |
|
| 99 | 99 | assert testEnumSliceEqual(); |
|
| 100 | 100 | assert testEnumSliceNotEqual(); |
|
| 101 | 101 | assert testEnumSizesSliceEqual(); |
|
| 102 | 102 | assert testEnumDifferentVariants(); |
|
| 103 | 103 | return 0; |
test/tests/bool.short.circuit.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test short-circuiting behavior of 'and' and 'or' operators. |
|
| 3 | - | fn modify(counter: *mut i32, ret: bool) -> bool { |
|
| 3 | + | fn modify(counter: &mut i32, ret: bool) -> bool { |
|
| 4 | 4 | set *counter += 1; |
|
| 5 | 5 | return ret; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | @default fn main() -> i32 { |
test/tests/builtin.sliceof.invalid.cap.rad
+3 -3
| 1 | 1 | //! returns: 133 |
|
| 2 | 2 | //! Test @sliceOf runtime validation for len > cap. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 6 | - | let ptr: *i32 = &arr[0]; |
|
| 7 | - | let slice: *[i32] = @sliceOf(ptr, 4, 3); |
|
| 6 | + | let ptr: *unsafe i32 = &arr[0]; |
|
| 7 | + | let slice: *unsafe [i32] = @sliceOf(ptr, 4, 3); |
|
| 8 | 8 | ||
| 9 | 9 | return slice.len as i32; |
|
| 10 | 10 | } |
test/tests/builtin.sliceof.mut.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | // Test the @sliceOf builtin with mutable slice. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 6 | 6 | ||
| 7 | 7 | // Get a mutable pointer to the first element |
|
| 8 | - | let ptr: *mut i32 = &mut arr[0]; |
|
| 8 | + | let ptr: *unsafe mut i32 = &mut arr[0]; |
|
| 9 | 9 | ||
| 10 | 10 | // Create a mutable slice from the pointer and length |
|
| 11 | - | let slice: *mut [i32] = @sliceOf(ptr, 4); |
|
| 11 | + | let slice: *unsafe mut [i32] = @sliceOf(ptr, 4); |
|
| 12 | 12 | ||
| 13 | 13 | // Double each element via the slice |
|
| 14 | 14 | set slice[0] *= 2; |
|
| 15 | 15 | set slice[1] *= 2; |
|
| 16 | 16 | set slice[2] *= 2; |
test/tests/builtin.sliceof.rad
+3 -3
| 1 | 1 | //! returns: 100 |
|
| 2 | 2 | // Test @sliceOf builtin: create a slice from a pointer and length. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 6 | 6 | ||
| 7 | 7 | // Get a pointer to the first element |
|
| 8 | - | let ptr: *i32 = &arr[0]; |
|
| 8 | + | let ptr: *unsafe i32 = &arr[0]; |
|
| 9 | 9 | ||
| 10 | 10 | // Create a slice from the pointer and length |
|
| 11 | - | let slice: *[i32] = @sliceOf(ptr, 4); |
|
| 11 | + | let slice: *unsafe [i32] = @sliceOf(ptr, 4); |
|
| 12 | 12 | ||
| 13 | 13 | // Access elements via the slice and sum them |
|
| 14 | 14 | let mut sum: i32 = 0; |
|
| 15 | 15 | set sum += slice[0]; |
|
| 16 | 16 | set sum += slice[1]; |
test/tests/call.aggregate.arg.snapshot.rad
+1 -1
| 5 | 5 | record Pair: Copy { |
|
| 6 | 6 | x: i32, |
|
| 7 | 7 | y: i32, |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | fn mutate(pair: *mut Pair) -> i32 { |
|
| 10 | + | fn mutate(pair: &mut Pair) -> i32 { |
|
| 11 | 11 | set pair.x = 99; |
|
| 12 | 12 | return 0; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | fn first(pair: Pair, ignored: i32) -> i32 { |
test/tests/call.clobber.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that values live across function calls are not clobbered. |
|
| 3 | - | fn modify(counter: *mut i32, ret: bool) -> bool { |
|
| 3 | + | unsafe fn modify(counter: *unsafe mut i32, ret: bool) -> bool { |
|
| 4 | 4 | set *counter += 1; |
|
| 5 | 5 | return ret; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let mut x: i32 = 0; |
|
| 10 | 10 | let r: bool = modify(&mut x, false); |
|
| 11 | 11 | assert x == 1; |
|
| 12 | 12 | assert not r; |
|
| 13 | 13 | ||
| 14 | 14 | // Pointer live across call. |
|
| 15 | 15 | let mut y: i32 = 42; |
|
| 16 | - | let p: *mut i32 = &mut y; |
|
| 16 | + | let p: *unsafe mut i32 = &mut y; |
|
| 17 | 17 | let r2: bool = modify(p, true); |
|
| 18 | 18 | assert *p == 43; |
|
| 19 | 19 | assert r2; |
|
| 20 | 20 | return 0; |
|
| 21 | 21 | } |
test/tests/compound.assign.index.once.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Compound assignment evaluates a side-effecting lvalue exactly once. |
|
| 3 | 3 | ||
| 4 | - | fn nextIndex(calls: *mut u32) -> u32 { |
|
| 4 | + | fn nextIndex(calls: &mut u32) -> u32 { |
|
| 5 | 5 | let index = *calls; |
|
| 6 | 6 | set *calls += 1; |
|
| 7 | 7 | return index; |
|
| 8 | 8 | } |
|
| 9 | 9 |
test/tests/compound.assign.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | /// Test compound assignment operators (+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=). |
|
| 3 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 4 | let mut a: i32 = 10; |
|
| 5 | 5 | ||
| 6 | 6 | // Test += |
|
| 7 | 7 | set a += 5; |
|
| 8 | 8 | assert a == 15; |
| 50 | 50 | set arr[1] += 5; |
|
| 51 | 51 | assert arr[1] == 25; |
|
| 52 | 52 | ||
| 53 | 53 | // Test compound assignment with pointer dereference. |
|
| 54 | 54 | let mut val: i32 = 100; |
|
| 55 | - | let p: *mut i32 = &mut val; |
|
| 55 | + | let p: *unsafe mut i32 = &mut val; |
|
| 56 | 56 | set *p += 50; |
|
| 57 | 57 | assert val == 150; |
|
| 58 | 58 | ||
| 59 | 59 | // Test chained compound assignments. |
|
| 60 | 60 | let mut x: i32 = 1; |
test/tests/cond.match.guard.regalloc.rad
+3 -3
| 18 | 18 | span1: u32, |
|
| 19 | 19 | span2: u32, |
|
| 20 | 20 | kind: Kind, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | fn getPath(node: *Node, buf: *mut [*[u8]]) -> *[*[u8]] { |
|
| 24 | - | let mut out: *[*[u8]] = &[]; |
|
| 23 | + | unsafe fn getPath(node: &Node, buf: &mut [*[u8]]) -> *unsafe [*[u8]] { |
|
| 24 | + | let mut out: *unsafe [*[u8]] = &[]; |
|
| 25 | 25 | ||
| 26 | 26 | match node.kind { |
|
| 27 | 27 | case Kind::Name(name) if name.len > 0 => { |
|
| 28 | 28 | set buf[0] = name; |
|
| 29 | 29 | set out = &buf[..1]; |
| 34 | 34 | else => {} |
|
| 35 | 35 | } |
|
| 36 | 36 | return out; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | - | @default fn main() -> i32 { |
|
| 39 | + | @default unsafe fn main() -> i32 { |
|
| 40 | 40 | let n = Node { |
|
| 41 | 41 | id: 0, span1: 0, span2: 5, |
|
| 42 | 42 | kind: Kind::Name("Hello"), |
|
| 43 | 43 | }; |
|
| 44 | 44 | let mut buffer: [*[u8]; 4] = undefined; |
test/tests/edge.cases.2.rad
+1 -1
| 3 | 3 | ||
| 4 | 4 | export record Scanner: Copy { |
|
| 5 | 5 | source: *[u8], |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn peek(s: *Scanner) -> ?u8 { |
|
| 8 | + | fn peek(s: &Scanner) -> ?u8 { |
|
| 9 | 9 | if 0 + 0 >= s.source.len { |
|
| 10 | 10 | return nil; |
|
| 11 | 11 | } |
|
| 12 | 12 | return 'a'; |
|
| 13 | 13 | } |
test/tests/edge.cases.3.rad
+3 -3
| 4 | 4 | export record Scanner: Copy { |
|
| 5 | 5 | source: *[u8], |
|
| 6 | 6 | cursor: u32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn isEof(s: *Scanner) -> bool { |
|
| 9 | + | fn isEof(s: &Scanner) -> bool { |
|
| 10 | 10 | return s.cursor >= s.source.len; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | export fn current(s: *Scanner) -> ?u8 { |
|
| 13 | + | export fn current(s: &Scanner) -> ?u8 { |
|
| 14 | 14 | if isEof(s) { |
|
| 15 | 15 | return nil; |
|
| 16 | 16 | } |
|
| 17 | 17 | return s.source[s.cursor]; |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | fn peek(s: *Scanner) -> ?u8 { |
|
| 20 | + | fn peek(s: &Scanner) -> ?u8 { |
|
| 21 | 21 | if s.cursor + 1 >= s.source.len { |
|
| 22 | 22 | return nil; |
|
| 23 | 23 | } |
|
| 24 | 24 | return s.source[s.cursor + 1]; |
|
| 25 | 25 | } |
test/tests/edge.cases.4.rad
+4 -4
| 18 | 18 | ||
| 19 | 19 | record Node: Copy { |
|
| 20 | 20 | value: NodeValue, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | fn node(nodes: *mut Node, count: *mut u32, value: NodeValue) -> *Node { |
|
| 23 | + | unsafe fn node(nodes: *unsafe mut Node, count: *unsafe mut u32, value: NodeValue) -> *unsafe Node { |
|
| 24 | 24 | let index = *count; |
|
| 25 | 25 | let slot = nodes + index; |
|
| 26 | 26 | set *slot = Node { value }; |
|
| 27 | 27 | set *count = index + 1; |
|
| 28 | 28 | return slot; |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | - | fn nodeTypeInt(nodes: *mut Node, count: *mut u32, width: u8, sign: Signedness) -> *Node { |
|
| 31 | + | unsafe fn nodeTypeInt(nodes: *unsafe mut Node, count: *unsafe mut u32, width: u8, sign: Signedness) -> *unsafe Node { |
|
| 32 | 32 | return node(nodes, count, NodeValue::TypeSig( |
|
| 33 | 33 | TypeSig::Integer { width, sign } |
|
| 34 | 34 | )); |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | - | @default fn main() -> i32 { |
|
| 37 | + | @default unsafe fn main() -> i32 { |
|
| 38 | 38 | let direct = TypeSig::Integer { |
|
| 39 | 39 | width: 4, |
|
| 40 | 40 | sign: Signedness::Signed, |
|
| 41 | 41 | }; |
|
| 42 | 42 | let case TypeSig::Integer { width: w1, .. } = direct |
|
| 43 | 43 | else return 50; |
|
| 44 | 44 | ||
| 45 | 45 | assert w1 == 4; |
|
| 46 | 46 | let mut nodes: [Node; 2] = undefined; |
|
| 47 | - | let nodesPtr: *mut Node = &mut nodes[0]; |
|
| 47 | + | let nodesPtr: *unsafe mut Node = &mut nodes[0]; |
|
| 48 | 48 | let mut count: u32 = 0; |
|
| 49 | 49 | let typeNode = nodeTypeInt(nodesPtr, &mut count, 4, Signedness::Signed); |
|
| 50 | 50 | let case NodeValue::TypeSig(sig) = typeNode.value |
|
| 51 | 51 | else return 40; |
|
| 52 | 52 |
test/tests/edge.cases.rad
+1 -1
| 2 | 2 | ||
| 3 | 3 | export record Scanner: Copy { |
|
| 4 | 4 | source: *[u8], |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn peek(s: *Scanner) -> i32 { |
|
| 7 | + | fn peek(s: &Scanner) -> i32 { |
|
| 8 | 8 | assert 0 + 0 <= s.source.len; |
|
| 9 | 9 | return 0; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | @default fn main() -> i32 { |
test/tests/error.slice.bounds.rad
+2 -2
| 1 | 1 | //! returns: 133 |
|
| 2 | 2 | //! Test slice bounds checking with runtime EBREAK. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | let arr: [i32; 3] = [1, 2, 3]; |
|
| 6 | - | let slice: *[i32] = &arr[..]; |
|
| 6 | + | let slice: *unsafe [i32] = &arr[..]; |
|
| 7 | 7 | let value: i32 = slice[3]; |
|
| 8 | 8 | ||
| 9 | 9 | return value; |
|
| 10 | 10 | } |
test/tests/error.try.rad
+3 -3
| 70 | 70 | throw TestError::Bust; |
|
| 71 | 71 | } |
|
| 72 | 72 | return 77; |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | - | fn catchReturn(flag: *mut u32, fail: bool) -> bool { |
|
| 75 | + | fn catchReturn(flag: &mut u32, fail: bool) -> bool { |
|
| 76 | 76 | let value: u32 = try maybeReturn(fail) catch { |
|
| 77 | 77 | return true; |
|
| 78 | 78 | }; |
|
| 79 | 79 | set *flag = value; |
|
| 80 | 80 | return false; |
| 96 | 96 | set idx += 1; |
|
| 97 | 97 | } |
|
| 98 | 98 | return total; |
|
| 99 | 99 | } |
|
| 100 | 100 | ||
| 101 | - | fn structSuccess(state: *mut ResultSink) -> u32 throws (TestError) { |
|
| 101 | + | fn structSuccess(state: &mut ResultSink) -> u32 throws (TestError) { |
|
| 102 | 102 | let value: u32 = try returnsOk(); |
|
| 103 | 103 | set state.last = value; |
|
| 104 | 104 | set state.count += 1; |
|
| 105 | 105 | return value; |
|
| 106 | 106 | } |
|
| 107 | 107 | ||
| 108 | - | fn structFailure(state: *mut ResultSink) -> u32 throws (TestError) { |
|
| 108 | + | fn structFailure(state: &mut ResultSink) -> u32 throws (TestError) { |
|
| 109 | 109 | try returnsErr(1); |
|
| 110 | 110 | set state.last = 999; |
|
| 111 | 111 | return 999; |
|
| 112 | 112 | } |
|
| 113 | 113 |
test/tests/field.aggregate.rad
+2 -2
| 4 | 4 | record Inner: Copy { x: i32, y: i32 } |
|
| 5 | 5 | union MaybeInt: Copy { None, Some(i32) } |
|
| 6 | 6 | ||
| 7 | 7 | record HasRecord: Copy { inner: Inner, z: i32 } |
|
| 8 | 8 | record HasUnion: Copy { maybe: MaybeInt, z: i32 } |
|
| 9 | - | record HasSlice: Copy { data: *[i32], z: i32 } |
|
| 9 | + | record HasSlice: Copy { data: *unsafe [i32], z: i32 } |
|
| 10 | 10 | ||
| 11 | 11 | /// Access a record field and read from it. |
|
| 12 | 12 | fn accessRecordField() -> i32 { |
|
| 13 | 13 | let r = HasRecord { inner: Inner { x: 10, y: 20 }, z: 30 }; |
|
| 14 | 14 | return r.inner.y; |
| 19 | 19 | let r = HasUnion { maybe: MaybeInt::Some(42), z: 99 }; |
|
| 20 | 20 | return r.maybe; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// Access a slice field and get its length. |
|
| 24 | - | fn accessSliceField() -> u32 { |
|
| 24 | + | unsafe fn accessSliceField() -> u32 { |
|
| 25 | 25 | let arr: [i32; 3] = [1, 2, 3]; |
|
| 26 | 26 | let r = HasSlice { data: &arr[..], z: 77 }; |
|
| 27 | 27 | return r.data.len; |
|
| 28 | 28 | } |
test/tests/fn.callback.nested.rad
+9 -5
| 15 | 15 | return n + 1; |
|
| 16 | 16 | } |
|
| 17 | 17 | return current; |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | fn maxRegCallback(reg: Reg, ctx: *mut opaque) { |
|
| 21 | - | let max = ctx as *mut u32; |
|
| 20 | + | /// Update the maximum register count through a borrowed counter. |
|
| 21 | + | fn updateMax(reg: Reg, max: &mut u32) { |
|
| 22 | 22 | set *max = maxRegNum(reg.n, *max); |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | - | fn withReg(val: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 25 | + | fn maxRegCallback(reg: Reg, ctx: &mut opaque) { |
|
| 26 | + | updateMax(reg, ctx as &mut u32); |
|
| 27 | + | } |
|
| 28 | + | ||
| 29 | + | fn withReg(val: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 26 | 30 | if let case Val::Reg(r) = val { |
|
| 27 | 31 | callback(r, ctx); |
|
| 28 | 32 | } |
|
| 29 | 33 | } |
|
| 30 | 34 | ||
| 31 | - | fn forEachVal(a: Val, b: Val, c: Val, callback: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 35 | + | fn forEachVal(a: Val, b: Val, c: Val, callback: fn(Reg, &mut opaque), ctx: &mut opaque) { |
|
| 32 | 36 | withReg(a, callback, ctx); |
|
| 33 | 37 | withReg(b, callback, ctx); |
|
| 34 | 38 | withReg(c, callback, ctx); |
|
| 35 | 39 | } |
|
| 36 | 40 |
| 40 | 44 | forEachVal( |
|
| 41 | 45 | Val::Reg(Reg { n: 0 }), |
|
| 42 | 46 | Val::Reg(Reg { n: 5 }), |
|
| 43 | 47 | Val::Reg(Reg { n: 2 }), |
|
| 44 | 48 | maxRegCallback, |
|
| 45 | - | &mut maxReg as *mut opaque |
|
| 49 | + | &mut maxReg as &mut opaque |
|
| 46 | 50 | ); |
|
| 47 | 51 | ||
| 48 | 52 | // maxReg should be 6 (max register 5 + 1) |
|
| 49 | 53 | if maxReg == 6 { |
|
| 50 | 54 | return 0; |
test/tests/fn.unsafe.callbacks.rad
added
+30 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | mod operations; |
|
| 4 | + | ||
| 5 | + | record Callback: Copy { |
|
| 6 | + | /// Function called with a raw pointer to a live integer. |
|
| 7 | + | invoke: unsafe fn(*unsafe u32) -> u32, |
|
| 8 | + | } |
|
| 9 | + | ||
| 10 | + | /// Call a safe function from a module that also exports unsafe functions. |
|
| 11 | + | fn readBorrow(value: &u32) -> u32 { |
|
| 12 | + | return operations::readBorrow(value); |
|
| 13 | + | } |
|
| 14 | + | ||
| 15 | + | /// Return a constant without reading the pointer. |
|
| 16 | + | fn constantValue(value: *unsafe u32) -> u32 { |
|
| 17 | + | return 7; |
|
| 18 | + | } |
|
| 19 | + | ||
| 20 | + | @default unsafe fn main() -> i32 { |
|
| 21 | + | let value: u32 = 42; |
|
| 22 | + | assert readBorrow(&value) == 42; |
|
| 23 | + | ||
| 24 | + | let raw = Callback { invoke: operations::readRaw }; |
|
| 25 | + | assert raw.invoke(&value) == 42; |
|
| 26 | + | ||
| 27 | + | let checked = Callback { invoke: constantValue }; |
|
| 28 | + | assert checked.invoke(&value) == 7; |
|
| 29 | + | return 0; |
|
| 30 | + | } |
test/tests/fn.unsafe.callbacks/operations.rad
added
+9 -0
| 1 | + | /// Read an integer through a checked borrow. |
|
| 2 | + | export fn readBorrow(value: &u32) -> u32 { |
|
| 3 | + | return *value; |
|
| 4 | + | } |
|
| 5 | + | ||
| 6 | + | /// Read an integer through a raw pointer that must remain valid for the call. |
|
| 7 | + | export unsafe fn readRaw(value: *unsafe u32) -> u32 { |
|
| 8 | + | return *value; |
|
| 9 | + | } |
test/tests/for.else.continue.rad
+1 -1
| 4 | 4 | record Field: Copy { |
|
| 5 | 5 | name: ?*[u8], |
|
| 6 | 6 | value: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn findField(fields: *[Field], target: *[u8]) -> ?i32 { |
|
| 9 | + | fn findField(fields: &[Field], target: &[u8]) -> ?i32 { |
|
| 10 | 10 | for i in 0..fields.len { |
|
| 11 | 11 | let name = fields[i].name |
|
| 12 | 12 | else continue; |
|
| 13 | 13 | if name.len == target.len { |
|
| 14 | 14 | let mut eq = true; |
test/tests/index.u8.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 4 | let arr: [i32; 4] = [10, 20, 42, 30]; |
|
| 5 | 5 | ||
| 6 | 6 | // u8 index. |
|
| 7 | 7 | let idx8: u8 = 2; |
|
| 8 | 8 | if arr[idx8] <> 42 { |
| 21 | 21 | // Unsuffixed integer literal index. |
|
| 22 | 22 | if arr[1] <> 20 { |
|
| 23 | 23 | return 4; |
|
| 24 | 24 | } |
|
| 25 | 25 | // u8 index into slice. |
|
| 26 | - | let s = &arr[..]; |
|
| 26 | + | let s: *unsafe [i32] = &arr[..]; |
|
| 27 | 27 | let si: u8 = 1; |
|
| 28 | 28 | if s[si] <> 20 { |
|
| 29 | 29 | return 5; |
|
| 30 | 30 | } |
|
| 31 | 31 | return 0; |
test/tests/match.mutref.push.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test pushing items through a mutable reference obtained via match. |
|
| 3 | 3 | ||
| 4 | 4 | record U32List: Copy { |
|
| 5 | - | data: *mut [u32], |
|
| 5 | + | data: *unsafe mut [u32], |
|
| 6 | 6 | len: u32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | union Sealed: Copy { |
|
| 10 | 10 | No { items: U32List }, |
|
| 11 | 11 | Yes, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn pushItem(list: *mut U32List, value: u32) { |
|
| 14 | + | unsafe fn pushItem(list: &mut U32List, value: u32) { |
|
| 15 | 15 | set list.data[list.len] = value; |
|
| 16 | 16 | set list.len += 1; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | fn addToUnsealedBlock(state: *mut Sealed, value: u32) -> bool { |
|
| 19 | + | unsafe fn addToUnsealedBlock(state: &mut Sealed, value: u32) -> bool { |
|
| 20 | 20 | match state { |
|
| 21 | 21 | case Sealed::No { items } => { |
|
| 22 | 22 | pushItem(items, value); |
|
| 23 | 23 | return true; |
|
| 24 | 24 | }, |
| 26 | 26 | return false; |
|
| 27 | 27 | }, |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | - | @default fn main() -> i32 { |
|
| 31 | + | @default unsafe fn main() -> i32 { |
|
| 32 | 32 | let mut buf: [u32; 8] = undefined; |
|
| 33 | 33 | set buf[0] = 0; |
|
| 34 | 34 | let mut state = Sealed::No { items: U32List { data: &mut buf[0..8], len: 0 } }; |
|
| 35 | 35 | ||
| 36 | 36 | assert addToUnsealedBlock(&mut state, 42); |
test/tests/match.mutref.union.rad
+1 -1
| 10 | 10 | record Data: Copy { |
|
| 11 | 11 | state: State, |
|
| 12 | 12 | value: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn process(d: *mut Data) -> u32 { |
|
| 15 | + | fn process(d: &mut Data) -> u32 { |
|
| 16 | 16 | match &mut d.state { |
|
| 17 | 17 | case State::A { count } => { |
|
| 18 | 18 | let c = *count; |
|
| 19 | 19 | set *count = c + 1; |
|
| 20 | 20 | return c; |
test/tests/match.nested.deref.rad
+12 -12
| 5 | 5 | A(i32), |
|
| 6 | 6 | B, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | union Outer: Copy { |
|
| 10 | - | Some(*Inner), |
|
| 10 | + | Some(*unsafe Inner), |
|
| 11 | 11 | None, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Match nested union variant through pointer dereference in match/case. |
|
| 15 | - | fn matchDeref(o: Outer) -> i32 { |
|
| 15 | + | unsafe fn matchDeref(o: Outer) -> i32 { |
|
| 16 | 16 | match o { |
|
| 17 | 17 | case Outer::Some(Inner::A(x)) => { |
|
| 18 | 18 | return x; |
|
| 19 | 19 | } |
|
| 20 | 20 | case Outer::Some(Inner::B) => { |
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// If-let-case with auto-deref nested pattern. |
|
| 30 | - | fn ifLetDeref(o: Outer) -> i32 { |
|
| 30 | + | unsafe fn ifLetDeref(o: Outer) -> i32 { |
|
| 31 | 31 | if let case Outer::Some(Inner::A(x)) = o { |
|
| 32 | 32 | return x; |
|
| 33 | 33 | } |
|
| 34 | 34 | return 0; |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Let-else with auto-deref nested pattern. |
|
| 38 | - | fn letElseDeref(o: Outer) -> i32 { |
|
| 38 | + | unsafe fn letElseDeref(o: Outer) -> i32 { |
|
| 39 | 39 | let case Outer::Some(Inner::A(x)) = o |
|
| 40 | 40 | else { return -1; }; |
|
| 41 | 41 | return x; |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Record with pointer field and nested pattern through deref. |
|
| 45 | 45 | record Container: Copy { |
|
| 46 | - | inner: *Inner, |
|
| 46 | + | inner: *unsafe Inner, |
|
| 47 | 47 | tag: i32, |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | union Boxed: Copy { |
|
| 51 | 51 | Some { c: Container }, |
|
| 52 | 52 | None, |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | 55 | /// Nested record with auto-deref on a pointer field. |
|
| 56 | - | fn matchRecordDeref(b: Boxed) -> i32 { |
|
| 56 | + | unsafe fn matchRecordDeref(b: Boxed) -> i32 { |
|
| 57 | 57 | match b { |
|
| 58 | 58 | case Boxed::Some { c: Container { inner: Inner::A(val), tag } } => { |
|
| 59 | 59 | return val + tag; |
|
| 60 | 60 | } |
|
| 61 | 61 | else => { |
| 63 | 63 | } |
|
| 64 | 64 | } |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | /// Auto-deref through pointer in if-let-case with record. |
|
| 68 | - | fn ifLetRecordDeref(b: Boxed) -> i32 { |
|
| 68 | + | unsafe fn ifLetRecordDeref(b: Boxed) -> i32 { |
|
| 69 | 69 | if let case Boxed::Some { c: Container { inner: Inner::A(val), tag } } = b { |
|
| 70 | 70 | return val + tag; |
|
| 71 | 71 | } |
|
| 72 | 72 | return 0; |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | /// Void variant through pointer deref. |
|
| 76 | - | fn matchDerefVoid(o: Outer) -> i32 { |
|
| 76 | + | unsafe fn matchDerefVoid(o: Outer) -> i32 { |
|
| 77 | 77 | if let case Outer::Some(Inner::B) = o { |
|
| 78 | 78 | return 1; |
|
| 79 | 79 | } |
|
| 80 | 80 | return 0; |
|
| 81 | 81 | } |
|
| 82 | 82 | ||
| 83 | 83 | /// Auto-deref with placeholder in nested pattern. |
|
| 84 | - | fn matchDerefPlaceholder(o: Outer) -> i32 { |
|
| 84 | + | unsafe fn matchDerefPlaceholder(o: Outer) -> i32 { |
|
| 85 | 85 | if let case Outer::Some(Inner::A(_)) = o { |
|
| 86 | 86 | return 1; |
|
| 87 | 87 | } |
|
| 88 | 88 | return 0; |
|
| 89 | 89 | } |
| 93 | 93 | x: i32, |
|
| 94 | 94 | y: i32, |
|
| 95 | 95 | } |
|
| 96 | 96 | ||
| 97 | 97 | union Holder: Copy { |
|
| 98 | - | Ptr { p: *Point, z: i32 }, |
|
| 98 | + | Ptr { p: *unsafe Point, z: i32 }, |
|
| 99 | 99 | Empty, |
|
| 100 | 100 | } |
|
| 101 | 101 | ||
| 102 | - | fn derefRecordField(h: Holder) -> i32 { |
|
| 102 | + | unsafe fn derefRecordField(h: Holder) -> i32 { |
|
| 103 | 103 | if let case Holder::Ptr { p: Point { x, y }, z } = h { |
|
| 104 | 104 | return x + y + z; |
|
| 105 | 105 | } |
|
| 106 | 106 | return 0; |
|
| 107 | 107 | } |
|
| 108 | 108 | ||
| 109 | - | @default fn main() -> i32 { |
|
| 109 | + | @default unsafe fn main() -> i32 { |
|
| 110 | 110 | let innerA = Inner::A(42); |
|
| 111 | 111 | let innerB = Inner::B; |
|
| 112 | 112 | ||
| 113 | 113 | // matchDeref |
|
| 114 | 114 | assert matchDeref(Outer::Some(&innerA)) == 42; |
test/tests/method.basic.rad
+4 -4
| 4 | 4 | record Point: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn (p: *Point) sum() -> i32 { |
|
| 9 | + | fn (p: &Point) sum() -> i32 { |
|
| 10 | 10 | return p.x + p.y; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn (p: *mut Point) translate(dx: i32, dy: i32) { |
|
| 13 | + | fn (p: &mut Point) translate(dx: i32, dy: i32) { |
|
| 14 | 14 | set p.x = p.x + dx; |
|
| 15 | 15 | set p.y = p.y + dy; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | @default fn main() -> i32 { |
|
| 18 | + | @default unsafe fn main() -> i32 { |
|
| 19 | 19 | let mut pt = Point { x: 3, y: 4 }; |
|
| 20 | 20 | ||
| 21 | 21 | // Call immutable method. |
|
| 22 | 22 | assert pt.sum() == 7; |
|
| 23 | 23 |
| 25 | 25 | pt.translate(10, 20); |
|
| 26 | 26 | assert pt.x == 13; |
|
| 27 | 27 | assert pt.y == 24; |
|
| 28 | 28 | ||
| 29 | 29 | // Call via pointer. |
|
| 30 | - | let ptr = &pt; |
|
| 30 | + | let ptr: *unsafe Point = &pt; |
|
| 31 | 31 | assert ptr.sum() == 37; |
|
| 32 | 32 | ||
| 33 | 33 | return 0; |
|
| 34 | 34 | } |
test/tests/method.multiple.rad
+5 -5
| 4 | 4 | record Vec2: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn (v: *Vec2) magnitudeSq() -> i32 { |
|
| 9 | + | fn (v: &Vec2) magnitudeSq() -> i32 { |
|
| 10 | 10 | return v.x * v.x + v.y * v.y; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn (v: *Vec2) dot(other: *Vec2) -> i32 { |
|
| 13 | + | unsafe fn (v: &Vec2) dot(other: *unsafe Vec2) -> i32 { |
|
| 14 | 14 | return v.x * other.x + v.y * other.y; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | fn (v: *mut Vec2) add(other: *Vec2) { |
|
| 17 | + | unsafe fn (v: &mut Vec2) add(other: *unsafe Vec2) { |
|
| 18 | 18 | set v.x = v.x + other.x; |
|
| 19 | 19 | set v.y = v.y + other.y; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | fn (v: *mut Vec2) scale(factor: i32) { |
|
| 22 | + | fn (v: &mut Vec2) scale(factor: i32) { |
|
| 23 | 23 | set v.x = v.x * factor; |
|
| 24 | 24 | set v.y = v.y * factor; |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | - | @default fn main() -> i32 { |
|
| 27 | + | @default unsafe fn main() -> i32 { |
|
| 28 | 28 | let mut a = Vec2 { x: 3, y: 4 }; |
|
| 29 | 29 | let b = Vec2 { x: 1, y: 2 }; |
|
| 30 | 30 | ||
| 31 | 31 | // Immutable method. |
|
| 32 | 32 | assert a.magnitudeSq() == 25; |
test/tests/method.ptr.rad
+5 -5
| 3 | 3 | ||
| 4 | 4 | record Counter: Copy { |
|
| 5 | 5 | value: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn (c: *Counter) get() -> i32 { |
|
| 8 | + | fn (c: &Counter) get() -> i32 { |
|
| 9 | 9 | return c.value; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | fn (c: *mut Counter) inc() { |
|
| 12 | + | fn (c: &mut Counter) inc() { |
|
| 13 | 13 | set c.value = c.value + 1; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | - | @default fn main() -> i32 { |
|
| 16 | + | @default unsafe fn main() -> i32 { |
|
| 17 | 17 | let mut c = Counter { value: 0 }; |
|
| 18 | 18 | ||
| 19 | 19 | // Direct call on value. |
|
| 20 | 20 | assert c.get() == 0; |
|
| 21 | 21 | ||
| 22 | 22 | // Mutable method on value. |
|
| 23 | 23 | c.inc(); |
|
| 24 | 24 | assert c.get() == 1; |
|
| 25 | 25 | ||
| 26 | 26 | // Call via immutable pointer. |
|
| 27 | - | let p = &c; |
|
| 27 | + | let p: *unsafe Counter = &c; |
|
| 28 | 28 | assert p.get() == 1; |
|
| 29 | 29 | ||
| 30 | 30 | // Call via mutable pointer. |
|
| 31 | - | let mp = &mut c; |
|
| 31 | + | let mp: *unsafe mut Counter = &mut c; |
|
| 32 | 32 | mp.inc(); |
|
| 33 | 33 | assert mp.get() == 2; |
|
| 34 | 34 | ||
| 35 | 35 | // Original value also updated. |
|
| 36 | 36 | assert c.get() == 2; |
test/tests/method.with.trait.rad
+5 -5
| 5 | 5 | x: i32, |
|
| 6 | 6 | y: i32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | // Standalone method. |
|
| 10 | - | fn (w: *Widget) area() -> i32 { |
|
| 10 | + | unsafe fn (w: *unsafe Widget) area() -> i32 { |
|
| 11 | 11 | return w.x * w.y; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | // Trait with its own method. |
|
| 15 | 15 | trait Printable { |
|
| 16 | - | fn (*Printable) code() -> i32; |
|
| 16 | + | unsafe fn (*unsafe Printable) code() -> i32; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | instance Printable for Widget { |
|
| 20 | - | fn (w: *Widget) code() -> i32 { |
|
| 20 | + | unsafe fn (w: *unsafe Widget) code() -> i32 { |
|
| 21 | 21 | return w.x + w.y; |
|
| 22 | 22 | } |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | - | @default fn main() -> i32 { |
|
| 25 | + | @default unsafe fn main() -> i32 { |
|
| 26 | 26 | let w = Widget { x: 3, y: 5 }; |
|
| 27 | 27 | ||
| 28 | 28 | // Standalone method call. |
|
| 29 | 29 | assert w.area() == 15; |
|
| 30 | 30 | ||
| 31 | 31 | // Trait method call via trait object. |
|
| 32 | - | let p: *opaque Printable = &w; |
|
| 32 | + | let p: *unsafe opaque Printable = &w; |
|
| 33 | 33 | assert p.code() == 8; |
|
| 34 | 34 | ||
| 35 | 35 | return 0; |
|
| 36 | 36 | } |
test/tests/mutref.call.result.rad
+3 -3
| 3 | 3 | ||
| 4 | 4 | record Box: Copy { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn idBox(b: *mut Box) -> *mut Box { |
|
| 8 | + | fn idBox(b: *unsafe mut Box) -> *unsafe mut Box { |
|
| 9 | 9 | return b; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | - | @default fn main() -> i32 { |
|
| 12 | + | @default unsafe fn main() -> i32 { |
|
| 13 | 13 | let mut b = Box { x: 1 }; |
|
| 14 | 14 | ||
| 15 | - | let px: *mut i32 = &mut idBox(&mut b).x; |
|
| 15 | + | let px: *unsafe mut i32 = &mut idBox(&mut b).x; |
|
| 16 | 16 | set *px = 9; |
|
| 17 | 17 | ||
| 18 | 18 | assert b.x == 9; |
|
| 19 | 19 | return 0; |
|
| 20 | 20 | } |
test/tests/mutref.loop.bug.rad
+1 -1
| 8 | 8 | //! |
|
| 9 | 9 | //! The critical case is when the loop executes zero iterations: the merge |
|
| 10 | 10 | //! block tries to `load` through the initial integer value (not a valid |
|
| 11 | 11 | //! pointer), crashing the program. |
|
| 12 | 12 | ||
| 13 | - | fn store(ptr: *mut u32, val: u32) { |
|
| 13 | + | fn store(ptr: &mut u32, val: u32) { |
|
| 14 | 14 | set *ptr = val; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Zero-iteration loop with &mut inside the body. |
|
| 18 | 18 | /// Without the fix, `val` starts as integer 42 in SSA, but the post-loop |
test/tests/mutref.loop.rad
+4 -4
| 1 | - | fn callback(val: u32, ctx: *mut opaque) { |
|
| 2 | - | let max = ctx as *mut u32; |
|
| 1 | + | unsafe fn callback(val: u32, ctx: *unsafe mut opaque) { |
|
| 2 | + | let max = ctx as *unsafe mut u32; |
|
| 3 | 3 | set *max = val; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | - | fn test() -> i32 { |
|
| 6 | + | unsafe fn test() -> i32 { |
|
| 7 | 7 | let mut maxReg: u32 = 0; |
|
| 8 | 8 | let mut i: u32 = 0; |
|
| 9 | 9 | while i < 3 { |
|
| 10 | - | callback(i, &mut maxReg as *mut opaque); |
|
| 10 | + | callback(i, &mut maxReg as *unsafe mut opaque); |
|
| 11 | 11 | set i += 1; |
|
| 12 | 12 | } |
|
| 13 | 13 | return maxReg as i32; |
|
| 14 | 14 | } |
test/tests/mutref.scalar.rad
+1 -1
| 1 | - | fn modify(counter: *mut i32, ret: bool) -> bool { |
|
| 1 | + | fn modify(counter: &mut i32, ret: bool) -> bool { |
|
| 2 | 2 | set *counter += 1; |
|
| 3 | 3 | return ret; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | fn test() -> i32 { |
test/tests/opt.nil.check.rad
+8 -8
| 2 | 2 | //! Test nil check for optional pointers and slices. |
|
| 3 | 3 | //! Ensure that nil checks compare the full pointer width, not just the low byte. |
|
| 4 | 4 | ||
| 5 | 5 | /// Return a pointer whose low byte is zero but is non-null. |
|
| 6 | 6 | /// This tests that nil checks use W64, not W8. |
|
| 7 | - | fn makeAlignedPtr() -> *u8 { |
|
| 7 | + | unsafe fn makeAlignedPtr() -> *unsafe u8 { |
|
| 8 | 8 | let arr: [u8; 512] = undefined; |
|
| 9 | 9 | // Find an address within arr whose low byte is 0x00. |
|
| 10 | 10 | let base: u64 = &arr[0] as u64; |
|
| 11 | 11 | let offset: u64 = 256 - (base % 256); |
|
| 12 | - | return &arr[offset as u32] as *u8; |
|
| 12 | + | return &arr[offset as u32] as *unsafe u8; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Test: optional pointer nil check must use full 64-bit comparison. |
|
| 16 | - | fn testOptionalPtrNilCheck() -> i32 { |
|
| 16 | + | unsafe fn testOptionalPtrNilCheck() -> i32 { |
|
| 17 | 17 | let p = makeAlignedPtr(); |
|
| 18 | - | let opt: ?*u8 = p; |
|
| 18 | + | let opt: ?*unsafe u8 = p; |
|
| 19 | 19 | ||
| 20 | 20 | // The pointer is not nil, but its low byte is 0x00. |
|
| 21 | 21 | // A W8 comparison would wrongly say it's nil. |
|
| 22 | 22 | assert opt <> nil; |
|
| 23 | 23 | if let v = opt { |
| 26 | 26 | } |
|
| 27 | 27 | return 2; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Test: optional slice nil check must use full 64-bit comparison. |
|
| 31 | - | fn testOptionalSliceNilCheck() -> i32 { |
|
| 31 | + | unsafe fn testOptionalSliceNilCheck() -> i32 { |
|
| 32 | 32 | let arr: [u8; 512] = undefined; |
|
| 33 | 33 | let base: u64 = &arr[0] as u64; |
|
| 34 | 34 | let offset: u64 = 256 - (base % 256); |
|
| 35 | 35 | // Create a slice starting at an address whose low byte is 0. |
|
| 36 | - | let s: *[u8] = &arr[offset as u32 ..]; |
|
| 36 | + | let s: *unsafe [u8] = &arr[offset as u32 ..]; |
|
| 37 | 37 | ||
| 38 | - | let opt: ?*[u8] = s; |
|
| 38 | + | let opt: ?*unsafe [u8] = s; |
|
| 39 | 39 | assert opt <> nil; |
|
| 40 | 40 | if let v = opt { |
|
| 41 | 41 | return 0; |
|
| 42 | 42 | } |
|
| 43 | 43 | return 2; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | - | @default fn main() -> i32 { |
|
| 46 | + | @default unsafe fn main() -> i32 { |
|
| 47 | 47 | let r1 = testOptionalPtrNilCheck(); |
|
| 48 | 48 | if r1 <> 0 { |
|
| 49 | 49 | return r1; |
|
| 50 | 50 | } |
|
| 51 | 51 | let r2 = testOptionalSliceNilCheck(); |
test/tests/opt.slice.npo.rad
+31 -31
| 3 | 3 | //! Optional slices should have the same size as slices (16 bytes), |
|
| 4 | 4 | //! using a null data pointer to represent `nil`. |
|
| 5 | 5 | ||
| 6 | 6 | fn checkSizes() -> u8 { |
|
| 7 | 7 | // ?*[T] should be the same size as *[T] (16 bytes, not 24). |
|
| 8 | - | assert @sizeOf(?*[u8]) == 16; |
|
| 9 | - | assert @alignOf(?*[u8]) == 8; |
|
| 10 | - | assert @sizeOf(?*[u16]) == 16; |
|
| 11 | - | assert @sizeOf(?*mut [u8]) == 16; |
|
| 8 | + | assert @sizeOf(?*unsafe [u8]) == 16; |
|
| 9 | + | assert @alignOf(?*unsafe [u8]) == 8; |
|
| 10 | + | assert @sizeOf(?*unsafe [u16]) == 16; |
|
| 11 | + | assert @sizeOf(?*unsafe mut [u8]) == 16; |
|
| 12 | 12 | return 0; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | fn checkNil() -> u8 { |
|
| 16 | - | let x: ?*[u8] = nil; |
|
| 16 | + | let x: ?*unsafe [u8] = nil; |
|
| 17 | 17 | assert x == nil; |
|
| 18 | 18 | return 0; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | fn checkWrap() -> u8 { |
|
| 21 | + | unsafe fn checkWrap() -> u8 { |
|
| 22 | 22 | let arr: [u8; 3] = [1, 2, 3]; |
|
| 23 | - | let s = &arr[..]; |
|
| 24 | - | let opt: ?*[u8] = s; |
|
| 23 | + | let s: *unsafe [u8] = &arr[..]; |
|
| 24 | + | let opt: ?*unsafe [u8] = s; |
|
| 25 | 25 | ||
| 26 | 26 | assert opt <> nil; |
|
| 27 | 27 | return 0; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | - | fn checkIfLet() -> u8 { |
|
| 30 | + | unsafe fn checkIfLet() -> u8 { |
|
| 31 | 31 | let arr: [u8; 3] = [10, 20, 30]; |
|
| 32 | - | let s = &arr[..]; |
|
| 33 | - | let opt: ?*[u8] = s; |
|
| 32 | + | let s: *unsafe [u8] = &arr[..]; |
|
| 33 | + | let opt: ?*unsafe [u8] = s; |
|
| 34 | 34 | ||
| 35 | 35 | if let val = opt { |
|
| 36 | 36 | assert val.len == 3; |
|
| 37 | 37 | assert val[0] == 10; |
|
| 38 | 38 | assert val[1] == 20; |
|
| 39 | 39 | } else { |
|
| 40 | 40 | return 34; |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | - | let none: ?*[u8] = nil; |
|
| 43 | + | let none: ?*unsafe [u8] = nil; |
|
| 44 | 44 | if let _ = none { |
|
| 45 | 45 | return 35; |
|
| 46 | 46 | } |
|
| 47 | 47 | return 0; |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | - | fn checkLetElse() -> u8 { |
|
| 50 | + | unsafe fn checkLetElse() -> u8 { |
|
| 51 | 51 | let arr: [u8; 3] = [10, 20, 30]; |
|
| 52 | - | let s = &arr[..]; |
|
| 53 | - | let opt: ?*[u8] = s; |
|
| 52 | + | let s: *unsafe [u8] = &arr[..]; |
|
| 53 | + | let opt: ?*unsafe [u8] = s; |
|
| 54 | 54 | ||
| 55 | 55 | let val = opt else { |
|
| 56 | 56 | return 40; |
|
| 57 | 57 | }; |
|
| 58 | 58 | assert val.len == 3; |
|
| 59 | 59 | return 0; |
|
| 60 | 60 | } |
|
| 61 | 61 | ||
| 62 | - | fn returnNil() -> ?*[u8] { |
|
| 62 | + | fn returnNil() -> ?*unsafe [u8] { |
|
| 63 | 63 | return nil; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | - | fn returnSome() -> ?*[u8] { |
|
| 67 | - | let arr: [u8; 2] = [42, 99]; |
|
| 68 | - | return &arr[..]; |
|
| 66 | + | unsafe fn returnSome() -> ?*unsafe [u8] { |
|
| 67 | + | static arr: [u8; 2] = [42, 99]; |
|
| 68 | + | return &arr[..] as *unsafe [u8]; |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | - | fn checkReturn() -> u8 { |
|
| 71 | + | unsafe fn checkReturn() -> u8 { |
|
| 72 | 72 | let a = returnNil(); |
|
| 73 | 73 | assert a == nil; |
|
| 74 | 74 | let b = returnSome(); |
|
| 75 | 75 | assert b <> nil; |
|
| 76 | 76 | if let val = b { |
| 79 | 79 | return 53; |
|
| 80 | 80 | } |
|
| 81 | 81 | return 0; |
|
| 82 | 82 | } |
|
| 83 | 83 | ||
| 84 | - | fn checkMatch() -> u8 { |
|
| 84 | + | unsafe fn checkMatch() -> u8 { |
|
| 85 | 85 | let arr: [u8; 2] = [5, 6]; |
|
| 86 | - | let s = &arr[..]; |
|
| 87 | - | let opt: ?*[u8] = s; |
|
| 86 | + | let s: *unsafe [u8] = &arr[..]; |
|
| 87 | + | let opt: ?*unsafe [u8] = s; |
|
| 88 | 88 | ||
| 89 | 89 | match opt { |
|
| 90 | 90 | case nil => { |
|
| 91 | 91 | return 60; |
|
| 92 | 92 | } |
|
| 93 | 93 | else => {} |
|
| 94 | 94 | } |
|
| 95 | 95 | ||
| 96 | - | let none: ?*[u8] = nil; |
|
| 96 | + | let none: ?*unsafe [u8] = nil; |
|
| 97 | 97 | match none { |
|
| 98 | 98 | case nil => {} |
|
| 99 | 99 | else => { |
|
| 100 | 100 | return 61; |
|
| 101 | 101 | } |
|
| 102 | 102 | } |
|
| 103 | 103 | return 0; |
|
| 104 | 104 | } |
|
| 105 | 105 | ||
| 106 | - | fn checkEq() -> u8 { |
|
| 107 | - | let a: ?*[u8] = nil; |
|
| 108 | - | let b: ?*[u8] = nil; |
|
| 106 | + | unsafe fn checkEq() -> u8 { |
|
| 107 | + | let a: ?*unsafe [u8] = nil; |
|
| 108 | + | let b: ?*unsafe [u8] = nil; |
|
| 109 | 109 | ||
| 110 | 110 | // nil == nil |
|
| 111 | 111 | assert a == b; |
|
| 112 | 112 | ||
| 113 | 113 | let arr: [u8; 2] = [1, 2]; |
|
| 114 | - | let s = &arr[..]; |
|
| 115 | - | let c: ?*[u8] = s; |
|
| 114 | + | let s: *unsafe [u8] = &arr[..]; |
|
| 115 | + | let c: ?*unsafe [u8] = s; |
|
| 116 | 116 | ||
| 117 | 117 | // some <> nil |
|
| 118 | 118 | assert c <> a; |
|
| 119 | 119 | ||
| 120 | 120 | // some == some (same pointer) |
|
| 121 | - | let d: ?*[u8] = s; |
|
| 121 | + | let d: ?*unsafe [u8] = s; |
|
| 122 | 122 | assert c == d; |
|
| 123 | 123 | ||
| 124 | 124 | return 0; |
|
| 125 | 125 | } |
|
| 126 | 126 | ||
| 127 | - | @default fn main() -> u8 { |
|
| 127 | + | @default unsafe fn main() -> u8 { |
|
| 128 | 128 | let r1 = checkSizes(); |
|
| 129 | 129 | if r1 <> 0 { |
|
| 130 | 130 | return r1; |
|
| 131 | 131 | } |
|
| 132 | 132 | let r2 = checkNil(); |
test/tests/opt.slice.npo.ril
+6 -3
| 1 | + | data mut $returnSome$nominal$arr align 1 { |
|
| 2 | + | w8 42; |
|
| 3 | + | w8 99; |
|
| 4 | + | } |
|
| 5 | + | ||
| 1 | 6 | fn w8 $checkSizes() { |
|
| 2 | 7 | @entry0 |
|
| 3 | 8 | br.eq w32 16 16 @assert.ok2 @assert.fail1; |
|
| 4 | 9 | @assert.fail1 |
|
| 5 | 10 | unreachable; |
| 161 | 166 | ret %0; |
|
| 162 | 167 | } |
|
| 163 | 168 | ||
| 164 | 169 | fn w64 $returnSome(w64 %0) { |
|
| 165 | 170 | @entry0 |
|
| 166 | - | reserve %1 2 1; |
|
| 167 | - | store w8 42 %1 0; |
|
| 168 | - | store w8 99 %1 1; |
|
| 171 | + | copy %1 $returnSome$nominal$arr; |
|
| 169 | 172 | reserve %2 16 8; |
|
| 170 | 173 | store w64 %1 %2 0; |
|
| 171 | 174 | store w32 2 %2 8; |
|
| 172 | 175 | store w32 2 %2 12; |
|
| 173 | 176 | blit %0 %2 16; |
test/tests/pointer.copy.edge.case.rad
+4 -4
| 17 | 17 | record Parser: Copy { |
|
| 18 | 18 | nodes: [Node; 1], |
|
| 19 | 19 | count: u32, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | fn makeNode(p: *mut Parser, kind: NodeKind) -> *mut Node { |
|
| 22 | + | unsafe fn makeNode(p: *unsafe mut Parser, kind: NodeKind) -> *unsafe mut Node { |
|
| 23 | 23 | let idx: u32 = p.count; |
|
| 24 | 24 | set p.nodes[idx] = Node { |
|
| 25 | 25 | span: Span { length: 0 }, |
|
| 26 | 26 | kind, |
|
| 27 | 27 | }; |
|
| 28 | 28 | set p.count = idx + 1; |
|
| 29 | 29 | return &mut p.nodes[idx]; |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | - | fn setLen(n: *mut Node, len: u32) { |
|
| 32 | + | unsafe fn setLen(n: *unsafe mut Node, len: u32) { |
|
| 33 | 33 | set n.span.length = len; |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | - | @default fn main() -> i32 { |
|
| 36 | + | @default unsafe fn main() -> i32 { |
|
| 37 | 37 | let mut parser: Parser = Parser { |
|
| 38 | 38 | nodes: [Node { |
|
| 39 | 39 | span: Span { length: 0 }, |
|
| 40 | 40 | kind: NodeKind::Placeholder, |
|
| 41 | 41 | }; 1], |
| 46 | 46 | set parser.nodes[0] = Node { |
|
| 47 | 47 | span: Span { length: 0 }, |
|
| 48 | 48 | kind: NodeKind::Placeholder, |
|
| 49 | 49 | }; |
|
| 50 | 50 | ||
| 51 | - | let node: *mut Node = makeNode(&mut parser, NodeKind::Bool(true)); |
|
| 51 | + | let node: *unsafe mut Node = makeNode(&mut parser, NodeKind::Bool(true)); |
|
| 52 | 52 | setLen(node, 4); |
|
| 53 | 53 | ||
| 54 | 54 | match parser.nodes[0].kind { |
|
| 55 | 55 | case NodeKind::Bool(value) => { |
|
| 56 | 56 | assert value; |
test/tests/pointer.slice.index.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | 3 | record Holder: Copy { |
|
| 4 | - | ptr: *[i32], |
|
| 4 | + | ptr: *unsafe [i32], |
|
| 5 | 5 | zero: u32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | // Test that pointer-to-slice indexing correctly dereferences the slice header. |
|
| 9 | - | @default fn main() -> i32 { |
|
| 9 | + | @default unsafe fn main() -> i32 { |
|
| 10 | 10 | let arr: [i32; 2] = [5, 7]; |
|
| 11 | 11 | let h = Holder { ptr: &arr[..], zero: 0 }; |
|
| 12 | 12 | ||
| 13 | 13 | return (h.ptr[1]) - 7; |
|
| 14 | 14 | } |
test/tests/pointer.stack.borrow.rad
added
+38 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Permanent values used by stored pointers. |
|
| 4 | + | static VALUES: [u32; 3] = [3, 5, 7]; |
|
| 5 | + | ||
| 6 | + | /// Add a value to a stack location during a call. |
|
| 7 | + | fn increment(value: &mut u32, amount: u32) { |
|
| 8 | + | set *value += amount; |
|
| 9 | + | } |
|
| 10 | + | ||
| 11 | + | /// Sum borrowed values during a call. |
|
| 12 | + | fn sum(values: &[u32]) -> u32 { |
|
| 13 | + | let mut result: u32 = 0; |
|
| 14 | + | for value in values { |
|
| 15 | + | set result += value; |
|
| 16 | + | } |
|
| 17 | + | return result; |
|
| 18 | + | } |
|
| 19 | + | ||
| 20 | + | /// Return a slice backed by permanent storage. |
|
| 21 | + | fn values() -> *[u32] { |
|
| 22 | + | return &VALUES[1..]; |
|
| 23 | + | } |
|
| 24 | + | ||
| 25 | + | /// Exercise stack borrows and stored pointers to permanent data. |
|
| 26 | + | @default fn main() -> i32 { |
|
| 27 | + | let mut value: u32 = 4; |
|
| 28 | + | increment(&mut value, 6); |
|
| 29 | + | if value <> 10 { return 1; } |
|
| 30 | + | ||
| 31 | + | let local: [u32; 3] = [value, 2, 3]; |
|
| 32 | + | if sum(&local[..]) <> 15 { return 2; } |
|
| 33 | + | if sum(values()) <> 12 { return 3; } |
|
| 34 | + | ||
| 35 | + | let pointer: *u32 = &VALUES[0]; |
|
| 36 | + | if *pointer <> 3 { return 4; } |
|
| 37 | + | return 0; |
|
| 38 | + | } |
test/tests/pointer.stack.unsafe.rad
added
+18 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Exercise raw pointers while their stack storage is alive. |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | + | let mut value: u32 = 4; |
|
| 6 | + | let pointer: *unsafe mut u32 = &mut value; |
|
| 7 | + | set *pointer = 9; |
|
| 8 | + | if value <> 9 { return 1; } |
|
| 9 | + | ||
| 10 | + | let mut values: [u32; 2] = [value, 2]; |
|
| 11 | + | let slice: *unsafe mut [u32] = &mut values[..]; |
|
| 12 | + | set slice[1] = 7; |
|
| 13 | + | if values[1] <> 7 { return 2; } |
|
| 14 | + | ||
| 15 | + | let cast = &value as *unsafe u32; |
|
| 16 | + | if *cast <> 9 { return 3; } |
|
| 17 | + | return 0; |
|
| 18 | + | } |
test/tests/prog.bignum.rad
+9 -9
| 6 | 6 | ||
| 7 | 7 | /// Number of limbs per big number (128 bits = 4 x 32-bit words). |
|
| 8 | 8 | constant LIMBS: u32 = 4; |
|
| 9 | 9 | ||
| 10 | 10 | /// Set a big number to a u32 value. |
|
| 11 | - | fn bnFromU32(dst: *mut [u32], val: u32) { |
|
| 11 | + | fn bnFromU32(dst: &mut [u32], val: u32) { |
|
| 12 | 12 | set dst[0] = val; |
|
| 13 | 13 | let mut i: u32 = 1; |
|
| 14 | 14 | while i < LIMBS { |
|
| 15 | 15 | set dst[i] = 0; |
|
| 16 | 16 | set i += 1; |
|
| 17 | 17 | } |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | /// Set a big number to zero. |
|
| 21 | - | fn bnZero(dst: *mut [u32]) { |
|
| 21 | + | fn bnZero(dst: &mut [u32]) { |
|
| 22 | 22 | let mut i: u32 = 0; |
|
| 23 | 23 | while i < LIMBS { |
|
| 24 | 24 | set dst[i] = 0; |
|
| 25 | 25 | set i += 1; |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// Copy src to dst. |
|
| 30 | - | fn bnCopy(dst: *mut [u32], src: *[u32]) { |
|
| 30 | + | fn bnCopy(dst: &mut [u32], src: &[u32]) { |
|
| 31 | 31 | let mut i: u32 = 0; |
|
| 32 | 32 | while i < LIMBS { |
|
| 33 | 33 | set dst[i] = src[i]; |
|
| 34 | 34 | set i += 1; |
|
| 35 | 35 | } |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Compare two big numbers. Returns 0 if equal, 1 if a > b, -1 if a < b. |
|
| 39 | - | fn bnCmp(a: *[u32], b: *[u32]) -> i32 { |
|
| 39 | + | fn bnCmp(a: &[u32], b: &[u32]) -> i32 { |
|
| 40 | 40 | let mut i: i32 = LIMBS as i32 - 1; |
|
| 41 | 41 | while i >= 0 { |
|
| 42 | 42 | if a[i as u32] > b[i as u32] { |
|
| 43 | 43 | return 1; |
|
| 44 | 44 | } |
| 52 | 52 | } |
|
| 53 | 53 | return 0; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Add two big numbers: dst = a + b. Returns carry (0 or 1). |
|
| 57 | - | fn bnAdd(dst: *mut [u32], a: *[u32], b: *[u32]) -> u32 { |
|
| 57 | + | fn bnAdd(dst: &mut [u32], a: &[u32], b: &[u32]) -> u32 { |
|
| 58 | 58 | let mut carry: u32 = 0; |
|
| 59 | 59 | let mut i: u32 = 0; |
|
| 60 | 60 | while i < LIMBS { |
|
| 61 | 61 | let sumLo: u32 = a[i] + b[i]; |
|
| 62 | 62 | let mut carry1: u32 = 0; |
| 74 | 74 | } |
|
| 75 | 75 | return carry; |
|
| 76 | 76 | } |
|
| 77 | 77 | ||
| 78 | 78 | /// Subtract two big numbers: dst = a - b. Returns borrow (0 or 1). |
|
| 79 | - | fn bnSub(dst: *mut [u32], a: *[u32], b: *[u32]) -> u32 { |
|
| 79 | + | fn bnSub(dst: &mut [u32], a: &[u32], b: &[u32]) -> u32 { |
|
| 80 | 80 | let mut borrow: u32 = 0; |
|
| 81 | 81 | let mut i: u32 = 0; |
|
| 82 | 82 | while i < LIMBS { |
|
| 83 | 83 | let diff: u32 = a[i] - b[i]; |
|
| 84 | 84 | let mut borrow1: u32 = 0; |
| 96 | 96 | } |
|
| 97 | 97 | return borrow; |
|
| 98 | 98 | } |
|
| 99 | 99 | ||
| 100 | 100 | /// Multiply two LIMBS-word numbers, producing a 2*LIMBS-word result in wide. |
|
| 101 | - | fn bnMul(wide: *mut [u32], a: *[u32], b: *[u32]) { |
|
| 101 | + | fn bnMul(wide: &mut [u32], a: &[u32], b: &[u32]) { |
|
| 102 | 102 | let mut i: u32 = 0; |
|
| 103 | 103 | while i < LIMBS * 2 { |
|
| 104 | 104 | set wide[i] = 0; |
|
| 105 | 105 | set i += 1; |
|
| 106 | 106 | } |
| 152 | 152 | set i += 1; |
|
| 153 | 153 | } |
|
| 154 | 154 | } |
|
| 155 | 155 | ||
| 156 | 156 | /// Left shift a big number by 1 bit. |
|
| 157 | - | fn bnShl1(dst: *mut [u32], src: *[u32]) { |
|
| 157 | + | fn bnShl1(dst: &mut [u32], src: &[u32]) { |
|
| 158 | 158 | let mut carry: u32 = 0; |
|
| 159 | 159 | let mut i: u32 = 0; |
|
| 160 | 160 | while i < LIMBS { |
|
| 161 | 161 | let newCarry: u32 = src[i] >> 31; |
|
| 162 | 162 | set dst[i] = (src[i] << 1) | carry; |
| 164 | 164 | set i += 1; |
|
| 165 | 165 | } |
|
| 166 | 166 | } |
|
| 167 | 167 | ||
| 168 | 168 | /// Right shift a big number by 1 bit. |
|
| 169 | - | fn bnShr1(dst: *mut [u32], src: *[u32]) { |
|
| 169 | + | fn bnShr1(dst: &mut [u32], src: &[u32]) { |
|
| 170 | 170 | let mut carry: u32 = 0; |
|
| 171 | 171 | let mut i: u32 = LIMBS; |
|
| 172 | 172 | while i > 0 { |
|
| 173 | 173 | set i -= 1; |
|
| 174 | 174 | let newCarry: u32 = src[i] & 1; |
test/tests/prog.binsearch.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Binary search. |
|
| 3 | 3 | //! Search a pre-sorted array for present and absent values. |
|
| 4 | 4 | ||
| 5 | 5 | /// Binary search for `target` in `data`. Returns the index if found, or nil. |
|
| 6 | - | fn binarySearch(data: *[i32], target: i32) -> ?u32 { |
|
| 6 | + | fn binarySearch(data: &[i32], target: i32) -> ?u32 { |
|
| 7 | 7 | let mut lo: i32 = 0; |
|
| 8 | 8 | let mut hi: i32 = data.len as i32 - 1; |
|
| 9 | 9 | ||
| 10 | 10 | while lo <= hi { |
|
| 11 | 11 | let mid: i32 = lo + (hi - lo) / 2; |
| 21 | 21 | } |
|
| 22 | 22 | return nil; |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | /// Test searching for elements that exist. |
|
| 26 | - | fn testPresent(data: *[i32]) -> i32 { |
|
| 26 | + | fn testPresent(data: &[i32]) -> i32 { |
|
| 27 | 27 | // First element. |
|
| 28 | 28 | let idx0 = binarySearch(data, 3) else { |
|
| 29 | 29 | return 1; |
|
| 30 | 30 | }; |
|
| 31 | 31 | assert idx0 == 0; |
| 56 | 56 | assert idx16 == 16; |
|
| 57 | 57 | return 0; |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | /// Test searching for elements that do not exist. |
|
| 61 | - | fn testAbsent(data: *[i32]) -> i32 { |
|
| 61 | + | fn testAbsent(data: &[i32]) -> i32 { |
|
| 62 | 62 | // Below range. |
|
| 63 | 63 | assert binarySearch(data, 1) == nil; |
|
| 64 | 64 | // Above range. |
|
| 65 | 65 | assert binarySearch(data, 200) == nil; |
|
| 66 | 66 | // Between existing elements. |
| 71 | 71 | assert binarySearch(data, 99) == nil; |
|
| 72 | 72 | return 0; |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | /// Test searching for every element in the array. |
|
| 76 | - | fn testAllPresent(data: *[i32]) -> i32 { |
|
| 76 | + | fn testAllPresent(data: &[i32]) -> i32 { |
|
| 77 | 77 | for value, index in data { |
|
| 78 | 78 | let found = binarySearch(data, value) else { |
|
| 79 | 79 | return index as i32 + 1; |
|
| 80 | 80 | }; |
|
| 81 | 81 | if found <> index { |
test/tests/prog.bubblesort.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Bubble sort. |
|
| 3 | 3 | //! Sort an array of integers and verify the result. |
|
| 4 | 4 | ||
| 5 | 5 | /// Bubble sort the array in ascending order. |
|
| 6 | - | fn bubbleSort(data: *mut [i32]) { |
|
| 6 | + | fn bubbleSort(data: &mut [i32]) { |
|
| 7 | 7 | let mut n: u32 = data.len; |
|
| 8 | 8 | while n > 1 { |
|
| 9 | 9 | let mut swapped: bool = false; |
|
| 10 | 10 | let mut i: u32 = 0; |
|
| 11 | 11 | while i < n - 1 { |
| 24 | 24 | set n -= 1; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | /// Verify the array is sorted in ascending order. |
|
| 29 | - | fn isSorted(data: *[i32]) -> bool { |
|
| 29 | + | fn isSorted(data: &[i32]) -> bool { |
|
| 30 | 30 | let mut prev: ?i32 = nil; |
|
| 31 | 31 | for val in data { |
|
| 32 | 32 | if let p = prev { |
|
| 33 | 33 | if p > val { |
|
| 34 | 34 | return false; |
| 38 | 38 | } |
|
| 39 | 39 | return true; |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// Compute the sum of all elements. |
|
| 43 | - | fn sum(data: *[i32]) -> i32 { |
|
| 43 | + | fn sum(data: &[i32]) -> i32 { |
|
| 44 | 44 | let mut total: i32 = 0; |
|
| 45 | 45 | for val in data { |
|
| 46 | 46 | set total += val; |
|
| 47 | 47 | } |
|
| 48 | 48 | return total; |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Verify specific positions in the sorted output. |
|
| 52 | - | fn verifyPositions(data: *[i32]) -> i32 { |
|
| 52 | + | fn verifyPositions(data: &[i32]) -> i32 { |
|
| 53 | 53 | // Sorted: 3 5 7 12 17 19 28 31 42 50 55 66 71 80 88 93 |
|
| 54 | 54 | assert data[0] == 3; |
|
| 55 | 55 | assert data[1] == 5; |
|
| 56 | 56 | assert data[2] == 7; |
|
| 57 | 57 | assert data[7] == 31; |
test/tests/prog.cordic.rad
+8 -8
| 27 | 27 | sin: i32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// CORDIC rotation mode: compute cos(angle) and sin(angle). |
|
| 31 | 31 | /// Input angle in Q16.16 radians, must be in [-pi/2, pi/2]. |
|
| 32 | - | fn cordicRotate(angle: i32, atanTable: *[i32]) -> CosSin { |
|
| 32 | + | fn cordicRotate(angle: i32, atanTable: &[i32]) -> CosSin { |
|
| 33 | 33 | let mut x: i32 = CORDIC_GAIN; |
|
| 34 | 34 | let mut y: i32 = 0; |
|
| 35 | 35 | let mut z: i32 = angle; |
|
| 36 | 36 | ||
| 37 | 37 | let mut i: u32 = 0; |
| 53 | 53 | ||
| 54 | 54 | return CosSin { cos: x, sin: y }; |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | 57 | /// Compute cos and sin for any angle by reducing to [-pi/2, pi/2]. |
|
| 58 | - | fn cosSin(angle: i32, atanTable: *[i32]) -> CosSin { |
|
| 58 | + | fn cosSin(angle: i32, atanTable: &[i32]) -> CosSin { |
|
| 59 | 59 | let mut a: i32 = angle; |
|
| 60 | 60 | ||
| 61 | 61 | // Reduce to [-pi, pi]. |
|
| 62 | 62 | while a > PI { |
|
| 63 | 63 | set a -= 2 * PI; |
| 120 | 120 | } |
|
| 121 | 121 | return result as i32; |
|
| 122 | 122 | } |
|
| 123 | 123 | ||
| 124 | 124 | /// Test cos(0) = 1, sin(0) = 0. |
|
| 125 | - | fn testZero(atanTable: *[i32]) -> i32 { |
|
| 125 | + | fn testZero(atanTable: &[i32]) -> i32 { |
|
| 126 | 126 | let r: CosSin = cosSin(0, atanTable); |
|
| 127 | 127 | // cos(0) should be close to 65536 (1.0 in Q16.16). |
|
| 128 | 128 | let cosErr: i32 = abs(r.cos - SCALE); |
|
| 129 | 129 | let sinErr: i32 = abs(r.sin); |
|
| 130 | 130 |
| 133 | 133 | assert sinErr <= 655; |
|
| 134 | 134 | return 0; |
|
| 135 | 135 | } |
|
| 136 | 136 | ||
| 137 | 137 | /// Test cos(pi/2) = 0, sin(pi/2) = 1. |
|
| 138 | - | fn testHalfPi(atanTable: *[i32]) -> i32 { |
|
| 138 | + | fn testHalfPi(atanTable: &[i32]) -> i32 { |
|
| 139 | 139 | let r: CosSin = cosSin(HALF_PI, atanTable); |
|
| 140 | 140 | let cosErr: i32 = abs(r.cos); |
|
| 141 | 141 | let sinErr: i32 = abs(r.sin - SCALE); |
|
| 142 | 142 | ||
| 143 | 143 | assert cosErr <= 655; |
|
| 144 | 144 | assert sinErr <= 655; |
|
| 145 | 145 | return 0; |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | 148 | /// Test cos(pi) = -1, sin(pi) = 0. |
|
| 149 | - | fn testPi(atanTable: *[i32]) -> i32 { |
|
| 149 | + | fn testPi(atanTable: &[i32]) -> i32 { |
|
| 150 | 150 | let r: CosSin = cosSin(PI, atanTable); |
|
| 151 | 151 | let cosErr: i32 = abs(r.cos + SCALE); |
|
| 152 | 152 | let sinErr: i32 = abs(r.sin); |
|
| 153 | 153 | ||
| 154 | 154 | assert cosErr <= 655; |
|
| 155 | 155 | assert sinErr <= 655; |
|
| 156 | 156 | return 0; |
|
| 157 | 157 | } |
|
| 158 | 158 | ||
| 159 | 159 | /// Test Pythagorean identity: sin^2 + cos^2 = 1 for several angles. |
|
| 160 | - | fn testPythagorean(atanTable: *[i32]) -> i32 { |
|
| 160 | + | fn testPythagorean(atanTable: &[i32]) -> i32 { |
|
| 161 | 161 | // Test at 16 evenly spaced angles from 0 to 2*pi. |
|
| 162 | 162 | let step: i32 = PI / 8; |
|
| 163 | 163 | let mut i: i32 = 0 - PI; |
|
| 164 | 164 | ||
| 165 | 165 | while i <= PI { |
| 176 | 176 | } |
|
| 177 | 177 | return 0; |
|
| 178 | 178 | } |
|
| 179 | 179 | ||
| 180 | 180 | /// Test symmetry: sin(-x) = -sin(x), cos(-x) = cos(x). |
|
| 181 | - | fn testSymmetry(atanTable: *[i32]) -> i32 { |
|
| 181 | + | fn testSymmetry(atanTable: &[i32]) -> i32 { |
|
| 182 | 182 | let angles: [i32; 5] = [16384, 32768, 51472, 65536, 81920]; |
|
| 183 | 183 | ||
| 184 | 184 | let mut i: u32 = 0; |
|
| 185 | 185 | while i < 5 { |
|
| 186 | 186 | let a: i32 = angles[i]; |
| 196 | 196 | } |
|
| 197 | 197 | return 0; |
|
| 198 | 198 | } |
|
| 199 | 199 | ||
| 200 | 200 | /// Test specific known value: cos(pi/3) = 0.5, sin(pi/3) = 0.866. |
|
| 201 | - | fn testPiThird(atanTable: *[i32]) -> i32 { |
|
| 201 | + | fn testPiThird(atanTable: &[i32]) -> i32 { |
|
| 202 | 202 | // pi/3 in Q16.16 = 68629. |
|
| 203 | 203 | let piThird: i32 = 68629; |
|
| 204 | 204 | let r: CosSin = cosSin(piThird, atanTable); |
|
| 205 | 205 | ||
| 206 | 206 | // cos(pi/3) = 0.5 = 32768 in Q16.16. |
test/tests/prog.crc32.rad
+5 -5
| 2 | 2 | //! CRC-32. |
|
| 3 | 3 | //! Compute CRC-32 of a byte buffer using a 256-entry lookup table. |
|
| 4 | 4 | //! The table is built at startup. Verify against known checksums. |
|
| 5 | 5 | ||
| 6 | 6 | /// Build the CRC-32 lookup table using the standard polynomial 0xEDB88320. |
|
| 7 | - | fn buildTable(table: *mut [u32]) { |
|
| 7 | + | fn buildTable(table: &mut [u32]) { |
|
| 8 | 8 | let mut i: u32 = 0; |
|
| 9 | 9 | while i < 256 { |
|
| 10 | 10 | let mut crc: u32 = i; |
|
| 11 | 11 | let mut j: u32 = 0; |
|
| 12 | 12 | while j < 8 { |
| 21 | 21 | set i += 1; |
|
| 22 | 22 | } |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | /// Compute CRC-32 of a byte slice. |
|
| 26 | - | fn crc32(table: *[u32], data: *[u8]) -> u32 { |
|
| 26 | + | fn crc32(table: &[u32], data: &[u8]) -> u32 { |
|
| 27 | 27 | let mut crc: u32 = 0xFFFFFFFF; |
|
| 28 | 28 | let mut i: u32 = 0; |
|
| 29 | 29 | while i < data.len { |
|
| 30 | 30 | let byte: u8 = data[i]; |
|
| 31 | 31 | let index: u32 = (crc ^ byte as u32) & 0xFF; |
| 34 | 34 | } |
|
| 35 | 35 | return crc ^ 0xFFFFFFFF; |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Test the lookup table has been built correctly. |
|
| 39 | - | fn testTable(table: *[u32]) -> i32 { |
|
| 39 | + | fn testTable(table: &[u32]) -> i32 { |
|
| 40 | 40 | // TABLE[0] should be 0 (zero input, all shifts produce zero). |
|
| 41 | 41 | assert table[0] == 0; |
|
| 42 | 42 | // Known value: TABLE[1] = 0x77073096 |
|
| 43 | 43 | assert table[1] == 0x77073096; |
|
| 44 | 44 | // TABLE[255] is a known value: 0x2D02EF8D |
|
| 45 | 45 | assert table[255] == 0x2D02EF8D; |
|
| 46 | 46 | return 0; |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Test CRC-32 of known strings. |
|
| 50 | - | fn testKnownCRC(table: *[u32]) -> i32 { |
|
| 50 | + | fn testKnownCRC(table: &[u32]) -> i32 { |
|
| 51 | 51 | // CRC-32 of empty data should be 0x00000000. |
|
| 52 | 52 | assert crc32(table, &[]) == 0x00000000; |
|
| 53 | 53 | ||
| 54 | 54 | // CRC-32 of "123456789" = 0xCBF43926 (the standard check value). |
|
| 55 | 55 | let check: [u8; 9] = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]; |
| 60 | 60 | assert crc32(table, &single[..]) == 0xD3D99E8B; |
|
| 61 | 61 | return 0; |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | 64 | /// Test CRC-32 of incrementally built data. |
|
| 65 | - | fn testIncremental(table: *[u32]) -> i32 { |
|
| 65 | + | fn testIncremental(table: &[u32]) -> i32 { |
|
| 66 | 66 | // Build a 32-byte buffer with values 0..31. |
|
| 67 | 67 | let mut buf: [u8; 32] = [0; 32]; |
|
| 68 | 68 | let mut i: u32 = 0; |
|
| 69 | 69 | while i < 32 { |
|
| 70 | 70 | set buf[i] = i as u8; |
test/tests/prog.dijkstra.rad
+23 -23
| 10 | 10 | dist: u32, |
|
| 11 | 11 | node: u32, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | record Graph: Copy { |
|
| 15 | - | adj: *mut [[u32; 16]], |
|
| 16 | - | dist: *mut [u32], |
|
| 17 | - | prev: *mut [i32], |
|
| 18 | - | visited: *mut [bool], |
|
| 15 | + | adj: *unsafe mut [[u32; 16]], |
|
| 16 | + | dist: *unsafe mut [u32], |
|
| 17 | + | prev: *unsafe mut [i32], |
|
| 18 | + | visited: *unsafe mut [bool], |
|
| 19 | 19 | numNodes: u32, |
|
| 20 | - | heap: *mut [HeapEntry], |
|
| 20 | + | heap: *unsafe mut [HeapEntry], |
|
| 21 | 21 | heapSize: u32, |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | fn heapSwap(g: *mut Graph, i: u32, j: u32) { |
|
| 24 | + | unsafe fn heapSwap(g: &mut Graph, i: u32, j: u32) { |
|
| 25 | 25 | let tmp: HeapEntry = g.heap[i]; |
|
| 26 | 26 | set g.heap[i] = g.heap[j]; |
|
| 27 | 27 | set g.heap[j] = tmp; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | - | fn siftUp(g: *mut Graph, pos: u32) { |
|
| 30 | + | unsafe fn siftUp(g: &mut Graph, pos: u32) { |
|
| 31 | 31 | let mut i: u32 = pos; |
|
| 32 | 32 | while i > 0 { |
|
| 33 | 33 | let parent: u32 = (i - 1) / 2; |
|
| 34 | 34 | if g.heap[i].dist < g.heap[parent].dist { |
|
| 35 | 35 | heapSwap(g, i, parent); |
| 38 | 38 | return; |
|
| 39 | 39 | } |
|
| 40 | 40 | } |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | - | fn siftDown(g: *mut Graph, pos: u32) { |
|
| 43 | + | unsafe fn siftDown(g: &mut Graph, pos: u32) { |
|
| 44 | 44 | let mut i: u32 = pos; |
|
| 45 | 45 | while true { |
|
| 46 | 46 | let left: u32 = 2 * i + 1; |
|
| 47 | 47 | let right: u32 = 2 * i + 2; |
|
| 48 | 48 | let mut smallest: u32 = i; |
| 59 | 59 | heapSwap(g, i, smallest); |
|
| 60 | 60 | set i = smallest; |
|
| 61 | 61 | } |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn heapPush(g: *mut Graph, dist: u32, node: u32) { |
|
| 64 | + | unsafe fn heapPush(g: &mut Graph, dist: u32, node: u32) { |
|
| 65 | 65 | set g.heap[g.heapSize] = HeapEntry { dist, node }; |
|
| 66 | 66 | set g.heapSize += 1; |
|
| 67 | 67 | siftUp(g, g.heapSize - 1); |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | /// Pop from the heap. Returns nil if the heap is empty. |
|
| 71 | - | fn heapPop(g: *mut Graph) -> ?HeapEntry { |
|
| 71 | + | unsafe fn heapPop(g: &mut Graph) -> ?HeapEntry { |
|
| 72 | 72 | if g.heapSize == 0 { |
|
| 73 | 73 | return nil; |
|
| 74 | 74 | } |
|
| 75 | 75 | let result: HeapEntry = g.heap[0]; |
|
| 76 | 76 | set g.heapSize -= 1; |
| 79 | 79 | siftDown(g, 0); |
|
| 80 | 80 | } |
|
| 81 | 81 | return result; |
|
| 82 | 82 | } |
|
| 83 | 83 | ||
| 84 | - | fn addEdge(g: *mut Graph, from: u32, to: u32, weight: u32) { |
|
| 84 | + | unsafe fn addEdge(g: &mut Graph, from: u32, to: u32, weight: u32) { |
|
| 85 | 85 | set g.adj[from][to] = weight; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn addBidiEdge(g: *mut Graph, from: u32, to: u32, weight: u32) { |
|
| 88 | + | unsafe fn addBidiEdge(g: &mut Graph, from: u32, to: u32, weight: u32) { |
|
| 89 | 89 | set g.adj[from][to] = weight; |
|
| 90 | 90 | set g.adj[to][from] = weight; |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | - | fn resetGraph(g: *mut Graph, n: u32) { |
|
| 93 | + | unsafe fn resetGraph(g: &mut Graph, n: u32) { |
|
| 94 | 94 | set g.numNodes = n; |
|
| 95 | 95 | let mut i: u32 = 0; |
|
| 96 | 96 | while i < MAX_NODES { |
|
| 97 | 97 | let mut j: u32 = 0; |
|
| 98 | 98 | while j < MAX_NODES { |
| 106 | 106 | } |
|
| 107 | 107 | set g.heapSize = 0; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | 110 | /// Get the predecessor as an optional; -1 means no predecessor. |
|
| 111 | - | fn getPrev(g: *Graph, node: u32) -> ?u32 { |
|
| 111 | + | unsafe fn getPrev(g: &Graph, node: u32) -> ?u32 { |
|
| 112 | 112 | let p = g.prev[node]; |
|
| 113 | 113 | if p < 0 { |
|
| 114 | 114 | return nil; |
|
| 115 | 115 | } |
|
| 116 | 116 | return p as u32; |
|
| 117 | 117 | } |
|
| 118 | 118 | ||
| 119 | - | fn dijkstra(g: *mut Graph, source: u32) { |
|
| 119 | + | unsafe fn dijkstra(g: &mut Graph, source: u32) { |
|
| 120 | 120 | set g.dist[source] = 0; |
|
| 121 | 121 | heapPush(g, 0, source); |
|
| 122 | 122 | ||
| 123 | 123 | while let entry = heapPop(g) { |
|
| 124 | 124 | let u: u32 = entry.node; |
| 142 | 142 | } |
|
| 143 | 143 | } |
|
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | 146 | /// Reconstruct the shortest path from source to target. |
|
| 147 | - | fn reconstructPath(g: *Graph, target: u32, path: *mut [u32]) -> u32 { |
|
| 147 | + | unsafe fn reconstructPath(g: &Graph, target: u32, path: &mut [u32]) -> u32 { |
|
| 148 | 148 | let mut len: u32 = 0; |
|
| 149 | 149 | ||
| 150 | 150 | set path[len] = target; |
|
| 151 | 151 | set len += 1; |
|
| 152 | 152 |
| 168 | 168 | set b -= 1; |
|
| 169 | 169 | } |
|
| 170 | 170 | return len; |
|
| 171 | 171 | } |
|
| 172 | 172 | ||
| 173 | - | fn testLinear(g: *mut Graph) -> i32 { |
|
| 173 | + | unsafe fn testLinear(g: &mut Graph) -> i32 { |
|
| 174 | 174 | resetGraph(g, 5); |
|
| 175 | 175 | addEdge(g, 0, 1, 10); |
|
| 176 | 176 | addEdge(g, 1, 2, 10); |
|
| 177 | 177 | addEdge(g, 2, 3, 10); |
|
| 178 | 178 | addEdge(g, 3, 4, 10); |
| 193 | 193 | assert path[4] == 4; |
|
| 194 | 194 | ||
| 195 | 195 | return 0; |
|
| 196 | 196 | } |
|
| 197 | 197 | ||
| 198 | - | fn testShortcut(g: *mut Graph) -> i32 { |
|
| 198 | + | unsafe fn testShortcut(g: &mut Graph) -> i32 { |
|
| 199 | 199 | resetGraph(g, 4); |
|
| 200 | 200 | addEdge(g, 0, 1, 10); |
|
| 201 | 201 | addEdge(g, 1, 2, 10); |
|
| 202 | 202 | addEdge(g, 0, 3, 5); |
|
| 203 | 203 | addEdge(g, 3, 2, 3); |
| 212 | 212 | assert prev2 == 3; |
|
| 213 | 213 | ||
| 214 | 214 | return 0; |
|
| 215 | 215 | } |
|
| 216 | 216 | ||
| 217 | - | fn testBidirectional(g: *mut Graph) -> i32 { |
|
| 217 | + | unsafe fn testBidirectional(g: &mut Graph) -> i32 { |
|
| 218 | 218 | resetGraph(g, 5); |
|
| 219 | 219 | addBidiEdge(g, 0, 1, 1); |
|
| 220 | 220 | addBidiEdge(g, 1, 2, 2); |
|
| 221 | 221 | addBidiEdge(g, 2, 3, 3); |
|
| 222 | 222 | addBidiEdge(g, 3, 0, 4); |
| 234 | 234 | } |
|
| 235 | 235 | ||
| 236 | 236 | return 0; |
|
| 237 | 237 | } |
|
| 238 | 238 | ||
| 239 | - | fn testComplete(g: *mut Graph) -> i32 { |
|
| 239 | + | unsafe fn testComplete(g: &mut Graph) -> i32 { |
|
| 240 | 240 | resetGraph(g, 8); |
|
| 241 | 241 | ||
| 242 | 242 | let mut i: u32 = 0; |
|
| 243 | 243 | while i < 8 { |
|
| 244 | 244 | let mut j: u32 = 0; |
| 267 | 267 | } |
|
| 268 | 268 | ||
| 269 | 269 | return 0; |
|
| 270 | 270 | } |
|
| 271 | 271 | ||
| 272 | - | fn testDisconnected(g: *mut Graph) -> i32 { |
|
| 272 | + | unsafe fn testDisconnected(g: &mut Graph) -> i32 { |
|
| 273 | 273 | resetGraph(g, 6); |
|
| 274 | 274 | addEdge(g, 0, 1, 5); |
|
| 275 | 275 | addEdge(g, 1, 2, 3); |
|
| 276 | 276 | addEdge(g, 3, 4, 2); |
|
| 277 | 277 | addEdge(g, 4, 5, 1); |
| 290 | 290 | assert getPrev(g, 3) == nil; |
|
| 291 | 291 | ||
| 292 | 292 | return 0; |
|
| 293 | 293 | } |
|
| 294 | 294 | ||
| 295 | - | fn testDiamond(g: *mut Graph) -> i32 { |
|
| 295 | + | unsafe fn testDiamond(g: &mut Graph) -> i32 { |
|
| 296 | 296 | resetGraph(g, 5); |
|
| 297 | 297 | addEdge(g, 0, 1, 5); |
|
| 298 | 298 | addEdge(g, 0, 2, 5); |
|
| 299 | 299 | addEdge(g, 1, 3, 5); |
|
| 300 | 300 | addEdge(g, 2, 3, 5); |
| 311 | 311 | assert prev3 == 1 or prev3 == 2; |
|
| 312 | 312 | ||
| 313 | 313 | return 0; |
|
| 314 | 314 | } |
|
| 315 | 315 | ||
| 316 | - | @default fn main() -> i32 { |
|
| 316 | + | @default unsafe fn main() -> i32 { |
|
| 317 | 317 | let mut adj: [[u32; 16]; 16] = [[0xFFFFFFFF; 16]; 16]; |
|
| 318 | 318 | let mut dist: [u32; 16] = [0xFFFFFFFF; 16]; |
|
| 319 | 319 | let mut prev: [i32; 16] = [-1; 16]; |
|
| 320 | 320 | let mut visited: [bool; 16] = [false; 16]; |
|
| 321 | 321 | let mut heap: [HeapEntry; 256] = [HeapEntry { dist: 0, node: 0 }; 256]; |
test/tests/prog.eval.rad
+17 -17
| 31 | 31 | union EvalError: Copy { |
|
| 32 | 32 | DivByZero |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Allocate a new node, returning its index. |
|
| 36 | - | fn newNode(pool: *mut Pool, expr: Expr) -> u32 { |
|
| 36 | + | fn newNode(pool: &mut Pool, expr: Expr) -> u32 { |
|
| 37 | 37 | let idx = pool.count; |
|
| 38 | 38 | set pool.nodes[idx] = expr; |
|
| 39 | 39 | set pool.count += 1; |
|
| 40 | 40 | return idx; |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | 43 | /// Convenience constructors. |
|
| 44 | - | fn num(pool: *mut Pool, n: i32) -> u32 { |
|
| 44 | + | fn num(pool: &mut Pool, n: i32) -> u32 { |
|
| 45 | 45 | return newNode(pool, Expr::Num(n)); |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | - | fn add(pool: *mut Pool, left: u32, right: u32) -> u32 { |
|
| 48 | + | fn add(pool: &mut Pool, left: u32, right: u32) -> u32 { |
|
| 49 | 49 | return newNode(pool, Expr::Add(BinOp { left, right })); |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | - | fn sub(pool: *mut Pool, left: u32, right: u32) -> u32 { |
|
| 52 | + | fn sub(pool: &mut Pool, left: u32, right: u32) -> u32 { |
|
| 53 | 53 | return newNode(pool, Expr::Sub(BinOp { left, right })); |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | - | fn mul(pool: *mut Pool, left: u32, right: u32) -> u32 { |
|
| 56 | + | fn mul(pool: &mut Pool, left: u32, right: u32) -> u32 { |
|
| 57 | 57 | return newNode(pool, Expr::Mul(BinOp { left, right })); |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | - | fn div(pool: *mut Pool, left: u32, right: u32) -> u32 { |
|
| 60 | + | fn div(pool: &mut Pool, left: u32, right: u32) -> u32 { |
|
| 61 | 61 | return newNode(pool, Expr::Div(BinOp { left, right })); |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn neg(pool: *mut Pool, child: u32) -> u32 { |
|
| 64 | + | fn neg(pool: &mut Pool, child: u32) -> u32 { |
|
| 65 | 65 | return newNode(pool, Expr::Neg(child)); |
|
| 66 | 66 | } |
|
| 67 | 67 | ||
| 68 | 68 | /// Recursively evaluate the expression tree rooted at nodes[idx]. |
|
| 69 | - | fn eval(nodes: *[Expr], idx: u32) -> i32 throws (EvalError) { |
|
| 69 | + | fn eval(nodes: &[Expr], idx: u32) -> i32 throws (EvalError) { |
|
| 70 | 70 | let node = nodes[idx]; |
|
| 71 | 71 | match node { |
|
| 72 | 72 | case Expr::Num(n) => { |
|
| 73 | 73 | return n; |
|
| 74 | 74 | } |
| 94 | 94 | } |
|
| 95 | 95 | } |
|
| 96 | 96 | } |
|
| 97 | 97 | ||
| 98 | 98 | /// Count the total number of nodes in the tree rooted at idx. |
|
| 99 | - | fn countNodes(nodes: *[Expr], idx: u32) -> u32 { |
|
| 99 | + | fn countNodes(nodes: &[Expr], idx: u32) -> u32 { |
|
| 100 | 100 | let node = nodes[idx]; |
|
| 101 | 101 | match node { |
|
| 102 | 102 | case Expr::Num(_) => { |
|
| 103 | 103 | return 1; |
|
| 104 | 104 | } |
| 119 | 119 | } |
|
| 120 | 120 | } |
|
| 121 | 121 | } |
|
| 122 | 122 | ||
| 123 | 123 | /// Reset the node pool. |
|
| 124 | - | fn reset(pool: *mut Pool) { |
|
| 124 | + | fn reset(pool: &mut Pool) { |
|
| 125 | 125 | set pool.count = 0; |
|
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | /// Test 1: Simple addition: 3 + 4 = 7 |
|
| 129 | - | fn testSimpleAdd(pool: *mut Pool) -> i32 { |
|
| 129 | + | fn testSimpleAdd(pool: &mut Pool) -> i32 { |
|
| 130 | 130 | reset(pool); |
|
| 131 | 131 | let root = add(pool, num(pool, 3), num(pool, 4)); |
|
| 132 | 132 | assert try! eval(&pool.nodes[..], root) == 7; |
|
| 133 | 133 | return 0; |
|
| 134 | 134 | } |
|
| 135 | 135 | ||
| 136 | 136 | /// Test 2: Nested expression: (2 + 3) * (4 - 1) = 5 * 3 = 15 |
|
| 137 | - | fn testNested(pool: *mut Pool) -> i32 { |
|
| 137 | + | fn testNested(pool: &mut Pool) -> i32 { |
|
| 138 | 138 | reset(pool); |
|
| 139 | 139 | let left = add(pool, num(pool, 2), num(pool, 3)); |
|
| 140 | 140 | let right = sub(pool, num(pool, 4), num(pool, 1)); |
|
| 141 | 141 | let root = mul(pool, left, right); |
|
| 142 | 142 | assert try! eval(&pool.nodes[..], root) == 15; |
|
| 143 | 143 | assert countNodes(&pool.nodes[..], root) == 7; |
|
| 144 | 144 | return 0; |
|
| 145 | 145 | } |
|
| 146 | 146 | ||
| 147 | 147 | /// Test 3: Complex expression: ((10 + 5) * 2 - 6) / 4 = (30 - 6) / 4 = 24 / 4 = 6 |
|
| 148 | - | fn testComplex(pool: *mut Pool) -> i32 { |
|
| 148 | + | fn testComplex(pool: &mut Pool) -> i32 { |
|
| 149 | 149 | reset(pool); |
|
| 150 | 150 | let a = add(pool, num(pool, 10), num(pool, 5)); |
|
| 151 | 151 | let b = mul(pool, a, num(pool, 2)); |
|
| 152 | 152 | let c = sub(pool, b, num(pool, 6)); |
|
| 153 | 153 | let root = div(pool, c, num(pool, 4)); |
|
| 154 | 154 | assert try! eval(&pool.nodes[..], root) == 6; |
|
| 155 | 155 | return 0; |
|
| 156 | 156 | } |
|
| 157 | 157 | ||
| 158 | 158 | /// Test 4: Negation: -(3 + 4) = -7 |
|
| 159 | - | fn testNeg(pool: *mut Pool) -> i32 { |
|
| 159 | + | fn testNeg(pool: &mut Pool) -> i32 { |
|
| 160 | 160 | reset(pool); |
|
| 161 | 161 | let root = neg(pool, add(pool, num(pool, 3), num(pool, 4))); |
|
| 162 | 162 | assert try! eval(&pool.nodes[..], root) == -7; |
|
| 163 | 163 | return 0; |
|
| 164 | 164 | } |
|
| 165 | 165 | ||
| 166 | 166 | /// Test 5: Deep tree: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36 |
|
| 167 | - | fn testDeep(pool: *mut Pool) -> i32 { |
|
| 167 | + | fn testDeep(pool: &mut Pool) -> i32 { |
|
| 168 | 168 | reset(pool); |
|
| 169 | 169 | let mut root = num(pool, 1); |
|
| 170 | 170 | let values: [i32; 7] = [2, 3, 4, 5, 6, 7, 8]; |
|
| 171 | 171 | for v in values { |
|
| 172 | 172 | set root = add(pool, root, num(pool, v)); |
| 175 | 175 | assert countNodes(&pool.nodes[..], root) == 15; |
|
| 176 | 176 | return 0; |
|
| 177 | 177 | } |
|
| 178 | 178 | ||
| 179 | 179 | /// Test 6: Mixed operations: (100 - 3 * (2 + 8)) / 7 = (100 - 30) / 7 = 70 / 7 = 10 |
|
| 180 | - | fn testMixed(pool: *mut Pool) -> i32 { |
|
| 180 | + | fn testMixed(pool: &mut Pool) -> i32 { |
|
| 181 | 181 | reset(pool); |
|
| 182 | 182 | let inner = add(pool, num(pool, 2), num(pool, 8)); |
|
| 183 | 183 | let product = mul(pool, num(pool, 3), inner); |
|
| 184 | 184 | let diff = sub(pool, num(pool, 100), product); |
|
| 185 | 185 | let root = div(pool, diff, num(pool, 7)); |
|
| 186 | 186 | assert try! eval(&pool.nodes[..], root) == 10; |
|
| 187 | 187 | return 0; |
|
| 188 | 188 | } |
|
| 189 | 189 | ||
| 190 | 190 | /// Test 7: Single number. |
|
| 191 | - | fn testSingleNum(pool: *mut Pool) -> i32 { |
|
| 191 | + | fn testSingleNum(pool: &mut Pool) -> i32 { |
|
| 192 | 192 | reset(pool); |
|
| 193 | 193 | let root = num(pool, 42); |
|
| 194 | 194 | assert try! eval(&pool.nodes[..], root) == 42; |
|
| 195 | 195 | assert countNodes(&pool.nodes[..], root) == 1; |
|
| 196 | 196 | return 0; |
test/tests/prog.hanoi.rad
+7 -7
| 19 | 19 | moves: [Move; 63], |
|
| 20 | 20 | count: u32, |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | /// Record a move. |
|
| 24 | - | fn recordMove(ml: *mut MoveLog, disk: u32, from: u32, to: u32) { |
|
| 24 | + | fn recordMove(ml: &mut MoveLog, disk: u32, from: u32, to: u32) { |
|
| 25 | 25 | if ml.count < MAX_MOVES { |
|
| 26 | 26 | set ml.moves[ml.count] = Move { disk, from, to }; |
|
| 27 | 27 | set ml.count += 1; |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | /// Solve Tower of Hanoi recursively. |
|
| 32 | 32 | /// Move `n` disks from peg `from` to peg `to` using `aux` as auxiliary. |
|
| 33 | - | fn hanoi(ml: *mut MoveLog, n: u32, from: u32, to: u32, aux: u32) { |
|
| 33 | + | fn hanoi(ml: &mut MoveLog, n: u32, from: u32, to: u32, aux: u32) { |
|
| 34 | 34 | if n == 0 { |
|
| 35 | 35 | return; |
|
| 36 | 36 | } |
|
| 37 | 37 | hanoi(ml, n - 1, from, aux, to); |
|
| 38 | 38 | recordMove(ml, n, from, to); |
|
| 39 | 39 | hanoi(ml, n - 1, aux, to, from); |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// Verify total move count. |
|
| 43 | - | fn testMoveCount(ml: *MoveLog) -> i32 { |
|
| 43 | + | fn testMoveCount(ml: &MoveLog) -> i32 { |
|
| 44 | 44 | // For N disks, there are 2^N - 1 moves. |
|
| 45 | 45 | assert ml.count == MAX_MOVES; |
|
| 46 | 46 | return 0; |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Verify specific moves. |
|
| 50 | - | fn testSpecificMoves(ml: *MoveLog) -> i32 { |
|
| 50 | + | fn testSpecificMoves(ml: &MoveLog) -> i32 { |
|
| 51 | 51 | // First move: smallest disk (1) from peg 0 to peg 2. |
|
| 52 | 52 | assert ml.moves[0].disk == 1; |
|
| 53 | 53 | assert ml.moves[0].from == 0; |
|
| 54 | 54 | assert ml.moves[0].to == 2; |
|
| 55 | 55 |
| 70 | 70 | record Pegs: Copy { |
|
| 71 | 71 | stacks: [[u32; 6]; 3], |
|
| 72 | 72 | top: [u32; 3], |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | - | fn pegPush(pegs: *mut Pegs, peg: u32, disk: u32) -> bool { |
|
| 75 | + | fn pegPush(pegs: &mut Pegs, peg: u32, disk: u32) -> bool { |
|
| 76 | 76 | let t = pegs.top[peg]; |
|
| 77 | 77 | if t > 0 { |
|
| 78 | 78 | // Check that the top disk is larger than the one being placed. |
|
| 79 | 79 | if pegs.stacks[peg][t - 1] < disk { |
|
| 80 | 80 | return false; |
| 83 | 83 | set pegs.stacks[peg][t] = disk; |
|
| 84 | 84 | set pegs.top[peg] = t + 1; |
|
| 85 | 85 | return true; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn pegPop(pegs: *mut Pegs, peg: u32) -> u32 { |
|
| 88 | + | fn pegPop(pegs: &mut Pegs, peg: u32) -> u32 { |
|
| 89 | 89 | let t = pegs.top[peg]; |
|
| 90 | 90 | if t == 0 { |
|
| 91 | 91 | // Should not happen in a valid solution. |
|
| 92 | 92 | return 0; |
|
| 93 | 93 | } |
| 97 | 97 | ||
| 98 | 98 | /// Simulate the peg state to verify correctness. |
|
| 99 | 99 | /// Replay all moves and check: |
|
| 100 | 100 | /// 1. No larger disk is placed on a smaller disk. |
|
| 101 | 101 | /// 2. All disks end up on peg 1. |
|
| 102 | - | fn testSimulate(ml: *MoveLog) -> i32 { |
|
| 102 | + | fn testSimulate(ml: &MoveLog) -> i32 { |
|
| 103 | 103 | let mut pegs = Pegs { |
|
| 104 | 104 | stacks: [[0; 6]; 3], |
|
| 105 | 105 | top: [0, 0, 0], |
|
| 106 | 106 | }; |
|
| 107 | 107 |
test/tests/prog.huffman.rad
+26 -26
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | constant NIL: u32 = 0xFFFFFFFF; |
|
| 26 | 26 | ||
| 27 | 27 | record HuffState: Copy { |
|
| 28 | - | nodes: *mut [HNode], |
|
| 28 | + | nodes: *unsafe mut [HNode], |
|
| 29 | 29 | nodeCount: u32, |
|
| 30 | - | heap: *mut [u32], |
|
| 30 | + | heap: *unsafe mut [u32], |
|
| 31 | 31 | heapSize: u32, |
|
| 32 | - | codeBits: *mut [u32], |
|
| 33 | - | codeLen: *mut [u32], |
|
| 34 | - | bitstream: *mut [u8], |
|
| 32 | + | codeBits: *unsafe mut [u32], |
|
| 33 | + | codeLen: *unsafe mut [u32], |
|
| 34 | + | bitstream: *unsafe mut [u8], |
|
| 35 | 35 | bitCount: u32, |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | - | fn newLeaf(s: *mut HuffState, freq: u32, symbol: u32) -> u32 { |
|
| 38 | + | unsafe fn newLeaf(s: *unsafe mut HuffState, freq: u32, symbol: u32) -> u32 { |
|
| 39 | 39 | let idx: u32 = s.nodeCount; |
|
| 40 | 40 | set s.nodes[idx] = HNode { freq, kind: HNodeKind::Leaf(symbol), left: NIL, right: NIL }; |
|
| 41 | 41 | set s.nodeCount += 1; |
|
| 42 | 42 | return idx; |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | - | fn newInterior(s: *mut HuffState, freq: u32, left: u32, right: u32) -> u32 { |
|
| 45 | + | unsafe fn newInterior(s: *unsafe mut HuffState, freq: u32, left: u32, right: u32) -> u32 { |
|
| 46 | 46 | let idx: u32 = s.nodeCount; |
|
| 47 | 47 | set s.nodes[idx] = HNode { freq, kind: HNodeKind::Interior, left, right }; |
|
| 48 | 48 | set s.nodeCount += 1; |
|
| 49 | 49 | return idx; |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | 52 | /// Get the symbol from a node, or nil if it's an interior node. |
|
| 53 | - | fn nodeSymbol(node: *HNode) -> ?u32 { |
|
| 53 | + | unsafe fn nodeSymbol(node: *unsafe HNode) -> ?u32 { |
|
| 54 | 54 | match node.kind { |
|
| 55 | 55 | case HNodeKind::Leaf(sym) => { |
|
| 56 | 56 | return sym; |
|
| 57 | 57 | } |
|
| 58 | 58 | case HNodeKind::Interior => { |
|
| 59 | 59 | return nil; |
|
| 60 | 60 | } |
|
| 61 | 61 | } |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn heapSwap(s: *mut HuffState, i: u32, j: u32) { |
|
| 64 | + | unsafe fn heapSwap(s: *unsafe mut HuffState, i: u32, j: u32) { |
|
| 65 | 65 | let tmp: u32 = s.heap[i]; |
|
| 66 | 66 | set s.heap[i] = s.heap[j]; |
|
| 67 | 67 | set s.heap[j] = tmp; |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | - | fn heapFreq(s: *HuffState, i: u32) -> u32 { |
|
| 70 | + | unsafe fn heapFreq(s: *unsafe HuffState, i: u32) -> u32 { |
|
| 71 | 71 | return s.nodes[s.heap[i]].freq; |
|
| 72 | 72 | } |
|
| 73 | 73 | ||
| 74 | - | fn siftUp(s: *mut HuffState, pos: u32) { |
|
| 74 | + | unsafe fn siftUp(s: *unsafe mut HuffState, pos: u32) { |
|
| 75 | 75 | let mut i: u32 = pos; |
|
| 76 | 76 | while i > 0 { |
|
| 77 | 77 | let parent: u32 = (i - 1) / 2; |
|
| 78 | 78 | if heapFreq(s, i) < heapFreq(s, parent) { |
|
| 79 | 79 | heapSwap(s, i, parent); |
| 82 | 82 | return; |
|
| 83 | 83 | } |
|
| 84 | 84 | } |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | - | fn siftDown(s: *mut HuffState, pos: u32) { |
|
| 87 | + | unsafe fn siftDown(s: *unsafe mut HuffState, pos: u32) { |
|
| 88 | 88 | let mut i: u32 = pos; |
|
| 89 | 89 | while true { |
|
| 90 | 90 | let left: u32 = 2 * i + 1; |
|
| 91 | 91 | let right: u32 = 2 * i + 2; |
|
| 92 | 92 | let mut smallest: u32 = i; |
| 103 | 103 | heapSwap(s, i, smallest); |
|
| 104 | 104 | set i = smallest; |
|
| 105 | 105 | } |
|
| 106 | 106 | } |
|
| 107 | 107 | ||
| 108 | - | fn heapPush(s: *mut HuffState, nodeIdx: u32) { |
|
| 108 | + | unsafe fn heapPush(s: *unsafe mut HuffState, nodeIdx: u32) { |
|
| 109 | 109 | set s.heap[s.heapSize] = nodeIdx; |
|
| 110 | 110 | set s.heapSize += 1; |
|
| 111 | 111 | siftUp(s, s.heapSize - 1); |
|
| 112 | 112 | } |
|
| 113 | 113 | ||
| 114 | - | fn heapPop(s: *mut HuffState) -> u32 { |
|
| 114 | + | unsafe fn heapPop(s: *unsafe mut HuffState) -> u32 { |
|
| 115 | 115 | let result: u32 = s.heap[0]; |
|
| 116 | 116 | set s.heapSize -= 1; |
|
| 117 | 117 | set s.heap[0] = s.heap[s.heapSize]; |
|
| 118 | 118 | if s.heapSize > 0 { |
|
| 119 | 119 | siftDown(s, 0); |
|
| 120 | 120 | } |
|
| 121 | 121 | return result; |
|
| 122 | 122 | } |
|
| 123 | 123 | ||
| 124 | - | fn buildTree(s: *mut HuffState, freqs: *[u32]) -> u32 { |
|
| 124 | + | unsafe fn buildTree(s: *unsafe mut HuffState, freqs: *unsafe [u32]) -> u32 { |
|
| 125 | 125 | set s.nodeCount = 0; |
|
| 126 | 126 | set s.heapSize = 0; |
|
| 127 | 127 | ||
| 128 | 128 | for freq, sym in freqs { |
|
| 129 | 129 | if freq > 0 { |
| 141 | 141 | } |
|
| 142 | 142 | ||
| 143 | 143 | return heapPop(s); |
|
| 144 | 144 | } |
|
| 145 | 145 | ||
| 146 | - | fn generateCodes(s: *mut HuffState, nodeIdx: u32, code: u32, depth: u32) { |
|
| 146 | + | unsafe fn generateCodes(s: *unsafe mut HuffState, nodeIdx: u32, code: u32, depth: u32) { |
|
| 147 | 147 | match s.nodes[nodeIdx].kind { |
|
| 148 | 148 | case HNodeKind::Leaf(sym) => { |
|
| 149 | 149 | set s.codeBits[sym] = code; |
|
| 150 | 150 | set s.codeLen[sym] = depth; |
|
| 151 | 151 | } |
| 158 | 158 | } |
|
| 159 | 159 | } |
|
| 160 | 160 | } |
|
| 161 | 161 | } |
|
| 162 | 162 | ||
| 163 | - | fn writeBit(s: *mut HuffState, bit: u32) { |
|
| 163 | + | unsafe fn writeBit(s: *unsafe mut HuffState, bit: u32) { |
|
| 164 | 164 | let byteIdx: u32 = s.bitCount / 8; |
|
| 165 | 165 | let bitIdx: u32 = 7 - (s.bitCount % 8); |
|
| 166 | 166 | if bit == 1 { |
|
| 167 | 167 | set s.bitstream[byteIdx] |= (1 as u8 << bitIdx as u8); |
|
| 168 | 168 | } |
|
| 169 | 169 | set s.bitCount += 1; |
|
| 170 | 170 | } |
|
| 171 | 171 | ||
| 172 | - | fn readBit(s: *HuffState, pos: u32) -> u32 { |
|
| 172 | + | unsafe fn readBit(s: *unsafe HuffState, pos: u32) -> u32 { |
|
| 173 | 173 | let byteIdx: u32 = pos / 8; |
|
| 174 | 174 | let bitIdx: u32 = 7 - (pos % 8); |
|
| 175 | 175 | return (s.bitstream[byteIdx] >> bitIdx as u8) as u32 & 1; |
|
| 176 | 176 | } |
|
| 177 | 177 | ||
| 178 | - | fn encode(s: *mut HuffState, msg: *[u32]) { |
|
| 178 | + | unsafe fn encode(s: *unsafe mut HuffState, msg: *unsafe [u32]) { |
|
| 179 | 179 | set s.bitCount = 0; |
|
| 180 | 180 | let mut i: u32 = 0; |
|
| 181 | 181 | while i < MAX_BITS { |
|
| 182 | 182 | set s.bitstream[i] = 0; |
|
| 183 | 183 | set i += 1; |
| 193 | 193 | set b += 1; |
|
| 194 | 194 | } |
|
| 195 | 195 | } |
|
| 196 | 196 | } |
|
| 197 | 197 | ||
| 198 | - | fn decode(s: *HuffState, root: u32, numSymbols: u32, out: *mut [u32]) -> u32 { |
|
| 198 | + | unsafe fn decode(s: *unsafe HuffState, root: u32, numSymbols: u32, out: *unsafe mut [u32]) -> u32 { |
|
| 199 | 199 | let mut bitPos: u32 = 0; |
|
| 200 | 200 | let mut decoded: u32 = 0; |
|
| 201 | 201 | ||
| 202 | 202 | while decoded < numSymbols { |
|
| 203 | 203 | let mut cur: u32 = root; |
| 218 | 218 | set decoded += 1; |
|
| 219 | 219 | } |
|
| 220 | 220 | return decoded; |
|
| 221 | 221 | } |
|
| 222 | 222 | ||
| 223 | - | fn resetCodes(s: *mut HuffState) { |
|
| 223 | + | unsafe fn resetCodes(s: *unsafe mut HuffState) { |
|
| 224 | 224 | let mut i: u32 = 0; |
|
| 225 | 225 | while i < MAX_SYMBOLS { |
|
| 226 | 226 | set s.codeBits[i] = 0; |
|
| 227 | 227 | set s.codeLen[i] = 0; |
|
| 228 | 228 | set i += 1; |
|
| 229 | 229 | } |
|
| 230 | 230 | } |
|
| 231 | 231 | ||
| 232 | - | fn testBasic(s: *mut HuffState) -> i32 { |
|
| 232 | + | unsafe fn testBasic(s: *unsafe mut HuffState) -> i32 { |
|
| 233 | 233 | let freqs: [u32; 5] = [5, 9, 12, 13, 16]; |
|
| 234 | 234 | let root: u32 = buildTree(s, &freqs[..]); |
|
| 235 | 235 | ||
| 236 | 236 | assert s.nodes[root].freq == 55; |
|
| 237 | 237 |
| 259 | 259 | } |
|
| 260 | 260 | ||
| 261 | 261 | return 0; |
|
| 262 | 262 | } |
|
| 263 | 263 | ||
| 264 | - | fn testRoundTrip(s: *mut HuffState) -> i32 { |
|
| 264 | + | unsafe fn testRoundTrip(s: *unsafe mut HuffState) -> i32 { |
|
| 265 | 265 | let freqs: [u32; 5] = [5, 9, 12, 13, 16]; |
|
| 266 | 266 | let root: u32 = buildTree(s, &freqs[..]); |
|
| 267 | 267 | ||
| 268 | 268 | resetCodes(s); |
|
| 269 | 269 | generateCodes(s, root, 0, 0); |
| 284 | 284 | } |
|
| 285 | 285 | ||
| 286 | 286 | return 0; |
|
| 287 | 287 | } |
|
| 288 | 288 | ||
| 289 | - | fn testSkewed(s: *mut HuffState) -> i32 { |
|
| 289 | + | unsafe fn testSkewed(s: *unsafe mut HuffState) -> i32 { |
|
| 290 | 290 | let freqs: [u32; 6] = [100, 1, 1, 1, 1, 1]; |
|
| 291 | 291 | let root: u32 = buildTree(s, &freqs[..]); |
|
| 292 | 292 | ||
| 293 | 293 | resetCodes(s); |
|
| 294 | 294 | generateCodes(s, root, 0, 0); |
| 318 | 318 | } |
|
| 319 | 319 | ||
| 320 | 320 | return 0; |
|
| 321 | 321 | } |
|
| 322 | 322 | ||
| 323 | - | fn testUniform(s: *mut HuffState) -> i32 { |
|
| 323 | + | unsafe fn testUniform(s: *unsafe mut HuffState) -> i32 { |
|
| 324 | 324 | let freqs: [u32; 8] = [10, 10, 10, 10, 10, 10, 10, 10]; |
|
| 325 | 325 | let root: u32 = buildTree(s, &freqs[..]); |
|
| 326 | 326 | ||
| 327 | 327 | assert s.nodes[root].freq == 80; |
|
| 328 | 328 |
| 349 | 349 | } |
|
| 350 | 350 | ||
| 351 | 351 | return 0; |
|
| 352 | 352 | } |
|
| 353 | 353 | ||
| 354 | - | @default fn main() -> i32 { |
|
| 354 | + | @default unsafe fn main() -> i32 { |
|
| 355 | 355 | let mut nodes: [HNode; 63] = [HNode { freq: 0, kind: HNodeKind::Interior, left: 0xFFFFFFFF, right: 0xFFFFFFFF }; 63]; |
|
| 356 | 356 | let mut heap: [u32; 63] = [0; 63]; |
|
| 357 | 357 | let mut codeBits: [u32; 32] = [0; 32]; |
|
| 358 | 358 | let mut codeLen: [u32; 32] = [0; 32]; |
|
| 359 | 359 | let mut bitstream: [u8; 512] = [0; 512]; |
test/tests/prog.hybridsort.rad
+7 -7
| 2 | 2 | //! Insertion sort and selection sort. |
|
| 3 | 3 | //! Sort two copies of the same data with each algorithm, then verify |
|
| 4 | 4 | //! both produce identical sorted output. |
|
| 5 | 5 | ||
| 6 | 6 | /// Copy `src` into `dst`. |
|
| 7 | - | fn copy(dst: *mut [i32], src: *[i32]) { |
|
| 7 | + | fn copy(dst: &mut [i32], src: &[i32]) { |
|
| 8 | 8 | for val, i in src { |
|
| 9 | 9 | set dst[i] = val; |
|
| 10 | 10 | } |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | /// Insertion sort on the given array. |
|
| 14 | - | fn insertionSort(data: *mut [i32]) { |
|
| 14 | + | fn insertionSort(data: &mut [i32]) { |
|
| 15 | 15 | let mut i: u32 = 1; |
|
| 16 | 16 | while i < data.len { |
|
| 17 | 17 | let key = data[i]; |
|
| 18 | 18 | let mut j: i32 = i as i32 - 1; |
|
| 19 | 19 | while j >= 0 and data[j as u32] > key { |
| 24 | 24 | set i += 1; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | /// Selection sort on the given array. |
|
| 29 | - | fn selectionSort(data: *mut [i32]) { |
|
| 29 | + | fn selectionSort(data: &mut [i32]) { |
|
| 30 | 30 | let mut i: u32 = 0; |
|
| 31 | 31 | while i < data.len - 1 { |
|
| 32 | 32 | let mut minIdx: u32 = i; |
|
| 33 | 33 | let mut j: u32 = i + 1; |
|
| 34 | 34 | while j < data.len { |
| 45 | 45 | set i += 1; |
|
| 46 | 46 | } |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Check that an array is sorted in ascending order. |
|
| 50 | - | fn isSorted(data: *[i32]) -> bool { |
|
| 50 | + | fn isSorted(data: &[i32]) -> bool { |
|
| 51 | 51 | let mut prev: ?i32 = nil; |
|
| 52 | 52 | for val in data { |
|
| 53 | 53 | if let p = prev { |
|
| 54 | 54 | if p > val { |
|
| 55 | 55 | return false; |
| 59 | 59 | } |
|
| 60 | 60 | return true; |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Compute sum of all elements. |
|
| 64 | - | fn sum(data: *[i32]) -> i32 { |
|
| 64 | + | fn sum(data: &[i32]) -> i32 { |
|
| 65 | 65 | let mut total: i32 = 0; |
|
| 66 | 66 | for val in data { |
|
| 67 | 67 | set total += val; |
|
| 68 | 68 | } |
|
| 69 | 69 | return total; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Compare two arrays element by element. |
|
| 73 | - | fn arraysEqual(a: *[i32], b: *[i32]) -> i32 { |
|
| 73 | + | fn arraysEqual(a: &[i32], b: &[i32]) -> i32 { |
|
| 74 | 74 | for val, i in a { |
|
| 75 | 75 | if val <> b[i] { |
|
| 76 | 76 | return i as i32 + 1; |
|
| 77 | 77 | } |
|
| 78 | 78 | } |
|
| 79 | 79 | return 0; |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | 82 | /// Verify specific positions in the sorted output. |
|
| 83 | - | fn verifyPositions(data: *[i32]) -> i32 { |
|
| 83 | + | fn verifyPositions(data: &[i32]) -> i32 { |
|
| 84 | 84 | // Sorted: 2 3 6 8 10 14 19 25 27 33 39 41 45 48 53 56 62 67 72 74 81 88 91 97 |
|
| 85 | 85 | let expected: [i32; 6] = [2, 3, 6, 41, 91, 97]; |
|
| 86 | 86 | let indices: [u32; 6] = [0, 1, 2, 11, 22, 23]; |
|
| 87 | 87 | ||
| 88 | 88 | for exp, i in expected { |
test/tests/prog.linkedlist.rad
+14 -14
| 15 | 15 | free: u32, |
|
| 16 | 16 | head: ?u32, |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | /// Allocate a node from the pool. Returns its index. |
|
| 20 | - | fn alloc(list: *mut List, value: i32) -> u32 { |
|
| 20 | + | fn alloc(list: &mut List, value: i32) -> u32 { |
|
| 21 | 21 | let idx = list.free; |
|
| 22 | 22 | set list.free += 1; |
|
| 23 | 23 | set list.pool[idx] = Node { value, next: nil }; |
|
| 24 | 24 | return idx; |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Push a value onto the front of the list. |
|
| 28 | - | fn push(list: *mut List, value: i32) { |
|
| 28 | + | fn push(list: &mut List, value: i32) { |
|
| 29 | 29 | let idx = alloc(list, value); |
|
| 30 | 30 | set list.pool[idx].next = list.head; |
|
| 31 | 31 | set list.head = idx; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | /// Pop a value from the front of the list. Returns nil if empty. |
|
| 35 | - | fn pop(list: *mut List) -> ?i32 { |
|
| 35 | + | fn pop(list: &mut List) -> ?i32 { |
|
| 36 | 36 | let idx = list.head else { |
|
| 37 | 37 | return nil; |
|
| 38 | 38 | }; |
|
| 39 | 39 | let value = list.pool[idx].value; |
|
| 40 | 40 | set list.head = list.pool[idx].next; |
|
| 41 | 41 | return value; |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Get the length of the list. |
|
| 45 | - | fn length(list: *List) -> u32 { |
|
| 45 | + | fn length(list: &List) -> u32 { |
|
| 46 | 46 | let mut count: u32 = 0; |
|
| 47 | 47 | let mut cur = list.head; |
|
| 48 | 48 | while let idx = cur { |
|
| 49 | 49 | set count += 1; |
|
| 50 | 50 | set cur = list.pool[idx].next; |
|
| 51 | 51 | } |
|
| 52 | 52 | return count; |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | 55 | /// Find a value in the list. Returns the node index if found, or nil. |
|
| 56 | - | fn find(list: *List, value: i32) -> ?u32 { |
|
| 56 | + | fn find(list: &List, value: i32) -> ?u32 { |
|
| 57 | 57 | let mut cur = list.head; |
|
| 58 | 58 | while let idx = cur { |
|
| 59 | 59 | if list.pool[idx].value == value { |
|
| 60 | 60 | return idx; |
|
| 61 | 61 | } |
| 63 | 63 | } |
|
| 64 | 64 | return nil; |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | /// Get the value at a given index (0-based from head). |
|
| 68 | - | fn valueAt(list: *List, index: u32) -> ?i32 { |
|
| 68 | + | fn valueAt(list: &List, index: u32) -> ?i32 { |
|
| 69 | 69 | let mut cur = list.head; |
|
| 70 | 70 | let mut i: u32 = 0; |
|
| 71 | 71 | while let idx = cur { |
|
| 72 | 72 | if i == index { |
|
| 73 | 73 | return list.pool[idx].value; |
| 77 | 77 | } |
|
| 78 | 78 | return nil; |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Reverse the linked list in-place. |
|
| 82 | - | fn reverse(list: *mut List) { |
|
| 82 | + | fn reverse(list: &mut List) { |
|
| 83 | 83 | let mut prev: ?u32 = nil; |
|
| 84 | 84 | let mut cur = list.head; |
|
| 85 | 85 | while let idx = cur { |
|
| 86 | 86 | let next = list.pool[idx].next; |
|
| 87 | 87 | set list.pool[idx].next = prev; |
| 90 | 90 | } |
|
| 91 | 91 | set list.head = prev; |
|
| 92 | 92 | } |
|
| 93 | 93 | ||
| 94 | 94 | /// Compute the sum of all values in the list. |
|
| 95 | - | fn sum(list: *List) -> i32 { |
|
| 95 | + | fn sum(list: &List) -> i32 { |
|
| 96 | 96 | let mut total: i32 = 0; |
|
| 97 | 97 | let mut cur = list.head; |
|
| 98 | 98 | while let idx = cur { |
|
| 99 | 99 | set total += list.pool[idx].value; |
|
| 100 | 100 | set cur = list.pool[idx].next; |
|
| 101 | 101 | } |
|
| 102 | 102 | return total; |
|
| 103 | 103 | } |
|
| 104 | 104 | ||
| 105 | 105 | /// Reset the list to empty. |
|
| 106 | - | fn reset(list: *mut List) { |
|
| 106 | + | fn reset(list: &mut List) { |
|
| 107 | 107 | set list.head = nil; |
|
| 108 | 108 | set list.free = 0; |
|
| 109 | 109 | } |
|
| 110 | 110 | ||
| 111 | 111 | /// Test basic push and length. |
|
| 112 | - | fn testPushLength(list: *mut List) -> i32 { |
|
| 112 | + | fn testPushLength(list: &mut List) -> i32 { |
|
| 113 | 113 | push(list, 10); |
|
| 114 | 114 | push(list, 20); |
|
| 115 | 115 | push(list, 30); |
|
| 116 | 116 | ||
| 117 | 117 | assert length(list) == 3; |
| 130 | 130 | assert v2 == 10; |
|
| 131 | 131 | return 0; |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | 134 | /// Test pop. |
|
| 135 | - | fn testPop(list: *mut List) -> i32 { |
|
| 135 | + | fn testPop(list: &mut List) -> i32 { |
|
| 136 | 136 | let v = pop(list) else { |
|
| 137 | 137 | return 1; |
|
| 138 | 138 | }; |
|
| 139 | 139 | assert v == 30; |
|
| 140 | 140 | assert length(list) == 2; |
| 144 | 144 | assert v0 == 20; |
|
| 145 | 145 | return 0; |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | 148 | /// Test find. |
|
| 149 | - | fn testFind(list: *mut List) -> i32 { |
|
| 149 | + | fn testFind(list: &mut List) -> i32 { |
|
| 150 | 150 | // 20 should be found. |
|
| 151 | 151 | assert find(list, 20) <> nil; |
|
| 152 | 152 | // 10 should be found. |
|
| 153 | 153 | assert find(list, 10) <> nil; |
|
| 154 | 154 | // 30 was popped, should not be found. |
| 157 | 157 | assert find(list, 99) == nil; |
|
| 158 | 158 | return 0; |
|
| 159 | 159 | } |
|
| 160 | 160 | ||
| 161 | 161 | /// Test reverse. |
|
| 162 | - | fn testReverse(list: *mut List) -> i32 { |
|
| 162 | + | fn testReverse(list: &mut List) -> i32 { |
|
| 163 | 163 | // Current list: 20 -> 10 |
|
| 164 | 164 | // Add more elements. |
|
| 165 | 165 | push(list, 40); |
|
| 166 | 166 | push(list, 50); |
|
| 167 | 167 | // List: 50 -> 40 -> 20 -> 10 |
| 193 | 193 | assert sum(list) == sumBefore; |
|
| 194 | 194 | return 0; |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// Test building a larger list. |
|
| 198 | - | fn testLargerList(list: *mut List) -> i32 { |
|
| 198 | + | fn testLargerList(list: &mut List) -> i32 { |
|
| 199 | 199 | reset(list); |
|
| 200 | 200 | ||
| 201 | 201 | // Push 32 elements. |
|
| 202 | 202 | let mut i: u32 = 0; |
|
| 203 | 203 | while i < 32 { |
test/tests/prog.lzw.rad
+23 -23
| 12 | 12 | prefix: u32, |
|
| 13 | 13 | suffix: u8, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | record LzwState: Copy { |
|
| 17 | - | encDict: *mut [DictEntry], |
|
| 17 | + | encDict: *unsafe mut [DictEntry], |
|
| 18 | 18 | encDictSize: u32, |
|
| 19 | - | decDict: *mut [DictEntry], |
|
| 19 | + | decDict: *unsafe mut [DictEntry], |
|
| 20 | 20 | decDictSize: u32, |
|
| 21 | - | encoded: *mut [u32], |
|
| 21 | + | encoded: *unsafe mut [u32], |
|
| 22 | 22 | encLen: u32, |
|
| 23 | - | decoded: *mut [u8], |
|
| 23 | + | decoded: *unsafe mut [u8], |
|
| 24 | 24 | decLen: u32, |
|
| 25 | - | temp: *mut [u8], |
|
| 25 | + | temp: *unsafe mut [u8], |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn initEncDict(s: *mut LzwState) { |
|
| 28 | + | unsafe fn initEncDict(s: *unsafe mut LzwState) { |
|
| 29 | 29 | set s.encDictSize = INIT_DICT; |
|
| 30 | 30 | let mut i: u32 = 0; |
|
| 31 | 31 | while i < 256 { |
|
| 32 | 32 | set s.encDict[i] = DictEntry { prefix: 0xFFFF, suffix: i as u8 }; |
|
| 33 | 33 | set i += 1; |
|
| 34 | 34 | } |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | - | fn initDecDict(s: *mut LzwState) { |
|
| 37 | + | unsafe fn initDecDict(s: *unsafe mut LzwState) { |
|
| 38 | 38 | set s.decDictSize = INIT_DICT; |
|
| 39 | 39 | let mut i: u32 = 0; |
|
| 40 | 40 | while i < 256 { |
|
| 41 | 41 | set s.decDict[i] = DictEntry { prefix: 0xFFFF, suffix: i as u8 }; |
|
| 42 | 42 | set i += 1; |
|
| 43 | 43 | } |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | - | fn dictLookup(s: *LzwState, prefix: u32, suffix: u8) -> u32 { |
|
| 46 | + | unsafe fn dictLookup(s: *unsafe LzwState, prefix: u32, suffix: u8) -> u32 { |
|
| 47 | 47 | let mut i: u32 = 0; |
|
| 48 | 48 | while i < s.encDictSize { |
|
| 49 | 49 | if s.encDict[i].prefix == prefix and s.encDict[i].suffix == suffix { |
|
| 50 | 50 | return i; |
|
| 51 | 51 | } |
|
| 52 | 52 | set i += 1; |
|
| 53 | 53 | } |
|
| 54 | 54 | return 0xFFFFFFFF; |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | - | fn dictAdd(s: *mut LzwState, prefix: u32, suffix: u8) { |
|
| 57 | + | unsafe fn dictAdd(s: *unsafe mut LzwState, prefix: u32, suffix: u8) { |
|
| 58 | 58 | if s.encDictSize < MAX_DICT { |
|
| 59 | 59 | set s.encDict[s.encDictSize] = DictEntry { prefix, suffix }; |
|
| 60 | 60 | set s.encDictSize += 1; |
|
| 61 | 61 | } |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | - | fn emitCode(s: *mut LzwState, code: u32) { |
|
| 64 | + | unsafe fn emitCode(s: *unsafe mut LzwState, code: u32) { |
|
| 65 | 65 | set s.encoded[s.encLen] = code; |
|
| 66 | 66 | set s.encLen += 1; |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | - | fn encode(s: *mut LzwState, data: *[u8]) { |
|
| 69 | + | unsafe fn encode(s: *unsafe mut LzwState, data: &[u8]) { |
|
| 70 | 70 | initEncDict(s); |
|
| 71 | 71 | set s.encLen = 0; |
|
| 72 | 72 | ||
| 73 | 73 | if data.len == 0 { |
|
| 74 | 74 | emitCode(s, EOI_CODE); |
| 92 | 92 | } |
|
| 93 | 93 | emitCode(s, w); |
|
| 94 | 94 | emitCode(s, EOI_CODE); |
|
| 95 | 95 | } |
|
| 96 | 96 | ||
| 97 | - | fn decodeString(s: *mut LzwState, code: u32) -> u32 { |
|
| 97 | + | unsafe fn decodeString(s: *unsafe mut LzwState, code: u32) -> u32 { |
|
| 98 | 98 | let mut len: u32 = 0; |
|
| 99 | 99 | let mut c: u32 = code; |
|
| 100 | 100 | while c <> 0xFFFF and c < s.decDictSize { |
|
| 101 | 101 | set s.temp[len] = s.decDict[c].suffix; |
|
| 102 | 102 | set len += 1; |
| 112 | 112 | set b -= 1; |
|
| 113 | 113 | } |
|
| 114 | 114 | return len; |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | - | fn firstByte(s: *LzwState, code: u32) -> u8 { |
|
| 117 | + | unsafe fn firstByte(s: *unsafe LzwState, code: u32) -> u8 { |
|
| 118 | 118 | let mut c: u32 = code; |
|
| 119 | 119 | while s.decDict[c].prefix <> 0xFFFF and s.decDict[c].prefix < s.decDictSize { |
|
| 120 | 120 | set c = s.decDict[c].prefix; |
|
| 121 | 121 | } |
|
| 122 | 122 | return s.decDict[c].suffix; |
|
| 123 | 123 | } |
|
| 124 | 124 | ||
| 125 | - | fn decDictAdd(s: *mut LzwState, prefix: u32, suffix: u8) { |
|
| 125 | + | unsafe fn decDictAdd(s: *unsafe mut LzwState, prefix: u32, suffix: u8) { |
|
| 126 | 126 | if s.decDictSize < MAX_DICT { |
|
| 127 | 127 | set s.decDict[s.decDictSize] = DictEntry { prefix, suffix }; |
|
| 128 | 128 | set s.decDictSize += 1; |
|
| 129 | 129 | } |
|
| 130 | 130 | } |
|
| 131 | 131 | ||
| 132 | - | fn outputByte(s: *mut LzwState, b: u8) { |
|
| 132 | + | unsafe fn outputByte(s: *unsafe mut LzwState, b: u8) { |
|
| 133 | 133 | set s.decoded[s.decLen] = b; |
|
| 134 | 134 | set s.decLen += 1; |
|
| 135 | 135 | } |
|
| 136 | 136 | ||
| 137 | - | fn decode(s: *mut LzwState) { |
|
| 137 | + | unsafe fn decode(s: *unsafe mut LzwState) { |
|
| 138 | 138 | initDecDict(s); |
|
| 139 | 139 | set s.decLen = 0; |
|
| 140 | 140 | ||
| 141 | 141 | if s.encLen == 0 { |
|
| 142 | 142 | return; |
| 182 | 182 | } |
|
| 183 | 183 | set prevCode = code; |
|
| 184 | 184 | } |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | - | fn testSimple(s: *mut LzwState) -> i32 { |
|
| 187 | + | unsafe fn testSimple(s: *unsafe mut LzwState) -> i32 { |
|
| 188 | 188 | let data: *[u8] = "ABABABABAB"; |
|
| 189 | 189 | encode(s, data); |
|
| 190 | 190 | ||
| 191 | 191 | assert s.encoded[s.encLen - 1] == EOI_CODE; |
|
| 192 | 192 |
| 198 | 198 | set i += 1; |
|
| 199 | 199 | } |
|
| 200 | 200 | return 0; |
|
| 201 | 201 | } |
|
| 202 | 202 | ||
| 203 | - | fn testDistinct(s: *mut LzwState) -> i32 { |
|
| 203 | + | unsafe fn testDistinct(s: *unsafe mut LzwState) -> i32 { |
|
| 204 | 204 | let data: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
|
| 205 | 205 | encode(s, &data[..]); |
|
| 206 | 206 | ||
| 207 | 207 | decode(s); |
|
| 208 | 208 | assert s.decLen == 16; |
| 212 | 212 | set i += 1; |
|
| 213 | 213 | } |
|
| 214 | 214 | return 0; |
|
| 215 | 215 | } |
|
| 216 | 216 | ||
| 217 | - | fn testRepetitive(s: *mut LzwState) -> i32 { |
|
| 217 | + | unsafe fn testRepetitive(s: *unsafe mut LzwState) -> i32 { |
|
| 218 | 218 | let mut data: [u8; 64] = [65; 64]; |
|
| 219 | 219 | encode(s, &data[..]); |
|
| 220 | 220 | ||
| 221 | 221 | assert s.encLen < 32; |
|
| 222 | 222 |
| 228 | 228 | set i += 1; |
|
| 229 | 229 | } |
|
| 230 | 230 | return 0; |
|
| 231 | 231 | } |
|
| 232 | 232 | ||
| 233 | - | fn testMixed(s: *mut LzwState) -> i32 { |
|
| 233 | + | unsafe fn testMixed(s: *unsafe mut LzwState) -> i32 { |
|
| 234 | 234 | let data: *[u8] = "TOBEORNOTTOBEORTOBEORNOT"; |
|
| 235 | 235 | encode(s, data); |
|
| 236 | 236 | ||
| 237 | 237 | decode(s); |
|
| 238 | 238 | assert s.decLen == 24; |
| 245 | 245 | assert s.encLen - 1 < 24; |
|
| 246 | 246 | ||
| 247 | 247 | return 0; |
|
| 248 | 248 | } |
|
| 249 | 249 | ||
| 250 | - | fn testEmpty(s: *mut LzwState) -> i32 { |
|
| 250 | + | unsafe fn testEmpty(s: *unsafe mut LzwState) -> i32 { |
|
| 251 | 251 | encode(s, &[]); |
|
| 252 | 252 | assert s.encLen == 1; |
|
| 253 | 253 | assert s.encoded[0] == EOI_CODE; |
|
| 254 | 254 | ||
| 255 | 255 | decode(s); |
|
| 256 | 256 | assert s.decLen == 0; |
|
| 257 | 257 | return 0; |
|
| 258 | 258 | } |
|
| 259 | 259 | ||
| 260 | - | fn testSingle(s: *mut LzwState) -> i32 { |
|
| 260 | + | unsafe fn testSingle(s: *unsafe mut LzwState) -> i32 { |
|
| 261 | 261 | let data: *[u8] = "*"; |
|
| 262 | 262 | encode(s, data); |
|
| 263 | 263 | ||
| 264 | 264 | decode(s); |
|
| 265 | 265 | assert s.decLen == 1; |
|
| 266 | 266 | assert s.decoded[0] == 42; |
|
| 267 | 267 | return 0; |
|
| 268 | 268 | } |
|
| 269 | 269 | ||
| 270 | - | @default fn main() -> i32 { |
|
| 270 | + | @default unsafe fn main() -> i32 { |
|
| 271 | 271 | let mut encDict: [DictEntry; 512] = [DictEntry { prefix: 0xFFFF, suffix: 0 }; 512]; |
|
| 272 | 272 | let mut decDict: [DictEntry; 512] = [DictEntry { prefix: 0xFFFF, suffix: 0 }; 512]; |
|
| 273 | 273 | let mut encoded: [u32; 512] = [0; 512]; |
|
| 274 | 274 | let mut decoded: [u8; 256] = [0; 256]; |
|
| 275 | 275 | let mut temp: [u8; 256] = [0; 256]; |
test/tests/prog.matmul.rad
+5 -5
| 9 | 9 | record Mat4: Copy { |
|
| 10 | 10 | rows: [[i32; 4]; 4], |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | /// Perform matrix multiplication: c = a * b. |
|
| 14 | - | fn matmul(c: *mut Mat4, a: *Mat4, b: *Mat4) { |
|
| 14 | + | fn matmul(c: &mut Mat4, a: &Mat4, b: &Mat4) { |
|
| 15 | 15 | let mut i: u32 = 0; |
|
| 16 | 16 | while i < N { |
|
| 17 | 17 | let mut j: u32 = 0; |
|
| 18 | 18 | while j < N { |
|
| 19 | 19 | let mut sum: i32 = 0; |
| 28 | 28 | set i += 1; |
|
| 29 | 29 | } |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | /// Verify c matches expected. |
|
| 33 | - | fn verifyResult(c: *Mat4, expected: *Mat4) -> i32 { |
|
| 33 | + | fn verifyResult(c: &Mat4, expected: &Mat4) -> i32 { |
|
| 34 | 34 | let mut i: u32 = 0; |
|
| 35 | 35 | while i < N { |
|
| 36 | 36 | let mut j: u32 = 0; |
|
| 37 | 37 | while j < N { |
|
| 38 | 38 | if c.rows[i][j] <> expected.rows[i][j] { |
| 44 | 44 | } |
|
| 45 | 45 | return 0; |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | 48 | /// Compute the trace (sum of diagonal) of a matrix. |
|
| 49 | - | fn trace(m: *Mat4) -> i32 { |
|
| 49 | + | fn trace(m: &Mat4) -> i32 { |
|
| 50 | 50 | let mut sum: i32 = 0; |
|
| 51 | 51 | let mut i: u32 = 0; |
|
| 52 | 52 | while i < N { |
|
| 53 | 53 | set sum += m.rows[i][i]; |
|
| 54 | 54 | set i += 1; |
|
| 55 | 55 | } |
|
| 56 | 56 | return sum; |
|
| 57 | 57 | } |
|
| 58 | 58 | ||
| 59 | 59 | /// Zero out a matrix. |
|
| 60 | - | fn zero(m: *mut Mat4) { |
|
| 60 | + | fn zero(m: &mut Mat4) { |
|
| 61 | 61 | let mut i: u32 = 0; |
|
| 62 | 62 | while i < N { |
|
| 63 | 63 | let mut j: u32 = 0; |
|
| 64 | 64 | while j < N { |
|
| 65 | 65 | set m.rows[i][j] = 0; |
| 68 | 68 | set i += 1; |
|
| 69 | 69 | } |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Multiply matrices multiple times to test repeated computation. |
|
| 73 | - | fn testRepeatedMultiply(c: *mut Mat4, a: *Mat4, b: *Mat4) -> i32 { |
|
| 73 | + | fn testRepeatedMultiply(c: &mut Mat4, a: &Mat4, b: &Mat4) -> i32 { |
|
| 74 | 74 | // First pass already done, trace = 13+43+43+85 = 184. |
|
| 75 | 75 | assert trace(c) == 184; |
|
| 76 | 76 | ||
| 77 | 77 | // Zero out c and re-multiply to ensure idempotent. |
|
| 78 | 78 | zero(c); |
test/tests/prog.mersenne.rad
+11 -11
| 6 | 6 | constant MATRIX_A: u32 = 0x9908B0DF; |
|
| 7 | 7 | constant UPPER_MASK: u32 = 0x80000000; |
|
| 8 | 8 | constant LOWER_MASK: u32 = 0x7FFFFFFF; |
|
| 9 | 9 | ||
| 10 | 10 | record MtState: Copy { |
|
| 11 | - | mt: *mut [u32], |
|
| 11 | + | mt: *unsafe mut [u32], |
|
| 12 | 12 | mti: u32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn mtInit(s: *mut MtState, seed: u32) { |
|
| 15 | + | unsafe fn mtInit(s: &mut MtState, seed: u32) { |
|
| 16 | 16 | set s.mt[0] = seed; |
|
| 17 | 17 | let mut i: u32 = 1; |
|
| 18 | 18 | while i < N { |
|
| 19 | 19 | let prev: u32 = s.mt[i - 1]; |
|
| 20 | 20 | let xored: u32 = prev ^ (prev >> 30); |
| 34 | 34 | set i += 1; |
|
| 35 | 35 | } |
|
| 36 | 36 | set s.mti = N; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | - | fn generateNumbers(s: *mut MtState) { |
|
| 39 | + | unsafe fn generateNumbers(s: &mut MtState) { |
|
| 40 | 40 | let mut i: u32 = 0; |
|
| 41 | 41 | ||
| 42 | 42 | while i < N - M { |
|
| 43 | 43 | let y: u32 = (s.mt[i] & UPPER_MASK) | (s.mt[i + 1] & LOWER_MASK); |
|
| 44 | 44 | let mut mag: u32 = 0; |
| 67 | 67 | set s.mt[N - 1] = s.mt[M - 1] ^ (y >> 1) ^ mag; |
|
| 68 | 68 | ||
| 69 | 69 | set s.mti = 0; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | - | fn mtNext(s: *mut MtState) -> u32 { |
|
| 72 | + | unsafe fn mtNext(s: &mut MtState) -> u32 { |
|
| 73 | 73 | if s.mti >= N { |
|
| 74 | 74 | generateNumbers(s); |
|
| 75 | 75 | } |
|
| 76 | 76 | ||
| 77 | 77 | let mut y: u32 = s.mt[s.mti]; |
| 83 | 83 | set y ^= (y >> 18); |
|
| 84 | 84 | ||
| 85 | 85 | return y; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn testKnownSequence(s: *mut MtState) -> i32 { |
|
| 88 | + | unsafe fn testKnownSequence(s: &mut MtState) -> i32 { |
|
| 89 | 89 | mtInit(s, 1); |
|
| 90 | 90 | ||
| 91 | 91 | let v0: u32 = mtNext(s); |
|
| 92 | 92 | assert v0 == 1791095845; |
|
| 93 | 93 |
| 104 | 104 | assert v4 == 491263; |
|
| 105 | 105 | ||
| 106 | 106 | return 0; |
|
| 107 | 107 | } |
|
| 108 | 108 | ||
| 109 | - | fn testDeterminism(s: *mut MtState) -> i32 { |
|
| 109 | + | unsafe fn testDeterminism(s: &mut MtState) -> i32 { |
|
| 110 | 110 | mtInit(s, 42); |
|
| 111 | 111 | ||
| 112 | 112 | let mut first: [u32; 10] = [0; 10]; |
|
| 113 | 113 | let mut i: u32 = 0; |
|
| 114 | 114 | while i < 10 { |
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | return 0; |
|
| 129 | 129 | } |
|
| 130 | 130 | ||
| 131 | - | fn testDifferentSeeds(s: *mut MtState) -> i32 { |
|
| 131 | + | unsafe fn testDifferentSeeds(s: &mut MtState) -> i32 { |
|
| 132 | 132 | mtInit(s, 1); |
|
| 133 | 133 | let a: u32 = mtNext(s); |
|
| 134 | 134 | ||
| 135 | 135 | mtInit(s, 2); |
|
| 136 | 136 | let b: u32 = mtNext(s); |
| 143 | 143 | assert a <> c; |
|
| 144 | 144 | ||
| 145 | 145 | return 0; |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | - | fn testChiSquared(s: *mut MtState) -> i32 { |
|
| 148 | + | unsafe fn testChiSquared(s: &mut MtState) -> i32 { |
|
| 149 | 149 | mtInit(s, 12345); |
|
| 150 | 150 | ||
| 151 | 151 | constant NUM_BINS: u32 = 16; |
|
| 152 | 152 | constant NUM_SAMPLES: u32 = 1600; |
|
| 153 | 153 | constant EXPECTED: u32 = 100; |
| 187 | 187 | } |
|
| 188 | 188 | ||
| 189 | 189 | return 0; |
|
| 190 | 190 | } |
|
| 191 | 191 | ||
| 192 | - | fn testRegeneration(s: *mut MtState) -> i32 { |
|
| 192 | + | unsafe fn testRegeneration(s: &mut MtState) -> i32 { |
|
| 193 | 193 | mtInit(s, 7); |
|
| 194 | 194 | ||
| 195 | 195 | let mut last: u32 = 0; |
|
| 196 | 196 | let mut i: u32 = 0; |
|
| 197 | 197 | while i < 700 { |
| 218 | 218 | assert more <> 0; |
|
| 219 | 219 | ||
| 220 | 220 | return 0; |
|
| 221 | 221 | } |
|
| 222 | 222 | ||
| 223 | - | fn testBitCoverage(s: *mut MtState) -> i32 { |
|
| 223 | + | unsafe fn testBitCoverage(s: &mut MtState) -> i32 { |
|
| 224 | 224 | mtInit(s, 999); |
|
| 225 | 225 | ||
| 226 | 226 | let mut orAll: u32 = 0; |
|
| 227 | 227 | let mut andAll: u32 = 0xFFFFFFFF; |
|
| 228 | 228 |
| 239 | 239 | assert andAll == 0; |
|
| 240 | 240 | ||
| 241 | 241 | return 0; |
|
| 242 | 242 | } |
|
| 243 | 243 | ||
| 244 | - | @default fn main() -> i32 { |
|
| 244 | + | @default unsafe fn main() -> i32 { |
|
| 245 | 245 | let mut mt: [u32; 624] = [0; 624]; |
|
| 246 | 246 | let mut s: MtState = MtState { |
|
| 247 | 247 | mt: &mut mt[..], |
|
| 248 | 248 | mti: 625, |
|
| 249 | 249 | }; |
test/tests/prog.nqueens.rad
+8 -8
| 4 | 4 | //! for boards of size 1 through 8 and verify against known solution counts. |
|
| 5 | 5 | ||
| 6 | 6 | constant MAX_N: u32 = 8; |
|
| 7 | 7 | ||
| 8 | 8 | record Board: Copy { |
|
| 9 | - | queens: *mut [i32], |
|
| 9 | + | queens: *unsafe mut [i32], |
|
| 10 | 10 | solutionCount: u32, |
|
| 11 | 11 | boardSize: u32, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn isSafe(b: *Board, row: u32, col: u32) -> bool { |
|
| 14 | + | unsafe fn isSafe(b: &Board, row: u32, col: u32) -> bool { |
|
| 15 | 15 | let mut i: u32 = 0; |
|
| 16 | 16 | while i < row { |
|
| 17 | 17 | let qcol: i32 = b.queens[i]; |
|
| 18 | 18 | if qcol == col as i32 { |
|
| 19 | 19 | return false; |
| 26 | 26 | set i += 1; |
|
| 27 | 27 | } |
|
| 28 | 28 | return true; |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | - | fn solve(b: *mut Board, row: u32) { |
|
| 31 | + | unsafe fn solve(b: &mut Board, row: u32) { |
|
| 32 | 32 | if row == b.boardSize { |
|
| 33 | 33 | set b.solutionCount += 1; |
|
| 34 | 34 | return; |
|
| 35 | 35 | } |
|
| 36 | 36 | let mut col: u32 = 0; |
| 42 | 42 | } |
|
| 43 | 43 | set col += 1; |
|
| 44 | 44 | } |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | - | fn resetBoard(b: *mut Board) { |
|
| 47 | + | unsafe fn resetBoard(b: &mut Board) { |
|
| 48 | 48 | let mut i: u32 = 0; |
|
| 49 | 49 | while i < MAX_N { |
|
| 50 | 50 | set b.queens[i] = -1; |
|
| 51 | 51 | set i += 1; |
|
| 52 | 52 | } |
|
| 53 | 53 | set b.solutionCount = 0; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | - | fn solveNQueens(b: *mut Board, n: u32) -> u32 { |
|
| 56 | + | unsafe fn solveNQueens(b: &mut Board, n: u32) -> u32 { |
|
| 57 | 57 | resetBoard(b); |
|
| 58 | 58 | set b.boardSize = n; |
|
| 59 | 59 | solve(b, 0); |
|
| 60 | 60 | return b.solutionCount; |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | - | fn testAllSizes(b: *mut Board) -> i32 { |
|
| 63 | + | unsafe fn testAllSizes(b: &mut Board) -> i32 { |
|
| 64 | 64 | let expected: [u32; 9] = [0, 1, 0, 0, 2, 10, 4, 40, 92]; |
|
| 65 | 65 | let mut n: u32 = 1; |
|
| 66 | 66 | while n <= 8 { |
|
| 67 | 67 | let count: u32 = solveNQueens(b, n); |
|
| 68 | 68 | if count <> expected[n] { |
| 101 | 101 | } |
|
| 102 | 102 | ||
| 103 | 103 | return 0; |
|
| 104 | 104 | } |
|
| 105 | 105 | ||
| 106 | - | fn testDeterminism(b: *mut Board) -> i32 { |
|
| 106 | + | unsafe fn testDeterminism(b: &mut Board) -> i32 { |
|
| 107 | 107 | let count1: u32 = solveNQueens(b, 8); |
|
| 108 | 108 | let count2: u32 = solveNQueens(b, 8); |
|
| 109 | 109 | let count3: u32 = solveNQueens(b, 8); |
|
| 110 | 110 | ||
| 111 | 111 | assert count1 == 92; |
| 129 | 129 | assert expected[8] > expected[7]; |
|
| 130 | 130 | ||
| 131 | 131 | return 0; |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | - | @default fn main() -> i32 { |
|
| 134 | + | @default unsafe fn main() -> i32 { |
|
| 135 | 135 | let mut queens: [i32; 8] = [-1; 8]; |
|
| 136 | 136 | let mut b: Board = Board { |
|
| 137 | 137 | queens: &mut queens[..], |
|
| 138 | 138 | solutionCount: 0, |
|
| 139 | 139 | boardSize: 0, |
test/tests/prog.rbtree.rad
+18 -18
| 15 | 15 | right: u32, |
|
| 16 | 16 | parent: u32, |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | record RBTree: Copy { |
|
| 20 | - | pool: *mut [RBNode], |
|
| 20 | + | pool: *unsafe mut [RBNode], |
|
| 21 | 21 | poolNext: u32, |
|
| 22 | 22 | root: u32, |
|
| 23 | - | inorder: *mut [i32], |
|
| 23 | + | inorder: *unsafe mut [i32], |
|
| 24 | 24 | inorderCount: u32, |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | - | fn allocNode(t: *mut RBTree, key: i32) -> u32 { |
|
| 27 | + | unsafe fn allocNode(t: *unsafe mut RBTree, key: i32) -> u32 { |
|
| 28 | 28 | let idx: u32 = t.poolNext; |
|
| 29 | 29 | set t.poolNext += 1; |
|
| 30 | 30 | set t.pool[idx] = RBNode { key, color: RED, left: NIL, right: NIL, parent: NIL }; |
|
| 31 | 31 | return idx; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | - | fn rotateLeft(t: *mut RBTree, x: u32) { |
|
| 34 | + | unsafe fn rotateLeft(t: *unsafe mut RBTree, x: u32) { |
|
| 35 | 35 | let y: u32 = t.pool[x].right; |
|
| 36 | 36 | set t.pool[x].right = t.pool[y].left; |
|
| 37 | 37 | if t.pool[y].left <> NIL { |
|
| 38 | 38 | set t.pool[t.pool[y].left].parent = x; |
|
| 39 | 39 | } |
| 47 | 47 | } |
|
| 48 | 48 | set t.pool[y].left = x; |
|
| 49 | 49 | set t.pool[x].parent = y; |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | - | fn rotateRight(t: *mut RBTree, x: u32) { |
|
| 52 | + | unsafe fn rotateRight(t: *unsafe mut RBTree, x: u32) { |
|
| 53 | 53 | let y: u32 = t.pool[x].left; |
|
| 54 | 54 | set t.pool[x].left = t.pool[y].right; |
|
| 55 | 55 | if t.pool[y].right <> NIL { |
|
| 56 | 56 | set t.pool[t.pool[y].right].parent = x; |
|
| 57 | 57 | } |
| 65 | 65 | } |
|
| 66 | 66 | set t.pool[y].right = x; |
|
| 67 | 67 | set t.pool[x].parent = y; |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | - | fn insertFixup(t: *mut RBTree, zArg: u32) { |
|
| 70 | + | unsafe fn insertFixup(t: *unsafe mut RBTree, zArg: u32) { |
|
| 71 | 71 | let mut z: u32 = zArg; |
|
| 72 | 72 | while t.pool[t.pool[z].parent].color == RED { |
|
| 73 | 73 | if t.pool[z].parent == t.pool[t.pool[t.pool[z].parent].parent].left { |
|
| 74 | 74 | let y: u32 = t.pool[t.pool[t.pool[z].parent].parent].right; |
|
| 75 | 75 | if t.pool[y].color == RED { |
| 105 | 105 | } |
|
| 106 | 106 | } |
|
| 107 | 107 | set t.pool[t.root].color = BLACK; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | - | fn insert(t: *mut RBTree, key: i32) { |
|
| 110 | + | unsafe fn insert(t: *unsafe mut RBTree, key: i32) { |
|
| 111 | 111 | let z: u32 = allocNode(t, key); |
|
| 112 | 112 | let mut y: u32 = NIL; |
|
| 113 | 113 | let mut x: u32 = t.root; |
|
| 114 | 114 | ||
| 115 | 115 | while x <> NIL { |
| 131 | 131 | } |
|
| 132 | 132 | ||
| 133 | 133 | insertFixup(t, z); |
|
| 134 | 134 | } |
|
| 135 | 135 | ||
| 136 | - | fn search(t: *RBTree, key: i32) -> bool { |
|
| 136 | + | unsafe fn search(t: *unsafe RBTree, key: i32) -> bool { |
|
| 137 | 137 | let mut x: u32 = t.root; |
|
| 138 | 138 | while x <> NIL { |
|
| 139 | 139 | if key == t.pool[x].key { |
|
| 140 | 140 | return true; |
|
| 141 | 141 | } else if key < t.pool[x].key { |
| 145 | 145 | } |
|
| 146 | 146 | } |
|
| 147 | 147 | return false; |
|
| 148 | 148 | } |
|
| 149 | 149 | ||
| 150 | - | fn inorderWalk(t: *mut RBTree, x: u32) { |
|
| 150 | + | unsafe fn inorderWalk(t: *unsafe mut RBTree, x: u32) { |
|
| 151 | 151 | if x == NIL { |
|
| 152 | 152 | return; |
|
| 153 | 153 | } |
|
| 154 | 154 | inorderWalk(t, t.pool[x].left); |
|
| 155 | 155 | set t.inorder[t.inorderCount] = t.pool[x].key; |
|
| 156 | 156 | set t.inorderCount += 1; |
|
| 157 | 157 | inorderWalk(t, t.pool[x].right); |
|
| 158 | 158 | } |
|
| 159 | 159 | ||
| 160 | - | fn countNodes(t: *RBTree, x: u32) -> u32 { |
|
| 160 | + | unsafe fn countNodes(t: *unsafe RBTree, x: u32) -> u32 { |
|
| 161 | 161 | if x == NIL { |
|
| 162 | 162 | return 0; |
|
| 163 | 163 | } |
|
| 164 | 164 | return 1 + countNodes(t, t.pool[x].left) + countNodes(t, t.pool[x].right); |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | - | fn blackHeight(t: *RBTree, x: u32) -> i32 { |
|
| 167 | + | unsafe fn blackHeight(t: *unsafe RBTree, x: u32) -> i32 { |
|
| 168 | 168 | if x == NIL { |
|
| 169 | 169 | return 1; |
|
| 170 | 170 | } |
|
| 171 | 171 | let leftBH: i32 = blackHeight(t, t.pool[x].left); |
|
| 172 | 172 | let rightBH: i32 = blackHeight(t, t.pool[x].right); |
| 182 | 182 | return leftBH + 1; |
|
| 183 | 183 | } |
|
| 184 | 184 | return leftBH; |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | - | fn noRedRed(t: *RBTree, x: u32) -> bool { |
|
| 187 | + | unsafe fn noRedRed(t: *unsafe RBTree, x: u32) -> bool { |
|
| 188 | 188 | if x == NIL { |
|
| 189 | 189 | return true; |
|
| 190 | 190 | } |
|
| 191 | 191 | if t.pool[x].color == RED { |
|
| 192 | 192 | if t.pool[t.pool[x].left].color == RED { |
| 200 | 200 | return false; |
|
| 201 | 201 | } |
|
| 202 | 202 | return noRedRed(t, t.pool[x].right); |
|
| 203 | 203 | } |
|
| 204 | 204 | ||
| 205 | - | fn resetTree(t: *mut RBTree) { |
|
| 205 | + | unsafe fn resetTree(t: *unsafe mut RBTree) { |
|
| 206 | 206 | let mut i: u32 = 0; |
|
| 207 | 207 | while i < POOL_SIZE { |
|
| 208 | 208 | set t.pool[i] = RBNode { key: 0, color: BLACK, left: NIL, right: NIL, parent: NIL }; |
|
| 209 | 209 | set i += 1; |
|
| 210 | 210 | } |
|
| 211 | 211 | set t.poolNext = 1; |
|
| 212 | 212 | set t.root = NIL; |
|
| 213 | 213 | set t.inorderCount = 0; |
|
| 214 | 214 | } |
|
| 215 | 215 | ||
| 216 | - | fn testAscending(t: *mut RBTree) -> i32 { |
|
| 216 | + | unsafe fn testAscending(t: *unsafe mut RBTree) -> i32 { |
|
| 217 | 217 | resetTree(t); |
|
| 218 | 218 | ||
| 219 | 219 | let mut i: i32 = 0; |
|
| 220 | 220 | while i < 32 { |
|
| 221 | 221 | insert(t, i); |
| 240 | 240 | } |
|
| 241 | 241 | ||
| 242 | 242 | return 0; |
|
| 243 | 243 | } |
|
| 244 | 244 | ||
| 245 | - | fn testDescending(t: *mut RBTree) -> i32 { |
|
| 245 | + | unsafe fn testDescending(t: *unsafe mut RBTree) -> i32 { |
|
| 246 | 246 | resetTree(t); |
|
| 247 | 247 | ||
| 248 | 248 | let mut i: i32 = 31; |
|
| 249 | 249 | while i >= 0 { |
|
| 250 | 250 | insert(t, i); |
| 267 | 267 | } |
|
| 268 | 268 | ||
| 269 | 269 | return 0; |
|
| 270 | 270 | } |
|
| 271 | 271 | ||
| 272 | - | fn testRandom(t: *mut RBTree) -> i32 { |
|
| 272 | + | unsafe fn testRandom(t: *unsafe mut RBTree) -> i32 { |
|
| 273 | 273 | resetTree(t); |
|
| 274 | 274 | ||
| 275 | 275 | let mut inserted: [bool; 48] = [false; 48]; |
|
| 276 | 276 | let mut seed: u32 = 42; |
|
| 277 | 277 | let mut count: u32 = 0; |
| 310 | 310 | } |
|
| 311 | 311 | ||
| 312 | 312 | return 0; |
|
| 313 | 313 | } |
|
| 314 | 314 | ||
| 315 | - | fn testHeight(t: *mut RBTree) -> i32 { |
|
| 315 | + | unsafe fn testHeight(t: *unsafe mut RBTree) -> i32 { |
|
| 316 | 316 | resetTree(t); |
|
| 317 | 317 | ||
| 318 | 318 | let mut i: i32 = 0; |
|
| 319 | 319 | while i < 63 { |
|
| 320 | 320 | insert(t, i); |
| 326 | 326 | assert bh <= 7; |
|
| 327 | 327 | ||
| 328 | 328 | return 0; |
|
| 329 | 329 | } |
|
| 330 | 330 | ||
| 331 | - | @default fn main() -> i32 { |
|
| 331 | + | @default unsafe fn main() -> i32 { |
|
| 332 | 332 | let mut pool: [RBNode; 128] = [RBNode { key: 0, color: 1, left: 0, right: 0, parent: 0 }; 128]; |
|
| 333 | 333 | let mut inorder: [i32; 128] = [0; 128]; |
|
| 334 | 334 | ||
| 335 | 335 | let mut t: RBTree = RBTree { |
|
| 336 | 336 | pool: &mut pool[..], |
test/tests/prog.regex.rad
+28 -28
| 22 | 22 | start: u32, |
|
| 23 | 23 | endState: u32, |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | 26 | record NfaState: Copy { |
|
| 27 | - | trans: *mut [Trans], |
|
| 27 | + | trans: *unsafe mut [Trans], |
|
| 28 | 28 | transCount: u32, |
|
| 29 | - | stateFirst: *mut [u32], |
|
| 30 | - | transNext: *mut [u32], |
|
| 29 | + | stateFirst: *unsafe mut [u32], |
|
| 30 | + | transNext: *unsafe mut [u32], |
|
| 31 | 31 | stateCount: u32, |
|
| 32 | 32 | acceptState: u32, |
|
| 33 | - | current: *mut [u32], |
|
| 34 | - | nextSet: *mut [u32], |
|
| 35 | - | closure: *mut [u32], |
|
| 36 | - | fragStack: *mut [Frag], |
|
| 33 | + | current: *unsafe mut [u32], |
|
| 34 | + | nextSet: *unsafe mut [u32], |
|
| 35 | + | closure: *unsafe mut [u32], |
|
| 36 | + | fragStack: *unsafe mut [Frag], |
|
| 37 | 37 | fragTop: u32, |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | - | fn newState(nfa: *mut NfaState) -> u32 { |
|
| 40 | + | unsafe fn newState(nfa: *unsafe mut NfaState) -> u32 { |
|
| 41 | 41 | let s: u32 = nfa.stateCount; |
|
| 42 | 42 | set nfa.stateFirst[s] = NIL; |
|
| 43 | 43 | set nfa.stateCount += 1; |
|
| 44 | 44 | return s; |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | - | fn addTrans(nfa: *mut NfaState, from: u32, kind: u32, ch: u8, to: u32) { |
|
| 47 | + | unsafe fn addTrans(nfa: *unsafe mut NfaState, from: u32, kind: u32, ch: u8, to: u32) { |
|
| 48 | 48 | let idx: u32 = nfa.transCount; |
|
| 49 | 49 | set nfa.trans[idx] = Trans { kind, ch, to }; |
|
| 50 | 50 | set nfa.transNext[idx] = nfa.stateFirst[from]; |
|
| 51 | 51 | set nfa.stateFirst[from] = idx; |
|
| 52 | 52 | set nfa.transCount += 1; |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | - | fn pushFrag(nfa: *mut NfaState, f: Frag) { |
|
| 55 | + | unsafe fn pushFrag(nfa: *unsafe mut NfaState, f: Frag) { |
|
| 56 | 56 | set nfa.fragStack[nfa.fragTop] = f; |
|
| 57 | 57 | set nfa.fragTop += 1; |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | - | fn popFrag(nfa: *mut NfaState) -> Frag { |
|
| 60 | + | unsafe fn popFrag(nfa: *unsafe mut NfaState) -> Frag { |
|
| 61 | 61 | set nfa.fragTop -= 1; |
|
| 62 | 62 | return nfa.fragStack[nfa.fragTop]; |
|
| 63 | 63 | } |
|
| 64 | 64 | ||
| 65 | - | fn setEmpty(s: *mut [u32]) { |
|
| 65 | + | unsafe fn setEmpty(s: *unsafe mut [u32]) { |
|
| 66 | 66 | set s[0] = 0; |
|
| 67 | 67 | set s[1] = 0; |
|
| 68 | 68 | set s[2] = 0; |
|
| 69 | 69 | set s[3] = 0; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | - | fn setAdd(s: *mut [u32], bit: u32) { |
|
| 72 | + | unsafe fn setAdd(s: *unsafe mut [u32], bit: u32) { |
|
| 73 | 73 | let word: u32 = bit / 32; |
|
| 74 | 74 | let pos: u32 = bit % 32; |
|
| 75 | 75 | set s[word] |= (1 << pos); |
|
| 76 | 76 | } |
|
| 77 | 77 | ||
| 78 | - | fn setHas(s: *[u32], bit: u32) -> bool { |
|
| 78 | + | unsafe fn setHas(s: *unsafe [u32], bit: u32) -> bool { |
|
| 79 | 79 | let word: u32 = bit / 32; |
|
| 80 | 80 | let pos: u32 = bit % 32; |
|
| 81 | 81 | return (s[word] >> pos) & 1 == 1; |
|
| 82 | 82 | } |
|
| 83 | 83 | ||
| 84 | - | fn setIsEmpty(s: *[u32]) -> bool { |
|
| 84 | + | unsafe fn setIsEmpty(s: *unsafe [u32]) -> bool { |
|
| 85 | 85 | return s[0] == 0 and s[1] == 0 and s[2] == 0 and s[3] == 0; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | - | fn setCopy(dst: *mut [u32], src: *[u32]) { |
|
| 88 | + | unsafe fn setCopy(dst: *unsafe mut [u32], src: *unsafe [u32]) { |
|
| 89 | 89 | set dst[0] = src[0]; |
|
| 90 | 90 | set dst[1] = src[1]; |
|
| 91 | 91 | set dst[2] = src[2]; |
|
| 92 | 92 | set dst[3] = src[3]; |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | - | fn epsilonClosure(nfa: *mut NfaState, states: *mut [u32]) { |
|
| 95 | + | unsafe fn epsilonClosure(nfa: *unsafe mut NfaState, states: *unsafe mut [u32]) { |
|
| 96 | 96 | setCopy(nfa.closure, states); |
|
| 97 | 97 | ||
| 98 | 98 | let mut changed: bool = true; |
|
| 99 | 99 | while changed { |
|
| 100 | 100 | set changed = false; |
| 117 | 117 | } |
|
| 118 | 118 | ||
| 119 | 119 | setCopy(states, nfa.closure); |
|
| 120 | 120 | } |
|
| 121 | 121 | ||
| 122 | - | fn resetNFA(nfa: *mut NfaState) { |
|
| 122 | + | unsafe fn resetNFA(nfa: *unsafe mut NfaState) { |
|
| 123 | 123 | set nfa.stateCount = 0; |
|
| 124 | 124 | set nfa.transCount = 0; |
|
| 125 | 125 | set nfa.fragTop = 0; |
|
| 126 | 126 | let mut i: u32 = 0; |
|
| 127 | 127 | while i < MAX_STATES { |
| 133 | 133 | set nfa.transNext[i] = NIL; |
|
| 134 | 134 | set i += 1; |
|
| 135 | 135 | } |
|
| 136 | 136 | } |
|
| 137 | 137 | ||
| 138 | - | fn compile(nfa: *mut NfaState, pattern: *[u8]) -> u32 { |
|
| 138 | + | unsafe fn compile(nfa: *unsafe mut NfaState, pattern: &[u8]) -> u32 { |
|
| 139 | 139 | resetNFA(nfa); |
|
| 140 | 140 | ||
| 141 | 141 | let mut i: u32 = 0; |
|
| 142 | 142 | while i < pattern.len { |
|
| 143 | 143 | let ch: u8 = pattern[i]; |
| 233 | 233 | ||
| 234 | 234 | set nfa.acceptState = frags[numFrags - 1].endState; |
|
| 235 | 235 | return frags[0].start; |
|
| 236 | 236 | } |
|
| 237 | 237 | ||
| 238 | - | fn nfaMatches(nfa: *mut NfaState, start: u32, input: *[u8]) -> bool { |
|
| 238 | + | unsafe fn nfaMatches(nfa: *unsafe mut NfaState, start: u32, input: &[u8]) -> bool { |
|
| 239 | 239 | setEmpty(nfa.current); |
|
| 240 | 240 | setAdd(nfa.current, start); |
|
| 241 | 241 | epsilonClosure(nfa, nfa.current); |
|
| 242 | 242 | ||
| 243 | 243 | let mut i: u32 = 0; |
| 271 | 271 | } |
|
| 272 | 272 | ||
| 273 | 273 | return setHas(nfa.current, nfa.acceptState); |
|
| 274 | 274 | } |
|
| 275 | 275 | ||
| 276 | - | fn testLiteral(nfa: *mut NfaState) -> i32 { |
|
| 276 | + | unsafe fn testLiteral(nfa: *unsafe mut NfaState) -> i32 { |
|
| 277 | 277 | let start: u32 = compile(nfa, "abc"); |
|
| 278 | 278 | ||
| 279 | 279 | assert nfaMatches(nfa, start, "abc"); |
|
| 280 | 280 | if nfaMatches(nfa, start, "ab") { return 2; } |
|
| 281 | 281 | if nfaMatches(nfa, start, "abcd") { return 3; } |
|
| 282 | 282 | if nfaMatches(nfa, start, "abd") { return 4; } |
|
| 283 | 283 | ||
| 284 | 284 | return 0; |
|
| 285 | 285 | } |
|
| 286 | 286 | ||
| 287 | - | fn testStar(nfa: *mut NfaState) -> i32 { |
|
| 287 | + | unsafe fn testStar(nfa: *unsafe mut NfaState) -> i32 { |
|
| 288 | 288 | let start: u32 = compile(nfa, "a*"); |
|
| 289 | 289 | ||
| 290 | 290 | assert nfaMatches(nfa, start, ""); |
|
| 291 | 291 | assert nfaMatches(nfa, start, "a"); |
|
| 292 | 292 | assert nfaMatches(nfa, start, "aaa"); |
|
| 293 | 293 | if nfaMatches(nfa, start, "b") { return 4; } |
|
| 294 | 294 | ||
| 295 | 295 | return 0; |
|
| 296 | 296 | } |
|
| 297 | 297 | ||
| 298 | - | fn testPlus(nfa: *mut NfaState) -> i32 { |
|
| 298 | + | unsafe fn testPlus(nfa: *unsafe mut NfaState) -> i32 { |
|
| 299 | 299 | let start: u32 = compile(nfa, "a+"); |
|
| 300 | 300 | ||
| 301 | 301 | if nfaMatches(nfa, start, "") { return 1; } |
|
| 302 | 302 | assert nfaMatches(nfa, start, "a"); |
|
| 303 | 303 | assert nfaMatches(nfa, start, "aaaaa"); |
|
| 304 | 304 | ||
| 305 | 305 | return 0; |
|
| 306 | 306 | } |
|
| 307 | 307 | ||
| 308 | - | fn testQuestion(nfa: *mut NfaState) -> i32 { |
|
| 308 | + | unsafe fn testQuestion(nfa: *unsafe mut NfaState) -> i32 { |
|
| 309 | 309 | let start: u32 = compile(nfa, "a?"); |
|
| 310 | 310 | ||
| 311 | 311 | assert nfaMatches(nfa, start, ""); |
|
| 312 | 312 | assert nfaMatches(nfa, start, "a"); |
|
| 313 | 313 | if nfaMatches(nfa, start, "aa") { return 3; } |
|
| 314 | 314 | ||
| 315 | 315 | return 0; |
|
| 316 | 316 | } |
|
| 317 | 317 | ||
| 318 | - | fn testDot(nfa: *mut NfaState) -> i32 { |
|
| 318 | + | unsafe fn testDot(nfa: *unsafe mut NfaState) -> i32 { |
|
| 319 | 319 | let start: u32 = compile(nfa, ".."); |
|
| 320 | 320 | ||
| 321 | 321 | assert nfaMatches(nfa, start, "ab"); |
|
| 322 | 322 | assert nfaMatches(nfa, start, "zz"); |
|
| 323 | 323 | if nfaMatches(nfa, start, "a") { return 3; } |
|
| 324 | 324 | if nfaMatches(nfa, start, "abc") { return 4; } |
|
| 325 | 325 | ||
| 326 | 326 | return 0; |
|
| 327 | 327 | } |
|
| 328 | 328 | ||
| 329 | - | fn testComplex(nfa: *mut NfaState) -> i32 { |
|
| 329 | + | unsafe fn testComplex(nfa: *unsafe mut NfaState) -> i32 { |
|
| 330 | 330 | let start: u32 = compile(nfa, "ab*c"); |
|
| 331 | 331 | ||
| 332 | 332 | assert nfaMatches(nfa, start, "ac"); |
|
| 333 | 333 | assert nfaMatches(nfa, start, "abc"); |
|
| 334 | 334 | assert nfaMatches(nfa, start, "abbc"); |
| 337 | 337 | if nfaMatches(nfa, start, "adc") { return 6; } |
|
| 338 | 338 | ||
| 339 | 339 | return 0; |
|
| 340 | 340 | } |
|
| 341 | 341 | ||
| 342 | - | fn testDotStar(nfa: *mut NfaState) -> i32 { |
|
| 342 | + | unsafe fn testDotStar(nfa: *unsafe mut NfaState) -> i32 { |
|
| 343 | 343 | let start: u32 = compile(nfa, ".*"); |
|
| 344 | 344 | ||
| 345 | 345 | assert nfaMatches(nfa, start, ""); |
|
| 346 | 346 | assert nfaMatches(nfa, start, "hello"); |
|
| 347 | 347 | assert nfaMatches(nfa, start, "x"); |
|
| 348 | 348 | ||
| 349 | 349 | return 0; |
|
| 350 | 350 | } |
|
| 351 | 351 | ||
| 352 | - | @default fn main() -> i32 { |
|
| 352 | + | @default unsafe fn main() -> i32 { |
|
| 353 | 353 | let mut trans: [Trans; 256] = [Trans { kind: 0, ch: 0, to: 0 }; 256]; |
|
| 354 | 354 | let mut stateFirst: [u32; 128] = [0xFFFFFFFF; 128]; |
|
| 355 | 355 | let mut transNext: [u32; 256] = [0xFFFFFFFF; 256]; |
|
| 356 | 356 | let mut current: [u32; 4] = [0; 4]; |
|
| 357 | 357 | let mut nextSet: [u32; 4] = [0; 4]; |
test/tests/prog.sha256.rad
+6 -6
| 50 | 50 | fn ssig1(x: u32) -> u32 { |
|
| 51 | 51 | return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10); |
|
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | 54 | /// Prepare the message schedule from a 16-word (512-bit) block. |
|
| 55 | - | fn prepareSchedule(s: *mut Sha256, block: *[u32]) { |
|
| 55 | + | fn prepareSchedule(s: &mut Sha256, block: &[u32]) { |
|
| 56 | 56 | // Copy the first 16 words directly. |
|
| 57 | 57 | let mut i: u32 = 0; |
|
| 58 | 58 | while i < 16 { |
|
| 59 | 59 | set s.w[i] = block[i]; |
|
| 60 | 60 | set i += 1; |
| 65 | 65 | set i += 1; |
|
| 66 | 66 | } |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | 69 | /// Run the 64-round compression function. |
|
| 70 | - | fn compress(s: *mut Sha256, k: *[u32]) { |
|
| 70 | + | fn compress(s: &mut Sha256, k: &[u32]) { |
|
| 71 | 71 | let mut a: u32 = s.h[0]; |
|
| 72 | 72 | let mut b: u32 = s.h[1]; |
|
| 73 | 73 | let mut c: u32 = s.h[2]; |
|
| 74 | 74 | let mut d: u32 = s.h[3]; |
|
| 75 | 75 | let mut e: u32 = s.h[4]; |
| 101 | 101 | set s.h[6] += g; |
|
| 102 | 102 | set s.h[7] += hh; |
|
| 103 | 103 | } |
|
| 104 | 104 | ||
| 105 | 105 | /// Reset hash state to initial values. |
|
| 106 | - | fn resetHash(s: *mut Sha256) { |
|
| 106 | + | fn resetHash(s: &mut Sha256) { |
|
| 107 | 107 | let mut i: u32 = 0; |
|
| 108 | 108 | while i < 8 { |
|
| 109 | 109 | set s.h[i] = INIT_H[i]; |
|
| 110 | 110 | set i += 1; |
|
| 111 | 111 | } |
|
| 112 | 112 | } |
|
| 113 | 113 | ||
| 114 | 114 | /// Pad and hash a short message (up to 55 bytes, fits in one 512-bit block). |
|
| 115 | - | fn hashMessage(s: *mut Sha256, k: *[u32], msg: *[u8]) -> i32 { |
|
| 115 | + | fn hashMessage(s: &mut Sha256, k: &[u32], msg: &[u8]) -> i32 { |
|
| 116 | 116 | // The message must fit in a single block (max 55 bytes for 1-block padding). |
|
| 117 | 117 | if msg.len > 55 { |
|
| 118 | 118 | return -1; |
|
| 119 | 119 | } |
|
| 120 | 120 |
| 157 | 157 | return 0; |
|
| 158 | 158 | } |
|
| 159 | 159 | ||
| 160 | 160 | /// Test SHA-256 of the empty string "". |
|
| 161 | 161 | /// Expected: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
|
| 162 | - | fn testEmpty(s: *mut Sha256, k: *[u32]) -> i32 { |
|
| 162 | + | fn testEmpty(s: &mut Sha256, k: &[u32]) -> i32 { |
|
| 163 | 163 | assert hashMessage(s, k, &[]) == 0; |
|
| 164 | 164 | ||
| 165 | 165 | assert s.h[0] == 0xE3B0C442; |
|
| 166 | 166 | assert s.h[1] == 0x98FC1C14; |
|
| 167 | 167 | assert s.h[2] == 0x9AFBF4C8; |
| 173 | 173 | return 0; |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Test SHA-256 of "abc". |
|
| 177 | 177 | /// Expected: ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad |
|
| 178 | - | fn testAbc(s: *mut Sha256, k: *[u32]) -> i32 { |
|
| 178 | + | fn testAbc(s: &mut Sha256, k: &[u32]) -> i32 { |
|
| 179 | 179 | let msg: [u8; 3] = [0x61, 0x62, 0x63]; |
|
| 180 | 180 | assert hashMessage(s, k, &msg[..]) == 0; |
|
| 181 | 181 | ||
| 182 | 182 | assert s.h[0] == 0xBA7816BF; |
|
| 183 | 183 | assert s.h[1] == 0x8F01CFEA; |
test/tests/prog.sieve.rad
+8 -8
| 2 | 2 | //! Sieve of Eratosthenes. |
|
| 3 | 3 | //! Find all primes up to 256 using a boolean array. |
|
| 4 | 4 | //! Verify the count of primes matches the known value (54 primes <= 256). |
|
| 5 | 5 | ||
| 6 | 6 | /// Mark all multiples of p as composite. |
|
| 7 | - | fn markMultiples(sieve: *mut [bool], p: u32) { |
|
| 7 | + | fn markMultiples(sieve: &mut [bool], p: u32) { |
|
| 8 | 8 | let mut i: u32 = p * p; |
|
| 9 | 9 | while i < sieve.len { |
|
| 10 | 10 | set sieve[i] = true; |
|
| 11 | 11 | set i += p; |
|
| 12 | 12 | } |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Run the sieve algorithm. |
|
| 16 | - | fn runSieve(sieve: *mut [bool]) { |
|
| 16 | + | fn runSieve(sieve: &mut [bool]) { |
|
| 17 | 17 | // 0 and 1 are not prime. |
|
| 18 | 18 | set sieve[0] = true; |
|
| 19 | 19 | set sieve[1] = true; |
|
| 20 | 20 | ||
| 21 | 21 | let mut p: u32 = 2; |
| 26 | 26 | set p += 1; |
|
| 27 | 27 | } |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Count the number of primes found. |
|
| 31 | - | fn countPrimes(sieve: *[bool]) -> u32 { |
|
| 31 | + | fn countPrimes(sieve: &[bool]) -> u32 { |
|
| 32 | 32 | let mut count: u32 = 0; |
|
| 33 | 33 | for composite in sieve { |
|
| 34 | 34 | if not composite { |
|
| 35 | 35 | set count += 1; |
|
| 36 | 36 | } |
|
| 37 | 37 | } |
|
| 38 | 38 | return count; |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Collect primes into a slice, return count. |
|
| 42 | - | fn collectPrimes(sieve: *[bool], primes: *mut [u32]) -> u32 { |
|
| 42 | + | fn collectPrimes(sieve: &[bool], primes: &mut [u32]) -> u32 { |
|
| 43 | 43 | let mut count: u32 = 0; |
|
| 44 | 44 | for composite, idx in sieve { |
|
| 45 | 45 | if not composite { |
|
| 46 | 46 | if count < primes.len { |
|
| 47 | 47 | set primes[count] = idx; |
| 51 | 51 | } |
|
| 52 | 52 | return count; |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | 55 | /// Check that specific known primes are marked correctly. |
|
| 56 | - | fn verifyKnownPrimes(sieve: *[bool]) -> i32 { |
|
| 56 | + | fn verifyKnownPrimes(sieve: &[bool]) -> i32 { |
|
| 57 | 57 | // Known small primes. |
|
| 58 | 58 | let primes: [u32; 6] = [2, 3, 5, 7, 11, 13]; |
|
| 59 | 59 | for p in primes { |
|
| 60 | 60 | if sieve[p] { |
|
| 61 | 61 | return 1; |
| 78 | 78 | assert sieve[250]; |
|
| 79 | 79 | return 0; |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | 82 | /// Verify that collected primes are in ascending order and all valid. |
|
| 83 | - | fn verifyCollected(sieve: *[bool]) -> i32 { |
|
| 83 | + | unsafe fn verifyCollected(sieve: &[bool]) -> i32 { |
|
| 84 | 84 | let mut primesBuf: [u32; 64] = [0; 64]; |
|
| 85 | 85 | let count = collectPrimes(sieve, &mut primesBuf[..]); |
|
| 86 | 86 | ||
| 87 | 87 | assert count == 54; |
|
| 88 | 88 |
| 90 | 90 | assert primesBuf[0] == 2; |
|
| 91 | 91 | // Last prime should be 251. |
|
| 92 | 92 | assert primesBuf[count - 1] == 251; |
|
| 93 | 93 | ||
| 94 | 94 | // Verify ascending order. |
|
| 95 | - | let collected = &primesBuf[0..count]; |
|
| 95 | + | let collected: *unsafe [u32] = &primesBuf[0..count]; |
|
| 96 | 96 | let mut prev: ?u32 = nil; |
|
| 97 | 97 | for p in collected { |
|
| 98 | 98 | if let prevVal = prev { |
|
| 99 | 99 | assert prevVal < p; |
|
| 100 | 100 | } |
|
| 101 | 101 | set prev = p; |
|
| 102 | 102 | } |
|
| 103 | 103 | return 0; |
|
| 104 | 104 | } |
|
| 105 | 105 | ||
| 106 | - | @default fn main() -> i32 { |
|
| 106 | + | @default unsafe fn main() -> i32 { |
|
| 107 | 107 | let mut sieve: [bool; 256] = [false; 256]; |
|
| 108 | 108 | runSieve(&mut sieve[..]); |
|
| 109 | 109 | ||
| 110 | 110 | let r1 = verifyKnownPrimes(&sieve[..]); |
|
| 111 | 111 | if r1 <> 0 { |
test/tests/prog.symtab.rad
+18 -18
| 30 | 30 | symbolCount: u32, |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | 33 | /// The symbol table. |
|
| 34 | 34 | record SymTab: Copy { |
|
| 35 | - | symbols: *mut [Symbol], |
|
| 35 | + | symbols: *unsafe mut [Symbol], |
|
| 36 | 36 | symbolCount: u32, |
|
| 37 | - | scopes: *mut [ScopeMarker], |
|
| 37 | + | scopes: *unsafe mut [ScopeMarker], |
|
| 38 | 38 | scopeDepth: u32, |
|
| 39 | - | buckets: *mut [u32], |
|
| 39 | + | buckets: *unsafe mut [u32], |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// Simple string hash function. |
|
| 43 | - | fn hashName(name: *[u8]) -> u32 { |
|
| 43 | + | fn hashName(name: &[u8]) -> u32 { |
|
| 44 | 44 | let mut h: u32 = 5381; |
|
| 45 | 45 | for ch in name { |
|
| 46 | 46 | set h = ((h << 5) + h) + ch as u32; |
|
| 47 | 47 | } |
|
| 48 | 48 | return h; |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Initialize the symbol table. |
|
| 52 | - | fn init(tab: *mut SymTab) { |
|
| 52 | + | unsafe fn init(tab: &mut SymTab) { |
|
| 53 | 53 | set tab.symbolCount = 0; |
|
| 54 | 54 | set tab.scopeDepth = 0; |
|
| 55 | 55 | ||
| 56 | 56 | for i in 0..HASH_SIZE { |
|
| 57 | 57 | set tab.buckets[i] = NIL; |
|
| 58 | 58 | } |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Push a new scope. |
|
| 62 | - | fn pushScope(tab: *mut SymTab) { |
|
| 62 | + | unsafe fn pushScope(tab: &mut SymTab) { |
|
| 63 | 63 | set tab.scopes[tab.scopeDepth] = ScopeMarker { symbolCount: tab.symbolCount }; |
|
| 64 | 64 | set tab.scopeDepth += 1; |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | /// Pop the current scope, removing all symbols defined in it. |
|
| 68 | - | fn popScope(tab: *mut SymTab) { |
|
| 68 | + | unsafe fn popScope(tab: &mut SymTab) { |
|
| 69 | 69 | if tab.scopeDepth == 0 { |
|
| 70 | 70 | return; |
|
| 71 | 71 | } |
|
| 72 | 72 | set tab.scopeDepth -= 1; |
|
| 73 | 73 | let marker = tab.scopes[tab.scopeDepth]; |
| 91 | 91 | } |
|
| 92 | 92 | } |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | 95 | /// Define a symbol in the current scope. |
|
| 96 | - | fn define(tab: *mut SymTab, name: *[u8], value: i32) -> u32 { |
|
| 96 | + | unsafe fn define(tab: &mut SymTab, name: &[u8], value: i32) -> u32 { |
|
| 97 | 97 | let h = hashName(name); |
|
| 98 | 98 | let bucket = h % HASH_SIZE; |
|
| 99 | 99 | ||
| 100 | 100 | // Check for shadowed symbol with same name. |
|
| 101 | 101 | let mut shadowIdx: u32 = NIL; |
| 126 | 126 | set tab.symbolCount += 1; |
|
| 127 | 127 | return idx; |
|
| 128 | 128 | } |
|
| 129 | 129 | ||
| 130 | 130 | /// Look up a symbol by name. Returns the value if found. |
|
| 131 | - | fn lookup(tab: *SymTab, name: *[u8]) -> ?i32 { |
|
| 131 | + | unsafe fn lookup(tab: &SymTab, name: &[u8]) -> ?i32 { |
|
| 132 | 132 | let h = hashName(name); |
|
| 133 | 133 | let bucket = h % HASH_SIZE; |
|
| 134 | 134 | let mut cur = tab.buckets[bucket]; |
|
| 135 | 135 | ||
| 136 | 136 | while cur <> NIL { |
| 141 | 141 | } |
|
| 142 | 142 | return nil; |
|
| 143 | 143 | } |
|
| 144 | 144 | ||
| 145 | 145 | /// Update a symbol's value. Returns true if the symbol was found. |
|
| 146 | - | fn update(tab: *mut SymTab, name: *[u8], newValue: i32) -> bool { |
|
| 146 | + | unsafe fn update(tab: &mut SymTab, name: &[u8], newValue: i32) -> bool { |
|
| 147 | 147 | let h = hashName(name); |
|
| 148 | 148 | let bucket = h % HASH_SIZE; |
|
| 149 | 149 | let mut cur = tab.buckets[bucket]; |
|
| 150 | 150 | ||
| 151 | 151 | while cur <> NIL { |
| 157 | 157 | } |
|
| 158 | 158 | return false; |
|
| 159 | 159 | } |
|
| 160 | 160 | ||
| 161 | 161 | /// Test basic define and lookup. |
|
| 162 | - | fn testBasic(tab: *mut SymTab) -> i32 { |
|
| 162 | + | unsafe fn testBasic(tab: &mut SymTab) -> i32 { |
|
| 163 | 163 | init(tab); |
|
| 164 | 164 | pushScope(tab); |
|
| 165 | 165 | ||
| 166 | 166 | define(tab, "x", 10); |
|
| 167 | 167 | define(tab, "y", 20); |
| 190 | 190 | popScope(tab); |
|
| 191 | 191 | return 0; |
|
| 192 | 192 | } |
|
| 193 | 193 | ||
| 194 | 194 | /// Test scope shadowing. |
|
| 195 | - | fn testShadowing(tab: *mut SymTab) -> i32 { |
|
| 195 | + | unsafe fn testShadowing(tab: &mut SymTab) -> i32 { |
|
| 196 | 196 | init(tab); |
|
| 197 | 197 | pushScope(tab); |
|
| 198 | 198 | define(tab, "x", 1); |
|
| 199 | 199 | ||
| 200 | 200 | // Verify outer x. |
| 223 | 223 | popScope(tab); |
|
| 224 | 224 | return 0; |
|
| 225 | 225 | } |
|
| 226 | 226 | ||
| 227 | 227 | /// Test deep nesting with shadowing. |
|
| 228 | - | fn testDeepNesting(tab: *mut SymTab) -> i32 { |
|
| 228 | + | unsafe fn testDeepNesting(tab: &mut SymTab) -> i32 { |
|
| 229 | 229 | init(tab); |
|
| 230 | 230 | ||
| 231 | 231 | // Define x at each of 8 scope levels. |
|
| 232 | 232 | let mut i: u32 = 0; |
|
| 233 | 233 | while i < 8 { |
| 257 | 257 | popScope(tab); |
|
| 258 | 258 | return 0; |
|
| 259 | 259 | } |
|
| 260 | 260 | ||
| 261 | 261 | /// Test multiple symbols per scope. |
|
| 262 | - | fn testMultipleSymbols(tab: *mut SymTab) -> i32 { |
|
| 262 | + | unsafe fn testMultipleSymbols(tab: &mut SymTab) -> i32 { |
|
| 263 | 263 | init(tab); |
|
| 264 | 264 | pushScope(tab); |
|
| 265 | 265 | ||
| 266 | 266 | // Define a bunch of symbols. |
|
| 267 | 267 | let names: [*[u8]; 8] = ["a", "bb", "ccc", "dddd", "eeeee", "ff", "ggg", "h"]; |
| 287 | 287 | popScope(tab); |
|
| 288 | 288 | return 0; |
|
| 289 | 289 | } |
|
| 290 | 290 | ||
| 291 | 291 | /// Test update functionality. |
|
| 292 | - | fn testUpdate(tab: *mut SymTab) -> i32 { |
|
| 292 | + | unsafe fn testUpdate(tab: &mut SymTab) -> i32 { |
|
| 293 | 293 | init(tab); |
|
| 294 | 294 | pushScope(tab); |
|
| 295 | 295 | ||
| 296 | 296 | define(tab, "counter", 0); |
|
| 297 | 297 |
| 318 | 318 | popScope(tab); |
|
| 319 | 319 | return 0; |
|
| 320 | 320 | } |
|
| 321 | 321 | ||
| 322 | 322 | /// Test scope isolation: symbols in popped scopes are gone. |
|
| 323 | - | fn testScopeIsolation(tab: *mut SymTab) -> i32 { |
|
| 323 | + | unsafe fn testScopeIsolation(tab: &mut SymTab) -> i32 { |
|
| 324 | 324 | init(tab); |
|
| 325 | 325 | ||
| 326 | 326 | pushScope(tab); |
|
| 327 | 327 | define(tab, "outer", 1); |
|
| 328 | 328 |
| 356 | 356 | popScope(tab); |
|
| 357 | 357 | return 0; |
|
| 358 | 358 | } |
|
| 359 | 359 | ||
| 360 | 360 | /// Test interleaved defines and lookups across scopes using while-let. |
|
| 361 | - | fn testInterleaved(tab: *mut SymTab) -> i32 { |
|
| 361 | + | unsafe fn testInterleaved(tab: &mut SymTab) -> i32 { |
|
| 362 | 362 | init(tab); |
|
| 363 | 363 | pushScope(tab); |
|
| 364 | 364 | ||
| 365 | 365 | define(tab, "a", 100); |
|
| 366 | 366 | define(tab, "b", 200); |
| 400 | 400 | popScope(tab); |
|
| 401 | 401 | popScope(tab); |
|
| 402 | 402 | return 0; |
|
| 403 | 403 | } |
|
| 404 | 404 | ||
| 405 | - | @default fn main() -> i32 { |
|
| 405 | + | @default unsafe fn main() -> i32 { |
|
| 406 | 406 | let mut symbols: [Symbol; 256] = [Symbol { nameHash: 0, value: 0, depth: 0, next: NIL, shadow: NIL }; 256]; |
|
| 407 | 407 | let mut scopes: [ScopeMarker; 16] = [ScopeMarker { symbolCount: 0 }; 16]; |
|
| 408 | 408 | let mut buckets: [u32; 64] = [NIL; 64]; |
|
| 409 | 409 | ||
| 410 | 410 | let mut tab = SymTab { |
test/tests/prog.tokenizer.rad
+34 -34
| 33 | 33 | pos: u32, |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | 36 | /// A list of tokens with a fixed-size backing store. |
|
| 37 | 37 | record TokenList: Copy { |
|
| 38 | - | tokens: *mut [Token], |
|
| 38 | + | tokens: *unsafe mut [Token], |
|
| 39 | 39 | count: u32, |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | /// AST node for parsed expressions. |
|
| 43 | 43 | union Expr: Copy { |
| 53 | 53 | right: u32, |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Pool of AST nodes. |
|
| 57 | 57 | record ExprPool: Copy { |
|
| 58 | - | nodes: *mut [Expr], |
|
| 58 | + | nodes: *unsafe mut [Expr], |
|
| 59 | 59 | count: u32, |
|
| 60 | 60 | } |
|
| 61 | 61 | ||
| 62 | 62 | /// Parser state. |
|
| 63 | 63 | record Parser: Copy { |
|
| 64 | - | tokens: *[Token], |
|
| 64 | + | tokens: *unsafe [Token], |
|
| 65 | 65 | tokenCount: u32, |
|
| 66 | 66 | pos: u32, |
|
| 67 | - | pool: *mut ExprPool, |
|
| 67 | + | pool: *unsafe mut ExprPool, |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | /// Check if a byte is a digit. |
|
| 71 | 71 | fn isDigit(c: u8) -> bool { |
|
| 72 | 72 | return c >= 48 and c <= 57; |
| 76 | 76 | fn isSpace(c: u8) -> bool { |
|
| 77 | 77 | return c == 32 or c == 9 or c == 10 or c == 13; |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// Peek at the current character, returning nil at end. |
|
| 81 | - | fn peek(lex: *Lexer) -> ?u8 { |
|
| 81 | + | unsafe fn peek(lex: *unsafe Lexer) -> ?u8 { |
|
| 82 | 82 | if lex.pos < lex.source.len { |
|
| 83 | 83 | return lex.source[lex.pos]; |
|
| 84 | 84 | } |
|
| 85 | 85 | return nil; |
|
| 86 | 86 | } |
|
| 87 | 87 | ||
| 88 | 88 | /// Advance the lexer by one character. |
|
| 89 | - | fn advance(lex: *mut Lexer) { |
|
| 89 | + | unsafe fn advance(lex: *unsafe mut Lexer) { |
|
| 90 | 90 | if lex.pos < lex.source.len { |
|
| 91 | 91 | set lex.pos += 1; |
|
| 92 | 92 | } |
|
| 93 | 93 | } |
|
| 94 | 94 | ||
| 95 | 95 | /// Skip whitespace characters. |
|
| 96 | - | fn skipWhitespace(lex: *mut Lexer) { |
|
| 96 | + | unsafe fn skipWhitespace(lex: *unsafe mut Lexer) { |
|
| 97 | 97 | while let ch = peek(lex); isSpace(ch) { |
|
| 98 | 98 | advance(lex); |
|
| 99 | 99 | } |
|
| 100 | 100 | } |
|
| 101 | 101 | ||
| 102 | 102 | /// Scan a number literal. |
|
| 103 | - | fn scanNumber(lex: *mut Lexer) -> i32 { |
|
| 103 | + | unsafe fn scanNumber(lex: *unsafe mut Lexer) -> i32 { |
|
| 104 | 104 | let mut value: i32 = 0; |
|
| 105 | 105 | while let ch = peek(lex); isDigit(ch) { |
|
| 106 | 106 | set value = value * 10 + (ch - 48) as i32; |
|
| 107 | 107 | advance(lex); |
|
| 108 | 108 | } |
|
| 109 | 109 | return value; |
|
| 110 | 110 | } |
|
| 111 | 111 | ||
| 112 | 112 | /// Get the next token from the lexer. |
|
| 113 | - | fn nextToken(lex: *mut Lexer) -> Token { |
|
| 113 | + | unsafe fn nextToken(lex: *unsafe mut Lexer) -> Token { |
|
| 114 | 114 | skipWhitespace(lex); |
|
| 115 | 115 | ||
| 116 | 116 | if let ch = peek(lex) { |
|
| 117 | 117 | if isDigit(ch) { |
|
| 118 | 118 | return Token::Number(scanNumber(lex)); |
| 131 | 131 | } |
|
| 132 | 132 | return Token::Eof; |
|
| 133 | 133 | } |
|
| 134 | 134 | ||
| 135 | 135 | /// Tokenize the entire source into a token list. |
|
| 136 | - | fn tokenize(lex: *mut Lexer, list: *mut TokenList) -> bool { |
|
| 136 | + | unsafe fn tokenize(lex: *unsafe mut Lexer, list: *unsafe mut TokenList) -> bool { |
|
| 137 | 137 | let mut done: bool = false; |
|
| 138 | 138 | while not done { |
|
| 139 | 139 | let tok = nextToken(lex); |
|
| 140 | 140 | match tok { |
|
| 141 | 141 | case Token::Invalid(_) => { |
| 157 | 157 | } |
|
| 158 | 158 | return true; |
|
| 159 | 159 | } |
|
| 160 | 160 | ||
| 161 | 161 | /// Allocate a new expression node. |
|
| 162 | - | fn newExpr(pool: *mut ExprPool, expr: Expr) -> u32 { |
|
| 162 | + | unsafe fn newExpr(pool: *unsafe mut ExprPool, expr: Expr) -> u32 { |
|
| 163 | 163 | let idx = pool.count; |
|
| 164 | 164 | set pool.nodes[idx] = expr; |
|
| 165 | 165 | set pool.count += 1; |
|
| 166 | 166 | return idx; |
|
| 167 | 167 | } |
|
| 168 | 168 | ||
| 169 | 169 | /// Get the current token in the parser. |
|
| 170 | - | fn currentToken(p: *Parser) -> Token { |
|
| 170 | + | unsafe fn currentToken(p: *unsafe Parser) -> Token { |
|
| 171 | 171 | if p.pos < p.tokenCount { |
|
| 172 | 172 | return p.tokens[p.pos]; |
|
| 173 | 173 | } |
|
| 174 | 174 | return Token::Eof; |
|
| 175 | 175 | } |
|
| 176 | 176 | ||
| 177 | 177 | /// Advance the parser to the next token. |
|
| 178 | - | fn advanceParser(p: *mut Parser) { |
|
| 178 | + | unsafe fn advanceParser(p: *unsafe mut Parser) { |
|
| 179 | 179 | if p.pos < p.tokenCount { |
|
| 180 | 180 | set p.pos += 1; |
|
| 181 | 181 | } |
|
| 182 | 182 | } |
|
| 183 | 183 | ||
| 184 | 184 | /// Parse a primary expression (number, parenthesized expression, or unary minus). |
|
| 185 | - | fn parsePrimary(p: *mut Parser) -> ?u32 { |
|
| 185 | + | unsafe fn parsePrimary(p: *unsafe mut Parser) -> ?u32 { |
|
| 186 | 186 | let tok = currentToken(p); |
|
| 187 | 187 | ||
| 188 | 188 | match tok { |
|
| 189 | 189 | case Token::Number(n) => { |
|
| 190 | 190 | advanceParser(p); |
| 220 | 220 | } |
|
| 221 | 221 | } |
|
| 222 | 222 | } |
|
| 223 | 223 | ||
| 224 | 224 | /// Parse multiplication and division (higher precedence). |
|
| 225 | - | fn parseMulDiv(p: *mut Parser, left: u32) -> u32 { |
|
| 225 | + | unsafe fn parseMulDiv(p: *unsafe mut Parser, left: u32) -> u32 { |
|
| 226 | 226 | let mut result: u32 = left; |
|
| 227 | 227 | let mut cont: bool = true; |
|
| 228 | 228 | ||
| 229 | 229 | while cont { |
|
| 230 | 230 | let tok = currentToken(p); |
| 252 | 252 | } |
|
| 253 | 253 | return result; |
|
| 254 | 254 | } |
|
| 255 | 255 | ||
| 256 | 256 | /// Parse addition and subtraction (lower precedence). |
|
| 257 | - | fn parseAddSub(p: *mut Parser, left: u32) -> u32 { |
|
| 257 | + | unsafe fn parseAddSub(p: *unsafe mut Parser, left: u32) -> u32 { |
|
| 258 | 258 | let mut result: u32 = parseMulDiv(p, left); |
|
| 259 | 259 | let mut cont: bool = true; |
|
| 260 | 260 | ||
| 261 | 261 | while cont { |
|
| 262 | 262 | let tok = currentToken(p); |
| 286 | 286 | } |
|
| 287 | 287 | return result; |
|
| 288 | 288 | } |
|
| 289 | 289 | ||
| 290 | 290 | /// Parse a full expression. |
|
| 291 | - | fn parseExpr(p: *mut Parser) -> ?u32 { |
|
| 291 | + | unsafe fn parseExpr(p: *unsafe mut Parser) -> ?u32 { |
|
| 292 | 292 | let left = parsePrimary(p) else { |
|
| 293 | 293 | return nil; |
|
| 294 | 294 | }; |
|
| 295 | 295 | return parseAddSub(p, left); |
|
| 296 | 296 | } |
|
| 297 | 297 | ||
| 298 | 298 | /// Evaluate an expression tree. |
|
| 299 | - | fn eval(nodes: *[Expr], idx: u32) -> i32 { |
|
| 299 | + | unsafe fn eval(nodes: *unsafe [Expr], idx: u32) -> i32 { |
|
| 300 | 300 | let node = nodes[idx]; |
|
| 301 | 301 | match node { |
|
| 302 | 302 | case Expr::Num(n) => { |
|
| 303 | 303 | return n; |
|
| 304 | 304 | } |
| 324 | 324 | } |
|
| 325 | 325 | } |
|
| 326 | 326 | } |
|
| 327 | 327 | ||
| 328 | 328 | /// Count nodes in an expression tree. |
|
| 329 | - | fn countNodes(nodes: *[Expr], idx: u32) -> u32 { |
|
| 329 | + | unsafe fn countNodes(nodes: *unsafe [Expr], idx: u32) -> u32 { |
|
| 330 | 330 | let node = nodes[idx]; |
|
| 331 | 331 | match node { |
|
| 332 | 332 | case Expr::Num(_) => { return 1; } |
|
| 333 | 333 | case Expr::BinOp(data) => { |
|
| 334 | 334 | return 1 + countNodes(nodes, data.left) + countNodes(nodes, data.right); |
| 338 | 338 | } |
|
| 339 | 339 | } |
|
| 340 | 340 | } |
|
| 341 | 341 | ||
| 342 | 342 | /// Helper: tokenize, parse, and evaluate a string expression. |
|
| 343 | - | fn evaluate( |
|
| 343 | + | unsafe fn evaluate( |
|
| 344 | 344 | source: *[u8], |
|
| 345 | - | tokenBuf: *mut [Token], |
|
| 346 | - | exprBuf: *mut [Expr] |
|
| 345 | + | tokenBuf: *unsafe mut [Token], |
|
| 346 | + | exprBuf: *unsafe mut [Expr] |
|
| 347 | 347 | ) -> ?i32 { |
|
| 348 | 348 | let mut lex = Lexer { source, pos: 0 }; |
|
| 349 | 349 | let mut list = TokenList { tokens: tokenBuf, count: 0 }; |
|
| 350 | 350 | ||
| 351 | 351 | if not tokenize(&mut lex, &mut list) { |
| 365 | 365 | }; |
|
| 366 | 366 | return eval(exprBuf, root); |
|
| 367 | 367 | } |
|
| 368 | 368 | ||
| 369 | 369 | /// Test simple number. |
|
| 370 | - | fn testNumber(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 370 | + | unsafe fn testNumber(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 371 | 371 | let result = evaluate("42", tokenBuf, exprBuf) else { |
|
| 372 | 372 | return 1; |
|
| 373 | 373 | }; |
|
| 374 | 374 | assert result == 42; |
|
| 375 | 375 | return 0; |
|
| 376 | 376 | } |
|
| 377 | 377 | ||
| 378 | 378 | /// Test addition. |
|
| 379 | - | fn testAdd(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 379 | + | unsafe fn testAdd(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 380 | 380 | let result = evaluate("3 + 4", tokenBuf, exprBuf) else { |
|
| 381 | 381 | return 1; |
|
| 382 | 382 | }; |
|
| 383 | 383 | assert result == 7; |
|
| 384 | 384 | return 0; |
|
| 385 | 385 | } |
|
| 386 | 386 | ||
| 387 | 387 | /// Test precedence: multiplication before addition. |
|
| 388 | - | fn testPrecedence(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 388 | + | unsafe fn testPrecedence(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 389 | 389 | let result = evaluate("2 + 3 * 4", tokenBuf, exprBuf) else { |
|
| 390 | 390 | return 1; |
|
| 391 | 391 | }; |
|
| 392 | 392 | assert result == 14; |
|
| 393 | 393 | return 0; |
|
| 394 | 394 | } |
|
| 395 | 395 | ||
| 396 | 396 | /// Test parenthesized expression. |
|
| 397 | - | fn testParens(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 397 | + | unsafe fn testParens(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 398 | 398 | let result = evaluate("(2 + 3) * 4", tokenBuf, exprBuf) else { |
|
| 399 | 399 | return 1; |
|
| 400 | 400 | }; |
|
| 401 | 401 | assert result == 20; |
|
| 402 | 402 | return 0; |
|
| 403 | 403 | } |
|
| 404 | 404 | ||
| 405 | 405 | /// Test negation. |
|
| 406 | - | fn testNeg(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 406 | + | unsafe fn testNeg(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 407 | 407 | let result = evaluate("-5 + 8", tokenBuf, exprBuf) else { |
|
| 408 | 408 | return 1; |
|
| 409 | 409 | }; |
|
| 410 | 410 | assert result == 3; |
|
| 411 | 411 | return 0; |
|
| 412 | 412 | } |
|
| 413 | 413 | ||
| 414 | 414 | /// Test complex expression. |
|
| 415 | - | fn testComplex(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 415 | + | unsafe fn testComplex(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 416 | 416 | // (10 - 3) * (2 + 1) = 7 * 3 = 21 |
|
| 417 | 417 | let result = evaluate("(10 - 3) * (2 + 1)", tokenBuf, exprBuf) else { |
|
| 418 | 418 | return 1; |
|
| 419 | 419 | }; |
|
| 420 | 420 | assert result == 21; |
|
| 421 | 421 | return 0; |
|
| 422 | 422 | } |
|
| 423 | 423 | ||
| 424 | 424 | /// Test chained operations. |
|
| 425 | - | fn testChained(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 425 | + | unsafe fn testChained(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 426 | 426 | // 100 - 20 - 30 - 10 = 40 |
|
| 427 | 427 | let result = evaluate("100 - 20 - 30 - 10", tokenBuf, exprBuf) else { |
|
| 428 | 428 | return 1; |
|
| 429 | 429 | }; |
|
| 430 | 430 | assert result == 40; |
|
| 431 | 431 | return 0; |
|
| 432 | 432 | } |
|
| 433 | 433 | ||
| 434 | 434 | /// Test token counting via for-in. |
|
| 435 | - | fn testTokenCount(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 435 | + | unsafe fn testTokenCount(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 436 | 436 | let mut lex = Lexer { source: "1 + 2 * 3", pos: 0 }; |
|
| 437 | 437 | let mut list = TokenList { tokens: tokenBuf, count: 0 }; |
|
| 438 | 438 | assert tokenize(&mut lex, &mut list); |
|
| 439 | 439 | ||
| 440 | 440 | // Should be: 1, +, 2, *, 3, Eof = 6 tokens |
| 455 | 455 | ||
| 456 | 456 | return 0; |
|
| 457 | 457 | } |
|
| 458 | 458 | ||
| 459 | 459 | /// Test division and mixed operations. |
|
| 460 | - | fn testDivision(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 460 | + | unsafe fn testDivision(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 461 | 461 | // 20 / 4 + 3 = 5 + 3 = 8 |
|
| 462 | 462 | let result = evaluate("20 / 4 + 3", tokenBuf, exprBuf) else { |
|
| 463 | 463 | return 1; |
|
| 464 | 464 | }; |
|
| 465 | 465 | assert result == 8; |
|
| 466 | 466 | return 0; |
|
| 467 | 467 | } |
|
| 468 | 468 | ||
| 469 | 469 | /// Test deeply nested parentheses. |
|
| 470 | - | fn testDeepNesting(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 470 | + | unsafe fn testDeepNesting(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 471 | 471 | // ((((5)))) = 5 |
|
| 472 | 472 | let result = evaluate("((((5))))", tokenBuf, exprBuf) else { |
|
| 473 | 473 | return 1; |
|
| 474 | 474 | }; |
|
| 475 | 475 | assert result == 5; |
|
| 476 | 476 | return 0; |
|
| 477 | 477 | } |
|
| 478 | 478 | ||
| 479 | 479 | /// Test node count of complex expression. |
|
| 480 | - | fn testNodeCount(tokenBuf: *mut [Token], exprBuf: *mut [Expr]) -> i32 { |
|
| 480 | + | unsafe fn testNodeCount(tokenBuf: *unsafe mut [Token], exprBuf: *unsafe mut [Expr]) -> i32 { |
|
| 481 | 481 | let mut lex = Lexer { source: "1 + 2 * 3", pos: 0 }; |
|
| 482 | 482 | let mut list = TokenList { tokens: tokenBuf, count: 0 }; |
|
| 483 | 483 | assert tokenize(&mut lex, &mut list); |
|
| 484 | 484 | ||
| 485 | 485 | let mut pool = ExprPool { nodes: exprBuf, count: 0 }; |
| 498 | 498 | let count = countNodes(exprBuf, root); |
|
| 499 | 499 | assert count == 5; |
|
| 500 | 500 | return 0; |
|
| 501 | 501 | } |
|
| 502 | 502 | ||
| 503 | - | @default fn main() -> i32 { |
|
| 503 | + | @default unsafe fn main() -> i32 { |
|
| 504 | 504 | let mut tokenBuf: [Token; 128] = [Token::Eof; 128]; |
|
| 505 | 505 | let mut exprBuf: [Expr; 128] = [Expr::Num(0); 128]; |
|
| 506 | 506 | ||
| 507 | 507 | let r1 = testNumber(&mut tokenBuf[..], &mut exprBuf[..]); |
|
| 508 | 508 | if r1 <> 0 { |
test/tests/prog.vm.rad
+27 -27
| 56 | 56 | localBase: u32, |
|
| 57 | 57 | } |
|
| 58 | 58 | ||
| 59 | 59 | /// The VM state. |
|
| 60 | 60 | record VM: Copy { |
|
| 61 | - | code: *[Op], |
|
| 61 | + | code: *unsafe [Op], |
|
| 62 | 62 | codeLen: u32, |
|
| 63 | - | stack: *mut [i32], |
|
| 63 | + | stack: *unsafe mut [i32], |
|
| 64 | 64 | sp: u32, |
|
| 65 | - | locals: *mut [i32], |
|
| 66 | - | frames: *mut [Frame], |
|
| 65 | + | locals: *unsafe mut [i32], |
|
| 66 | + | frames: *unsafe mut [Frame], |
|
| 67 | 67 | frameCount: u32, |
|
| 68 | 68 | pc: u32, |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | 71 | /// VM error types. |
| 81 | 81 | /// Too many nested calls. |
|
| 82 | 82 | CallOverflow, |
|
| 83 | 83 | } |
|
| 84 | 84 | ||
| 85 | 85 | /// Push a value onto the stack. |
|
| 86 | - | fn push(vm: *mut VM, value: i32) throws (VmError) { |
|
| 86 | + | unsafe fn push(vm: *unsafe mut VM, value: i32) throws (VmError) { |
|
| 87 | 87 | if vm.sp >= MAX_STACK { |
|
| 88 | 88 | throw VmError::StackOverflow; |
|
| 89 | 89 | } |
|
| 90 | 90 | set vm.stack[vm.sp] = value; |
|
| 91 | 91 | set vm.sp += 1; |
|
| 92 | 92 | } |
|
| 93 | 93 | ||
| 94 | 94 | /// Pop a value from the stack. |
|
| 95 | - | fn pop(vm: *mut VM) -> i32 throws (VmError) { |
|
| 95 | + | unsafe fn pop(vm: *unsafe mut VM) -> i32 throws (VmError) { |
|
| 96 | 96 | if vm.sp == 0 { |
|
| 97 | 97 | throw VmError::StackUnderflow; |
|
| 98 | 98 | } |
|
| 99 | 99 | set vm.sp -= 1; |
|
| 100 | 100 | return vm.stack[vm.sp]; |
|
| 101 | 101 | } |
|
| 102 | 102 | ||
| 103 | 103 | /// Peek at the top of the stack without removing. |
|
| 104 | - | fn peek(vm: *VM) -> i32 throws (VmError) { |
|
| 104 | + | unsafe fn peek(vm: *unsafe VM) -> i32 throws (VmError) { |
|
| 105 | 105 | if vm.sp == 0 { |
|
| 106 | 106 | throw VmError::StackUnderflow; |
|
| 107 | 107 | } |
|
| 108 | 108 | return vm.stack[vm.sp - 1]; |
|
| 109 | 109 | } |
|
| 110 | 110 | ||
| 111 | 111 | /// Execute the bytecode program. |
|
| 112 | - | fn execute(vm: *mut VM) -> i32 throws (VmError) { |
|
| 112 | + | unsafe fn execute(vm: *unsafe mut VM) -> i32 throws (VmError) { |
|
| 113 | 113 | while vm.pc < vm.codeLen { |
|
| 114 | 114 | let instr = vm.code[vm.pc]; |
|
| 115 | 115 | set vm.pc += 1; |
|
| 116 | 116 | ||
| 117 | 117 | match instr { |
| 234 | 234 | } |
|
| 235 | 235 | throw VmError::InvalidPC; |
|
| 236 | 236 | } |
|
| 237 | 237 | ||
| 238 | 238 | /// Helper to initialize VM and run a program. |
|
| 239 | - | fn runProgram( |
|
| 240 | - | code: *[Op], |
|
| 239 | + | unsafe fn runProgram( |
|
| 240 | + | code: *unsafe [Op], |
|
| 241 | 241 | codeLen: u32, |
|
| 242 | - | stackBuf: *mut [i32], |
|
| 243 | - | localsBuf: *mut [i32], |
|
| 244 | - | framesBuf: *mut [Frame] |
|
| 242 | + | stackBuf: *unsafe mut [i32], |
|
| 243 | + | localsBuf: *unsafe mut [i32], |
|
| 244 | + | framesBuf: *unsafe mut [Frame] |
|
| 245 | 245 | ) -> i32 throws (VmError) { |
|
| 246 | 246 | let mut vm = VM { |
|
| 247 | 247 | code, |
|
| 248 | 248 | codeLen, |
|
| 249 | 249 | stack: stackBuf, |
| 255 | 255 | }; |
|
| 256 | 256 | return try execute(&mut vm); |
|
| 257 | 257 | } |
|
| 258 | 258 | ||
| 259 | 259 | /// Test basic arithmetic: 3 + 4 * 2 = 11 |
|
| 260 | - | fn testArith(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 260 | + | unsafe fn testArith(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 261 | 261 | let mut code: [Op; 8] = [Op::Halt; 8]; |
|
| 262 | 262 | set code[0] = Op::Push(3); |
|
| 263 | 263 | set code[1] = Op::Push(4); |
|
| 264 | 264 | set code[2] = Op::Push(2); |
|
| 265 | 265 | set code[3] = Op::Mul; |
| 270 | 270 | assert result == 11; |
|
| 271 | 271 | return 0; |
|
| 272 | 272 | } |
|
| 273 | 273 | ||
| 274 | 274 | /// Test local variables: x = 5, y = 7, push x + y. |
|
| 275 | - | fn testLocals(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 275 | + | unsafe fn testLocals(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 276 | 276 | let mut code: [Op; 16] = [Op::Halt; 16]; |
|
| 277 | 277 | set code[0] = Op::Push(5); |
|
| 278 | 278 | set code[1] = Op::Store(0); // x = 5 |
|
| 279 | 279 | set code[2] = Op::Push(7); |
|
| 280 | 280 | set code[3] = Op::Store(1); // y = 7 |
| 287 | 287 | assert result == 12; |
|
| 288 | 288 | return 0; |
|
| 289 | 289 | } |
|
| 290 | 290 | ||
| 291 | 291 | /// Test conditional jump: if 3 > 2 then push 42 else push 99. |
|
| 292 | - | fn testConditional(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 292 | + | unsafe fn testConditional(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 293 | 293 | let mut code: [Op; 16] = [Op::Halt; 16]; |
|
| 294 | 294 | set code[0] = Op::Push(3); |
|
| 295 | 295 | set code[1] = Op::Push(2); |
|
| 296 | 296 | set code[2] = Op::Gt; // 3 > 2 => 1 |
|
| 297 | 297 | set code[3] = Op::JumpIfZero(6); // if false, jump to 6 |
| 305 | 305 | return 0; |
|
| 306 | 306 | } |
|
| 307 | 307 | ||
| 308 | 308 | /// Test loop: sum 1..5 using jumps. |
|
| 309 | 309 | /// local[0] = counter (starts at 1), local[1] = sum (starts at 0). |
|
| 310 | - | fn testLoop(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 310 | + | unsafe fn testLoop(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 311 | 311 | let mut code: [Op; 32] = [Op::Halt; 32]; |
|
| 312 | 312 | set code[0] = Op::Push(1); |
|
| 313 | 313 | set code[1] = Op::Store(0); // counter = 1 |
|
| 314 | 314 | set code[2] = Op::Push(0); |
|
| 315 | 315 | set code[3] = Op::Store(1); // sum = 0 |
| 335 | 335 | assert result == 15; |
|
| 336 | 336 | return 0; |
|
| 337 | 337 | } |
|
| 338 | 338 | ||
| 339 | 339 | /// Test function call: call a function that computes n*2+1 for n=10. |
|
| 340 | - | fn testCall(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 340 | + | unsafe fn testCall(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 341 | 341 | let mut code: [Op; 32] = [Op::Halt; 32]; |
|
| 342 | 342 | ||
| 343 | 343 | // Main: push argument on stack, call function, halt. |
|
| 344 | 344 | set code[0] = Op::Push(10); // push argument |
|
| 345 | 345 | set code[1] = Op::Call(5); // call function at 5 |
| 362 | 362 | assert result == 21; |
|
| 363 | 363 | return 0; |
|
| 364 | 364 | } |
|
| 365 | 365 | ||
| 366 | 366 | /// Test division by zero detection using try...catch with error binding. |
|
| 367 | - | fn testDivByZero(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 367 | + | unsafe fn testDivByZero(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 368 | 368 | let mut code: [Op; 8] = [Op::Halt; 8]; |
|
| 369 | 369 | set code[0] = Op::Push(42); |
|
| 370 | 370 | set code[1] = Op::Push(0); |
|
| 371 | 371 | set code[2] = Op::Div; |
|
| 372 | 372 | set code[3] = Op::Halt; |
| 382 | 382 | assert caught == 1; |
|
| 383 | 383 | return 0; |
|
| 384 | 384 | } |
|
| 385 | 385 | ||
| 386 | 386 | /// Test negation and equality. |
|
| 387 | - | fn testNegAndEq(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 387 | + | unsafe fn testNegAndEq(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 388 | 388 | let mut code: [Op; 16] = [Op::Halt; 16]; |
|
| 389 | 389 | set code[0] = Op::Push(5); |
|
| 390 | 390 | set code[1] = Op::Neg; // -5 |
|
| 391 | 391 | set code[2] = Op::Push(-5); |
|
| 392 | 392 | set code[3] = Op::Eq; // -5 == -5 => 1 |
| 396 | 396 | assert result == 1; |
|
| 397 | 397 | return 0; |
|
| 398 | 398 | } |
|
| 399 | 399 | ||
| 400 | 400 | /// Test factorial using recursive calls: fact(6) = 720. |
|
| 401 | - | fn testFactorial(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 401 | + | unsafe fn testFactorial(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 402 | 402 | let mut code: [Op; 32] = [Op::Halt; 32]; |
|
| 403 | 403 | ||
| 404 | 404 | // Main: push 6, call fact, halt. |
|
| 405 | 405 | set code[0] = Op::Push(6); |
|
| 406 | 406 | set code[1] = Op::Call(4); // call fact at 4 |
| 432 | 432 | assert result == 720; |
|
| 433 | 433 | return 0; |
|
| 434 | 434 | } |
|
| 435 | 435 | ||
| 436 | 436 | /// Test dup instruction. |
|
| 437 | - | fn testDup(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 437 | + | unsafe fn testDup(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 438 | 438 | let mut code: [Op; 8] = [Op::Halt; 8]; |
|
| 439 | 439 | set code[0] = Op::Push(7); |
|
| 440 | 440 | set code[1] = Op::Dup; |
|
| 441 | 441 | set code[2] = Op::Add; // 7 + 7 = 14 |
|
| 442 | 442 | set code[3] = Op::Halt; |
| 445 | 445 | assert result == 14; |
|
| 446 | 446 | return 0; |
|
| 447 | 447 | } |
|
| 448 | 448 | ||
| 449 | 449 | /// Test stack underflow detection using try...catch with error binding. |
|
| 450 | - | fn testStackUnderflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 450 | + | unsafe fn testStackUnderflow(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 451 | 451 | let mut code: [Op; 4] = [Op::Halt; 4]; |
|
| 452 | 452 | set code[0] = Op::Add; // nothing on stack |
|
| 453 | 453 | set code[1] = Op::Halt; |
|
| 454 | 454 | ||
| 455 | 455 | let mut caught: i32 = 0; |
| 463 | 463 | assert caught == 1; |
|
| 464 | 464 | return 0; |
|
| 465 | 465 | } |
|
| 466 | 466 | ||
| 467 | 467 | /// Test that try...catch on success path does not execute catch block. |
|
| 468 | - | fn testSuccessNoCatch(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 468 | + | unsafe fn testSuccessNoCatch(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 469 | 469 | let mut code: [Op; 4] = [Op::Halt; 4]; |
|
| 470 | 470 | set code[0] = Op::Push(99); |
|
| 471 | 471 | set code[1] = Op::Halt; |
|
| 472 | 472 | ||
| 473 | 473 | let mut caught: i32 = 0; |
| 481 | 481 | assert result == 99; |
|
| 482 | 482 | return 0; |
|
| 483 | 483 | } |
|
| 484 | 484 | ||
| 485 | 485 | /// Test call overflow detection by exhausting frames. |
|
| 486 | - | fn testCallOverflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 486 | + | unsafe fn testCallOverflow(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 487 | 487 | let mut code: [Op; 4] = [Op::Halt; 4]; |
|
| 488 | 488 | // Infinite recursion: function calls itself. |
|
| 489 | 489 | set code[0] = Op::Call(0); |
|
| 490 | 490 | set code[1] = Op::Halt; |
|
| 491 | 491 |
| 500 | 500 | assert caught == 1; |
|
| 501 | 501 | return 0; |
|
| 502 | 502 | } |
|
| 503 | 503 | ||
| 504 | 504 | /// Test that catch with no binding works (discard the error). |
|
| 505 | - | fn testCatchNoBinding(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 { |
|
| 505 | + | unsafe fn testCatchNoBinding(stackBuf: *unsafe mut [i32], localsBuf: *unsafe mut [i32], framesBuf: *unsafe mut [Frame]) -> i32 { |
|
| 506 | 506 | let mut code: [Op; 4] = [Op::Halt; 4]; |
|
| 507 | 507 | set code[0] = Op::Pop; // underflow |
|
| 508 | 508 | set code[1] = Op::Halt; |
|
| 509 | 509 | ||
| 510 | 510 | // Catch without binding - just swallow the error. |
|
| 511 | 511 | try runProgram(&code[..], 2, stackBuf, localsBuf, framesBuf) catch {}; |
|
| 512 | 512 | return 0; |
|
| 513 | 513 | } |
|
| 514 | 514 | ||
| 515 | - | @default fn main() -> i32 { |
|
| 515 | + | @default unsafe fn main() -> i32 { |
|
| 516 | 516 | let mut stackBuf: [i32; 64] = [0; 64]; |
|
| 517 | 517 | let mut localsBuf: [i32; 128] = [0; 128]; |
|
| 518 | 518 | let mut framesBuf: [Frame; 8] = [Frame { returnAddr: 0, localBase: 0 }; 8]; |
|
| 519 | 519 | ||
| 520 | 520 | let r1 = testArith(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]); |
test/tests/ptr.addressof.local.rad
+3 -3
| 1 | 1 | record Point: Copy { x: i32, y: i32 } |
|
| 2 | 2 | ||
| 3 | 3 | /// Address of a record local (aggregate) - value is already a pointer. |
|
| 4 | - | fn addressOfRecordLocal() -> *Point { |
|
| 4 | + | unsafe fn addressOfRecordLocal() -> *unsafe Point { |
|
| 5 | 5 | let p = Point { x: 1, y: 2 }; |
|
| 6 | 6 | return &p; |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | /// Address of a scalar local - requires stack allocation. |
|
| 10 | - | fn addressOfScalarLocal() -> *i32 { |
|
| 10 | + | unsafe fn addressOfScalarLocal() -> *unsafe i32 { |
|
| 11 | 11 | let x: i32 = 42; |
|
| 12 | 12 | return &x; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | /// Address of dereference - should return the original pointer. |
|
| 16 | - | fn addressOfDeref(ptr: *i32) -> *i32 { |
|
| 16 | + | unsafe fn addressOfDeref(ptr: &i32) -> *unsafe i32 { |
|
| 17 | 17 | return &(*ptr); |
|
| 18 | 18 | } |
test/tests/ptr.addressof.rad
+1 -1
| 1 | 1 | /// Takes the address of an array element. |
|
| 2 | - | fn addressOfElem(arr: [i32; 4], idx: u32) -> *i32 { |
|
| 2 | + | unsafe fn addressOfElem(arr: [i32; 4], idx: u32) -> *unsafe i32 { |
|
| 3 | 3 | return &arr[idx]; |
|
| 4 | 4 | } |
test/tests/ptr.assign.rad
+2 -2
| 1 | 1 | //! returns: 42 |
|
| 2 | - | @default fn main() -> i32 { |
|
| 2 | + | @default unsafe fn main() -> i32 { |
|
| 3 | 3 | let mut x: i32 = 1; |
|
| 4 | - | let mut ptr: *mut i32 = &mut x; |
|
| 4 | + | let mut ptr: *unsafe mut i32 = &mut x; |
|
| 5 | 5 | ||
| 6 | 6 | set *ptr = 42; |
|
| 7 | 7 | ||
| 8 | 8 | return x; |
|
| 9 | 9 | } |
test/tests/ptr.deref.rad
+9 -9
| 1 | 1 | //! returns: 65 |
|
| 2 | 2 | //! Test pointer dereference in various contexts. |
|
| 3 | 3 | ||
| 4 | - | fn derefArrayIndex(ary: [i32; 3]) -> i32 { |
|
| 5 | - | let x: *i32 = &ary[1]; |
|
| 4 | + | unsafe fn derefArrayIndex(ary: [i32; 3]) -> i32 { |
|
| 5 | + | let x: *unsafe i32 = &ary[1]; |
|
| 6 | 6 | return *x; |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | fn derefSliceIndex(slc: *[i32]) -> i32 { |
|
| 10 | - | let x: *i32 = &slc[2]; |
|
| 9 | + | unsafe fn derefSliceIndex(slc: &[i32]) -> i32 { |
|
| 10 | + | let x: *unsafe i32 = &slc[2]; |
|
| 11 | 11 | return *x; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn derefBinop(x: i32, y: i32) -> i32 { |
|
| 15 | - | let px: *i32 = &x; |
|
| 16 | - | let py: *i32 = &y; |
|
| 14 | + | unsafe fn derefBinop(x: i32, y: i32) -> i32 { |
|
| 15 | + | let px: *unsafe i32 = &x; |
|
| 16 | + | let py: *unsafe i32 = &y; |
|
| 17 | 17 | ||
| 18 | 18 | return *px + *py; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | - | @default fn main() -> i32 { |
|
| 21 | + | @default unsafe fn main() -> i32 { |
|
| 22 | 22 | let x: i32 = 42; |
|
| 23 | - | let y: *i32 = &x; |
|
| 23 | + | let y: *unsafe i32 = &x; |
|
| 24 | 24 | let z: i32 = *y; // 42 |
|
| 25 | 25 | let r: i32 = derefBinop(3, 6); // 9 |
|
| 26 | 26 | let a: i32 = derefArrayIndex([7, 8, 9]); // 8 |
|
| 27 | 27 | let s: i32 = derefSliceIndex(&[2, 4, 6]); // 6 |
|
| 28 | 28 |
test/tests/ptr.eq.rad
+10 -10
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | // Test that pointer equality uses address comparison, not value comparison. |
|
| 9 | - | fn testPtrSameAddress() -> bool { |
|
| 9 | + | unsafe fn testPtrSameAddress() -> bool { |
|
| 10 | 10 | let p1 = Point { x: 1, y: 2 }; |
|
| 11 | - | let a = &p1; |
|
| 12 | - | let b = &p1; |
|
| 11 | + | let a: *unsafe Point = &p1; |
|
| 12 | + | let b: *unsafe Point = &p1; |
|
| 13 | 13 | ||
| 14 | 14 | return a == b; // Same address, should be equal. |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | - | fn testPtrDifferentAddressSameValues() -> bool { |
|
| 17 | + | unsafe fn testPtrDifferentAddressSameValues() -> bool { |
|
| 18 | 18 | let p1 = Point { x: 1, y: 2 }; |
|
| 19 | 19 | let p2 = Point { x: 1, y: 2 }; // Same values as p1, but different address. |
|
| 20 | - | let a = &p1; |
|
| 21 | - | let b = &p2; |
|
| 20 | + | let a: *unsafe Point = &p1; |
|
| 21 | + | let b: *unsafe Point = &p2; |
|
| 22 | 22 | ||
| 23 | 23 | return not (a == b); // Different addresses, should NOT be equal. |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | - | fn testPtrDifferentAddressDifferentValues() -> bool { |
|
| 26 | + | unsafe fn testPtrDifferentAddressDifferentValues() -> bool { |
|
| 27 | 27 | let p1 = Point { x: 1, y: 2 }; |
|
| 28 | 28 | let p2 = Point { x: 3, y: 4 }; |
|
| 29 | - | let a = &p1; |
|
| 30 | - | let b = &p2; |
|
| 29 | + | let a: *unsafe Point = &p1; |
|
| 30 | + | let b: *unsafe Point = &p2; |
|
| 31 | 31 | ||
| 32 | 32 | return not (a == b); // Different addresses, should NOT be equal. |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | - | @default fn main() -> i32 { |
|
| 35 | + | @default unsafe fn main() -> i32 { |
|
| 36 | 36 | assert testPtrSameAddress(); |
|
| 37 | 37 | assert testPtrDifferentAddressSameValues(); |
|
| 38 | 38 | assert testPtrDifferentAddressDifferentValues(); |
|
| 39 | 39 | return 0; |
|
| 40 | 40 | } |
test/tests/ptr.mutate.rad
+2 -2
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | fn mutate1(ptr: *mut i32) { |
|
| 3 | + | fn mutate1(ptr: &mut i32) { |
|
| 4 | 4 | set *ptr = 39; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn mutate2(ptr: *mut i32) { |
|
| 7 | + | fn mutate2(ptr: &mut i32) { |
|
| 8 | 8 | set *ptr += 2; |
|
| 9 | 9 | set *ptr += 1; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | @default fn main() -> i32 { |
test/tests/ptr.opaque.rad
+18 -18
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test basic opaque pointer usage (automatic coercion). |
|
| 3 | - | fn testOpaqueCasting() -> bool { |
|
| 3 | + | unsafe fn testOpaqueCasting() -> bool { |
|
| 4 | 4 | let x: u32 = 42; |
|
| 5 | - | let ptr: *u8 = &x as *u8; |
|
| 6 | - | let opq: *opaque = ptr; // Automatic coercion from *u8 to *opaque. |
|
| 7 | - | let back: *u8 = opq as *u8; |
|
| 5 | + | let ptr: *unsafe u8 = &x as *unsafe u8; |
|
| 6 | + | let opq: *unsafe opaque = ptr; // Automatic coercion from *u8 to *opaque. |
|
| 7 | + | let back: *unsafe u8 = opq as *unsafe u8; |
|
| 8 | 8 | ||
| 9 | 9 | return back == ptr; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// Test that opaque can be used in function parameters. |
|
| 13 | - | fn takesOpaque(ptr: *opaque, orig: *u8) -> bool { |
|
| 14 | - | let back: *u8 = ptr as *u8; |
|
| 13 | + | unsafe fn takesOpaque(ptr: *unsafe opaque, orig: *unsafe u8) -> bool { |
|
| 14 | + | let back: *unsafe u8 = ptr as *unsafe u8; |
|
| 15 | 15 | return back == orig; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | fn testOpaqueParams() -> bool { |
|
| 18 | + | unsafe fn testOpaqueParams() -> bool { |
|
| 19 | 19 | let x: u32 = 42; |
|
| 20 | - | let ptr: *u8 = &x as *u8; |
|
| 20 | + | let ptr: *unsafe u8 = &x as *unsafe u8; |
|
| 21 | 21 | return takesOpaque(ptr, ptr); // Automatic coercion in function call. |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | /// Test that opaque can be used in return types. |
|
| 25 | - | fn returnsOpaque(ptr: *u8) -> *opaque { |
|
| 25 | + | fn returnsOpaque(ptr: *unsafe u8) -> *unsafe opaque { |
|
| 26 | 26 | return ptr; // Automatic coercion in return. |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | - | fn testOpaqueReturn() -> bool { |
|
| 29 | + | unsafe fn testOpaqueReturn() -> bool { |
|
| 30 | 30 | let x: u32 = 42; |
|
| 31 | - | let ptr: *u8 = &x as *u8; |
|
| 32 | - | let opq: *opaque = returnsOpaque(ptr); |
|
| 33 | - | return opq as *u8 == ptr; |
|
| 31 | + | let ptr: *unsafe u8 = &x as *unsafe u8; |
|
| 32 | + | let opq: *unsafe opaque = returnsOpaque(ptr); |
|
| 33 | + | return opq as *unsafe u8 == ptr; |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | 36 | /// Test nullable opaque pointers. |
|
| 37 | - | fn testNullableOpaque() -> bool { |
|
| 38 | - | let mut opt: ?*opaque = nil; |
|
| 37 | + | unsafe fn testNullableOpaque() -> bool { |
|
| 38 | + | let mut opt: ?*unsafe opaque = nil; |
|
| 39 | 39 | if opt == nil { |
|
| 40 | 40 | let x: u32 = 42; |
|
| 41 | - | let ptr: *u8 = &x as *u8; |
|
| 41 | + | let ptr: *unsafe u8 = &x as *unsafe u8; |
|
| 42 | 42 | set opt = ptr; // Automatic coercion in assignment. |
|
| 43 | 43 | ||
| 44 | 44 | if let p = opt { |
|
| 45 | - | return p as *u8 == ptr; |
|
| 45 | + | return p as *unsafe u8 == ptr; |
|
| 46 | 46 | } |
|
| 47 | 47 | } |
|
| 48 | 48 | return false; |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | - | @default fn main() -> u32 { |
|
| 51 | + | @default unsafe fn main() -> u32 { |
|
| 52 | 52 | assert testOpaqueCasting(); |
|
| 53 | 53 | assert testOpaqueParams(); |
|
| 54 | 54 | assert testOpaqueReturn(); |
|
| 55 | 55 | assert testNullableOpaque(); |
|
| 56 | 56 | return 0; |
test/tests/range.arithmetic.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 4 | let arr: [i32; 6] = [10, 20, 30, 40, 50, 60]; |
|
| 5 | 5 | let len: u32 = 6; |
|
| 6 | 6 | ||
| 7 | 7 | // Arithmetic in range start (no parens needed). |
|
| 8 | - | let s = &arr[len - 3..len]; |
|
| 8 | + | let s: *unsafe [i32] = &arr[len - 3..len]; |
|
| 9 | 9 | if s.len <> 3 { return 1; } |
|
| 10 | 10 | if s[0] <> 40 { return 2; } |
|
| 11 | 11 | if s[2] <> 60 { return 3; } |
|
| 12 | 12 | ||
| 13 | 13 | // Arithmetic in range end. |
|
| 14 | - | let s2 = &arr[0..len - 2]; |
|
| 14 | + | let s2: *unsafe [i32] = &arr[0..len - 2]; |
|
| 15 | 15 | if s2.len <> 4 { return 4; } |
|
| 16 | 16 | if s2[3] <> 40 { return 5; } |
|
| 17 | 17 | ||
| 18 | 18 | // Arithmetic in both. |
|
| 19 | - | let s3 = &arr[len - 4..len - 1]; |
|
| 19 | + | let s3: *unsafe [i32] = &arr[len - 4..len - 1]; |
|
| 20 | 20 | if s3.len <> 3 { return 6; } |
|
| 21 | 21 | if s3[0] <> 30 { return 7; } |
|
| 22 | 22 | if s3[2] <> 50 { return 8; } |
|
| 23 | 23 | ||
| 24 | 24 | // Arithmetic in subscript. |
test/tests/record.ptr.access.rad
+2 -2
| 3 | 3 | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let mut p: Point = Point { x: 11, y: 31 }; |
|
| 10 | - | let mut r: *Point = &p; |
|
| 10 | + | let mut r: *unsafe Point = &p; |
|
| 11 | 11 | ||
| 12 | 12 | let mut x: i32 = r.x; |
|
| 13 | 13 | let mut y: i32 = r.y; |
|
| 14 | 14 | ||
| 15 | 15 | return (x + y) - 42; |
test/tests/record.ptr.mutate.rad
+1 -1
| 3 | 3 | record Point: Copy { |
|
| 4 | 4 | x: i32, |
|
| 5 | 5 | y: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | fn mutate(p: *mut Point) { |
|
| 8 | + | fn mutate(p: &mut Point) { |
|
| 9 | 9 | set p.x = 4; |
|
| 10 | 10 | set p.y = 38; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | @default fn main() -> i32 { |
test/tests/ref.if.bug.rad
+1 -1
| 2 | 2 | //! Regression test: address-of inside an if branch. |
|
| 3 | 3 | //! |
|
| 4 | 4 | //! When `&mut var` appears in only one branch of an if/else, the merge |
|
| 5 | 5 | //! block's phi merges the original integer value with a stack pointer. |
|
| 6 | 6 | ||
| 7 | - | fn store(ptr: *mut u32, val: u32) { |
|
| 7 | + | fn store(ptr: &mut u32, val: u32) { |
|
| 8 | 8 | set *ptr = val; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | fn testIfBranch(cond: bool) -> u32 { |
|
| 12 | 12 | let mut val: u32 = 42; |
test/tests/ref.immut.loop.bug.rad
+1 -1
| 4 | 4 | //! Even though the variable is not `mut`, taking its address inside a |
|
| 5 | 5 | //! loop can produce the same SSA/pointer phi conflict as with mutable |
|
| 6 | 6 | //! variables: the loop header merges the original integer with a pointer |
|
| 7 | 7 | //! from the stack slot created by `&x`. |
|
| 8 | 8 | ||
| 9 | - | fn read(ptr: *u32) -> u32 { |
|
| 9 | + | fn read(ptr: &u32) -> u32 { |
|
| 10 | 10 | return *ptr; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | fn testZeroIter(n: u32) -> u32 { |
|
| 14 | 14 | let val: u32 = 42; |
test/tests/ref.mut.ptr.rad
+3 -3
| 1 | 1 | //! returns: 84 |
|
| 2 | 2 | //! Test mutable pointer references. |
|
| 3 | 3 | ||
| 4 | - | fn store(ptr: *mut i32) { |
|
| 4 | + | unsafe fn store(ptr: *unsafe mut i32) { |
|
| 5 | 5 | set *ptr = 42; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let mut a: i32 = 0; |
|
| 10 | - | let p: *mut i32 = &mut a; |
|
| 10 | + | let p: *unsafe mut i32 = &mut a; |
|
| 11 | 11 | store(p); |
|
| 12 | 12 | ||
| 13 | 13 | let mut b: i32 = 0; |
|
| 14 | 14 | store(&mut b); |
|
| 15 | 15 |
test/tests/slice.alloc.loop.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test returning a slice from a function and iterating over it. |
|
| 3 | 3 | //! Exercises the same code path as createBlock's vars initialization. |
|
| 4 | 4 | //! The slice is returned through a return buffer (> 8 bytes). |
|
| 5 | 5 | ||
| 6 | - | fn makeSlice(buf: *mut [u8], count: u32) -> *mut [u8] { |
|
| 6 | + | unsafe fn makeSlice(buf: *unsafe mut [u8], count: u32) -> *unsafe mut [u8] { |
|
| 7 | 7 | if count == 0 { |
|
| 8 | 8 | return &mut []; |
|
| 9 | 9 | } |
|
| 10 | 10 | return @sliceOf(&mut buf[0], count); |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | fn initSlice(buf: *mut [u8], count: u32) -> i32 { |
|
| 13 | + | unsafe fn initSlice(buf: *unsafe mut [u8], count: u32) -> i32 { |
|
| 14 | 14 | let s = makeSlice(buf, count); |
|
| 15 | 15 | ||
| 16 | 16 | // This loop pattern matches createBlock's vars initialization. |
|
| 17 | 17 | for i in 0..s.len { |
|
| 18 | 18 | set s[i] = 0; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | return s.len as i32; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | @default fn main() -> i32 { |
|
| 24 | + | @default unsafe fn main() -> i32 { |
|
| 25 | 25 | let mut buf: [u8; 64] = undefined; |
|
| 26 | 26 | ||
| 27 | 27 | let n = initSlice(&mut buf[..], 5); |
|
| 28 | 28 | assert n == 5; |
|
| 29 | 29 |
test/tests/slice.append.rad
+3 -2
| 9 | 9 | ||
| 10 | 10 | fn newArena(data: *mut [u8]) -> Arena { |
|
| 11 | 11 | return Arena { data, offset: 0 }; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn arenaAlloc(arena: *mut Arena, size: u32, al: u32) -> *mut opaque { |
|
| 14 | + | fn arenaAlloc(arena: &mut Arena, size: u32, al: u32) -> *mut opaque { |
|
| 15 | 15 | let aligned = (arena.offset + al - 1) / al * al; |
|
| 16 | 16 | let newOffset = aligned + size; |
|
| 17 | 17 | ||
| 18 | 18 | assert newOffset <= arena.data.len as u32; |
|
| 19 | 19 |
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | static BUF: [u8; 4096] = undefined; |
|
| 45 | 45 | ||
| 46 | 46 | @default fn main() -> i32 { |
|
| 47 | - | let mut arena = newArena(&mut BUF[..]); |
|
| 47 | + | static arena: Arena = undefined; |
|
| 48 | + | set arena = newArena(&mut BUF[..]); |
|
| 48 | 49 | let a = arenaAllocator(&mut arena); |
|
| 49 | 50 | ||
| 50 | 51 | // Allocate initial capacity of 4. |
|
| 51 | 52 | let ptr = arenaAlloc(&mut arena, @sizeOf(i32) * 4, @alignOf(i32)); |
|
| 52 | 53 | let mut nums = @sliceOf(ptr as *mut i32, 0, 4); |
test/tests/slice.append.ril
+494 -485
| 1 | 1 | data mut $BUF align 1 { |
|
| 2 | 2 | undef * 4096; |
|
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | + | data mut $main$nominal$arena align 8 { |
|
| 6 | + | undef * 24; |
|
| 7 | + | } |
|
| 8 | + | ||
| 5 | 9 | fn w64 $newArena(w64 %0, w64 %1) { |
|
| 6 | 10 | @entry0 |
|
| 7 | 11 | reserve %2 24 8; |
|
| 8 | 12 | blit %2 %1 16; |
|
| 9 | 13 | store w32 0 %2 16; |
| 59 | 63 | store w64 %0 %1 0; |
|
| 60 | 64 | store w32 4096 %1 8; |
|
| 61 | 65 | store w32 4096 %1 12; |
|
| 62 | 66 | reserve %2 24 8; |
|
| 63 | 67 | call w64 %3 $newArena(%2, %1); |
|
| 64 | - | reserve %4 16 8; |
|
| 65 | - | call w64 %5 $arenaAllocator(%4, %3); |
|
| 66 | - | mul w32 %6 4 4; |
|
| 67 | - | call w64 %7 $arenaAlloc(%3, %6, 4); |
|
| 68 | - | reserve %8 16 8; |
|
| 69 | - | store w64 %7 %8 0; |
|
| 70 | - | store w32 0 %8 8; |
|
| 71 | - | store w32 4 %8 12; |
|
| 72 | - | load w32 %9 %8 8; |
|
| 73 | - | load w32 %10 %8 12; |
|
| 74 | - | br.ult w32 %9 %10 @append.store1 @append.grow2; |
|
| 68 | + | copy %4 $main$nominal$arena; |
|
| 69 | + | blit %4 %3 24; |
|
| 70 | + | copy %5 $main$nominal$arena; |
|
| 71 | + | reserve %6 16 8; |
|
| 72 | + | call w64 %7 $arenaAllocator(%6, %5); |
|
| 73 | + | copy %8 $main$nominal$arena; |
|
| 74 | + | mul w32 %9 4 4; |
|
| 75 | + | call w64 %10 $arenaAlloc(%8, %9, 4); |
|
| 76 | + | reserve %11 16 8; |
|
| 77 | + | store w64 %10 %11 0; |
|
| 78 | + | store w32 0 %11 8; |
|
| 79 | + | store w32 4 %11 12; |
|
| 80 | + | load w32 %12 %11 8; |
|
| 81 | + | load w32 %13 %11 12; |
|
| 82 | + | br.ult w32 %12 %13 @append.store1 @append.grow2; |
|
| 75 | 83 | @append.store1 |
|
| 76 | - | load w64 %24 %8 0; |
|
| 77 | - | mul w64 %25 %9 4; |
|
| 78 | - | add w64 %26 %24 %25; |
|
| 79 | - | store w32 10 %26 0; |
|
| 80 | - | add w32 %27 %9 1; |
|
| 81 | - | store w32 %27 %8 8; |
|
| 82 | - | load w32 %32 %8 8; |
|
| 83 | - | load w32 %33 %8 12; |
|
| 84 | - | br.ult w32 %32 %33 @append.store6 @append.grow7; |
|
| 84 | + | load w64 %27 %11 0; |
|
| 85 | + | mul w64 %28 %12 4; |
|
| 86 | + | add w64 %29 %27 %28; |
|
| 87 | + | store w32 10 %29 0; |
|
| 88 | + | add w32 %30 %12 1; |
|
| 89 | + | store w32 %30 %11 8; |
|
| 90 | + | load w32 %35 %11 8; |
|
| 91 | + | load w32 %36 %11 12; |
|
| 92 | + | br.ult w32 %35 %36 @append.store6 @append.grow7; |
|
| 85 | 93 | @append.grow2 |
|
| 86 | - | shl w32 %11 %10 1; |
|
| 87 | - | or w32 %12 %11 1; |
|
| 88 | - | load w64 %13 %5 0; |
|
| 89 | - | load w64 %14 %5 8; |
|
| 90 | - | mul w32 %15 %12 4; |
|
| 91 | - | call w64 %16 %13(%14, %15, 4); |
|
| 92 | - | load w64 %17 %8 0; |
|
| 93 | - | mul w32 %18 %9 4; |
|
| 94 | + | shl w32 %14 %13 1; |
|
| 95 | + | or w32 %15 %14 1; |
|
| 96 | + | load w64 %16 %7 0; |
|
| 97 | + | load w64 %17 %7 8; |
|
| 98 | + | mul w32 %18 %15 4; |
|
| 99 | + | call w64 %19 %16(%17, %18, 4); |
|
| 100 | + | load w64 %20 %11 0; |
|
| 101 | + | mul w32 %21 %12 4; |
|
| 94 | 102 | jmp @append3(0); |
|
| 95 | - | @append3(w32 %19) |
|
| 96 | - | br.ult w32 %19 %18 @append4 @append5; |
|
| 103 | + | @append3(w32 %22) |
|
| 104 | + | br.ult w32 %22 %21 @append4 @append5; |
|
| 97 | 105 | @append4 |
|
| 98 | - | add w64 %20 %17 %19; |
|
| 99 | - | load w8 %21 %20 0; |
|
| 100 | - | add w64 %22 %16 %19; |
|
| 101 | - | store w8 %21 %22 0; |
|
| 102 | - | add w32 %23 %19 1; |
|
| 103 | - | jmp @append3(%23); |
|
| 106 | + | add w64 %23 %20 %22; |
|
| 107 | + | load w8 %24 %23 0; |
|
| 108 | + | add w64 %25 %19 %22; |
|
| 109 | + | store w8 %24 %25 0; |
|
| 110 | + | add w32 %26 %22 1; |
|
| 111 | + | jmp @append3(%26); |
|
| 104 | 112 | @append5 |
|
| 105 | - | store w64 %16 %8 0; |
|
| 106 | - | store w32 %12 %8 12; |
|
| 113 | + | store w64 %19 %11 0; |
|
| 114 | + | store w32 %15 %11 12; |
|
| 107 | 115 | jmp @append.store1; |
|
| 108 | 116 | @append.store6 |
|
| 109 | - | load w64 %47 %8 0; |
|
| 110 | - | mul w64 %48 %32 4; |
|
| 111 | - | add w64 %49 %47 %48; |
|
| 112 | - | store w32 20 %49 0; |
|
| 113 | - | add w32 %50 %32 1; |
|
| 114 | - | store w32 %50 %8 8; |
|
| 115 | - | load w32 %55 %8 8; |
|
| 116 | - | load w32 %56 %8 12; |
|
| 117 | - | br.ult w32 %55 %56 @append.store11 @append.grow12; |
|
| 117 | + | load w64 %50 %11 0; |
|
| 118 | + | mul w64 %51 %35 4; |
|
| 119 | + | add w64 %52 %50 %51; |
|
| 120 | + | store w32 20 %52 0; |
|
| 121 | + | add w32 %53 %35 1; |
|
| 122 | + | store w32 %53 %11 8; |
|
| 123 | + | load w32 %58 %11 8; |
|
| 124 | + | load w32 %59 %11 12; |
|
| 125 | + | br.ult w32 %58 %59 @append.store11 @append.grow12; |
|
| 118 | 126 | @append.grow7 |
|
| 119 | - | shl w32 %34 %33 1; |
|
| 120 | - | or w32 %35 %34 1; |
|
| 121 | - | load w64 %36 %5 0; |
|
| 122 | - | load w64 %37 %5 8; |
|
| 123 | - | mul w32 %38 %35 4; |
|
| 124 | - | call w64 %39 %36(%37, %38, 4); |
|
| 125 | - | load w64 %40 %8 0; |
|
| 126 | - | mul w32 %41 %32 4; |
|
| 127 | + | shl w32 %37 %36 1; |
|
| 128 | + | or w32 %38 %37 1; |
|
| 129 | + | load w64 %39 %7 0; |
|
| 130 | + | load w64 %40 %7 8; |
|
| 131 | + | mul w32 %41 %38 4; |
|
| 132 | + | call w64 %42 %39(%40, %41, 4); |
|
| 133 | + | load w64 %43 %11 0; |
|
| 134 | + | mul w32 %44 %35 4; |
|
| 127 | 135 | jmp @append8(0); |
|
| 128 | - | @append8(w32 %42) |
|
| 129 | - | br.ult w32 %42 %41 @append9 @append10; |
|
| 136 | + | @append8(w32 %45) |
|
| 137 | + | br.ult w32 %45 %44 @append9 @append10; |
|
| 130 | 138 | @append9 |
|
| 131 | - | add w64 %43 %40 %42; |
|
| 132 | - | load w8 %44 %43 0; |
|
| 133 | - | add w64 %45 %39 %42; |
|
| 134 | - | store w8 %44 %45 0; |
|
| 135 | - | add w32 %46 %42 1; |
|
| 136 | - | jmp @append8(%46); |
|
| 139 | + | add w64 %46 %43 %45; |
|
| 140 | + | load w8 %47 %46 0; |
|
| 141 | + | add w64 %48 %42 %45; |
|
| 142 | + | store w8 %47 %48 0; |
|
| 143 | + | add w32 %49 %45 1; |
|
| 144 | + | jmp @append8(%49); |
|
| 137 | 145 | @append10 |
|
| 138 | - | store w64 %39 %8 0; |
|
| 139 | - | store w32 %35 %8 12; |
|
| 146 | + | store w64 %42 %11 0; |
|
| 147 | + | store w32 %38 %11 12; |
|
| 140 | 148 | jmp @append.store6; |
|
| 141 | 149 | @append.store11 |
|
| 142 | - | load w64 %70 %8 0; |
|
| 143 | - | mul w64 %71 %55 4; |
|
| 144 | - | add w64 %72 %70 %71; |
|
| 145 | - | store w32 30 %72 0; |
|
| 146 | - | add w32 %73 %55 1; |
|
| 147 | - | store w32 %73 %8 8; |
|
| 148 | - | load w32 %76 %8 8; |
|
| 149 | - | br.ne w32 %76 3 @then16 @merge17; |
|
| 150 | + | load w64 %73 %11 0; |
|
| 151 | + | mul w64 %74 %58 4; |
|
| 152 | + | add w64 %75 %73 %74; |
|
| 153 | + | store w32 30 %75 0; |
|
| 154 | + | add w32 %76 %58 1; |
|
| 155 | + | store w32 %76 %11 8; |
|
| 156 | + | load w32 %79 %11 8; |
|
| 157 | + | br.ne w32 %79 3 @then16 @merge17; |
|
| 150 | 158 | @append.grow12 |
|
| 151 | - | shl w32 %57 %56 1; |
|
| 152 | - | or w32 %58 %57 1; |
|
| 153 | - | load w64 %59 %5 0; |
|
| 154 | - | load w64 %60 %5 8; |
|
| 155 | - | mul w32 %61 %58 4; |
|
| 156 | - | call w64 %62 %59(%60, %61, 4); |
|
| 157 | - | load w64 %63 %8 0; |
|
| 158 | - | mul w32 %64 %55 4; |
|
| 159 | + | shl w32 %60 %59 1; |
|
| 160 | + | or w32 %61 %60 1; |
|
| 161 | + | load w64 %62 %7 0; |
|
| 162 | + | load w64 %63 %7 8; |
|
| 163 | + | mul w32 %64 %61 4; |
|
| 164 | + | call w64 %65 %62(%63, %64, 4); |
|
| 165 | + | load w64 %66 %11 0; |
|
| 166 | + | mul w32 %67 %58 4; |
|
| 159 | 167 | jmp @append13(0); |
|
| 160 | - | @append13(w32 %65) |
|
| 161 | - | br.ult w32 %65 %64 @append14 @append15; |
|
| 168 | + | @append13(w32 %68) |
|
| 169 | + | br.ult w32 %68 %67 @append14 @append15; |
|
| 162 | 170 | @append14 |
|
| 163 | - | add w64 %66 %63 %65; |
|
| 164 | - | load w8 %67 %66 0; |
|
| 165 | - | add w64 %68 %62 %65; |
|
| 166 | - | store w8 %67 %68 0; |
|
| 167 | - | add w32 %69 %65 1; |
|
| 168 | - | jmp @append13(%69); |
|
| 171 | + | add w64 %69 %66 %68; |
|
| 172 | + | load w8 %70 %69 0; |
|
| 173 | + | add w64 %71 %65 %68; |
|
| 174 | + | store w8 %70 %71 0; |
|
| 175 | + | add w32 %72 %68 1; |
|
| 176 | + | jmp @append13(%72); |
|
| 169 | 177 | @append15 |
|
| 170 | - | store w64 %62 %8 0; |
|
| 171 | - | store w32 %58 %8 12; |
|
| 178 | + | store w64 %65 %11 0; |
|
| 179 | + | store w32 %61 %11 12; |
|
| 172 | 180 | jmp @append.store11; |
|
| 173 | 181 | @then16 |
|
| 174 | 182 | ret 1; |
|
| 175 | 183 | @merge17 |
|
| 176 | - | load w32 %77 %8 12; |
|
| 177 | - | br.ne w32 %77 4 @then18 @merge19; |
|
| 184 | + | load w32 %80 %11 12; |
|
| 185 | + | br.ne w32 %80 4 @then18 @merge19; |
|
| 178 | 186 | @then18 |
|
| 179 | 187 | ret 2; |
|
| 180 | 188 | @merge19 |
|
| 181 | - | load w32 %78 %8 8; |
|
| 182 | - | br.ult w32 0 %78 @guard#pass22 @guard#trap23; |
|
| 189 | + | load w32 %81 %11 8; |
|
| 190 | + | br.ult w32 0 %81 @guard#pass22 @guard#trap23; |
|
| 183 | 191 | @then20 |
|
| 184 | 192 | ret 3; |
|
| 185 | 193 | @merge21 |
|
| 186 | - | load w32 %81 %8 8; |
|
| 187 | - | br.ult w32 1 %81 @guard#pass26 @guard#trap27; |
|
| 194 | + | load w32 %84 %11 8; |
|
| 195 | + | br.ult w32 1 %84 @guard#pass26 @guard#trap27; |
|
| 188 | 196 | @guard#pass22 |
|
| 189 | - | load w64 %79 %8 0; |
|
| 190 | - | sload w32 %80 %79 0; |
|
| 191 | - | br.ne w32 %80 10 @then20 @merge21; |
|
| 197 | + | load w64 %82 %11 0; |
|
| 198 | + | sload w32 %83 %82 0; |
|
| 199 | + | br.ne w32 %83 10 @then20 @merge21; |
|
| 192 | 200 | @guard#trap23 |
|
| 193 | 201 | ebreak; |
|
| 194 | 202 | unreachable; |
|
| 195 | 203 | @then24 |
|
| 196 | 204 | ret 4; |
|
| 197 | 205 | @merge25 |
|
| 198 | - | load w32 %86 %8 8; |
|
| 199 | - | br.ult w32 2 %86 @guard#pass30 @guard#trap31; |
|
| 206 | + | load w32 %89 %11 8; |
|
| 207 | + | br.ult w32 2 %89 @guard#pass30 @guard#trap31; |
|
| 200 | 208 | @guard#pass26 |
|
| 201 | - | load w64 %82 %8 0; |
|
| 202 | - | mul w64 %83 1 4; |
|
| 203 | - | add w64 %84 %82 %83; |
|
| 204 | - | sload w32 %85 %84 0; |
|
| 205 | - | br.ne w32 %85 20 @then24 @merge25; |
|
| 209 | + | load w64 %85 %11 0; |
|
| 210 | + | mul w64 %86 1 4; |
|
| 211 | + | add w64 %87 %85 %86; |
|
| 212 | + | sload w32 %88 %87 0; |
|
| 213 | + | br.ne w32 %88 20 @then24 @merge25; |
|
| 206 | 214 | @guard#trap27 |
|
| 207 | 215 | ebreak; |
|
| 208 | 216 | unreachable; |
|
| 209 | 217 | @then28 |
|
| 210 | 218 | ret 5; |
|
| 211 | 219 | @merge29 |
|
| 212 | - | load w32 %93 %8 8; |
|
| 213 | - | load w32 %94 %8 12; |
|
| 214 | - | br.ult w32 %93 %94 @append.store32 @append.grow33; |
|
| 220 | + | load w32 %96 %11 8; |
|
| 221 | + | load w32 %97 %11 12; |
|
| 222 | + | br.ult w32 %96 %97 @append.store32 @append.grow33; |
|
| 215 | 223 | @guard#pass30 |
|
| 216 | - | load w64 %87 %8 0; |
|
| 217 | - | mul w64 %88 2 4; |
|
| 218 | - | add w64 %89 %87 %88; |
|
| 219 | - | sload w32 %90 %89 0; |
|
| 220 | - | br.ne w32 %90 30 @then28 @merge29; |
|
| 224 | + | load w64 %90 %11 0; |
|
| 225 | + | mul w64 %91 2 4; |
|
| 226 | + | add w64 %92 %90 %91; |
|
| 227 | + | sload w32 %93 %92 0; |
|
| 228 | + | br.ne w32 %93 30 @then28 @merge29; |
|
| 221 | 229 | @guard#trap31 |
|
| 222 | 230 | ebreak; |
|
| 223 | 231 | unreachable; |
|
| 224 | 232 | @append.store32 |
|
| 225 | - | load w64 %108 %8 0; |
|
| 226 | - | mul w64 %109 %93 4; |
|
| 227 | - | add w64 %110 %108 %109; |
|
| 228 | - | store w32 40 %110 0; |
|
| 229 | - | add w32 %111 %93 1; |
|
| 230 | - | store w32 %111 %8 8; |
|
| 231 | - | load w32 %114 %8 8; |
|
| 232 | - | br.ne w32 %114 4 @then37 @merge38; |
|
| 233 | + | load w64 %111 %11 0; |
|
| 234 | + | mul w64 %112 %96 4; |
|
| 235 | + | add w64 %113 %111 %112; |
|
| 236 | + | store w32 40 %113 0; |
|
| 237 | + | add w32 %114 %96 1; |
|
| 238 | + | store w32 %114 %11 8; |
|
| 239 | + | load w32 %117 %11 8; |
|
| 240 | + | br.ne w32 %117 4 @then37 @merge38; |
|
| 233 | 241 | @append.grow33 |
|
| 234 | - | shl w32 %95 %94 1; |
|
| 235 | - | or w32 %96 %95 1; |
|
| 236 | - | load w64 %97 %5 0; |
|
| 237 | - | load w64 %98 %5 8; |
|
| 238 | - | mul w32 %99 %96 4; |
|
| 239 | - | call w64 %100 %97(%98, %99, 4); |
|
| 240 | - | load w64 %101 %8 0; |
|
| 241 | - | mul w32 %102 %93 4; |
|
| 242 | + | shl w32 %98 %97 1; |
|
| 243 | + | or w32 %99 %98 1; |
|
| 244 | + | load w64 %100 %7 0; |
|
| 245 | + | load w64 %101 %7 8; |
|
| 246 | + | mul w32 %102 %99 4; |
|
| 247 | + | call w64 %103 %100(%101, %102, 4); |
|
| 248 | + | load w64 %104 %11 0; |
|
| 249 | + | mul w32 %105 %96 4; |
|
| 242 | 250 | jmp @append34(0); |
|
| 243 | - | @append34(w32 %103) |
|
| 244 | - | br.ult w32 %103 %102 @append35 @append36; |
|
| 251 | + | @append34(w32 %106) |
|
| 252 | + | br.ult w32 %106 %105 @append35 @append36; |
|
| 245 | 253 | @append35 |
|
| 246 | - | add w64 %104 %101 %103; |
|
| 247 | - | load w8 %105 %104 0; |
|
| 248 | - | add w64 %106 %100 %103; |
|
| 249 | - | store w8 %105 %106 0; |
|
| 250 | - | add w32 %107 %103 1; |
|
| 251 | - | jmp @append34(%107); |
|
| 254 | + | add w64 %107 %104 %106; |
|
| 255 | + | load w8 %108 %107 0; |
|
| 256 | + | add w64 %109 %103 %106; |
|
| 257 | + | store w8 %108 %109 0; |
|
| 258 | + | add w32 %110 %106 1; |
|
| 259 | + | jmp @append34(%110); |
|
| 252 | 260 | @append36 |
|
| 253 | - | store w64 %100 %8 0; |
|
| 254 | - | store w32 %96 %8 12; |
|
| 261 | + | store w64 %103 %11 0; |
|
| 262 | + | store w32 %99 %11 12; |
|
| 255 | 263 | jmp @append.store32; |
|
| 256 | 264 | @then37 |
|
| 257 | 265 | ret 6; |
|
| 258 | 266 | @merge38 |
|
| 259 | - | load w32 %115 %8 12; |
|
| 260 | - | br.ne w32 %115 4 @then39 @merge40; |
|
| 267 | + | load w32 %118 %11 12; |
|
| 268 | + | br.ne w32 %118 4 @then39 @merge40; |
|
| 261 | 269 | @then39 |
|
| 262 | 270 | ret 7; |
|
| 263 | 271 | @merge40 |
|
| 264 | - | load w32 %118 %8 8; |
|
| 265 | - | load w32 %119 %8 12; |
|
| 266 | - | br.ult w32 %118 %119 @append.store41 @append.grow42; |
|
| 272 | + | load w32 %121 %11 8; |
|
| 273 | + | load w32 %122 %11 12; |
|
| 274 | + | br.ult w32 %121 %122 @append.store41 @append.grow42; |
|
| 267 | 275 | @append.store41 |
|
| 268 | - | load w64 %133 %8 0; |
|
| 269 | - | mul w64 %134 %118 4; |
|
| 270 | - | add w64 %135 %133 %134; |
|
| 271 | - | store w32 50 %135 0; |
|
| 272 | - | add w32 %136 %118 1; |
|
| 273 | - | store w32 %136 %8 8; |
|
| 274 | - | load w32 %139 %8 8; |
|
| 275 | - | br.ne w32 %139 5 @then46 @merge47; |
|
| 276 | + | load w64 %136 %11 0; |
|
| 277 | + | mul w64 %137 %121 4; |
|
| 278 | + | add w64 %138 %136 %137; |
|
| 279 | + | store w32 50 %138 0; |
|
| 280 | + | add w32 %139 %121 1; |
|
| 281 | + | store w32 %139 %11 8; |
|
| 282 | + | load w32 %142 %11 8; |
|
| 283 | + | br.ne w32 %142 5 @then46 @merge47; |
|
| 276 | 284 | @append.grow42 |
|
| 277 | - | shl w32 %120 %119 1; |
|
| 278 | - | or w32 %121 %120 1; |
|
| 279 | - | load w64 %122 %5 0; |
|
| 280 | - | load w64 %123 %5 8; |
|
| 281 | - | mul w32 %124 %121 4; |
|
| 282 | - | call w64 %125 %122(%123, %124, 4); |
|
| 283 | - | load w64 %126 %8 0; |
|
| 284 | - | mul w32 %127 %118 4; |
|
| 285 | + | shl w32 %123 %122 1; |
|
| 286 | + | or w32 %124 %123 1; |
|
| 287 | + | load w64 %125 %7 0; |
|
| 288 | + | load w64 %126 %7 8; |
|
| 289 | + | mul w32 %127 %124 4; |
|
| 290 | + | call w64 %128 %125(%126, %127, 4); |
|
| 291 | + | load w64 %129 %11 0; |
|
| 292 | + | mul w32 %130 %121 4; |
|
| 285 | 293 | jmp @append43(0); |
|
| 286 | - | @append43(w32 %128) |
|
| 287 | - | br.ult w32 %128 %127 @append44 @append45; |
|
| 294 | + | @append43(w32 %131) |
|
| 295 | + | br.ult w32 %131 %130 @append44 @append45; |
|
| 288 | 296 | @append44 |
|
| 289 | - | add w64 %129 %126 %128; |
|
| 290 | - | load w8 %130 %129 0; |
|
| 291 | - | add w64 %131 %125 %128; |
|
| 292 | - | store w8 %130 %131 0; |
|
| 293 | - | add w32 %132 %128 1; |
|
| 294 | - | jmp @append43(%132); |
|
| 297 | + | add w64 %132 %129 %131; |
|
| 298 | + | load w8 %133 %132 0; |
|
| 299 | + | add w64 %134 %128 %131; |
|
| 300 | + | store w8 %133 %134 0; |
|
| 301 | + | add w32 %135 %131 1; |
|
| 302 | + | jmp @append43(%135); |
|
| 295 | 303 | @append45 |
|
| 296 | - | store w64 %125 %8 0; |
|
| 297 | - | store w32 %121 %8 12; |
|
| 304 | + | store w64 %128 %11 0; |
|
| 305 | + | store w32 %124 %11 12; |
|
| 298 | 306 | jmp @append.store41; |
|
| 299 | 307 | @then46 |
|
| 300 | 308 | ret 8; |
|
| 301 | 309 | @merge47 |
|
| 302 | - | load w32 %140 %8 12; |
|
| 303 | - | br.ne w32 %140 9 @then48 @merge49; |
|
| 310 | + | load w32 %143 %11 12; |
|
| 311 | + | br.ne w32 %143 9 @then48 @merge49; |
|
| 304 | 312 | @then48 |
|
| 305 | 313 | ret 9; |
|
| 306 | 314 | @merge49 |
|
| 307 | - | load w32 %141 %8 8; |
|
| 308 | - | br.ult w32 4 %141 @guard#pass52 @guard#trap53; |
|
| 315 | + | load w32 %144 %11 8; |
|
| 316 | + | br.ult w32 4 %144 @guard#pass52 @guard#trap53; |
|
| 309 | 317 | @then50 |
|
| 310 | 318 | ret 10; |
|
| 311 | 319 | @merge51 |
|
| 312 | - | load w32 %146 %8 8; |
|
| 313 | - | br.ult w32 0 %146 @guard#pass56 @guard#trap57; |
|
| 320 | + | load w32 %149 %11 8; |
|
| 321 | + | br.ult w32 0 %149 @guard#pass56 @guard#trap57; |
|
| 314 | 322 | @guard#pass52 |
|
| 315 | - | load w64 %142 %8 0; |
|
| 316 | - | mul w64 %143 4 4; |
|
| 317 | - | add w64 %144 %142 %143; |
|
| 318 | - | sload w32 %145 %144 0; |
|
| 319 | - | br.ne w32 %145 50 @then50 @merge51; |
|
| 323 | + | load w64 %145 %11 0; |
|
| 324 | + | mul w64 %146 4 4; |
|
| 325 | + | add w64 %147 %145 %146; |
|
| 326 | + | sload w32 %148 %147 0; |
|
| 327 | + | br.ne w32 %148 50 @then50 @merge51; |
|
| 320 | 328 | @guard#trap53 |
|
| 321 | 329 | ebreak; |
|
| 322 | 330 | unreachable; |
|
| 323 | 331 | @then54 |
|
| 324 | 332 | ret 11; |
|
| 325 | 333 | @merge55 |
|
| 326 | - | load w32 %149 %8 8; |
|
| 327 | - | br.ult w32 3 %149 @guard#pass60 @guard#trap61; |
|
| 334 | + | load w32 %152 %11 8; |
|
| 335 | + | br.ult w32 3 %152 @guard#pass60 @guard#trap61; |
|
| 328 | 336 | @guard#pass56 |
|
| 329 | - | load w64 %147 %8 0; |
|
| 330 | - | sload w32 %148 %147 0; |
|
| 331 | - | br.ne w32 %148 10 @then54 @merge55; |
|
| 337 | + | load w64 %150 %11 0; |
|
| 338 | + | sload w32 %151 %150 0; |
|
| 339 | + | br.ne w32 %151 10 @then54 @merge55; |
|
| 332 | 340 | @guard#trap57 |
|
| 333 | 341 | ebreak; |
|
| 334 | 342 | unreachable; |
|
| 335 | 343 | @then58 |
|
| 336 | 344 | ret 12; |
|
| 337 | 345 | @merge59 |
|
| 338 | - | reserve %164 16 8; |
|
| 339 | - | store w64 %7 %164 0; |
|
| 340 | - | store w32 0 %164 8; |
|
| 341 | - | store w32 0 %164 12; |
|
| 342 | - | load w32 %167 %164 8; |
|
| 343 | - | load w32 %168 %164 12; |
|
| 344 | - | br.ult w32 %167 %168 @append.store62 @append.grow63; |
|
| 346 | + | reserve %167 16 8; |
|
| 347 | + | store w64 %10 %167 0; |
|
| 348 | + | store w32 0 %167 8; |
|
| 349 | + | store w32 0 %167 12; |
|
| 350 | + | load w32 %170 %167 8; |
|
| 351 | + | load w32 %171 %167 12; |
|
| 352 | + | br.ult w32 %170 %171 @append.store62 @append.grow63; |
|
| 345 | 353 | @guard#pass60 |
|
| 346 | - | load w64 %150 %8 0; |
|
| 347 | - | mul w64 %151 3 4; |
|
| 348 | - | add w64 %152 %150 %151; |
|
| 349 | - | sload w32 %153 %152 0; |
|
| 350 | - | br.ne w32 %153 40 @then58 @merge59; |
|
| 354 | + | load w64 %153 %11 0; |
|
| 355 | + | mul w64 %154 3 4; |
|
| 356 | + | add w64 %155 %153 %154; |
|
| 357 | + | sload w32 %156 %155 0; |
|
| 358 | + | br.ne w32 %156 40 @then58 @merge59; |
|
| 351 | 359 | @guard#trap61 |
|
| 352 | 360 | ebreak; |
|
| 353 | 361 | unreachable; |
|
| 354 | 362 | @append.store62 |
|
| 355 | - | load w64 %182 %164 0; |
|
| 356 | - | mul w64 %183 %167 4; |
|
| 357 | - | add w64 %184 %182 %183; |
|
| 358 | - | store w32 99 %184 0; |
|
| 359 | - | add w32 %185 %167 1; |
|
| 360 | - | store w32 %185 %164 8; |
|
| 361 | - | load w32 %188 %164 8; |
|
| 362 | - | br.ne w32 %188 1 @then67 @merge68; |
|
| 363 | + | load w64 %185 %167 0; |
|
| 364 | + | mul w64 %186 %170 4; |
|
| 365 | + | add w64 %187 %185 %186; |
|
| 366 | + | store w32 99 %187 0; |
|
| 367 | + | add w32 %188 %170 1; |
|
| 368 | + | store w32 %188 %167 8; |
|
| 369 | + | load w32 %191 %167 8; |
|
| 370 | + | br.ne w32 %191 1 @then67 @merge68; |
|
| 363 | 371 | @append.grow63 |
|
| 364 | - | shl w32 %169 %168 1; |
|
| 365 | - | or w32 %170 %169 1; |
|
| 366 | - | load w64 %171 %5 0; |
|
| 367 | - | load w64 %172 %5 8; |
|
| 368 | - | mul w32 %173 %170 4; |
|
| 369 | - | call w64 %174 %171(%172, %173, 4); |
|
| 370 | - | load w64 %175 %164 0; |
|
| 371 | - | mul w32 %176 %167 4; |
|
| 372 | + | shl w32 %172 %171 1; |
|
| 373 | + | or w32 %173 %172 1; |
|
| 374 | + | load w64 %174 %7 0; |
|
| 375 | + | load w64 %175 %7 8; |
|
| 376 | + | mul w32 %176 %173 4; |
|
| 377 | + | call w64 %177 %174(%175, %176, 4); |
|
| 378 | + | load w64 %178 %167 0; |
|
| 379 | + | mul w32 %179 %170 4; |
|
| 372 | 380 | jmp @append64(0); |
|
| 373 | - | @append64(w32 %177) |
|
| 374 | - | br.ult w32 %177 %176 @append65 @append66; |
|
| 381 | + | @append64(w32 %180) |
|
| 382 | + | br.ult w32 %180 %179 @append65 @append66; |
|
| 375 | 383 | @append65 |
|
| 376 | - | add w64 %178 %175 %177; |
|
| 377 | - | load w8 %179 %178 0; |
|
| 378 | - | add w64 %180 %174 %177; |
|
| 379 | - | store w8 %179 %180 0; |
|
| 380 | - | add w32 %181 %177 1; |
|
| 381 | - | jmp @append64(%181); |
|
| 384 | + | add w64 %181 %178 %180; |
|
| 385 | + | load w8 %182 %181 0; |
|
| 386 | + | add w64 %183 %177 %180; |
|
| 387 | + | store w8 %182 %183 0; |
|
| 388 | + | add w32 %184 %180 1; |
|
| 389 | + | jmp @append64(%184); |
|
| 382 | 390 | @append66 |
|
| 383 | - | store w64 %174 %164 0; |
|
| 384 | - | store w32 %170 %164 12; |
|
| 391 | + | store w64 %177 %167 0; |
|
| 392 | + | store w32 %173 %167 12; |
|
| 385 | 393 | jmp @append.store62; |
|
| 386 | 394 | @then67 |
|
| 387 | 395 | ret 13; |
|
| 388 | 396 | @merge68 |
|
| 389 | - | load w32 %189 %164 12; |
|
| 390 | - | br.ne w32 %189 1 @then69 @merge70; |
|
| 397 | + | load w32 %192 %167 12; |
|
| 398 | + | br.ne w32 %192 1 @then69 @merge70; |
|
| 391 | 399 | @then69 |
|
| 392 | 400 | ret 14; |
|
| 393 | 401 | @merge70 |
|
| 394 | - | load w32 %190 %164 8; |
|
| 395 | - | br.ult w32 0 %190 @guard#pass73 @guard#trap74; |
|
| 402 | + | load w32 %193 %167 8; |
|
| 403 | + | br.ult w32 0 %193 @guard#pass73 @guard#trap74; |
|
| 396 | 404 | @then71 |
|
| 397 | 405 | ret 15; |
|
| 398 | 406 | @merge72 |
|
| 399 | - | load w32 %195 %8 8; |
|
| 400 | - | load w64 %196 %8 0; |
|
| 407 | + | load w32 %198 %11 8; |
|
| 408 | + | load w64 %199 %11 0; |
|
| 401 | 409 | jmp @loop75(0, 0); |
|
| 402 | 410 | @guard#pass73 |
|
| 403 | - | load w64 %191 %164 0; |
|
| 404 | - | sload w32 %192 %191 0; |
|
| 405 | - | br.ne w32 %192 99 @then71 @merge72; |
|
| 411 | + | load w64 %194 %167 0; |
|
| 412 | + | sload w32 %195 %194 0; |
|
| 413 | + | br.ne w32 %195 99 @then71 @merge72; |
|
| 406 | 414 | @guard#trap74 |
|
| 407 | 415 | ebreak; |
|
| 408 | 416 | unreachable; |
|
| 409 | - | @loop75(w32 %197, w32 %201) |
|
| 410 | - | br.slt w32 %197 %195 @body76 @merge77; |
|
| 417 | + | @loop75(w32 %200, w32 %204) |
|
| 418 | + | br.slt w32 %200 %198 @body76 @merge77; |
|
| 411 | 419 | @body76 |
|
| 412 | - | mul w64 %198 %197 4; |
|
| 413 | - | add w64 %199 %196 %198; |
|
| 414 | - | sload w32 %200 %199 0; |
|
| 415 | - | add w32 %202 %201 %200; |
|
| 416 | - | add w32 %203 %197 1; |
|
| 417 | - | jmp @loop75(%203, %202); |
|
| 420 | + | mul w64 %201 %200 4; |
|
| 421 | + | add w64 %202 %199 %201; |
|
| 422 | + | sload w32 %203 %202 0; |
|
| 423 | + | add w32 %205 %204 %203; |
|
| 424 | + | add w32 %206 %200 1; |
|
| 425 | + | jmp @loop75(%206, %205); |
|
| 418 | 426 | @merge77 |
|
| 419 | - | br.ne w32 %201 150 @then78 @merge79; |
|
| 427 | + | br.ne w32 %204 150 @then78 @merge79; |
|
| 420 | 428 | @then78 |
|
| 421 | 429 | ret 16; |
|
| 422 | 430 | @merge79 |
|
| 423 | - | call w64 %217 $arenaAlloc(%3, 2, 1); |
|
| 424 | - | reserve %218 16 8; |
|
| 425 | - | store w64 %217 %218 0; |
|
| 426 | - | store w32 0 %218 8; |
|
| 427 | - | store w32 2 %218 12; |
|
| 428 | - | load w32 %222 %218 8; |
|
| 429 | - | load w32 %223 %218 12; |
|
| 430 | - | br.ult w32 %222 %223 @append.store80 @append.grow81; |
|
| 431 | + | copy %207 $main$nominal$arena; |
|
| 432 | + | call w64 %208 $arenaAlloc(%207, 2, 1); |
|
| 433 | + | reserve %209 16 8; |
|
| 434 | + | store w64 %208 %209 0; |
|
| 435 | + | store w32 0 %209 8; |
|
| 436 | + | store w32 2 %209 12; |
|
| 437 | + | load w32 %213 %209 8; |
|
| 438 | + | load w32 %214 %209 12; |
|
| 439 | + | br.ult w32 %213 %214 @append.store80 @append.grow81; |
|
| 431 | 440 | @append.store80 |
|
| 432 | - | load w64 %237 %218 0; |
|
| 433 | - | add w64 %238 %237 %222; |
|
| 434 | - | store w8 171 %238 0; |
|
| 435 | - | add w32 %239 %222 1; |
|
| 436 | - | store w32 %239 %218 8; |
|
| 437 | - | load w32 %244 %218 8; |
|
| 438 | - | load w32 %245 %218 12; |
|
| 439 | - | br.ult w32 %244 %245 @append.store85 @append.grow86; |
|
| 441 | + | load w64 %228 %209 0; |
|
| 442 | + | add w64 %229 %228 %213; |
|
| 443 | + | store w8 171 %229 0; |
|
| 444 | + | add w32 %230 %213 1; |
|
| 445 | + | store w32 %230 %209 8; |
|
| 446 | + | load w32 %235 %209 8; |
|
| 447 | + | load w32 %236 %209 12; |
|
| 448 | + | br.ult w32 %235 %236 @append.store85 @append.grow86; |
|
| 440 | 449 | @append.grow81 |
|
| 441 | - | shl w32 %224 %223 1; |
|
| 442 | - | or w32 %225 %224 1; |
|
| 443 | - | load w64 %226 %5 0; |
|
| 444 | - | load w64 %227 %5 8; |
|
| 445 | - | mul w32 %228 %225 1; |
|
| 446 | - | call w64 %229 %226(%227, %228, 1); |
|
| 447 | - | load w64 %230 %218 0; |
|
| 448 | - | mul w32 %231 %222 1; |
|
| 450 | + | shl w32 %215 %214 1; |
|
| 451 | + | or w32 %216 %215 1; |
|
| 452 | + | load w64 %217 %7 0; |
|
| 453 | + | load w64 %218 %7 8; |
|
| 454 | + | mul w32 %219 %216 1; |
|
| 455 | + | call w64 %220 %217(%218, %219, 1); |
|
| 456 | + | load w64 %221 %209 0; |
|
| 457 | + | mul w32 %222 %213 1; |
|
| 449 | 458 | jmp @append82(0); |
|
| 450 | - | @append82(w32 %232) |
|
| 451 | - | br.ult w32 %232 %231 @append83 @append84; |
|
| 459 | + | @append82(w32 %223) |
|
| 460 | + | br.ult w32 %223 %222 @append83 @append84; |
|
| 452 | 461 | @append83 |
|
| 453 | - | add w64 %233 %230 %232; |
|
| 454 | - | load w8 %234 %233 0; |
|
| 455 | - | add w64 %235 %229 %232; |
|
| 456 | - | store w8 %234 %235 0; |
|
| 457 | - | add w32 %236 %232 1; |
|
| 458 | - | jmp @append82(%236); |
|
| 462 | + | add w64 %224 %221 %223; |
|
| 463 | + | load w8 %225 %224 0; |
|
| 464 | + | add w64 %226 %220 %223; |
|
| 465 | + | store w8 %225 %226 0; |
|
| 466 | + | add w32 %227 %223 1; |
|
| 467 | + | jmp @append82(%227); |
|
| 459 | 468 | @append84 |
|
| 460 | - | store w64 %229 %218 0; |
|
| 461 | - | store w32 %225 %218 12; |
|
| 469 | + | store w64 %220 %209 0; |
|
| 470 | + | store w32 %216 %209 12; |
|
| 462 | 471 | jmp @append.store80; |
|
| 463 | 472 | @append.store85 |
|
| 464 | - | load w64 %259 %218 0; |
|
| 465 | - | add w64 %260 %259 %244; |
|
| 466 | - | store w8 205 %260 0; |
|
| 467 | - | add w32 %261 %244 1; |
|
| 468 | - | store w32 %261 %218 8; |
|
| 469 | - | load w32 %264 %218 8; |
|
| 470 | - | br.ne w32 %264 2 @then90 @merge91; |
|
| 473 | + | load w64 %250 %209 0; |
|
| 474 | + | add w64 %251 %250 %235; |
|
| 475 | + | store w8 205 %251 0; |
|
| 476 | + | add w32 %252 %235 1; |
|
| 477 | + | store w32 %252 %209 8; |
|
| 478 | + | load w32 %255 %209 8; |
|
| 479 | + | br.ne w32 %255 2 @then90 @merge91; |
|
| 471 | 480 | @append.grow86 |
|
| 472 | - | shl w32 %246 %245 1; |
|
| 473 | - | or w32 %247 %246 1; |
|
| 474 | - | load w64 %248 %5 0; |
|
| 475 | - | load w64 %249 %5 8; |
|
| 476 | - | mul w32 %250 %247 1; |
|
| 477 | - | call w64 %251 %248(%249, %250, 1); |
|
| 478 | - | load w64 %252 %218 0; |
|
| 479 | - | mul w32 %253 %244 1; |
|
| 481 | + | shl w32 %237 %236 1; |
|
| 482 | + | or w32 %238 %237 1; |
|
| 483 | + | load w64 %239 %7 0; |
|
| 484 | + | load w64 %240 %7 8; |
|
| 485 | + | mul w32 %241 %238 1; |
|
| 486 | + | call w64 %242 %239(%240, %241, 1); |
|
| 487 | + | load w64 %243 %209 0; |
|
| 488 | + | mul w32 %244 %235 1; |
|
| 480 | 489 | jmp @append87(0); |
|
| 481 | - | @append87(w32 %254) |
|
| 482 | - | br.ult w32 %254 %253 @append88 @append89; |
|
| 490 | + | @append87(w32 %245) |
|
| 491 | + | br.ult w32 %245 %244 @append88 @append89; |
|
| 483 | 492 | @append88 |
|
| 484 | - | add w64 %255 %252 %254; |
|
| 485 | - | load w8 %256 %255 0; |
|
| 486 | - | add w64 %257 %251 %254; |
|
| 487 | - | store w8 %256 %257 0; |
|
| 488 | - | add w32 %258 %254 1; |
|
| 489 | - | jmp @append87(%258); |
|
| 493 | + | add w64 %246 %243 %245; |
|
| 494 | + | load w8 %247 %246 0; |
|
| 495 | + | add w64 %248 %242 %245; |
|
| 496 | + | store w8 %247 %248 0; |
|
| 497 | + | add w32 %249 %245 1; |
|
| 498 | + | jmp @append87(%249); |
|
| 490 | 499 | @append89 |
|
| 491 | - | store w64 %251 %218 0; |
|
| 492 | - | store w32 %247 %218 12; |
|
| 500 | + | store w64 %242 %209 0; |
|
| 501 | + | store w32 %238 %209 12; |
|
| 493 | 502 | jmp @append.store85; |
|
| 494 | 503 | @then90 |
|
| 495 | 504 | ret 17; |
|
| 496 | 505 | @merge91 |
|
| 497 | - | load w32 %265 %218 8; |
|
| 498 | - | br.ult w32 0 %265 @guard#pass94 @guard#trap95; |
|
| 506 | + | load w32 %256 %209 8; |
|
| 507 | + | br.ult w32 0 %256 @guard#pass94 @guard#trap95; |
|
| 499 | 508 | @then92 |
|
| 500 | 509 | ret 18; |
|
| 501 | 510 | @merge93 |
|
| 502 | - | load w32 %268 %218 8; |
|
| 503 | - | br.ult w32 1 %268 @guard#pass98 @guard#trap99; |
|
| 511 | + | load w32 %259 %209 8; |
|
| 512 | + | br.ult w32 1 %259 @guard#pass98 @guard#trap99; |
|
| 504 | 513 | @guard#pass94 |
|
| 505 | - | load w64 %266 %218 0; |
|
| 506 | - | load w8 %267 %266 0; |
|
| 507 | - | br.ne w8 %267 171 @then92 @merge93; |
|
| 514 | + | load w64 %257 %209 0; |
|
| 515 | + | load w8 %258 %257 0; |
|
| 516 | + | br.ne w8 %258 171 @then92 @merge93; |
|
| 508 | 517 | @guard#trap95 |
|
| 509 | 518 | ebreak; |
|
| 510 | 519 | unreachable; |
|
| 511 | 520 | @then96 |
|
| 512 | 521 | ret 19; |
|
| 513 | 522 | @merge97 |
|
| 514 | - | load w32 %274 %218 8; |
|
| 515 | - | load w32 %275 %218 12; |
|
| 516 | - | br.ult w32 %274 %275 @append.store100 @append.grow101; |
|
| 523 | + | load w32 %265 %209 8; |
|
| 524 | + | load w32 %266 %209 12; |
|
| 525 | + | br.ult w32 %265 %266 @append.store100 @append.grow101; |
|
| 517 | 526 | @guard#pass98 |
|
| 518 | - | load w64 %269 %218 0; |
|
| 519 | - | add w64 %270 %269 1; |
|
| 520 | - | load w8 %271 %270 0; |
|
| 521 | - | br.ne w8 %271 205 @then96 @merge97; |
|
| 527 | + | load w64 %260 %209 0; |
|
| 528 | + | add w64 %261 %260 1; |
|
| 529 | + | load w8 %262 %261 0; |
|
| 530 | + | br.ne w8 %262 205 @then96 @merge97; |
|
| 522 | 531 | @guard#trap99 |
|
| 523 | 532 | ebreak; |
|
| 524 | 533 | unreachable; |
|
| 525 | 534 | @append.store100 |
|
| 526 | - | load w64 %289 %218 0; |
|
| 527 | - | add w64 %290 %289 %274; |
|
| 528 | - | store w8 239 %290 0; |
|
| 529 | - | add w32 %291 %274 1; |
|
| 530 | - | store w32 %291 %218 8; |
|
| 531 | - | load w32 %294 %218 8; |
|
| 532 | - | br.ne w32 %294 3 @then105 @merge106; |
|
| 535 | + | load w64 %280 %209 0; |
|
| 536 | + | add w64 %281 %280 %265; |
|
| 537 | + | store w8 239 %281 0; |
|
| 538 | + | add w32 %282 %265 1; |
|
| 539 | + | store w32 %282 %209 8; |
|
| 540 | + | load w32 %285 %209 8; |
|
| 541 | + | br.ne w32 %285 3 @then105 @merge106; |
|
| 533 | 542 | @append.grow101 |
|
| 534 | - | shl w32 %276 %275 1; |
|
| 535 | - | or w32 %277 %276 1; |
|
| 536 | - | load w64 %278 %5 0; |
|
| 537 | - | load w64 %279 %5 8; |
|
| 538 | - | mul w32 %280 %277 1; |
|
| 539 | - | call w64 %281 %278(%279, %280, 1); |
|
| 540 | - | load w64 %282 %218 0; |
|
| 541 | - | mul w32 %283 %274 1; |
|
| 543 | + | shl w32 %267 %266 1; |
|
| 544 | + | or w32 %268 %267 1; |
|
| 545 | + | load w64 %269 %7 0; |
|
| 546 | + | load w64 %270 %7 8; |
|
| 547 | + | mul w32 %271 %268 1; |
|
| 548 | + | call w64 %272 %269(%270, %271, 1); |
|
| 549 | + | load w64 %273 %209 0; |
|
| 550 | + | mul w32 %274 %265 1; |
|
| 542 | 551 | jmp @append102(0); |
|
| 543 | - | @append102(w32 %284) |
|
| 544 | - | br.ult w32 %284 %283 @append103 @append104; |
|
| 552 | + | @append102(w32 %275) |
|
| 553 | + | br.ult w32 %275 %274 @append103 @append104; |
|
| 545 | 554 | @append103 |
|
| 546 | - | add w64 %285 %282 %284; |
|
| 547 | - | load w8 %286 %285 0; |
|
| 548 | - | add w64 %287 %281 %284; |
|
| 549 | - | store w8 %286 %287 0; |
|
| 550 | - | add w32 %288 %284 1; |
|
| 551 | - | jmp @append102(%288); |
|
| 555 | + | add w64 %276 %273 %275; |
|
| 556 | + | load w8 %277 %276 0; |
|
| 557 | + | add w64 %278 %272 %275; |
|
| 558 | + | store w8 %277 %278 0; |
|
| 559 | + | add w32 %279 %275 1; |
|
| 560 | + | jmp @append102(%279); |
|
| 552 | 561 | @append104 |
|
| 553 | - | store w64 %281 %218 0; |
|
| 554 | - | store w32 %277 %218 12; |
|
| 562 | + | store w64 %272 %209 0; |
|
| 563 | + | store w32 %268 %209 12; |
|
| 555 | 564 | jmp @append.store100; |
|
| 556 | 565 | @then105 |
|
| 557 | 566 | ret 20; |
|
| 558 | 567 | @merge106 |
|
| 559 | - | load w32 %295 %218 12; |
|
| 560 | - | br.ne w32 %295 5 @then107 @merge108; |
|
| 568 | + | load w32 %286 %209 12; |
|
| 569 | + | br.ne w32 %286 5 @then107 @merge108; |
|
| 561 | 570 | @then107 |
|
| 562 | 571 | ret 21; |
|
| 563 | 572 | @merge108 |
|
| 564 | - | load w32 %296 %218 8; |
|
| 565 | - | br.ult w32 2 %296 @guard#pass111 @guard#trap112; |
|
| 573 | + | load w32 %287 %209 8; |
|
| 574 | + | br.ult w32 2 %287 @guard#pass111 @guard#trap112; |
|
| 566 | 575 | @then109 |
|
| 567 | 576 | ret 22; |
|
| 568 | 577 | @merge110 |
|
| 569 | - | load w32 %300 %218 8; |
|
| 570 | - | br.ult w32 0 %300 @guard#pass115 @guard#trap116; |
|
| 578 | + | load w32 %291 %209 8; |
|
| 579 | + | br.ult w32 0 %291 @guard#pass115 @guard#trap116; |
|
| 571 | 580 | @guard#pass111 |
|
| 572 | - | load w64 %297 %218 0; |
|
| 573 | - | add w64 %298 %297 2; |
|
| 574 | - | load w8 %299 %298 0; |
|
| 575 | - | br.ne w8 %299 239 @then109 @merge110; |
|
| 581 | + | load w64 %288 %209 0; |
|
| 582 | + | add w64 %289 %288 2; |
|
| 583 | + | load w8 %290 %289 0; |
|
| 584 | + | br.ne w8 %290 239 @then109 @merge110; |
|
| 576 | 585 | @guard#trap112 |
|
| 577 | 586 | ebreak; |
|
| 578 | 587 | unreachable; |
|
| 579 | 588 | @then113 |
|
| 580 | 589 | ret 23; |
|
| 581 | 590 | @merge114 |
|
| 582 | - | mul w32 %309 4 2; |
|
| 583 | - | call w64 %310 $arenaAlloc(%3, %309, 4); |
|
| 584 | - | reserve %311 16 8; |
|
| 585 | - | store w64 %310 %311 0; |
|
| 586 | - | store w32 0 %311 8; |
|
| 587 | - | store w32 2 %311 12; |
|
| 588 | - | load w32 %314 %311 8; |
|
| 589 | - | load w32 %315 %311 12; |
|
| 590 | - | br.ult w32 %314 %315 @append.store117 @append.grow118; |
|
| 591 | + | copy %294 $main$nominal$arena; |
|
| 592 | + | mul w32 %295 4 2; |
|
| 593 | + | call w64 %296 $arenaAlloc(%294, %295, 4); |
|
| 594 | + | reserve %297 16 8; |
|
| 595 | + | store w64 %296 %297 0; |
|
| 596 | + | store w32 0 %297 8; |
|
| 597 | + | store w32 2 %297 12; |
|
| 598 | + | load w32 %300 %297 8; |
|
| 599 | + | load w32 %301 %297 12; |
|
| 600 | + | br.ult w32 %300 %301 @append.store117 @append.grow118; |
|
| 591 | 601 | @guard#pass115 |
|
| 592 | - | load w64 %301 %218 0; |
|
| 593 | - | load w8 %302 %301 0; |
|
| 594 | - | br.ne w8 %302 171 @then113 @merge114; |
|
| 602 | + | load w64 %292 %209 0; |
|
| 603 | + | load w8 %293 %292 0; |
|
| 604 | + | br.ne w8 %293 171 @then113 @merge114; |
|
| 595 | 605 | @guard#trap116 |
|
| 596 | 606 | ebreak; |
|
| 597 | 607 | unreachable; |
|
| 598 | 608 | @append.store117 |
|
| 599 | - | load w64 %329 %311 0; |
|
| 600 | - | mul w64 %330 %314 4; |
|
| 601 | - | add w64 %331 %329 %330; |
|
| 602 | - | store w32 42 %331 0; |
|
| 603 | - | add w32 %332 %314 1; |
|
| 604 | - | store w32 %332 %311 8; |
|
| 605 | - | blit %311 %311 16; |
|
| 606 | - | load w32 %335 %311 8; |
|
| 607 | - | br.eq w32 %335 1 @assert.ok123 @assert.fail122; |
|
| 609 | + | load w64 %315 %297 0; |
|
| 610 | + | mul w64 %316 %300 4; |
|
| 611 | + | add w64 %317 %315 %316; |
|
| 612 | + | store w32 42 %317 0; |
|
| 613 | + | add w32 %318 %300 1; |
|
| 614 | + | store w32 %318 %297 8; |
|
| 615 | + | blit %297 %297 16; |
|
| 616 | + | load w32 %321 %297 8; |
|
| 617 | + | br.eq w32 %321 1 @assert.ok123 @assert.fail122; |
|
| 608 | 618 | @append.grow118 |
|
| 609 | - | shl w32 %316 %315 1; |
|
| 610 | - | or w32 %317 %316 1; |
|
| 611 | - | load w64 %318 %5 0; |
|
| 612 | - | load w64 %319 %5 8; |
|
| 613 | - | mul w32 %320 %317 4; |
|
| 614 | - | call w64 %321 %318(%319, %320, 4); |
|
| 615 | - | load w64 %322 %311 0; |
|
| 616 | - | mul w32 %323 %314 4; |
|
| 619 | + | shl w32 %302 %301 1; |
|
| 620 | + | or w32 %303 %302 1; |
|
| 621 | + | load w64 %304 %7 0; |
|
| 622 | + | load w64 %305 %7 8; |
|
| 623 | + | mul w32 %306 %303 4; |
|
| 624 | + | call w64 %307 %304(%305, %306, 4); |
|
| 625 | + | load w64 %308 %297 0; |
|
| 626 | + | mul w32 %309 %300 4; |
|
| 617 | 627 | jmp @append119(0); |
|
| 618 | - | @append119(w32 %324) |
|
| 619 | - | br.ult w32 %324 %323 @append120 @append121; |
|
| 628 | + | @append119(w32 %310) |
|
| 629 | + | br.ult w32 %310 %309 @append120 @append121; |
|
| 620 | 630 | @append120 |
|
| 621 | - | add w64 %325 %322 %324; |
|
| 622 | - | load w8 %326 %325 0; |
|
| 623 | - | add w64 %327 %321 %324; |
|
| 624 | - | store w8 %326 %327 0; |
|
| 625 | - | add w32 %328 %324 1; |
|
| 626 | - | jmp @append119(%328); |
|
| 631 | + | add w64 %311 %308 %310; |
|
| 632 | + | load w8 %312 %311 0; |
|
| 633 | + | add w64 %313 %307 %310; |
|
| 634 | + | store w8 %312 %313 0; |
|
| 635 | + | add w32 %314 %310 1; |
|
| 636 | + | jmp @append119(%314); |
|
| 627 | 637 | @append121 |
|
| 628 | - | store w64 %321 %311 0; |
|
| 629 | - | store w32 %317 %311 12; |
|
| 638 | + | store w64 %307 %297 0; |
|
| 639 | + | store w32 %303 %297 12; |
|
| 630 | 640 | jmp @append.store117; |
|
| 631 | 641 | @assert.fail122 |
|
| 632 | 642 | unreachable; |
|
| 633 | 643 | @assert.ok123 |
|
| 634 | - | load w32 %336 %311 8; |
|
| 635 | - | br.ult w32 0 %336 @guard#pass126 @guard#trap127; |
|
| 644 | + | load w32 %322 %297 8; |
|
| 645 | + | br.ult w32 0 %322 @guard#pass126 @guard#trap127; |
|
| 636 | 646 | @assert.fail124 |
|
| 637 | 647 | unreachable; |
|
| 638 | 648 | @assert.ok125 |
|
| 639 | - | load w32 %341 %311 8; |
|
| 640 | - | load w32 %342 %311 12; |
|
| 641 | - | br.ult w32 %341 %342 @append.store128 @append.grow129; |
|
| 649 | + | load w32 %327 %297 8; |
|
| 650 | + | load w32 %328 %297 12; |
|
| 651 | + | br.ult w32 %327 %328 @append.store128 @append.grow129; |
|
| 642 | 652 | @guard#pass126 |
|
| 643 | - | load w64 %337 %311 0; |
|
| 644 | - | sload w32 %338 %337 0; |
|
| 645 | - | br.eq w32 %338 42 @assert.ok125 @assert.fail124; |
|
| 653 | + | load w64 %323 %297 0; |
|
| 654 | + | sload w32 %324 %323 0; |
|
| 655 | + | br.eq w32 %324 42 @assert.ok125 @assert.fail124; |
|
| 646 | 656 | @guard#trap127 |
|
| 647 | 657 | ebreak; |
|
| 648 | 658 | unreachable; |
|
| 649 | 659 | @append.store128 |
|
| 650 | - | load w64 %356 %311 0; |
|
| 651 | - | mul w64 %357 %341 4; |
|
| 652 | - | add w64 %358 %356 %357; |
|
| 653 | - | store w32 43 %358 0; |
|
| 654 | - | add w32 %359 %341 1; |
|
| 655 | - | store w32 %359 %311 8; |
|
| 656 | - | blit %311 %311 16; |
|
| 657 | - | load w32 %362 %311 8; |
|
| 658 | - | br.eq w32 %362 2 @assert.ok134 @assert.fail133; |
|
| 660 | + | load w64 %342 %297 0; |
|
| 661 | + | mul w64 %343 %327 4; |
|
| 662 | + | add w64 %344 %342 %343; |
|
| 663 | + | store w32 43 %344 0; |
|
| 664 | + | add w32 %345 %327 1; |
|
| 665 | + | store w32 %345 %297 8; |
|
| 666 | + | blit %297 %297 16; |
|
| 667 | + | load w32 %348 %297 8; |
|
| 668 | + | br.eq w32 %348 2 @assert.ok134 @assert.fail133; |
|
| 659 | 669 | @append.grow129 |
|
| 660 | - | shl w32 %343 %342 1; |
|
| 661 | - | or w32 %344 %343 1; |
|
| 662 | - | load w64 %345 %5 0; |
|
| 663 | - | load w64 %346 %5 8; |
|
| 664 | - | mul w32 %347 %344 4; |
|
| 665 | - | call w64 %348 %345(%346, %347, 4); |
|
| 666 | - | load w64 %349 %311 0; |
|
| 667 | - | mul w32 %350 %341 4; |
|
| 670 | + | shl w32 %329 %328 1; |
|
| 671 | + | or w32 %330 %329 1; |
|
| 672 | + | load w64 %331 %7 0; |
|
| 673 | + | load w64 %332 %7 8; |
|
| 674 | + | mul w32 %333 %330 4; |
|
| 675 | + | call w64 %334 %331(%332, %333, 4); |
|
| 676 | + | load w64 %335 %297 0; |
|
| 677 | + | mul w32 %336 %327 4; |
|
| 668 | 678 | jmp @append130(0); |
|
| 669 | - | @append130(w32 %351) |
|
| 670 | - | br.ult w32 %351 %350 @append131 @append132; |
|
| 679 | + | @append130(w32 %337) |
|
| 680 | + | br.ult w32 %337 %336 @append131 @append132; |
|
| 671 | 681 | @append131 |
|
| 672 | - | add w64 %352 %349 %351; |
|
| 673 | - | load w8 %353 %352 0; |
|
| 674 | - | add w64 %354 %348 %351; |
|
| 675 | - | store w8 %353 %354 0; |
|
| 676 | - | add w32 %355 %351 1; |
|
| 677 | - | jmp @append130(%355); |
|
| 682 | + | add w64 %338 %335 %337; |
|
| 683 | + | load w8 %339 %338 0; |
|
| 684 | + | add w64 %340 %334 %337; |
|
| 685 | + | store w8 %339 %340 0; |
|
| 686 | + | add w32 %341 %337 1; |
|
| 687 | + | jmp @append130(%341); |
|
| 678 | 688 | @append132 |
|
| 679 | - | store w64 %348 %311 0; |
|
| 680 | - | store w32 %344 %311 12; |
|
| 689 | + | store w64 %334 %297 0; |
|
| 690 | + | store w32 %330 %297 12; |
|
| 681 | 691 | jmp @append.store128; |
|
| 682 | 692 | @assert.fail133 |
|
| 683 | 693 | unreachable; |
|
| 684 | 694 | @assert.ok134 |
|
| 685 | - | load w32 %363 %311 8; |
|
| 686 | - | br.ult w32 0 %363 @guard#pass135 @guard#trap136; |
|
| 695 | + | load w32 %349 %297 8; |
|
| 696 | + | br.ult w32 0 %349 @guard#pass135 @guard#trap136; |
|
| 687 | 697 | @guard#pass135 |
|
| 688 | - | load w64 %364 %311 0; |
|
| 689 | - | load w32 %367 %311 8; |
|
| 690 | - | load w32 %368 %311 12; |
|
| 691 | - | br.ult w32 %367 %368 @append.store137 @append.grow138; |
|
| 698 | + | load w64 %350 %297 0; |
|
| 699 | + | load w32 %353 %297 8; |
|
| 700 | + | load w32 %354 %297 12; |
|
| 701 | + | br.ult w32 %353 %354 @append.store137 @append.grow138; |
|
| 692 | 702 | @guard#trap136 |
|
| 693 | 703 | ebreak; |
|
| 694 | 704 | unreachable; |
|
| 695 | 705 | @append.store137 |
|
| 696 | - | load w64 %382 %311 0; |
|
| 697 | - | mul w64 %383 %367 4; |
|
| 698 | - | add w64 %384 %382 %383; |
|
| 699 | - | store w32 44 %384 0; |
|
| 700 | - | add w32 %385 %367 1; |
|
| 701 | - | store w32 %385 %311 8; |
|
| 702 | - | blit %311 %311 16; |
|
| 703 | - | load w32 %388 %311 8; |
|
| 704 | - | br.eq w32 %388 3 @assert.ok143 @assert.fail142; |
|
| 706 | + | load w64 %368 %297 0; |
|
| 707 | + | mul w64 %369 %353 4; |
|
| 708 | + | add w64 %370 %368 %369; |
|
| 709 | + | store w32 44 %370 0; |
|
| 710 | + | add w32 %371 %353 1; |
|
| 711 | + | store w32 %371 %297 8; |
|
| 712 | + | blit %297 %297 16; |
|
| 713 | + | load w32 %374 %297 8; |
|
| 714 | + | br.eq w32 %374 3 @assert.ok143 @assert.fail142; |
|
| 705 | 715 | @append.grow138 |
|
| 706 | - | shl w32 %369 %368 1; |
|
| 707 | - | or w32 %370 %369 1; |
|
| 708 | - | load w64 %371 %5 0; |
|
| 709 | - | load w64 %372 %5 8; |
|
| 710 | - | mul w32 %373 %370 4; |
|
| 711 | - | call w64 %374 %371(%372, %373, 4); |
|
| 712 | - | load w64 %375 %311 0; |
|
| 713 | - | mul w32 %376 %367 4; |
|
| 716 | + | shl w32 %355 %354 1; |
|
| 717 | + | or w32 %356 %355 1; |
|
| 718 | + | load w64 %357 %7 0; |
|
| 719 | + | load w64 %358 %7 8; |
|
| 720 | + | mul w32 %359 %356 4; |
|
| 721 | + | call w64 %360 %357(%358, %359, 4); |
|
| 722 | + | load w64 %361 %297 0; |
|
| 723 | + | mul w32 %362 %353 4; |
|
| 714 | 724 | jmp @append139(0); |
|
| 715 | - | @append139(w32 %377) |
|
| 716 | - | br.ult w32 %377 %376 @append140 @append141; |
|
| 725 | + | @append139(w32 %363) |
|
| 726 | + | br.ult w32 %363 %362 @append140 @append141; |
|
| 717 | 727 | @append140 |
|
| 718 | - | add w64 %378 %375 %377; |
|
| 719 | - | load w8 %379 %378 0; |
|
| 720 | - | add w64 %380 %374 %377; |
|
| 721 | - | store w8 %379 %380 0; |
|
| 722 | - | add w32 %381 %377 1; |
|
| 723 | - | jmp @append139(%381); |
|
| 728 | + | add w64 %364 %361 %363; |
|
| 729 | + | load w8 %365 %364 0; |
|
| 730 | + | add w64 %366 %360 %363; |
|
| 731 | + | store w8 %365 %366 0; |
|
| 732 | + | add w32 %367 %363 1; |
|
| 733 | + | jmp @append139(%367); |
|
| 724 | 734 | @append141 |
|
| 725 | - | store w64 %374 %311 0; |
|
| 726 | - | store w32 %370 %311 12; |
|
| 735 | + | store w64 %360 %297 0; |
|
| 736 | + | store w32 %356 %297 12; |
|
| 727 | 737 | jmp @append.store137; |
|
| 728 | 738 | @assert.fail142 |
|
| 729 | 739 | unreachable; |
|
| 730 | 740 | @assert.ok143 |
|
| 731 | - | load w32 %389 %311 12; |
|
| 732 | - | br.eq w32 %389 5 @assert.ok145 @assert.fail144; |
|
| 741 | + | load w32 %375 %297 12; |
|
| 742 | + | br.eq w32 %375 5 @assert.ok145 @assert.fail144; |
|
| 733 | 743 | @assert.fail144 |
|
| 734 | 744 | unreachable; |
|
| 735 | 745 | @assert.ok145 |
|
| 736 | - | load w32 %390 %311 8; |
|
| 737 | - | br.ult w32 0 %390 @guard#pass146 @guard#trap147; |
|
| 746 | + | load w32 %376 %297 8; |
|
| 747 | + | br.ult w32 0 %376 @guard#pass146 @guard#trap147; |
|
| 738 | 748 | @guard#pass146 |
|
| 739 | - | load w64 %391 %311 0; |
|
| 740 | - | br.ne w64 %364 %391 @assert.ok149 @assert.fail148; |
|
| 749 | + | load w64 %377 %297 0; |
|
| 750 | + | br.ne w64 %350 %377 @assert.ok149 @assert.fail148; |
|
| 741 | 751 | @guard#trap147 |
|
| 742 | 752 | ebreak; |
|
| 743 | 753 | unreachable; |
|
| 744 | 754 | @assert.fail148 |
|
| 745 | 755 | unreachable; |
|
| 746 | 756 | @assert.ok149 |
|
| 747 | - | load w32 %394 %311 8; |
|
| 748 | - | br.ult w32 0 %394 @guard#pass152 @guard#trap153; |
|
| 757 | + | load w32 %380 %297 8; |
|
| 758 | + | br.ult w32 0 %380 @guard#pass152 @guard#trap153; |
|
| 749 | 759 | @assert.fail150 |
|
| 750 | 760 | unreachable; |
|
| 751 | 761 | @assert.ok151 |
|
| 752 | - | load w32 %397 %311 8; |
|
| 753 | - | br.ult w32 1 %397 @guard#pass156 @guard#trap157; |
|
| 762 | + | load w32 %383 %297 8; |
|
| 763 | + | br.ult w32 1 %383 @guard#pass156 @guard#trap157; |
|
| 754 | 764 | @guard#pass152 |
|
| 755 | - | load w64 %395 %311 0; |
|
| 756 | - | sload w32 %396 %395 0; |
|
| 757 | - | br.eq w32 %396 42 @assert.ok151 @assert.fail150; |
|
| 765 | + | load w64 %381 %297 0; |
|
| 766 | + | sload w32 %382 %381 0; |
|
| 767 | + | br.eq w32 %382 42 @assert.ok151 @assert.fail150; |
|
| 758 | 768 | @guard#trap153 |
|
| 759 | 769 | ebreak; |
|
| 760 | 770 | unreachable; |
|
| 761 | 771 | @assert.fail154 |
|
| 762 | 772 | unreachable; |
|
| 763 | 773 | @assert.ok155 |
|
| 764 | - | load w32 %402 %311 8; |
|
| 765 | - | br.ult w32 2 %402 @guard#pass160 @guard#trap161; |
|
| 774 | + | load w32 %388 %297 8; |
|
| 775 | + | br.ult w32 2 %388 @guard#pass160 @guard#trap161; |
|
| 766 | 776 | @guard#pass156 |
|
| 767 | - | load w64 %398 %311 0; |
|
| 768 | - | mul w64 %399 1 4; |
|
| 769 | - | add w64 %400 %398 %399; |
|
| 770 | - | sload w32 %401 %400 0; |
|
| 771 | - | br.eq w32 %401 43 @assert.ok155 @assert.fail154; |
|
| 777 | + | load w64 %384 %297 0; |
|
| 778 | + | mul w64 %385 1 4; |
|
| 779 | + | add w64 %386 %384 %385; |
|
| 780 | + | sload w32 %387 %386 0; |
|
| 781 | + | br.eq w32 %387 43 @assert.ok155 @assert.fail154; |
|
| 772 | 782 | @guard#trap157 |
|
| 773 | 783 | ebreak; |
|
| 774 | 784 | unreachable; |
|
| 775 | 785 | @assert.fail158 |
|
| 776 | 786 | unreachable; |
|
| 777 | 787 | @assert.ok159 |
|
| 778 | 788 | ret 0; |
|
| 779 | 789 | @guard#pass160 |
|
| 780 | - | load w64 %403 %311 0; |
|
| 781 | - | mul w64 %404 2 4; |
|
| 782 | - | add w64 %405 %403 %404; |
|
| 783 | - | sload w32 %406 %405 0; |
|
| 784 | - | br.eq w32 %406 44 @assert.ok159 @assert.fail158; |
|
| 790 | + | load w64 %389 %297 0; |
|
| 791 | + | mul w64 %390 2 4; |
|
| 792 | + | add w64 %391 %389 %390; |
|
| 793 | + | sload w32 %392 %391 0; |
|
| 794 | + | br.eq w32 %392 44 @assert.ok159 @assert.fail158; |
|
| 785 | 795 | @guard#trap161 |
|
| 786 | 796 | ebreak; |
|
| 787 | 797 | unreachable; |
|
| 788 | 798 | } |
|
| 789 | - |
test/tests/slice.assign.rad
+4 -4
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | @default fn main() -> i32 { |
|
| 3 | + | @default unsafe fn main() -> i32 { |
|
| 4 | 4 | // Fill entire array. |
|
| 5 | 5 | let mut a: [i32; 4] = [1, 2, 3, 4]; |
|
| 6 | 6 | set a[..] = 0; |
|
| 7 | 7 | assert a == [0, 0, 0, 0]; |
|
| 8 | 8 |
| 11 | 11 | set b[1..3] = 99; |
|
| 12 | 12 | assert b == [1, 99, 99, 4]; |
|
| 13 | 13 | ||
| 14 | 14 | // Fill mutable slice. |
|
| 15 | 15 | let mut c: [i32; 4] = [10, 20, 30, 40]; |
|
| 16 | - | let cs: *mut [i32] = &mut c[..]; |
|
| 16 | + | let cs: *unsafe mut [i32] = &mut c[..]; |
|
| 17 | 17 | set cs[..] = 7; |
|
| 18 | 18 | assert c == [7, 7, 7, 7]; |
|
| 19 | 19 | ||
| 20 | 20 | // Fill sub-range of mutable slice. |
|
| 21 | 21 | let mut d: [i32; 5] = [1, 2, 3, 4, 5]; |
|
| 22 | - | let ds: *mut [i32] = &mut d[..]; |
|
| 22 | + | let ds: *unsafe mut [i32] = &mut d[..]; |
|
| 23 | 23 | set ds[1..4] = 0; |
|
| 24 | 24 | assert d == [1, 0, 0, 0, 5]; |
|
| 25 | 25 | ||
| 26 | 26 | // Copy slice into array. |
|
| 27 | 27 | let mut e: [i32; 3] = [0, 0, 0]; |
| 40 | 40 | set g[1..1] = 99; |
|
| 41 | 41 | assert g == [1, 2, 3]; |
|
| 42 | 42 | ||
| 43 | 43 | // Copy between sub-ranges. |
|
| 44 | 44 | let mut i: [i32; 10] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; |
|
| 45 | - | let s: *mut [i32] = &mut i[..]; |
|
| 45 | + | let s: *unsafe mut [i32] = &mut i[..]; |
|
| 46 | 46 | set s[1..4] = &s[7..10]; |
|
| 47 | 47 | assert i[1] == 80 and i[2] == 90 and i[3] == 100; |
|
| 48 | 48 | ||
| 49 | 49 | // Fill u8 array. |
|
| 50 | 50 | let mut h: [u8; 4] = [65, 66, 67, 68]; |
test/tests/slice.cap.rad
+3 -3
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test slice capacity field and @sliceOf builtin. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | // Test that regular slices have cap == len. |
|
| 6 | 6 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 7 | - | let s = &mut arr[..]; |
|
| 7 | + | let s: *unsafe mut [i32] = &mut arr[..]; |
|
| 8 | 8 | ||
| 9 | 9 | if s.cap <> 4 { |
|
| 10 | 10 | return 1; |
|
| 11 | 11 | } |
|
| 12 | 12 | if s.len <> s.cap { |
|
| 13 | 13 | return 2; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | // Test @sliceOf with explicit capacity. |
|
| 17 | - | let ptr = &mut arr[0]; |
|
| 17 | + | let ptr: *unsafe mut i32 = &mut arr[0]; |
|
| 18 | 18 | let s2 = @sliceOf(ptr, 2, 4); |
|
| 19 | 19 | ||
| 20 | 20 | if s2.len <> 2 { |
|
| 21 | 21 | return 3; |
|
| 22 | 22 | } |
test/tests/slice.delete.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test slice .delete() method. |
|
| 3 | 3 | ||
| 4 | - | @default fn main() -> i32 { |
|
| 4 | + | @default unsafe fn main() -> i32 { |
|
| 5 | 5 | let mut arr: [i32; 5] = [10, 20, 30, 40, 50]; |
|
| 6 | - | let mut s = &mut arr[..]; |
|
| 6 | + | let mut s: *unsafe mut [i32] = &mut arr[..]; |
|
| 7 | 7 | ||
| 8 | 8 | // Delete middle element (index 2: value 30). |
|
| 9 | 9 | s.delete(2); |
|
| 10 | 10 | ||
| 11 | 11 | if s.len <> 4 { |
test/tests/slice.empty.suffix.rad
+5 -5
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | fn checkArraySuffix() { |
|
| 3 | + | unsafe fn checkArraySuffix() { |
|
| 4 | 4 | let xs: [u8; 3] = [10, 20, 30]; |
|
| 5 | - | let empty = &xs[xs.len..xs.len]; |
|
| 5 | + | let empty: *unsafe [u8] = &xs[xs.len..xs.len]; |
|
| 6 | 6 | ||
| 7 | 7 | assert empty.len == 0; |
|
| 8 | 8 | } |
|
| 9 | 9 | ||
| 10 | - | fn checkSliceSuffix() { |
|
| 10 | + | unsafe fn checkSliceSuffix() { |
|
| 11 | 11 | let xs: [u8; 3] = [10, 20, 30]; |
|
| 12 | - | let slice: *[u8] = &xs[..]; |
|
| 12 | + | let slice: *unsafe [u8] = &xs[..]; |
|
| 13 | 13 | let empty = &slice[slice.len..slice.len]; |
|
| 14 | 14 | ||
| 15 | 15 | assert empty.len == 0; |
|
| 16 | 16 | } |
|
| 17 | 17 |
| 20 | 20 | let empty = &text[text.len..text.len]; |
|
| 21 | 21 | ||
| 22 | 22 | assert empty.len == 0; |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | - | @default fn main() -> i32 { |
|
| 25 | + | @default unsafe fn main() -> i32 { |
|
| 26 | 26 | checkArraySuffix(); |
|
| 27 | 27 | checkSliceSuffix(); |
|
| 28 | 28 | checkStringSuffix(); |
|
| 29 | 29 | return 0; |
|
| 30 | 30 | } |
test/tests/slice.of.rad
+2 -2
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that @sliceOf produces a fat pointer with the correct length. |
|
| 3 | 3 | ||
| 4 | - | fn makeSlice(ptr: *mut i32, count: u32) -> *mut [i32] { |
|
| 4 | + | fn makeSlice(ptr: *unsafe mut i32, count: u32) -> *unsafe mut [i32] { |
|
| 5 | 5 | return @sliceOf(ptr, count); |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 10 | 10 | let s = makeSlice(&mut arr[0], 4); |
|
| 11 | 11 | ||
| 12 | 12 | assert s.len == 4; |
|
| 13 | 13 | assert s[0] == 10; |
test/tests/slice.range.bounds.check.rad
+2 -2
| 3 | 3 | ||
| 4 | 4 | fn id(n: u32) -> u32 { |
|
| 5 | 5 | return n; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let xs: [i32; 3] = [1, 2, 3]; |
|
| 10 | - | let slice: *[i32] = &xs[..]; |
|
| 10 | + | let slice: *unsafe [i32] = &xs[..]; |
|
| 11 | 11 | let bad = &slice[id(4)..id(4)]; |
|
| 12 | 12 | ||
| 13 | 13 | return bad.len as i32; |
|
| 14 | 14 | } |
test/tests/slice.range.dynamic.rad
+2 -2
| 2 | 2 | ||
| 3 | 3 | fn id(n: u32) -> u32 { |
|
| 4 | 4 | return n; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | @default fn main() -> i32 { |
|
| 7 | + | @default unsafe fn main() -> i32 { |
|
| 8 | 8 | let xs: [u8; 5] = [10, 20, 30, 40, 50]; |
|
| 9 | - | let slice: *[u8] = &xs[..]; |
|
| 9 | + | let slice: *unsafe [u8] = &xs[..]; |
|
| 10 | 10 | ||
| 11 | 11 | let start = id(1); |
|
| 12 | 12 | let end = id(4); |
|
| 13 | 13 | let mid = &slice[start..end]; |
|
| 14 | 14 |
test/tests/slice.range.order.check.rad
+2 -2
| 3 | 3 | ||
| 4 | 4 | fn id(n: u32) -> u32 { |
|
| 5 | 5 | return n; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | @default fn main() -> i32 { |
|
| 8 | + | @default unsafe fn main() -> i32 { |
|
| 9 | 9 | let xs: [i32; 3] = [1, 2, 3]; |
|
| 10 | - | let slice: *[i32] = &xs[..]; |
|
| 10 | + | let slice: *unsafe [i32] = &xs[..]; |
|
| 11 | 11 | let bad = &slice[id(2)..id(1)]; |
|
| 12 | 12 | ||
| 13 | 13 | return bad.len as i32; |
|
| 14 | 14 | } |
test/tests/slice.range.rad
+5 -5
| 1 | 1 | /// Returns a subslice with explicit bounds. |
|
| 2 | - | fn sliceRange(s: *[i32], start: u32, end: u32) -> *[i32] { |
|
| 2 | + | unsafe fn sliceRange(s: &[i32], start: u32, end: u32) -> *unsafe [i32] { |
|
| 3 | 3 | return &s[start..end]; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | /// Returns a subslice with an open end bound. |
|
| 7 | - | fn sliceRangeOpenEnd(s: *[i32], start: u32) -> *[i32] { |
|
| 7 | + | unsafe fn sliceRangeOpenEnd(s: &[i32], start: u32) -> *unsafe [i32] { |
|
| 8 | 8 | return &s[start..]; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | /// Returns a subslice with an open start bound. |
|
| 12 | - | fn sliceRangeOpenStart(s: *[i32], end: u32) -> *[i32] { |
|
| 12 | + | unsafe fn sliceRangeOpenStart(s: &[i32], end: u32) -> *unsafe [i32] { |
|
| 13 | 13 | return &s[..end]; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | /// Returns a full reslice of a slice. |
|
| 17 | - | fn sliceRangeFull(s: *[i32]) -> *[i32] { |
|
| 17 | + | unsafe fn sliceRangeFull(s: &[i32]) -> *unsafe [i32] { |
|
| 18 | 18 | return &s[..]; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | /// Returns a subslice from an array parameter. |
|
| 22 | - | fn sliceArray(a: [i32; 4]) -> *[i32] { |
|
| 22 | + | unsafe fn sliceArray(a: [i32; 4]) -> *unsafe [i32] { |
|
| 23 | 23 | return &a[1..3]; |
|
| 24 | 24 | } |
test/tests/slice.runtime.i32.rad
+3 -3
| 1 | 1 | // Test runtime slice literal with i32 elements |
|
| 2 | 2 | ||
| 3 | - | fn sliceWithVar(c: i32) -> *[i32] { |
|
| 3 | + | unsafe fn sliceWithVar(c: i32) -> *unsafe [i32] { |
|
| 4 | 4 | return &[c]; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | - | fn sliceWithVars(a: i32, b: i32) -> *[i32] { |
|
| 7 | + | unsafe fn sliceWithVars(a: i32, b: i32) -> *unsafe [i32] { |
|
| 8 | 8 | return &[a, b]; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | fn sliceWithMixed(a: i32) -> *[i32] { |
|
| 11 | + | unsafe fn sliceWithMixed(a: i32) -> *unsafe [i32] { |
|
| 12 | 12 | return &[a, 42, a]; |
|
| 13 | 13 | } |
test/tests/slice.runtime.literal.rad
+2 -2
| 1 | 1 | /// Create a slice literal with a runtime (non-constant) element. |
|
| 2 | - | fn sliceWithVar(c: u8) -> *[u8] { |
|
| 2 | + | unsafe fn sliceWithVar(c: u8) -> *unsafe [u8] { |
|
| 3 | 3 | return &[c]; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | 6 | /// Create a slice literal with multiple runtime elements. |
|
| 7 | - | fn sliceWithVars(a: u8, b: u8) -> *[u8] { |
|
| 7 | + | unsafe fn sliceWithVars(a: u8, b: u8) -> *unsafe [u8] { |
|
| 8 | 8 | return &[a, b]; |
|
| 9 | 9 | } |
test/tests/slice.subslice.rad
+15 -15
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | - | fn testBasicSubslice() -> bool { |
|
| 3 | + | unsafe fn testBasicSubslice() -> bool { |
|
| 4 | 4 | let arr: [i32; 5] = [10, 20, 30, 40, 50]; |
|
| 5 | - | let slice: *[i32] = &arr[..]; // Full slice [10, 20, 30, 40, 50] |
|
| 6 | - | let subslice: *[i32] = &slice[1..4]; // Sub-slice [20, 30, 40] |
|
| 5 | + | let slice: *unsafe [i32] = &arr[..]; // Full slice [10, 20, 30, 40, 50] |
|
| 6 | + | let subslice: *unsafe [i32] = &slice[1..4]; // Sub-slice [20, 30, 40] |
|
| 7 | 7 | ||
| 8 | 8 | return subslice[0] == 20 and subslice[1] == 30 and subslice[2] == 40; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | - | fn testNestedSubslice() -> bool { |
|
| 11 | + | unsafe fn testNestedSubslice() -> bool { |
|
| 12 | 12 | let arr: [i32; 6] = [100, 200, 300, 400, 500, 600]; |
|
| 13 | - | let slice1: *[i32] = &arr[1..5]; // [200, 300, 400, 500] |
|
| 14 | - | let slice2: *[i32] = &slice1[1..3]; // [300, 400] |
|
| 15 | - | let slice3: *[i32] = &slice2[0..1]; // [300] |
|
| 13 | + | let slice1: *unsafe [i32] = &arr[1..5]; // [200, 300, 400, 500] |
|
| 14 | + | let slice2: *unsafe [i32] = &slice1[1..3]; // [300, 400] |
|
| 15 | + | let slice3: *unsafe [i32] = &slice2[0..1]; // [300] |
|
| 16 | 16 | ||
| 17 | 17 | return slice3[0] == 300; |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | - | fn testSubsliceLength() -> bool { |
|
| 20 | + | unsafe fn testSubsliceLength() -> bool { |
|
| 21 | 21 | let arr: [i32; 4] = [1, 2, 3, 4]; |
|
| 22 | - | let slice: *[i32] = &arr[..]; |
|
| 23 | - | let subslice: *[i32] = &slice[1..3]; |
|
| 22 | + | let slice: *unsafe [i32] = &arr[..]; |
|
| 23 | + | let subslice: *unsafe [i32] = &slice[1..3]; |
|
| 24 | 24 | ||
| 25 | 25 | return subslice.len == 2; |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn testEdgeCases() -> bool { |
|
| 28 | + | unsafe fn testEdgeCases() -> bool { |
|
| 29 | 29 | let arr: [i32; 3] = [7, 8, 9]; |
|
| 30 | - | let slice: *[i32] = &arr[..]; |
|
| 30 | + | let slice: *unsafe [i32] = &arr[..]; |
|
| 31 | 31 | ||
| 32 | 32 | // Test single element subslice |
|
| 33 | - | let single: *[i32] = &slice[1..2]; |
|
| 33 | + | let single: *unsafe [i32] = &slice[1..2]; |
|
| 34 | 34 | if (single[0] <> 8 or single.len <> 1) { |
|
| 35 | 35 | return false; |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | // Test empty subslice |
|
| 39 | - | let empty: *[i32] = &slice[2..2]; |
|
| 39 | + | let empty: *unsafe [i32] = &slice[2..2]; |
|
| 40 | 40 | if (empty.len <> 0) { |
|
| 41 | 41 | return false; |
|
| 42 | 42 | } |
|
| 43 | 43 | return true; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | - | @default fn main() -> i32 { |
|
| 46 | + | @default unsafe fn main() -> i32 { |
|
| 47 | 47 | if ( |
|
| 48 | 48 | testBasicSubslice() and |
|
| 49 | 49 | testNestedSubslice() and |
|
| 50 | 50 | testSubsliceLength() and |
|
| 51 | 51 | testEdgeCases() |
test/tests/spill.blockarg.clobber.rad
+2 -2
| 36 | 36 | record State: Copy { |
|
| 37 | 37 | h: [u32; 8], |
|
| 38 | 38 | w: [u32; 64], |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | - | fn prepareSchedule(s: *mut State, block: *[u32]) { |
|
| 41 | + | fn prepareSchedule(s: &mut State, block: &[u32]) { |
|
| 42 | 42 | let mut i: u32 = 0; |
|
| 43 | 43 | while i < 16 { |
|
| 44 | 44 | set s.w[i] = block[i]; |
|
| 45 | 45 | set i += 1; |
|
| 46 | 46 | } |
| 50 | 50 | } |
|
| 51 | 51 | } |
|
| 52 | 52 | ||
| 53 | 53 | /// SHA-256 compression: 64 rounds with 8 rotating working variables. |
|
| 54 | 54 | /// Exercises heavy register pressure and spilled block-argument shuffles. |
|
| 55 | - | fn compress(s: *mut State, k: *[u32]) { |
|
| 55 | + | fn compress(s: &mut State, k: &[u32]) { |
|
| 56 | 56 | let mut a: u32 = s.h[0]; |
|
| 57 | 57 | let mut b: u32 = s.h[1]; |
|
| 58 | 58 | let mut c: u32 = s.h[2]; |
|
| 59 | 59 | let mut d: u32 = s.h[3]; |
|
| 60 | 60 | let mut e: u32 = s.h[4]; |
test/tests/spill.loop.rad
+1 -1
| 29 | 29 | return n; |
|
| 30 | 30 | } |
|
| 31 | 31 | return (n + a - 1) & (0 - a); |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | - | fn computeRecordLayout(tags: *[u8]) -> Layout { |
|
| 34 | + | fn computeRecordLayout(tags: &[u8]) -> Layout { |
|
| 35 | 35 | let mut currentOffset: u32 = 0; |
|
| 36 | 36 | let mut maxAlignment: u32 = 1; |
|
| 37 | 37 | for i in 0..tags.len { |
|
| 38 | 38 | let layout = getLayout(tags[i]); |
|
| 39 | 39 | set currentOffset = alignUp(currentOffset, layout.alignment); |
test/tests/trait.aggregate.ret.rad
+9 -9
| 14 | 14 | y: i32, |
|
| 15 | 15 | z: i32, |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | trait Geometry { |
|
| 19 | - | fn (*Geometry) origin() -> Point; |
|
| 20 | - | fn (*Geometry) center() -> Vec3; |
|
| 21 | - | fn (*Geometry) maybe() -> ?i32; |
|
| 19 | + | unsafe fn (*unsafe Geometry) origin() -> Point; |
|
| 20 | + | unsafe fn (*unsafe Geometry) center() -> Vec3; |
|
| 21 | + | unsafe fn (*unsafe Geometry) maybe() -> ?i32; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | record Circle: Copy { |
|
| 25 | 25 | cx: i32, |
|
| 26 | 26 | cy: i32, |
|
| 27 | 27 | radius: i32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | instance Geometry for Circle { |
|
| 31 | - | fn (c: *Circle) origin() -> Point { |
|
| 31 | + | unsafe fn (c: *unsafe Circle) origin() -> Point { |
|
| 32 | 32 | return Point { x: c.cx, y: c.cy }; |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | - | fn (c: *Circle) center() -> Vec3 { |
|
| 35 | + | unsafe fn (c: *unsafe Circle) center() -> Vec3 { |
|
| 36 | 36 | return Vec3 { x: c.cx, y: c.cy, z: 0 }; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | - | fn (c: *Circle) maybe() -> ?i32 { |
|
| 39 | + | unsafe fn (c: *unsafe Circle) maybe() -> ?i32 { |
|
| 40 | 40 | if c.radius > 0 { |
|
| 41 | 41 | return c.radius; |
|
| 42 | 42 | } |
|
| 43 | 43 | return nil; |
|
| 44 | 44 | } |
|
| 45 | 45 | } |
|
| 46 | 46 | ||
| 47 | - | @default fn main() -> i32 { |
|
| 47 | + | @default unsafe fn main() -> i32 { |
|
| 48 | 48 | let c = Circle { cx: 10, cy: 20, radius: 5 }; |
|
| 49 | - | let g: *opaque Geometry = &c; |
|
| 49 | + | let g: *unsafe opaque Geometry = &c; |
|
| 50 | 50 | ||
| 51 | 51 | // Small struct return (Point, 8 bytes = pointer size). |
|
| 52 | 52 | let p = g.origin(); |
|
| 53 | 53 | assert p.x == 10; |
|
| 54 | 54 | assert p.y == 20; |
| 67 | 67 | return 7; |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | // Optional return - None case. |
|
| 71 | 71 | let c2 = Circle { cx: 0, cy: 0, radius: 0 }; |
|
| 72 | - | let g2: *opaque Geometry = &c2; |
|
| 72 | + | let g2: *unsafe opaque Geometry = &c2; |
|
| 73 | 73 | let m2 = g2.maybe(); |
|
| 74 | 74 | if let _ = m2 { |
|
| 75 | 75 | return 8; |
|
| 76 | 76 | } |
|
| 77 | 77 |
test/tests/trait.array.optional.rad
+12 -12
| 11 | 11 | record Multiplier: Copy { |
|
| 12 | 12 | n: i32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | trait Transform { |
|
| 16 | - | fn (*Transform) apply(x: i32) -> i32; |
|
| 16 | + | unsafe fn (*unsafe Transform) apply(x: i32) -> i32; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | instance Transform for Adder { |
|
| 20 | - | fn (a: *Adder) apply(x: i32) -> i32 { |
|
| 20 | + | unsafe fn (a: *unsafe Adder) apply(x: i32) -> i32 { |
|
| 21 | 21 | return x + a.n; |
|
| 22 | 22 | } |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | instance Transform for Multiplier { |
|
| 26 | - | fn (m: *Multiplier) apply(x: i32) -> i32 { |
|
| 26 | + | unsafe fn (m: *unsafe Multiplier) apply(x: i32) -> i32 { |
|
| 27 | 27 | return x * m.n; |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | /// Apply a chain of transforms to a value. |
|
| 32 | - | fn applyAll(transforms: *[*opaque Transform], value: i32) -> i32 { |
|
| 32 | + | unsafe fn applyAll(transforms: *unsafe [*unsafe opaque Transform], value: i32) -> i32 { |
|
| 33 | 33 | let mut result = value; |
|
| 34 | 34 | for i in 0..transforms.len { |
|
| 35 | 35 | set result = transforms[i].apply(result); |
|
| 36 | 36 | } |
|
| 37 | 37 | return result; |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Apply an optional transform, returning the original value if nil. |
|
| 41 | - | fn applyMaybe(t: ?*opaque Transform, value: i32) -> i32 { |
|
| 41 | + | unsafe fn applyMaybe(t: ?*unsafe opaque Transform, value: i32) -> i32 { |
|
| 42 | 42 | if let tr = t { |
|
| 43 | 43 | return tr.apply(value); |
|
| 44 | 44 | } |
|
| 45 | 45 | return value; |
|
| 46 | 46 | } |
|
| 47 | 47 | ||
| 48 | - | @default fn main() -> i32 { |
|
| 48 | + | @default unsafe fn main() -> i32 { |
|
| 49 | 49 | let a1 = Adder { n: 10 }; |
|
| 50 | 50 | let m1 = Multiplier { n: 3 }; |
|
| 51 | 51 | let a2 = Adder { n: 5 }; |
|
| 52 | 52 | ||
| 53 | - | let t1: *opaque Transform = &a1; |
|
| 54 | - | let t2: *opaque Transform = &m1; |
|
| 55 | - | let t3: *opaque Transform = &a2; |
|
| 53 | + | let t1: *unsafe opaque Transform = &a1; |
|
| 54 | + | let t2: *unsafe opaque Transform = &m1; |
|
| 55 | + | let t3: *unsafe opaque Transform = &a2; |
|
| 56 | 56 | ||
| 57 | 57 | // Array of trait objects. |
|
| 58 | - | let transforms: [*opaque Transform; 3] = [t1, t2, t3]; |
|
| 58 | + | let transforms: [*unsafe opaque Transform; 3] = [t1, t2, t3]; |
|
| 59 | 59 | // Chain: (1 + 10) * 3 + 5 = 38 |
|
| 60 | 60 | let result = applyAll(&transforms[..], 1); |
|
| 61 | 61 | assert result == 38; |
|
| 62 | 62 | ||
| 63 | 63 | // Optional trait object - Some case. |
|
| 64 | - | let opt: ?*opaque Transform = t1; |
|
| 64 | + | let opt: ?*unsafe opaque Transform = t1; |
|
| 65 | 65 | let r2 = applyMaybe(opt, 5); |
|
| 66 | 66 | assert r2 == 15; |
|
| 67 | 67 | ||
| 68 | 68 | // Optional trait object - None case. |
|
| 69 | - | let none: ?*opaque Transform = nil; |
|
| 69 | + | let none: ?*unsafe opaque Transform = nil; |
|
| 70 | 70 | let r3 = applyMaybe(none, 5); |
|
| 71 | 71 | assert r3 == 5; |
|
| 72 | 72 | ||
| 73 | 73 | return 0; |
|
| 74 | 74 | } |
test/tests/trait.basic.rad
+4 -4
| 3 | 3 | record Counter: Copy { |
|
| 4 | 4 | value: i32, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | trait Adder { |
|
| 8 | - | fn (*mut Adder) add(n: i32) -> i32; |
|
| 8 | + | unsafe fn (*unsafe mut Adder) add(n: i32) -> i32; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | instance Adder for Counter { |
|
| 12 | - | fn (c: *mut Counter) add(n: i32) -> i32 { |
|
| 12 | + | unsafe fn (c: *unsafe mut Counter) add(n: i32) -> i32 { |
|
| 13 | 13 | set c.value = c.value + n; |
|
| 14 | 14 | return c.value; |
|
| 15 | 15 | } |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | @default fn main() -> i32 { |
|
| 18 | + | @default unsafe fn main() -> i32 { |
|
| 19 | 19 | let mut c = Counter { value: 10 }; |
|
| 20 | - | let a: *mut opaque Adder = &mut c; |
|
| 20 | + | let a: *unsafe mut opaque Adder = &mut c; |
|
| 21 | 21 | ||
| 22 | 22 | let result = a.add(5); |
|
| 23 | 23 | // c.value should be 15 now. |
|
| 24 | 24 | assert result == 15; |
|
| 25 | 25 | let result2 = a.add(3); |
test/tests/trait.control.flow.rad
+6 -6
| 8 | 8 | record Counter: Copy { |
|
| 9 | 9 | value: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Stepper { |
|
| 13 | - | fn (*mut Stepper) step() -> i32; |
|
| 14 | - | fn (*Stepper) current() -> i32; |
|
| 13 | + | unsafe fn (*unsafe mut Stepper) step() -> i32; |
|
| 14 | + | unsafe fn (*unsafe Stepper) current() -> i32; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | instance Stepper for Counter { |
|
| 18 | - | fn (c: *mut Counter) step() -> i32 { |
|
| 18 | + | unsafe fn (c: *unsafe mut Counter) step() -> i32 { |
|
| 19 | 19 | set c.value = c.value + 1; |
|
| 20 | 20 | return c.value; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | fn (c: *Counter) current() -> i32 { |
|
| 23 | + | unsafe fn (c: *unsafe Counter) current() -> i32 { |
|
| 24 | 24 | return c.value; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | @default fn main() -> i32 { |
|
| 28 | + | @default unsafe fn main() -> i32 { |
|
| 29 | 29 | let mut c = Counter { value: 0 }; |
|
| 30 | - | let s: *mut opaque Stepper = &mut c; |
|
| 30 | + | let s: *unsafe mut opaque Stepper = &mut c; |
|
| 31 | 31 | ||
| 32 | 32 | // Dispatch in a while loop. |
|
| 33 | 33 | let mut i: i32 = 0; |
|
| 34 | 34 | while i < 5 { |
|
| 35 | 35 | s.step(); |
test/tests/trait.fn.param.rad
+12 -12
| 11 | 11 | record Square: Copy { |
|
| 12 | 12 | side: i32, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | trait Shape { |
|
| 16 | - | fn (*Shape) area() -> i32; |
|
| 16 | + | unsafe fn (*unsafe Shape) area() -> i32; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | trait Scalable { |
|
| 20 | - | fn (*mut Scalable) scale(factor: i32); |
|
| 20 | + | unsafe fn (*unsafe mut Scalable) scale(factor: i32); |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | instance Shape for Circle { |
|
| 24 | - | fn (c: *Circle) area() -> i32 { |
|
| 24 | + | unsafe fn (c: *unsafe Circle) area() -> i32 { |
|
| 25 | 25 | return c.radius * c.radius * 3; |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | instance Shape for Square { |
|
| 30 | - | fn (s: *Square) area() -> i32 { |
|
| 30 | + | unsafe fn (s: *unsafe Square) area() -> i32 { |
|
| 31 | 31 | return s.side * s.side; |
|
| 32 | 32 | } |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | instance Scalable for Circle { |
|
| 36 | - | fn (c: *mut Circle) scale(factor: i32) { |
|
| 36 | + | unsafe fn (c: *unsafe mut Circle) scale(factor: i32) { |
|
| 37 | 37 | set c.radius = c.radius * factor; |
|
| 38 | 38 | } |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | 41 | /// Accept an immutable trait object parameter. |
|
| 42 | - | fn getArea(s: *opaque Shape) -> i32 { |
|
| 42 | + | unsafe fn getArea(s: *unsafe opaque Shape) -> i32 { |
|
| 43 | 43 | return s.area(); |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | /// Accept a mutable trait object parameter. |
|
| 47 | - | fn doubleSize(s: *mut opaque Scalable) { |
|
| 47 | + | unsafe fn doubleSize(s: *unsafe mut opaque Scalable) { |
|
| 48 | 48 | s.scale(2); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Accept two trait object parameters. |
|
| 52 | - | fn totalArea(a: *opaque Shape, b: *opaque Shape) -> i32 { |
|
| 52 | + | unsafe fn totalArea(a: *unsafe opaque Shape, b: *unsafe opaque Shape) -> i32 { |
|
| 53 | 53 | return a.area() + b.area(); |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | - | @default fn main() -> i32 { |
|
| 56 | + | @default unsafe fn main() -> i32 { |
|
| 57 | 57 | let c = Circle { radius: 5 }; |
|
| 58 | 58 | let s = Square { side: 4 }; |
|
| 59 | 59 | ||
| 60 | 60 | // Pass immutable trait objects to function. |
|
| 61 | - | let cs: *opaque Shape = &c; |
|
| 61 | + | let cs: *unsafe opaque Shape = &c; |
|
| 62 | 62 | let ca = getArea(cs); |
|
| 63 | 63 | assert ca == 75; |
|
| 64 | 64 | ||
| 65 | - | let ss: *opaque Shape = &s; |
|
| 65 | + | let ss: *unsafe opaque Shape = &s; |
|
| 66 | 66 | let sa = getArea(ss); |
|
| 67 | 67 | assert sa == 16; |
|
| 68 | 68 | ||
| 69 | 69 | // Pass two different trait objects to same function. |
|
| 70 | 70 | let total = totalArea(cs, ss); |
|
| 71 | 71 | assert total == 91; |
|
| 72 | 72 | ||
| 73 | 73 | // Pass mutable trait object to function. |
|
| 74 | 74 | let mut c2 = Circle { radius: 3 }; |
|
| 75 | - | let sc: *mut opaque Scalable = &mut c2; |
|
| 75 | + | let sc: *unsafe mut opaque Scalable = &mut c2; |
|
| 76 | 76 | doubleSize(sc); |
|
| 77 | 77 | assert c2.radius == 6; |
|
| 78 | 78 | return 0; |
|
| 79 | 79 | } |
test/tests/trait.multiple.methods.rad
+8 -8
| 8 | 8 | record Accumulator: Copy { |
|
| 9 | 9 | total: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Collector { |
|
| 13 | - | fn (*mut Collector) add(n: i32) -> i32; |
|
| 14 | - | fn (*mut Collector) clear(); |
|
| 15 | - | fn (*mut Collector) isEmpty() -> bool; |
|
| 13 | + | unsafe fn (*unsafe mut Collector) add(n: i32) -> i32; |
|
| 14 | + | unsafe fn (*unsafe mut Collector) clear(); |
|
| 15 | + | unsafe fn (*unsafe mut Collector) isEmpty() -> bool; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | instance Collector for Accumulator { |
|
| 19 | - | fn (a: *mut Accumulator) add(n: i32) -> i32 { |
|
| 19 | + | unsafe fn (a: *unsafe mut Accumulator) add(n: i32) -> i32 { |
|
| 20 | 20 | set a.total = a.total + n; |
|
| 21 | 21 | return a.total; |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | fn (a: *mut Accumulator) clear() { |
|
| 24 | + | unsafe fn (a: *unsafe mut Accumulator) clear() { |
|
| 25 | 25 | set a.total = 0; |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn (a: *mut Accumulator) isEmpty() -> bool { |
|
| 28 | + | unsafe fn (a: *unsafe mut Accumulator) isEmpty() -> bool { |
|
| 29 | 29 | return a.total == 0; |
|
| 30 | 30 | } |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | - | @default fn main() -> i32 { |
|
| 33 | + | @default unsafe fn main() -> i32 { |
|
| 34 | 34 | let mut acc = Accumulator { total: 0 }; |
|
| 35 | - | let c: *mut opaque Collector = &mut acc; |
|
| 35 | + | let c: *unsafe mut opaque Collector = &mut acc; |
|
| 36 | 36 | ||
| 37 | 37 | // Initially empty. |
|
| 38 | 38 | assert c.isEmpty(); |
|
| 39 | 39 | ||
| 40 | 40 | // Add returns running total. |
test/tests/trait.multiple.traits.rad
+9 -9
| 8 | 8 | record Counter: Copy { |
|
| 9 | 9 | value: i32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | trait Incrementable { |
|
| 13 | - | fn (*mut Incrementable) inc() -> i32; |
|
| 13 | + | unsafe fn (*unsafe mut Incrementable) inc() -> i32; |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | trait Resettable { |
|
| 17 | - | fn (*mut Resettable) reset(); |
|
| 18 | - | fn (*mut Resettable) isZero() -> bool; |
|
| 17 | + | unsafe fn (*unsafe mut Resettable) reset(); |
|
| 18 | + | unsafe fn (*unsafe mut Resettable) isZero() -> bool; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | instance Incrementable for Counter { |
|
| 22 | - | fn (c: *mut Counter) inc() -> i32 { |
|
| 22 | + | unsafe fn (c: *unsafe mut Counter) inc() -> i32 { |
|
| 23 | 23 | set c.value = c.value + 1; |
|
| 24 | 24 | return c.value; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | instance Resettable for Counter { |
|
| 29 | - | fn (c: *mut Counter) reset() { |
|
| 29 | + | unsafe fn (c: *unsafe mut Counter) reset() { |
|
| 30 | 30 | set c.value = 0; |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | - | fn (c: *mut Counter) isZero() -> bool { |
|
| 33 | + | unsafe fn (c: *unsafe mut Counter) isZero() -> bool { |
|
| 34 | 34 | return c.value == 0; |
|
| 35 | 35 | } |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | - | @default fn main() -> i32 { |
|
| 38 | + | @default unsafe fn main() -> i32 { |
|
| 39 | 39 | let mut c = Counter { value: 10 }; |
|
| 40 | 40 | ||
| 41 | 41 | // Dispatch through Incrementable. |
|
| 42 | - | let i: *mut opaque Incrementable = &mut c; |
|
| 42 | + | let i: *unsafe mut opaque Incrementable = &mut c; |
|
| 43 | 43 | let v1 = i.inc(); |
|
| 44 | 44 | assert v1 == 11; |
|
| 45 | 45 | ||
| 46 | 46 | // Dispatch through Resettable. |
|
| 47 | - | let r: *mut opaque Resettable = &mut c; |
|
| 47 | + | let r: *unsafe mut opaque Resettable = &mut c; |
|
| 48 | 48 | if r.isZero() { |
|
| 49 | 49 | return 2; |
|
| 50 | 50 | } |
|
| 51 | 51 | r.reset(); |
|
| 52 | 52 | assert r.isZero(); |
test/tests/trait.multiple.types.rad
+10 -10
| 12 | 12 | record Cat: Copy { |
|
| 13 | 13 | lives: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | trait Speaker { |
|
| 17 | - | fn (*Speaker) speak() -> i32; |
|
| 18 | - | fn (*Speaker) isOld() -> bool; |
|
| 17 | + | unsafe fn (*unsafe Speaker) speak() -> i32; |
|
| 18 | + | unsafe fn (*unsafe Speaker) isOld() -> bool; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | instance Speaker for Dog { |
|
| 22 | - | fn (d: *Dog) speak() -> i32 { |
|
| 22 | + | unsafe fn (d: *unsafe Dog) speak() -> i32 { |
|
| 23 | 23 | return d.age; |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | - | fn (d: *Dog) isOld() -> bool { |
|
| 26 | + | unsafe fn (d: *unsafe Dog) isOld() -> bool { |
|
| 27 | 27 | return d.age > 10; |
|
| 28 | 28 | } |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | instance Speaker for Cat { |
|
| 32 | - | fn (c: *Cat) speak() -> i32 { |
|
| 32 | + | unsafe fn (c: *unsafe Cat) speak() -> i32 { |
|
| 33 | 33 | return c.lives * 10; |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | - | fn (c: *Cat) isOld() -> bool { |
|
| 36 | + | unsafe fn (c: *unsafe Cat) isOld() -> bool { |
|
| 37 | 37 | return c.lives < 5; |
|
| 38 | 38 | } |
|
| 39 | 39 | } |
|
| 40 | 40 | ||
| 41 | - | @default fn main() -> i32 { |
|
| 41 | + | @default unsafe fn main() -> i32 { |
|
| 42 | 42 | let d = Dog { age: 5 }; |
|
| 43 | 43 | let c = Cat { lives: 9 }; |
|
| 44 | 44 | ||
| 45 | 45 | // Immutable trait object from Dog. |
|
| 46 | - | let sd: *opaque Speaker = &d; |
|
| 46 | + | let sd: *unsafe opaque Speaker = &d; |
|
| 47 | 47 | let v1 = sd.speak(); |
|
| 48 | 48 | assert v1 == 5; |
|
| 49 | 49 | if sd.isOld() { |
|
| 50 | 50 | return 2; |
|
| 51 | 51 | } |
|
| 52 | 52 | ||
| 53 | 53 | // Immutable trait object from Cat. |
|
| 54 | - | let sc: *opaque Speaker = &c; |
|
| 54 | + | let sc: *unsafe opaque Speaker = &c; |
|
| 55 | 55 | let v2 = sc.speak(); |
|
| 56 | 56 | assert v2 == 90; |
|
| 57 | 57 | if sc.isOld() { |
|
| 58 | 58 | return 4; |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | // Old dog. |
|
| 62 | 62 | let oldDog = Dog { age: 15 }; |
|
| 63 | - | let so: *opaque Speaker = &oldDog; |
|
| 63 | + | let so: *unsafe opaque Speaker = &oldDog; |
|
| 64 | 64 | assert so.isOld(); |
|
| 65 | 65 | return 0; |
|
| 66 | 66 | } |
test/tests/trait.object.rad
+4 -4
| 1 | 1 | record Counter: Copy { |
|
| 2 | 2 | value: i32, |
|
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | trait Adder { |
|
| 6 | - | fn (*mut Adder) add(n: i32) -> i32; |
|
| 6 | + | unsafe fn (*unsafe mut Adder) add(n: i32) -> i32; |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | instance Adder for Counter { |
|
| 10 | - | fn (c: *mut Counter) add(n: i32) -> i32 { |
|
| 10 | + | unsafe fn (c: *unsafe mut Counter) add(n: i32) -> i32 { |
|
| 11 | 11 | set c.value = c.value + n; |
|
| 12 | 12 | return c.value; |
|
| 13 | 13 | } |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | - | fn use_adder() -> i32 { |
|
| 16 | + | unsafe fn use_adder() -> i32 { |
|
| 17 | 17 | let mut c = Counter { value: 0 }; |
|
| 18 | - | let a: *mut opaque Adder = &mut c; |
|
| 18 | + | let a: *unsafe mut opaque Adder = &mut c; |
|
| 19 | 19 | return a.add(1); |
|
| 20 | 20 | } |
test/tests/trait.supertrait.forward.rad
+6 -6
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! A supertrait declared later must contribute methods to its child trait. |
|
| 3 | 3 | ||
| 4 | 4 | trait Child: Parent { |
|
| 5 | - | fn (*Child) child() -> i32; |
|
| 5 | + | unsafe fn (*unsafe Child) child() -> i32; |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | trait Parent { |
|
| 9 | - | fn (*Parent) parent() -> i32; |
|
| 9 | + | unsafe fn (*unsafe Parent) parent() -> i32; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | record Value: Copy { |
|
| 13 | 13 | n: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | instance Parent for Value { |
|
| 17 | - | fn (self: *Value) parent() -> i32 { |
|
| 17 | + | unsafe fn (self: *unsafe Value) parent() -> i32 { |
|
| 18 | 18 | return self.n; |
|
| 19 | 19 | } |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | instance Child for Value { |
|
| 23 | - | fn (self: *Value) child() -> i32 { |
|
| 23 | + | unsafe fn (self: *unsafe Value) child() -> i32 { |
|
| 24 | 24 | return self.n + 1; |
|
| 25 | 25 | } |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | @default fn main() -> i32 { |
|
| 28 | + | @default unsafe fn main() -> i32 { |
|
| 29 | 29 | let value = Value { n: 41 }; |
|
| 30 | - | let object: *opaque Child = &value; |
|
| 30 | + | let object: *unsafe opaque Child = &value; |
|
| 31 | 31 | assert object.parent() == 41; |
|
| 32 | 32 | assert object.child() == 42; |
|
| 33 | 33 | return 0; |
|
| 34 | 34 | } |
test/tests/trait.supertrait.rad
+8 -8
| 7 | 7 | //! A Socket type implements all three. Coercing a `*opaque ReadWriter` to |
|
| 8 | 8 | //! `*opaque Reader` or `*opaque Writer` should work, and dispatching through |
|
| 9 | 9 | //! any of the three trait objects should call the correct methods. |
|
| 10 | 10 | ||
| 11 | 11 | trait Reader { |
|
| 12 | - | fn (*mut Reader) read(buf: *mut [u8]) -> i32; |
|
| 12 | + | unsafe fn (*unsafe mut Reader) read(buf: *unsafe mut [u8]) -> i32; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | trait Writer { |
|
| 16 | - | fn (*mut Writer) write(data: *[u8]) -> i32; |
|
| 16 | + | unsafe fn (*unsafe mut Writer) write(data: *[u8]) -> i32; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | 19 | trait ReadWriter: Reader + Writer { |
|
| 20 | - | fn (*mut ReadWriter) flush() -> i32; |
|
| 20 | + | unsafe fn (*unsafe mut ReadWriter) flush() -> i32; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | record Socket: Copy { |
|
| 24 | 24 | rbuf: [u8; 32], |
|
| 25 | 25 | rpos: i32, |
| 27 | 27 | wbuf: [u8; 32], |
|
| 28 | 28 | wpos: i32, |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | instance Reader for Socket { |
|
| 32 | - | fn (s: *mut Socket) read(buf: *mut [u8]) -> i32 { |
|
| 32 | + | unsafe fn (s: *unsafe mut Socket) read(buf: *unsafe mut [u8]) -> i32 { |
|
| 33 | 33 | let mut i: u32 = 0; |
|
| 34 | 34 | while i < buf.len and s.rpos < s.rlen { |
|
| 35 | 35 | set buf[i] = s.rbuf[s.rpos as u32]; |
|
| 36 | 36 | set s.rpos = s.rpos + 1; |
|
| 37 | 37 | set i = i + 1; |
| 39 | 39 | return i as i32; |
|
| 40 | 40 | } |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | 43 | instance Writer for Socket { |
|
| 44 | - | fn (s: *mut Socket) write(data: *[u8]) -> i32 { |
|
| 44 | + | unsafe fn (s: *unsafe mut Socket) write(data: *[u8]) -> i32 { |
|
| 45 | 45 | let mut i: u32 = 0; |
|
| 46 | 46 | while i < data.len { |
|
| 47 | 47 | if s.wpos >= 32 { |
|
| 48 | 48 | return s.wpos; |
|
| 49 | 49 | } |
| 54 | 54 | return s.wpos; |
|
| 55 | 55 | } |
|
| 56 | 56 | } |
|
| 57 | 57 | ||
| 58 | 58 | instance ReadWriter for Socket { |
|
| 59 | - | fn (s: *mut Socket) flush() -> i32 { |
|
| 59 | + | unsafe fn (s: *unsafe mut Socket) flush() -> i32 { |
|
| 60 | 60 | let pos = s.wpos; |
|
| 61 | 61 | set s.wpos = 0; |
|
| 62 | 62 | return pos; |
|
| 63 | 63 | } |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | - | @default fn main() -> i32 { |
|
| 66 | + | @default unsafe fn main() -> i32 { |
|
| 67 | 67 | let mut sock = Socket { |
|
| 68 | 68 | rbuf: undefined, |
|
| 69 | 69 | rpos: 0, |
|
| 70 | 70 | rlen: 5, |
|
| 71 | 71 | wbuf: undefined, |
| 77 | 77 | set sock.rbuf[2] = 'l' as u8; |
|
| 78 | 78 | set sock.rbuf[3] = 'l' as u8; |
|
| 79 | 79 | set sock.rbuf[4] = 'o' as u8; |
|
| 80 | 80 | ||
| 81 | 81 | // Test 1: Use as ReadWriter trait object. |
|
| 82 | - | let rw: *mut opaque ReadWriter = &mut sock; |
|
| 82 | + | let rw: *unsafe mut opaque ReadWriter = &mut sock; |
|
| 83 | 83 | ||
| 84 | 84 | // Test 2: Write through the ReadWriter (dispatches via Writer supertrait). |
|
| 85 | 85 | let wn = rw.write("abc"); |
|
| 86 | 86 | assert wn == 3; |
|
| 87 | 87 | assert sock.wpos == 3; |
test/tests/trait.throws.rad
+4 -4
| 9 | 9 | record StrictParser: Copy { |
|
| 10 | 10 | limit: i32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | trait Parser { |
|
| 14 | - | fn (*Parser) parse(n: i32) -> i32 throws (ParseError); |
|
| 14 | + | unsafe fn (*unsafe Parser) parse(n: i32) -> i32 throws (ParseError); |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | instance Parser for StrictParser { |
|
| 18 | - | fn (p: *StrictParser) parse(n: i32) -> i32 throws (ParseError) { |
|
| 18 | + | unsafe fn (p: *unsafe StrictParser) parse(n: i32) -> i32 throws (ParseError) { |
|
| 19 | 19 | if n < 0 { |
|
| 20 | 20 | throw ParseError::InvalidInput; |
|
| 21 | 21 | } |
|
| 22 | 22 | if n > p.limit { |
|
| 23 | 23 | throw ParseError::Overflow; |
|
| 24 | 24 | } |
|
| 25 | 25 | return n * 2; |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | - | @default fn main() -> i32 { |
|
| 29 | + | @default unsafe fn main() -> i32 { |
|
| 30 | 30 | let sp = StrictParser { limit: 100 }; |
|
| 31 | - | let p: *opaque Parser = &sp; |
|
| 31 | + | let p: *unsafe opaque Parser = &sp; |
|
| 32 | 32 | ||
| 33 | 33 | // Success path. |
|
| 34 | 34 | let r1 = try p.parse(5) catch { |
|
| 35 | 35 | return 1; |
|
| 36 | 36 | }; |
test/tests/trait.writer.rad
+12 -12
| 5 | 5 | //! A BufferWriter writes to an in-memory buffer; a CountingWriter wraps any |
|
| 6 | 6 | //! Writer and tracks how many bytes flow through it. This tests trait objects |
|
| 7 | 7 | //! as struct fields, dispatch chains, and a real-world composition pattern. |
|
| 8 | 8 | ||
| 9 | 9 | trait Writer { |
|
| 10 | - | fn (*mut Writer) write(data: *[u8]) -> i32; |
|
| 11 | - | fn (*Writer) total() -> i32; |
|
| 10 | + | unsafe fn (*unsafe mut Writer) write(data: *[u8]) -> i32; |
|
| 11 | + | unsafe fn (*unsafe Writer) total() -> i32; |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | 14 | /// Writes bytes into a fixed-size buffer. |
|
| 15 | 15 | record BufferWriter: Copy { |
|
| 16 | 16 | buf: [u8; 64], |
|
| 17 | 17 | pos: i32, |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | instance Writer for BufferWriter { |
|
| 21 | - | fn (w: *mut BufferWriter) write(data: *[u8]) -> i32 { |
|
| 21 | + | unsafe fn (w: *unsafe mut BufferWriter) write(data: *[u8]) -> i32 { |
|
| 22 | 22 | let mut i: u32 = 0; |
|
| 23 | 23 | while i < data.len { |
|
| 24 | 24 | if w.pos >= 64 { |
|
| 25 | 25 | return w.pos; |
|
| 26 | 26 | } |
| 29 | 29 | set i = i + 1; |
|
| 30 | 30 | } |
|
| 31 | 31 | return w.pos; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | - | fn (w: *BufferWriter) total() -> i32 { |
|
| 34 | + | unsafe fn (w: *unsafe BufferWriter) total() -> i32 { |
|
| 35 | 35 | return w.pos; |
|
| 36 | 36 | } |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Counts bytes written through it, forwarding to an inner writer. |
|
| 40 | 40 | record CountingWriter: Copy { |
|
| 41 | - | inner: *mut opaque Writer, |
|
| 41 | + | inner: *unsafe mut opaque Writer, |
|
| 42 | 42 | count: i32, |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | 45 | instance Writer for CountingWriter { |
|
| 46 | - | fn (w: *mut CountingWriter) write(data: *[u8]) -> i32 { |
|
| 46 | + | unsafe fn (w: *unsafe mut CountingWriter) write(data: *[u8]) -> i32 { |
|
| 47 | 47 | set w.count = w.count + data.len as i32; |
|
| 48 | 48 | return w.inner.write(data); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | - | fn (w: *CountingWriter) total() -> i32 { |
|
| 51 | + | unsafe fn (w: *unsafe CountingWriter) total() -> i32 { |
|
| 52 | 52 | return w.count; |
|
| 53 | 53 | } |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Write a slice through any Writer. |
|
| 57 | - | fn emit(w: *mut opaque Writer, data: *[u8]) -> i32 { |
|
| 57 | + | unsafe fn emit(w: *unsafe mut opaque Writer, data: *[u8]) -> i32 { |
|
| 58 | 58 | return w.write(data); |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | - | @default fn main() -> i32 { |
|
| 61 | + | @default unsafe fn main() -> i32 { |
|
| 62 | 62 | // Direct BufferWriter usage through trait. |
|
| 63 | 63 | let mut buf = BufferWriter { buf: undefined, pos: 0 }; |
|
| 64 | - | let w: *mut opaque Writer = &mut buf; |
|
| 64 | + | let w: *unsafe mut opaque Writer = &mut buf; |
|
| 65 | 65 | emit(w, "hello"); |
|
| 66 | 66 | assert w.total() == 5; |
|
| 67 | 67 | emit(w, " world"); |
|
| 68 | 68 | assert w.total() == 11; |
|
| 69 | 69 |
| 73 | 73 | assert buf.buf[5] == ' ' as u8; |
|
| 74 | 74 | assert buf.buf[10] == 'd' as u8; |
|
| 75 | 75 | ||
| 76 | 76 | // Counting writer wrapping a buffer writer. |
|
| 77 | 77 | let mut buf2 = BufferWriter { buf: undefined, pos: 0 }; |
|
| 78 | - | let bw2: *mut opaque Writer = &mut buf2; |
|
| 78 | + | let bw2: *unsafe mut opaque Writer = &mut buf2; |
|
| 79 | 79 | let mut cw = CountingWriter { inner: bw2, count: 0 }; |
|
| 80 | - | let w2: *mut opaque Writer = &mut cw; |
|
| 80 | + | let w2: *unsafe mut opaque Writer = &mut cw; |
|
| 81 | 81 | ||
| 82 | 82 | emit(w2, "abc"); |
|
| 83 | 83 | assert cw.count == 3; |
|
| 84 | 84 | // Underlying buffer also received the bytes. |
|
| 85 | 85 | assert buf2.pos == 3; |
test/tests/type.unify.rad
+18 -18
| 45 | 45 | ||
| 46 | 46 | return true; |
|
| 47 | 47 | } |
|
| 48 | 48 | ||
| 49 | 49 | /// Verifies assignment compatibility for pointers with identical target types. |
|
| 50 | - | fn testPointerUnification() -> bool { |
|
| 50 | + | unsafe fn testPointerUnification() -> bool { |
|
| 51 | 51 | let x: i32 = 42; |
|
| 52 | 52 | let y: i32 = 24; |
|
| 53 | - | let ptr1: *i32 = &x; |
|
| 54 | - | let ptr2: *i32 = &y; |
|
| 55 | - | let mut ptrResult: *i32 = ptr1; |
|
| 53 | + | let ptr1: *unsafe i32 = &x; |
|
| 54 | + | let ptr2: *unsafe i32 = &y; |
|
| 55 | + | let mut ptrResult: *unsafe i32 = ptr1; |
|
| 56 | 56 | set ptrResult = ptr2; |
|
| 57 | 57 | ||
| 58 | 58 | return true; |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Verifies conversion from array references to slices. |
|
| 62 | - | fn testArrayToSlice() -> bool { |
|
| 62 | + | unsafe fn testArrayToSlice() -> bool { |
|
| 63 | 63 | let arr: [i32; 3] = [1, 2, 3]; |
|
| 64 | - | let slice: *[i32] = &arr[..]; |
|
| 64 | + | let slice: *unsafe [i32] = &arr[..]; |
|
| 65 | 65 | ||
| 66 | 66 | return true; |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | 69 | /// Verifies mixed signed and unsigned arithmetic using an explicit cast. |
| 97 | 97 | ||
| 98 | 98 | return true; |
|
| 99 | 99 | } |
|
| 100 | 100 | ||
| 101 | 101 | /// Verifies coercion from pointer values to optional pointer values. |
|
| 102 | - | fn testPointerToOptional() -> bool { |
|
| 102 | + | unsafe fn testPointerToOptional() -> bool { |
|
| 103 | 103 | let value: i32 = 42; |
|
| 104 | - | let ptr: *i32 = &value; |
|
| 105 | - | let optPtr: ?*i32 = ptr; |
|
| 104 | + | let ptr: *unsafe i32 = &value; |
|
| 105 | + | let optPtr: ?*unsafe i32 = ptr; |
|
| 106 | 106 | ||
| 107 | 107 | return true; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | 110 | /// Verifies creation of slices for different, but internally consistent, element types. |
|
| 111 | - | fn testSliceElementUnification() -> bool { |
|
| 111 | + | unsafe fn testSliceElementUnification() -> bool { |
|
| 112 | 112 | let arrSmall: [i8; 3] = [1, 2, 3]; |
|
| 113 | - | let sliceSmall: *[i8] = &arrSmall[..]; |
|
| 113 | + | let sliceSmall: *unsafe [i8] = &arrSmall[..]; |
|
| 114 | 114 | ||
| 115 | 115 | let arrLarge: [i32; 3] = [10, 20, 30]; |
|
| 116 | - | let sliceLarge: *[i32] = &arrLarge[..]; |
|
| 116 | + | let sliceLarge: *unsafe [i32] = &arrLarge[..]; |
|
| 117 | 117 | ||
| 118 | 118 | return true; |
|
| 119 | 119 | } |
|
| 120 | 120 | ||
| 121 | 121 | /// Verifies optional assignments involving `nil` and concrete values. |
| 136 | 136 | ||
| 137 | 137 | return true; |
|
| 138 | 138 | } |
|
| 139 | 139 | ||
| 140 | 140 | /// Verifies repeated pointer assignments across multiple values of the same type. |
|
| 141 | - | fn testMultiplePointerAssignments() -> bool { |
|
| 141 | + | unsafe fn testMultiplePointerAssignments() -> bool { |
|
| 142 | 142 | let value1: i32 = 42; |
|
| 143 | 143 | let value2: i32 = 24; |
|
| 144 | 144 | let value3: i32 = 100; |
|
| 145 | 145 | ||
| 146 | - | let ptr1: *i32 = &value1; |
|
| 147 | - | let ptr2: *i32 = &value2; |
|
| 148 | - | let ptr3: *i32 = &value3; |
|
| 146 | + | let ptr1: *unsafe i32 = &value1; |
|
| 147 | + | let ptr2: *unsafe i32 = &value2; |
|
| 148 | + | let ptr3: *unsafe i32 = &value3; |
|
| 149 | 149 | ||
| 150 | - | let mut result: *i32 = ptr1; |
|
| 150 | + | let mut result: *unsafe i32 = ptr1; |
|
| 151 | 151 | set result = ptr2; |
|
| 152 | 152 | set result = ptr3; |
|
| 153 | 153 | ||
| 154 | 154 | return true; |
|
| 155 | 155 | } |
| 162 | 162 | set result = b2; |
|
| 163 | 163 | ||
| 164 | 164 | return true; |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | - | @default fn main() -> i32 { |
|
| 167 | + | @default unsafe fn main() -> i32 { |
|
| 168 | 168 | let testResult: bool = |
|
| 169 | 169 | testNumericUnification() and |
|
| 170 | 170 | testOptionalUnification() and |
|
| 171 | 171 | testArrayUnification() and |
|
| 172 | 172 | testPointerUnification() and |
test/tests/union-tag.rad
+3 -3
| 23 | 23 | V18, |
|
| 24 | 24 | V19, |
|
| 25 | 25 | V20, |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | - | fn tag(u: *BigUnion) -> u8 { |
|
| 29 | - | let p = u as *opaque as *u8; |
|
| 28 | + | unsafe fn tag(u: *unsafe BigUnion) -> u8 { |
|
| 29 | + | let p = u as *unsafe opaque as *unsafe u8; |
|
| 30 | 30 | return *p; |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | - | @default fn main() -> i32 { |
|
| 33 | + | @default unsafe fn main() -> i32 { |
|
| 34 | 34 | let v0 = BigUnion::V0; |
|
| 35 | 35 | assert tag(&v0) == 0; |
|
| 36 | 36 | let v7 = BigUnion::V7; |
|
| 37 | 37 | assert tag(&v7) == 7; |
|
| 38 | 38 | let v13 = BigUnion::V13; |
test/tests/union.edge.case.3.rad
+3 -3
| 9 | 9 | ||
| 10 | 10 | record Parser: Copy { |
|
| 11 | 11 | root: Node, |
|
| 12 | 12 | } |
|
| 13 | 13 | ||
| 14 | - | fn node(p: *mut Parser, value: Node) -> *Node { |
|
| 14 | + | unsafe fn node(p: &mut Parser, value: Node) -> *unsafe Node { |
|
| 15 | 15 | set p.root = value; |
|
| 16 | 16 | return &p.root; |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | fn nodeBool(p: *mut Parser, value: bool) -> *Node { |
|
| 19 | + | unsafe fn nodeBool(p: &mut Parser, value: bool) -> *unsafe Node { |
|
| 20 | 20 | return node(p, Node::Bool(value)); |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | - | @default fn main() -> u32 { |
|
| 23 | + | @default unsafe fn main() -> u32 { |
|
| 24 | 24 | let mut parser = Parser { root: undefined }; |
|
| 25 | 25 | ||
| 26 | 26 | match *nodeBool(&mut parser, true) { |
|
| 27 | 27 | case Node::Bool(v) => { |
|
| 28 | 28 | assert v == true; |
test/tests/union.mixed.assign.rad
+2 -2
| 10 | 10 | ||
| 11 | 11 | record Holder: Copy { |
|
| 12 | 12 | value: Mixed, |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | - | fn storePayload(holder: *mut Holder, value: i32) { |
|
| 15 | + | fn storePayload(holder: &mut Holder, value: i32) { |
|
| 16 | 16 | set holder.value = Mixed::Payload(value); |
|
| 17 | 17 | } |
|
| 18 | 18 | ||
| 19 | - | fn storeFinal(holder: *mut Holder) { |
|
| 19 | + | fn storeFinal(holder: &mut Holder) { |
|
| 20 | 20 | set holder.value = Mixed::Final; |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | fn checkIfLet(value: Mixed) -> i32 { |
|
| 24 | 24 | if let case Mixed::Payload(v) = value { |
test/tests/union.payload.mutref.rad
+3 -3
| 2 | 2 | //! Test accessing and modifying union payload through mutable reference. |
|
| 3 | 3 | //! This mirrors the pattern in createBlockParam where we match on |
|
| 4 | 4 | //! &mut blk.sealState and push to incompleteVars. |
|
| 5 | 5 | ||
| 6 | 6 | record U32List: Copy { |
|
| 7 | - | data: *mut [u32], |
|
| 7 | + | data: *unsafe mut [u32], |
|
| 8 | 8 | len: u32, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | union Sealed: Copy { |
|
| 12 | 12 | No { items: U32List }, |
| 17 | 17 | padding1: i32, |
|
| 18 | 18 | padding2: i32, |
|
| 19 | 19 | state: Sealed, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | - | fn addItemViaMatchRef(blk: *mut Block, val: u32) -> bool { |
|
| 22 | + | unsafe fn addItemViaMatchRef(blk: &mut Block, val: u32) -> bool { |
|
| 23 | 23 | match &mut blk.state { |
|
| 24 | 24 | case Sealed::No { items } => { |
|
| 25 | 25 | set items.data[items.len] = val; |
|
| 26 | 26 | set items.len += 1; |
|
| 27 | 27 | return true; |
| 30 | 30 | return false; |
|
| 31 | 31 | }, |
|
| 32 | 32 | } |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | - | @default fn main() -> i32 { |
|
| 35 | + | @default unsafe fn main() -> i32 { |
|
| 36 | 36 | let mut buf: [u32; 8] = undefined; |
|
| 37 | 37 | let mut blk = Block { |
|
| 38 | 38 | padding1: 0, |
|
| 39 | 39 | padding2: 0, |
|
| 40 | 40 | state: Sealed::No { items: U32List { data: &mut buf[0..8], len: 0 } }, |
test/tests/wildcard.import.owner.rad
+1 -0
| 1 | 1 | //! A wildcard import keeps the defining module for imported functions. |
|
| 2 | 2 | //! returns: 42 |
|
| 3 | 3 | ||
| 4 | 4 | mod wildcard_import_owner; |
|
| 5 | + | ||
| 5 | 6 | use wildcard_import_owner::*; |
|
| 6 | 7 | ||
| 7 | 8 | @default fn main() -> i32 { |
|
| 8 | 9 | return answer(); |
|
| 9 | 10 | } |