compiler: Retain safe module identities in the resolver

cbcd09d4cc2097a78adf1c10df6449c4504e0f4de927b3c1a779590791b4558d
Alexis Sellier committed ago 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