compiler: Retain safe module identities in the resolver
cbcd09d4cc2097a78adf1c10df6449c4504e0f4de927b3c1a779590791b4558d
1 parent
3e51617a
compiler/radiance.rad
+8 -8
| 529 | 529 | } |
|
| 530 | 530 | ||
| 531 | 531 | /// Lower all packages into a single IL program. |
|
| 532 | 532 | /// Dependencies are lowered first, then the entry package. |
|
| 533 | 533 | unsafe fn lowerAllPackages 'arena ( |
|
| 534 | - | ctx: *unsafe mut CompileContext, |
|
| 534 | + | ctx: &CompileContext, |
|
| 535 | 535 | res: *unsafe mut resolver::Resolver 'arena |
|
| 536 | 536 | ) -> il::Program throws (Error) { |
|
| 537 | 537 | let entryIdx = ctx.entryPkgIdx else { |
|
| 538 | 538 | panic "lowerAllPackages: no entry package"; |
|
| 539 | 539 | }; |
| 899 | 899 | } |
|
| 900 | 900 | try writeDataWithExt(&buf[..pos], basePath, DEBUG_EXT); |
|
| 901 | 901 | } |
|
| 902 | 902 | ||
| 903 | 903 | /// Run the resolver on the parsed modules. |
|
| 904 | - | unsafe fn runResolver 'arena (ctx: *unsafe mut CompileContext, mainArena: &'arena mut alloc::Arena, nodeCount: u32) -> resolver::Resolver 'arena throws (Error) { |
|
| 904 | + | unsafe fn runResolver 'arena (ctx: &CompileContext, mainArena: &'arena mut alloc::Arena, nodeCount: u32) -> resolver::Resolver 'arena throws (Error) { |
|
| 905 | 905 | let entryPkg = try getEntryPackage(ctx); |
|
| 906 | 906 | ||
| 907 | 907 | pkgLog(&entryPkg, &["resolving", ".."]); |
|
| 908 | 908 | ||
| 909 | 909 | let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData); |
| 1074 | 1074 | } |
|
| 1075 | 1075 | } |
|
| 1076 | 1076 | ||
| 1077 | 1077 | /// Lower all packages while streaming each lowered function into RV64 codegen. |
|
| 1078 | 1078 | unsafe fn lowerAndGenerateAllPackages 'arena ( |
|
| 1079 | - | ctx: *unsafe mut CompileContext, |
|
| 1079 | + | ctx: &CompileContext, |
|
| 1080 | 1080 | res: *unsafe mut resolver::Resolver 'arena, |
|
| 1081 | 1081 | fnArena: &mut alloc::Arena, |
|
| 1082 | 1082 | codegenOptions: CodegenOptions |
|
| 1083 | 1083 | ) -> rv64::Program throws (Error) { |
|
| 1084 | 1084 | let entryIdx = ctx.entryPkgIdx else { |
| 1244 | 1244 | return PackageExports { count, entry: entryName }; |
|
| 1245 | 1245 | } |
|
| 1246 | 1246 | ||
| 1247 | 1247 | /// Emit one binary RIL file per package into an existing directory. |
|
| 1248 | 1248 | unsafe fn emitPackages 'arena ( |
|
| 1249 | - | ctx: *unsafe mut CompileContext, |
|
| 1249 | + | ctx: &CompileContext, |
|
| 1250 | 1250 | res: *unsafe mut resolver::Resolver 'arena, |
|
| 1251 | 1251 | directory: *[u8] |
|
| 1252 | 1252 | ) throws (Error) { |
|
| 1253 | 1253 | for i in 0..ctx.packageCount { |
|
| 1254 | 1254 | if ctx.inputs[i].asmPathCount > 0 or ctx.inputs[i].startupPath <> nil { |
| 1303 | 1303 | return suffix.len > 2 and suffix[0] == ':' and suffix[1] == ':'; |
|
| 1304 | 1304 | } |
|
| 1305 | 1305 | ||
| 1306 | 1306 | /// Lower, optionally dump, and optionally generate binary output. |
|
| 1307 | 1307 | unsafe fn compile 'arena ( |
|
| 1308 | - | ctx: *unsafe mut CompileContext, |
|
| 1308 | + | ctx: &CompileContext, |
|
| 1309 | 1309 | res: *unsafe mut resolver::Resolver 'arena, |
|
| 1310 | 1310 | fnArena: &mut alloc::Arena |
|
| 1311 | 1311 | ) throws (Error) { |
|
| 1312 | 1312 | let entryPkg = try getEntryPackage(ctx); |
|
| 1313 | 1313 | if let directory = ctx.rilDirectory { |
| 1388 | 1388 | return 1; |
|
| 1389 | 1389 | }; |
|
| 1390 | 1390 | } |
|
| 1391 | 1391 | // Run resolution phase. |
|
| 1392 | 1392 | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
|
| 1393 | - | let arenaRef: 'arena = &mut mainArena in { |
|
| 1394 | - | let mut res = try runResolver(&mut ctx, arenaRef, arena.nextId) catch { |
|
| 1393 | + | let arenaRef: 'arena = &mut mainArena, context = &ctx in { |
|
| 1394 | + | let mut res = try runResolver(context, arenaRef, arena.nextId) catch { |
|
| 1395 | 1395 | return 1; |
|
| 1396 | 1396 | }; |
|
| 1397 | 1397 | let mut fnArena = alloc::new(&mut FN_ARENA[..]); |
|
| 1398 | 1398 | ||
| 1399 | 1399 | // Lower, dump, and/or generate output. |
|
| 1400 | - | try compile(&mut ctx, &mut res, &mut fnArena) catch { |
|
| 1400 | + | try compile(context, &mut res, &mut fnArena) catch { |
|
| 1401 | 1401 | return 1; |
|
| 1402 | 1402 | }; |
|
| 1403 | 1403 | return 0; |
|
| 1404 | 1404 | } |
|
| 1405 | 1405 | } |
lib/std/lang/resolver.rad
+34 -11
| 1043 | 1043 | nodeData: NodeDataTable, |
|
| 1044 | 1044 | /// Linked list of interned types. |
|
| 1045 | 1045 | types: ?*TypeNode, |
|
| 1046 | 1046 | /// Diagnostics recorded so far. |
|
| 1047 | 1047 | errors: DiagnosticBuffer, |
|
| 1048 | - | /// Module graph for the current package. |
|
| 1049 | - | moduleGraph: *unsafe module::ModuleGraph, |
|
| 1048 | + | /// Stable module identities indexed by module ID. |
|
| 1049 | + | moduleEntries: [?*module::ModuleEntry; module::MAX_MODULES], |
|
| 1050 | 1050 | /// Cache of module scopes indexed by module ID. |
|
| 1051 | 1051 | moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES], |
|
| 1052 | 1052 | /// Trait instance registry. |
|
| 1053 | 1053 | instances: [InstanceEntry; MAX_INSTANCES], |
|
| 1054 | 1054 | /// Number of registered instances. |
| 1505 | 1505 | config, |
|
| 1506 | 1506 | arena, |
|
| 1507 | 1507 | nodeData: NodeDataTable { entries: nodeData }, |
|
| 1508 | 1508 | types: nil, |
|
| 1509 | 1509 | errors: DiagnosticBuffer { entries: errors, len: 0 }, |
|
| 1510 | - | // TODO: Shouldn't be undefined. |
|
| 1511 | - | moduleGraph: undefined, |
|
| 1510 | + | moduleEntries: [nil; module::MAX_MODULES], |
|
| 1512 | 1511 | moduleScopes, |
|
| 1513 | 1512 | instances: undefined, |
|
| 1514 | 1513 | instancesLen: 0, |
|
| 1515 | 1514 | methods: undefined, |
|
| 1516 | 1515 | methodsLen: 0, |
| 1612 | 1611 | return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod }; |
|
| 1613 | 1612 | } |
|
| 1614 | 1613 | ||
| 1615 | 1614 | /// Enter a sub-module. Changes the current scope into that of the sub-module. |
|
| 1616 | 1615 | unsafe fn enterSubModule 'arena (self: &mut Resolver 'arena, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) { |
|
| 1617 | - | let modEntry = module::findChild(self.moduleGraph, name, self.currentMod) |
|
| 1616 | + | let modEntry = findChildModule(self, name, self.currentMod) |
|
| 1618 | 1617 | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 1619 | 1618 | let modRoot = module::astFor(modEntry) |
|
| 1620 | 1619 | else panic "enterSubModule: analyzing module that wasn't parsed"; |
|
| 1621 | 1620 | ||
| 1622 | 1621 | return enterModuleScope(self, modRoot, modEntry); |
| 3283 | 3282 | return nil; |
|
| 3284 | 3283 | } |
|
| 3285 | 3284 | } |
|
| 3286 | 3285 | } |
|
| 3287 | 3286 | ||
| 3287 | + | /// Return a retained module identity, if its ID is registered. |
|
| 3288 | + | export fn moduleFor 'arena (self: &Resolver 'arena, id: u16) -> ?*module::ModuleEntry { |
|
| 3289 | + | if id as u32 >= self.moduleEntries.len { |
|
| 3290 | + | return nil; |
|
| 3291 | + | } |
|
| 3292 | + | return self.moduleEntries[id as u32]; |
|
| 3293 | + | } |
|
| 3294 | + | ||
| 3295 | + | /// Find a retained child identity by its parent and name. |
|
| 3296 | + | fn findChildModule 'arena (self: &Resolver 'arena, name: *[u8], parentId: u16) -> ?*module::ModuleEntry { |
|
| 3297 | + | let parent = moduleFor(self, parentId) else return nil; |
|
| 3298 | + | for i in 0..module::childCount(parent) { |
|
| 3299 | + | let child = moduleFor(self, module::childAt(parent, i)) |
|
| 3300 | + | else panic "findChildModule: missing child identity"; |
|
| 3301 | + | if mem::eq(child.name, name) { |
|
| 3302 | + | return child; |
|
| 3303 | + | } |
|
| 3304 | + | } |
|
| 3305 | + | return nil; |
|
| 3306 | + | } |
|
| 3307 | + | ||
| 3288 | 3308 | /// Get the parent module scope for the current module. |
|
| 3289 | 3309 | /// Returns the scope of the parent module, or `nil` if this is a root module. |
|
| 3290 | - | unsafe fn getParentModuleScope 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) { |
|
| 3291 | - | let currentMod = module::get(self.moduleGraph, self.currentMod) |
|
| 3310 | + | fn getParentModuleScope 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) { |
|
| 3311 | + | let currentMod = moduleFor(self, self.currentMod) |
|
| 3292 | 3312 | else throw emitError(self, node, ErrorKind::Internal); |
|
| 3293 | 3313 | let parentId = currentMod.parent |
|
| 3294 | 3314 | else return nil; // No parent module. |
|
| 3295 | 3315 | ||
| 3296 | 3316 | return self.moduleScopes[parentId as u32]; |
| 5574 | 5594 | }); |
|
| 5575 | 5595 | } |
|
| 5576 | 5596 | ||
| 5577 | 5597 | /// Check whether an attributed module or import is active in this build. |
|
| 5578 | 5598 | /// A test module is active only when its source module was registered. |
|
| 5579 | - | unsafe fn shouldAnalyzeModule 'arena (self: &Resolver 'arena, attrs: ?ast::Attributes, name: ?*[u8]) -> bool { |
|
| 5599 | + | fn shouldAnalyzeModule 'arena (self: &Resolver 'arena, attrs: ?ast::Attributes, name: ?*[u8]) -> bool { |
|
| 5580 | 5600 | if let attributes = attrs { |
|
| 5581 | 5601 | if ast::attributesContains(&attributes, ast::Attribute::Test) { |
|
| 5582 | 5602 | if not self.config.buildTest { |
|
| 5583 | 5603 | return false; |
|
| 5584 | 5604 | } |
|
| 5585 | 5605 | if let moduleName = name { |
|
| 5586 | - | return module::findChild(self.moduleGraph, moduleName, self.currentMod) <> nil; |
|
| 5606 | + | return findChildModule(self, moduleName, self.currentMod) <> nil; |
|
| 5587 | 5607 | } |
|
| 5588 | 5608 | } |
|
| 5589 | 5609 | } |
|
| 5590 | 5610 | return true; |
|
| 5591 | 5611 | } |
| 11518 | 11538 | try resolveNominalApplications(self, stmt); |
|
| 11519 | 11539 | } |
|
| 11520 | 11540 | } |
|
| 11521 | 11541 | ||
| 11522 | 11542 | /// Resolve all packages. |
|
| 11523 | - | /// The graph must outlive later uses of the resolver. |
|
| 11543 | + | /// Module entries retain their identity throughout resolution and diagnostics. |
|
| 11524 | 11544 | export unsafe fn resolve 'arena (self: &mut Resolver 'arena, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 11525 | - | set self.moduleGraph = graph as *unsafe module::ModuleGraph; |
|
| 11545 | + | assert graph.entriesLen <= self.moduleEntries.len, "resolve: module registry capacity exceeded"; |
|
| 11546 | + | for i in 0..self.moduleEntries.len { |
|
| 11547 | + | set self.moduleEntries[i] = module::get(graph, i as u16); |
|
| 11548 | + | } |
|
| 11526 | 11549 | ||
| 11527 | 11550 | // 1. Bind all package roots to enable cross-package references. |
|
| 11528 | 11551 | for i in 0..packages.len { |
|
| 11529 | 11552 | let pkg = packages[i]; |
|
| 11530 | 11553 | // Enter a new scope for the module. |
lib/std/lang/resolver/printer.rad
+1 -1
| 279 | 279 | ||
| 280 | 280 | /// Print a single diagnostic entry. |
|
| 281 | 281 | unsafe fn printError 'arena (err: &super::Error, res: &super::Resolver 'arena) { |
|
| 282 | 282 | if let node = err.node { |
|
| 283 | 283 | // Find the module containing this error. |
|
| 284 | - | if let moduleEntry = module::get(res.moduleGraph, err.moduleId) { |
|
| 284 | + | if let moduleEntry = super::moduleFor(res, err.moduleId) { |
|
| 285 | 285 | // Get the source text if available. |
|
| 286 | 286 | if let source = module::sourceFor(moduleEntry) { |
|
| 287 | 287 | // Convert offset to location. |
|
| 288 | 288 | if let loc = scanner::getLocation(scanner::SourceLoc::File(moduleEntry.filePath), source, node.span.offset) { |
|
| 289 | 289 | // Print: filename:line:col: error: message |
lib/std/lang/resolver/tests.rad
+41 -0
| 8144 | 8144 | let enabled = try resolveProgramStr(&mut a, source); |
|
| 8145 | 8145 | try testing::expect(enabled.diagnostics.errors.len > 0); |
|
| 8146 | 8146 | } |
|
| 8147 | 8147 | } |
|
| 8148 | 8148 | ||
| 8149 | + | /// Retained module identities are accessible through safe lookup. |
|
| 8150 | + | fn checkModuleQueries 'arena ( |
|
| 8151 | + | res: &super::Resolver 'arena, root: u16, child: u16 |
|
| 8152 | + | ) throws (testing::TestError) { |
|
| 8153 | + | let rootEntry = super::moduleFor(res, root) else throw testing::TestError::Failed; |
|
| 8154 | + | let childEntry = super::moduleFor(res, child) else throw testing::TestError::Failed; |
|
| 8155 | + | assert rootEntry.id == root; |
|
| 8156 | + | assert childEntry.id == child; |
|
| 8157 | + | let parent = childEntry.parent else throw testing::TestError::Failed; |
|
| 8158 | + | assert parent == root; |
|
| 8159 | + | try testing::expectBytesEq(rootEntry.name, "root"); |
|
| 8160 | + | try testing::expectBytesEq(childEntry.name, "child"); |
|
| 8161 | + | if let _ = super::moduleFor(res, 0xffff) { |
|
| 8162 | + | throw testing::TestError::Failed; |
|
| 8163 | + | } |
|
| 8164 | + | } |
|
| 8165 | + | ||
| 8166 | + | /// Module identities remain valid independently of the graph container. |
|
| 8167 | + | @test unsafe fn testSafeModuleQueries() throws (testing::TestError) { |
|
| 8168 | + | let mut arena = testArena(); |
|
| 8169 | + | let storage: 'test = &mut arena in { |
|
| 8170 | + | let mut res = testResolver(storage); |
|
| 8171 | + | if let _ = super::moduleFor(&res, 0) { |
|
| 8172 | + | throw testing::TestError::Failed; |
|
| 8173 | + | } |
|
| 8174 | + | let root = try! module::registerRootWithName(&mut MODULE_GRAPH, &mut STRING_POOL, 0, "root", "/root.rad"); |
|
| 8175 | + | let child = try! module::registerChild(&mut MODULE_GRAPH, &mut STRING_POOL, root, "child", "/child.rad"); |
|
| 8176 | + | let diagnostics = try! super::resolve(&mut res, &MODULE_GRAPH, &[]); |
|
| 8177 | + | assert super::success(&diagnostics); |
|
| 8178 | + | set MODULE_GRAPH.entriesLen = 0; |
|
| 8179 | + | set MODULE_ENTRIES[root as u32] = nil; |
|
| 8180 | + | set MODULE_ENTRIES[child as u32] = nil; |
|
| 8181 | + | try checkModuleQueries(&res, root, child); |
|
| 8182 | + | let empty = try! super::resolve(&mut res, &MODULE_GRAPH, &[]); |
|
| 8183 | + | assert super::success(&empty); |
|
| 8184 | + | if let _ = super::moduleFor(&res, root) { |
|
| 8185 | + | throw testing::TestError::Failed; |
|
| 8186 | + | } |
|
| 8187 | + | } |
|
| 8188 | + | } |
|
| 8189 | + | ||
| 8149 | 8190 | /// Resolver metadata retains its caller's arena when the context moves. |
|
| 8150 | 8191 | unsafe fn relocateResolver 'arena (context: super::Resolver 'arena) -> super::Resolver 'arena { |
|
| 8151 | 8192 | return context; |
|
| 8152 | 8193 | } |
|
| 8153 | 8194 |