implements PR-05.0.2
This commit is contained in:
parent
3b5e8e0b24
commit
bafab68eac
@ -21,6 +21,9 @@ import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||
import p.studio.compiler.source.diagnostics.DiagnosticPhase;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.identifiers.ModuleId;
|
||||
import p.studio.compiler.source.tables.ModuleReference;
|
||||
import p.studio.compiler.source.tables.ModuleTable;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
import p.studio.utilities.logs.LogAggregator;
|
||||
|
||||
@ -62,8 +65,9 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
final var irBackendAggregator = IRBackend.aggregator();
|
||||
final var parsedSourceFiles = new ArrayList<ParsedSourceFile>();
|
||||
final Map<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates = new LinkedHashMap<>();
|
||||
final var moduleKeyByFile = new HashMap<FileId, String>();
|
||||
final var failedModuleKeys = new HashSet<String>();
|
||||
final var moduleTable = new ModuleTable();
|
||||
final var moduleIdByFile = new HashMap<FileId, ModuleId>();
|
||||
final var failedModuleIds = new HashSet<ModuleId>();
|
||||
|
||||
for (final var pId : ctx.stack.reverseTopologicalOrder) {
|
||||
final var projectDescriptor = ctx.projectTable.get(pId);
|
||||
@ -75,36 +79,36 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
sourceHandle.readUtf8().ifPresentOrElse(
|
||||
utf8Content -> {
|
||||
final var coordinates = resolveModuleCoordinates(projectDescriptor, sourceHandle);
|
||||
final var moduleKey = moduleKey(coordinates);
|
||||
final var moduleId = moduleId(moduleTable, coordinates);
|
||||
final var moduleUnit = modulesByCoordinates.computeIfAbsent(
|
||||
coordinates,
|
||||
ignored -> new MutableModuleUnit());
|
||||
switch (sourceHandle.getExtension()) {
|
||||
case "pbs" -> {
|
||||
moduleKeyByFile.put(fId, moduleKey);
|
||||
moduleIdByFile.put(fId, moduleId);
|
||||
final var parseErrorBaseline = diagnostics.errorCount();
|
||||
final var ast = parseSourceFile(fId, utf8Content, diagnostics, projectSourceKind);
|
||||
moduleUnit.sources.add(new PbsModuleVisibilityValidator.SourceFile(fId, ast));
|
||||
parsedSourceFiles.add(new ParsedSourceFile(fId, ast, moduleKey, projectSourceKind));
|
||||
parsedSourceFiles.add(new ParsedSourceFile(fId, ast, moduleId, projectSourceKind));
|
||||
if (diagnostics.errorCount() > parseErrorBaseline) {
|
||||
failedModuleKeys.add(moduleKey);
|
||||
failedModuleIds.add(moduleId);
|
||||
}
|
||||
}
|
||||
case "barrel" -> {
|
||||
moduleKeyByFile.put(fId, moduleKey);
|
||||
moduleIdByFile.put(fId, moduleId);
|
||||
if ("mod.barrel".equals(sourceHandle.getFilename())) {
|
||||
final var parseErrorBaseline = diagnostics.errorCount();
|
||||
final var barrelAst = parseBarrelFile(fId, utf8Content, diagnostics);
|
||||
moduleUnit.barrels.add(new PbsModuleVisibilityValidator.BarrelFile(fId, barrelAst));
|
||||
if (diagnostics.errorCount() > parseErrorBaseline) {
|
||||
failedModuleKeys.add(moduleKey);
|
||||
failedModuleIds.add(moduleId);
|
||||
}
|
||||
} else {
|
||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
||||
PbsLinkErrors.E_LINK_INVALID_BARREL_FILENAME.name(),
|
||||
"Only 'mod.barrel' is allowed as barrel filename",
|
||||
new Span(fId, 0, utf8Content.getBytes(StandardCharsets.UTF_8).length));
|
||||
failedModuleKeys.add(moduleKey);
|
||||
failedModuleIds.add(moduleId);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
@ -120,7 +124,8 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
loadReservedStdlibModules(
|
||||
modulesByCoordinates,
|
||||
parsedSourceFiles,
|
||||
moduleKeyByFile,
|
||||
moduleIdByFile,
|
||||
moduleTable,
|
||||
diagnostics,
|
||||
ctx.stdlibVersion());
|
||||
|
||||
@ -134,13 +139,13 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
ReadOnlyList.wrap(moduleUnit.barrels)));
|
||||
}
|
||||
moduleVisibilityValidator.validate(ReadOnlyList.wrap(modules), nameTable, diagnostics);
|
||||
markModulesWithLinkingErrors(diagnostics, moduleKeyByFile, failedModuleKeys);
|
||||
final var moduleDependencyGraph = buildModuleDependencyGraph(parsedSourceFiles);
|
||||
markModulesWithLinkingErrors(diagnostics, moduleIdByFile, failedModuleIds);
|
||||
final var moduleDependencyGraph = buildModuleDependencyGraph(parsedSourceFiles, moduleTable);
|
||||
|
||||
final var compiledSourceFiles = new ArrayList<CompiledSourceFile>(parsedSourceFiles.size());
|
||||
for (final var parsedSource : parsedSourceFiles) {
|
||||
final var blockedModuleKeys = blockedModulesByDependency(failedModuleKeys, moduleDependencyGraph);
|
||||
if (blockedModuleKeys.contains(parsedSource.moduleKey())) {
|
||||
final var blockedModuleIds = blockedModulesByDependency(failedModuleIds, moduleDependencyGraph);
|
||||
if (blockedModuleIds.contains(parsedSource.moduleId())) {
|
||||
continue;
|
||||
}
|
||||
final var compileErrorBaseline = diagnostics.errorCount();
|
||||
@ -149,18 +154,18 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
parsedSource.ast(),
|
||||
diagnostics,
|
||||
parsedSource.sourceKind(),
|
||||
parsedSource.moduleKey(),
|
||||
renderModuleKey(moduleTable, parsedSource.moduleId()),
|
||||
ctx.hostAdmissionContext(),
|
||||
nameTable);
|
||||
if (diagnostics.errorCount() > compileErrorBaseline) {
|
||||
failedModuleKeys.add(parsedSource.moduleKey());
|
||||
failedModuleIds.add(parsedSource.moduleId());
|
||||
}
|
||||
compiledSourceFiles.add(new CompiledSourceFile(parsedSource.moduleKey(), irBackendFile));
|
||||
compiledSourceFiles.add(new CompiledSourceFile(parsedSource.moduleId(), irBackendFile));
|
||||
}
|
||||
|
||||
final var blockedModuleKeys = blockedModulesByDependency(failedModuleKeys, moduleDependencyGraph);
|
||||
final var blockedModuleIds = blockedModulesByDependency(failedModuleIds, moduleDependencyGraph);
|
||||
for (final var compiledSource : compiledSourceFiles) {
|
||||
if (blockedModuleKeys.contains(compiledSource.moduleKey())) {
|
||||
if (blockedModuleIds.contains(compiledSource.moduleId())) {
|
||||
continue;
|
||||
}
|
||||
irBackendAggregator.merge(compiledSource.irBackendFile());
|
||||
@ -174,18 +179,19 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
private void loadReservedStdlibModules(
|
||||
final Map<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates,
|
||||
final ArrayList<ParsedSourceFile> parsedSourceFiles,
|
||||
final Map<FileId, String> moduleKeyByFile,
|
||||
final Map<FileId, ModuleId> moduleIdByFile,
|
||||
final ModuleTable moduleTable,
|
||||
final DiagnosticSink diagnostics,
|
||||
final int stdlibVersion) {
|
||||
final var stdlibEnvironment = stdlibEnvironmentResolver.resolve(stdlibVersion);
|
||||
final var pending = new ArrayDeque<PbsModuleVisibilityValidator.ModuleCoordinates>();
|
||||
final var resolved = new HashSet<String>();
|
||||
final var resolved = new HashSet<ModuleId>();
|
||||
|
||||
enqueueReservedImportsFromKnownModules(modulesByCoordinates, pending);
|
||||
while (!pending.isEmpty()) {
|
||||
final var target = pending.removeFirst();
|
||||
final var targetKey = moduleKey(target);
|
||||
if (!resolved.add(targetKey)) {
|
||||
final var targetId = moduleId(moduleTable, target);
|
||||
if (!resolved.add(targetId)) {
|
||||
continue;
|
||||
}
|
||||
if (modulesByCoordinates.containsKey(target)) {
|
||||
@ -203,15 +209,15 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
moduleData.barrels.addAll(loadedModule.barrelFiles().asList());
|
||||
modulesByCoordinates.put(loadedModule.coordinates(), moduleData);
|
||||
for (final var sourceFile : loadedModule.sourceFiles()) {
|
||||
moduleKeyByFile.put(sourceFile.fileId(), targetKey);
|
||||
moduleIdByFile.put(sourceFile.fileId(), targetId);
|
||||
parsedSourceFiles.add(new ParsedSourceFile(
|
||||
sourceFile.fileId(),
|
||||
sourceFile.ast(),
|
||||
targetKey,
|
||||
targetId,
|
||||
SourceKind.SDK_INTERFACE));
|
||||
}
|
||||
for (final var barrelFile : loadedModule.barrelFiles()) {
|
||||
moduleKeyByFile.put(barrelFile.fileId(), targetKey);
|
||||
moduleIdByFile.put(barrelFile.fileId(), targetId);
|
||||
}
|
||||
enqueueReservedImportsFromSourceFiles(loadedModule.sourceFiles().asList(), pending);
|
||||
}
|
||||
@ -296,8 +302,8 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
|
||||
private void markModulesWithLinkingErrors(
|
||||
final DiagnosticSink diagnostics,
|
||||
final Map<FileId, String> moduleKeyByFile,
|
||||
final Set<String> failedModuleKeys) {
|
||||
final Map<FileId, ModuleId> moduleIdByFile,
|
||||
final Set<ModuleId> failedModuleIds) {
|
||||
for (final var diagnostic : diagnostics) {
|
||||
if (!diagnostic.getSeverity().isError()) {
|
||||
continue;
|
||||
@ -305,32 +311,33 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
if (diagnostic.getPhase() != DiagnosticPhase.LINKING) {
|
||||
continue;
|
||||
}
|
||||
final var moduleKey = moduleKeyByFile.get(diagnostic.getSpan().getFileId());
|
||||
if (moduleKey != null) {
|
||||
failedModuleKeys.add(moduleKey);
|
||||
final var moduleId = moduleIdByFile.get(diagnostic.getSpan().getFileId());
|
||||
if (moduleId != null) {
|
||||
failedModuleIds.add(moduleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Set<String>> buildModuleDependencyGraph(
|
||||
final ArrayList<ParsedSourceFile> parsedSourceFiles) {
|
||||
final Map<String, Set<String>> dependenciesByModule = new HashMap<>();
|
||||
private Map<ModuleId, Set<ModuleId>> buildModuleDependencyGraph(
|
||||
final ArrayList<ParsedSourceFile> parsedSourceFiles,
|
||||
final ModuleTable moduleTable) {
|
||||
final Map<ModuleId, Set<ModuleId>> dependenciesByModule = new HashMap<>();
|
||||
for (final var parsedSource : parsedSourceFiles) {
|
||||
final var moduleDependencies = dependenciesByModule.computeIfAbsent(
|
||||
parsedSource.moduleKey(),
|
||||
parsedSource.moduleId(),
|
||||
ignored -> new HashSet<>());
|
||||
for (final var importDecl : parsedSource.ast().imports()) {
|
||||
final var moduleRef = importDecl.moduleRef();
|
||||
moduleDependencies.add(moduleKey(moduleRef.project(), moduleRef.pathSegments()));
|
||||
moduleDependencies.add(moduleId(moduleTable, moduleRef.project(), moduleRef.pathSegments()));
|
||||
}
|
||||
}
|
||||
return dependenciesByModule;
|
||||
}
|
||||
|
||||
private Set<String> blockedModulesByDependency(
|
||||
final Set<String> failedModuleKeys,
|
||||
final Map<String, Set<String>> dependenciesByModule) {
|
||||
final Map<String, Set<String>> dependentsByModule = new HashMap<>();
|
||||
private Set<ModuleId> blockedModulesByDependency(
|
||||
final Set<ModuleId> failedModuleIds,
|
||||
final Map<ModuleId, Set<ModuleId>> dependenciesByModule) {
|
||||
final Map<ModuleId, Set<ModuleId>> dependentsByModule = new HashMap<>();
|
||||
for (final var entry : dependenciesByModule.entrySet()) {
|
||||
final var importer = entry.getKey();
|
||||
for (final var dependency : entry.getValue()) {
|
||||
@ -338,8 +345,8 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
}
|
||||
}
|
||||
|
||||
final var blocked = new HashSet<String>(failedModuleKeys);
|
||||
final var pending = new ArrayDeque<String>(failedModuleKeys);
|
||||
final var blocked = new HashSet<ModuleId>(failedModuleIds);
|
||||
final var pending = new ArrayDeque<ModuleId>(failedModuleIds);
|
||||
while (!pending.isEmpty()) {
|
||||
final var failedOrBlocked = pending.removeFirst();
|
||||
final var dependents = dependentsByModule.getOrDefault(failedOrBlocked, Set.of());
|
||||
@ -353,14 +360,24 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
private String moduleKey(final PbsModuleVisibilityValidator.ModuleCoordinates coordinates) {
|
||||
return coordinates.project() + ":" + String.join("/", coordinates.pathSegments().asList());
|
||||
private ModuleId moduleId(
|
||||
final ModuleTable moduleTable,
|
||||
final PbsModuleVisibilityValidator.ModuleCoordinates coordinates) {
|
||||
return moduleId(moduleTable, coordinates.project(), coordinates.pathSegments());
|
||||
}
|
||||
|
||||
private String moduleKey(
|
||||
private ModuleId moduleId(
|
||||
final ModuleTable moduleTable,
|
||||
final String project,
|
||||
final ReadOnlyList<String> pathSegments) {
|
||||
return project + ":" + String.join("/", pathSegments.asList());
|
||||
return moduleTable.register(new ModuleReference(project, pathSegments));
|
||||
}
|
||||
|
||||
private String renderModuleKey(
|
||||
final ModuleTable moduleTable,
|
||||
final ModuleId moduleId) {
|
||||
final var moduleReference = moduleTable.get(moduleId);
|
||||
return moduleReference.project() + ":" + String.join("/", moduleReference.pathSegments().asList());
|
||||
}
|
||||
|
||||
private static final class MutableModuleUnit {
|
||||
@ -371,12 +388,12 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
private record ParsedSourceFile(
|
||||
FileId fileId,
|
||||
PbsAst.File ast,
|
||||
String moduleKey,
|
||||
ModuleId moduleId,
|
||||
SourceKind sourceKind) {
|
||||
}
|
||||
|
||||
private record CompiledSourceFile(
|
||||
String moduleKey,
|
||||
ModuleId moduleId,
|
||||
p.studio.compiler.models.IRBackendFile irBackendFile) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
package p.studio.compiler.source.tables;
|
||||
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record ModuleReference(
|
||||
String project,
|
||||
ReadOnlyList<String> pathSegments) {
|
||||
public ModuleReference {
|
||||
project = Objects.requireNonNull(project, "project");
|
||||
if (project.isBlank()) {
|
||||
throw new IllegalArgumentException("project must not be blank");
|
||||
}
|
||||
pathSegments = pathSegments == null ? ReadOnlyList.empty() : pathSegments;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package p.studio.compiler.source.tables;
|
||||
|
||||
import p.studio.compiler.source.identifiers.ModuleId;
|
||||
|
||||
public class ModuleTable extends InternTable<ModuleId, ModuleReference> {
|
||||
|
||||
public ModuleTable() {
|
||||
super(ModuleId::new);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package p.studio.compiler.source.tables;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
class ModuleTableTest {
|
||||
|
||||
@Test
|
||||
void shouldInternSameReferenceWithStableId() {
|
||||
final var table = new ModuleTable();
|
||||
final var reference = new ModuleReference("core", ReadOnlyList.wrap(java.util.List.of("math", "vec")));
|
||||
|
||||
final var first = table.register(reference);
|
||||
final var second = table.register(reference);
|
||||
|
||||
assertEquals(first, second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllocateDifferentIdForDifferentReference() {
|
||||
final var table = new ModuleTable();
|
||||
|
||||
final var first = table.register(new ModuleReference("core", ReadOnlyList.wrap(java.util.List.of("math"))));
|
||||
final var second = table.register(new ModuleReference("core", ReadOnlyList.wrap(java.util.List.of("gfx"))));
|
||||
|
||||
assertNotEquals(first, second);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user