implements PR-19.7 explicit backend globals and synthetic callable model
This commit is contained in:
parent
7fc38010a2
commit
c61b9db786
@ -3,7 +3,11 @@ package p.studio.compiler.pbs;
|
||||
import p.studio.compiler.messages.HostAdmissionContext;
|
||||
import p.studio.compiler.models.IRBackendFile;
|
||||
import p.studio.compiler.models.IRFunction;
|
||||
import p.studio.compiler.models.IRGlobal;
|
||||
import p.studio.compiler.models.IRReservedMetadata;
|
||||
import p.studio.compiler.models.IRSyntheticCallableKind;
|
||||
import p.studio.compiler.models.IRSyntheticFunction;
|
||||
import p.studio.compiler.models.IRSyntheticOrigin;
|
||||
import p.studio.compiler.models.SourceKind;
|
||||
import p.studio.compiler.pbs.ast.PbsAst;
|
||||
import p.studio.compiler.pbs.lexer.PbsLexer;
|
||||
@ -149,6 +153,12 @@ public final class PbsFrontendCompiler {
|
||||
final ReadOnlyList<IRFunction> functions = sourceKind == SourceKind.SDK_INTERFACE
|
||||
? ReadOnlyList.empty()
|
||||
: lowerFunctions(fileId, ast);
|
||||
final ReadOnlyList<IRSyntheticFunction> syntheticFunctions = sourceKind == SourceKind.SDK_INTERFACE
|
||||
? ReadOnlyList.empty()
|
||||
: lowerSyntheticFunctions(fileId, effectiveModuleId, ast);
|
||||
final ReadOnlyList<IRGlobal> globals = sourceKind == SourceKind.SDK_INTERFACE
|
||||
? ReadOnlyList.empty()
|
||||
: lowerGlobals(fileId, effectiveModuleId, ast);
|
||||
final var loweringErrorBaseline = diagnostics.errorCount();
|
||||
final var executableLowering = executableLoweringService.lower(
|
||||
fileId,
|
||||
@ -165,6 +175,8 @@ public final class PbsFrontendCompiler {
|
||||
return new IRBackendFile(
|
||||
fileId,
|
||||
functions,
|
||||
syntheticFunctions,
|
||||
globals,
|
||||
executableLowering.executableFunctions(),
|
||||
reservedMetadata,
|
||||
effectiveModulePool,
|
||||
@ -210,6 +222,78 @@ public final class PbsFrontendCompiler {
|
||||
return ReadOnlyList.wrap(functions);
|
||||
}
|
||||
|
||||
private ReadOnlyList<IRGlobal> lowerGlobals(
|
||||
final FileId fileId,
|
||||
final ModuleId moduleId,
|
||||
final PbsAst.File ast) {
|
||||
final var globals = new ArrayList<IRGlobal>();
|
||||
for (final var topDecl : ast.topDecls()) {
|
||||
if (!(topDecl instanceof PbsAst.GlobalDecl globalDecl)) {
|
||||
continue;
|
||||
}
|
||||
globals.add(new IRGlobal(
|
||||
fileId,
|
||||
moduleId,
|
||||
globalDecl.name(),
|
||||
PbsReservedMetadataExtractor.typeSurfaceKey(globalDecl.explicitType()),
|
||||
globalDecl.span()));
|
||||
}
|
||||
return ReadOnlyList.wrap(globals);
|
||||
}
|
||||
|
||||
private ReadOnlyList<IRSyntheticFunction> lowerSyntheticFunctions(
|
||||
final FileId fileId,
|
||||
final ModuleId moduleId,
|
||||
final PbsAst.File ast) {
|
||||
final var syntheticFunctions = new ArrayList<IRSyntheticFunction>();
|
||||
PbsAst.FunctionDecl initDecl = null;
|
||||
PbsAst.FunctionDecl frameDecl = null;
|
||||
PbsAst.GlobalDecl firstGlobalDecl = null;
|
||||
for (final var topDecl : ast.topDecls()) {
|
||||
if (topDecl instanceof PbsAst.FunctionDecl functionDecl) {
|
||||
if (functionDecl.lifecycleMarker() == PbsAst.LifecycleMarker.INIT && initDecl == null) {
|
||||
initDecl = functionDecl;
|
||||
}
|
||||
if (functionDecl.lifecycleMarker() == PbsAst.LifecycleMarker.FRAME && frameDecl == null) {
|
||||
frameDecl = functionDecl;
|
||||
}
|
||||
}
|
||||
if (topDecl instanceof PbsAst.GlobalDecl globalDecl && firstGlobalDecl == null) {
|
||||
firstGlobalDecl = globalDecl;
|
||||
}
|
||||
}
|
||||
|
||||
final var fileInitAnchorSpan = initDecl != null ? initDecl.span() : firstGlobalDecl == null ? null : firstGlobalDecl.span();
|
||||
final var fileInitAnchorName = initDecl != null ? initDecl.name() : firstGlobalDecl == null ? "" : firstGlobalDecl.name();
|
||||
if (fileInitAnchorSpan != null) {
|
||||
syntheticFunctions.add(new IRSyntheticFunction(
|
||||
moduleId,
|
||||
"__pbs.file_init",
|
||||
IRSyntheticCallableKind.FILE_INIT_FRAGMENT,
|
||||
new IRSyntheticOrigin(fileId, fileInitAnchorName, fileInitAnchorSpan)));
|
||||
syntheticFunctions.add(new IRSyntheticFunction(
|
||||
moduleId,
|
||||
"__pbs.module_init",
|
||||
IRSyntheticCallableKind.MODULE_INIT,
|
||||
new IRSyntheticOrigin(fileId, fileInitAnchorName, fileInitAnchorSpan)));
|
||||
}
|
||||
if (initDecl != null && frameDecl != null) {
|
||||
syntheticFunctions.add(new IRSyntheticFunction(
|
||||
moduleId,
|
||||
"__pbs.project_init",
|
||||
IRSyntheticCallableKind.PROJECT_INIT,
|
||||
new IRSyntheticOrigin(fileId, initDecl.name(), initDecl.span())));
|
||||
}
|
||||
if (frameDecl != null) {
|
||||
syntheticFunctions.add(new IRSyntheticFunction(
|
||||
moduleId,
|
||||
"__pbs.frame_wrapper",
|
||||
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
||||
new IRSyntheticOrigin(fileId, frameDecl.name(), frameDecl.span())));
|
||||
}
|
||||
return ReadOnlyList.wrap(syntheticFunctions);
|
||||
}
|
||||
|
||||
private IRReservedMetadata mergeReservedMetadata(
|
||||
final IRReservedMetadata primary,
|
||||
final IRReservedMetadata imported) {
|
||||
|
||||
@ -2,6 +2,7 @@ package p.studio.compiler.pbs;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.compiler.messages.HostAdmissionContext;
|
||||
import p.studio.compiler.models.IRSyntheticCallableKind;
|
||||
import p.studio.compiler.models.SourceKind;
|
||||
import p.studio.compiler.pbs.lexer.LexErrors;
|
||||
import p.studio.compiler.pbs.semantics.PbsSemanticsErrors;
|
||||
@ -40,6 +41,33 @@ class PbsFrontendCompilerTest {
|
||||
assertEquals(1, functions.get(1).parameterCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeGlobalsAndSyntheticLifecycleArtifactsInBackendFile() {
|
||||
final var source = """
|
||||
declare global SCORE: int = 0;
|
||||
|
||||
[Init]
|
||||
fn boot() -> void { return; }
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void { return; }
|
||||
""";
|
||||
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
final var compiler = new PbsFrontendCompiler();
|
||||
final var fileBackend = compiler.compileFile(new FileId(1), source, diagnostics);
|
||||
|
||||
assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
|
||||
assertEquals(1, fileBackend.globals().size());
|
||||
assertEquals("SCORE", fileBackend.globals().getFirst().name());
|
||||
assertEquals(4, fileBackend.syntheticFunctions().size());
|
||||
assertTrue(fileBackend.syntheticFunctions().stream().anyMatch(fn -> fn.kind() == IRSyntheticCallableKind.FILE_INIT_FRAGMENT));
|
||||
assertTrue(fileBackend.syntheticFunctions().stream().anyMatch(fn -> fn.kind() == IRSyntheticCallableKind.MODULE_INIT));
|
||||
assertTrue(fileBackend.syntheticFunctions().stream().anyMatch(fn -> fn.kind() == IRSyntheticCallableKind.PROJECT_INIT));
|
||||
assertTrue(fileBackend.syntheticFunctions().stream().anyMatch(fn -> fn.kind() == IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER));
|
||||
assertTrue(fileBackend.syntheticFunctions().stream().allMatch(fn -> !fn.origin().anchorCallableName().isBlank()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReportDuplicateFunctionNames() {
|
||||
final var source = """
|
||||
|
||||
@ -21,6 +21,10 @@ public class IRBackend {
|
||||
@Builder.Default
|
||||
private final ReadOnlyList<IRFunction> functions = ReadOnlyList.empty();
|
||||
@Builder.Default
|
||||
private final ReadOnlyList<IRSyntheticFunction> syntheticFunctions = ReadOnlyList.empty();
|
||||
@Builder.Default
|
||||
private final ReadOnlyList<IRGlobal> globals = ReadOnlyList.empty();
|
||||
@Builder.Default
|
||||
private final ReadOnlyList<IRBackendExecutableFunction> executableFunctions = ReadOnlyList.empty();
|
||||
@Builder.Default
|
||||
private final ReadOnlyList<ModuleReference> modulePool = ReadOnlyList.empty();
|
||||
@ -39,6 +43,8 @@ public class IRBackend {
|
||||
private String entryPointCallableName;
|
||||
private ModuleId entryPointModuleId = ModuleId.none();
|
||||
private final ArrayList<IRFunction> functions = new ArrayList<>();
|
||||
private final ArrayList<IRSyntheticFunction> syntheticFunctions = new ArrayList<>();
|
||||
private final ArrayList<IRGlobal> globals = new ArrayList<>();
|
||||
private final ArrayList<IRBackendExecutableFunction> executableFunctions = new ArrayList<>();
|
||||
private final ModuleTable moduleTable = new ModuleTable();
|
||||
private final CallableTable callableTable = new CallableTable();
|
||||
@ -54,6 +60,21 @@ public class IRBackend {
|
||||
}
|
||||
functions.addAll(backendFile.functions().asList());
|
||||
final var moduleRemap = reindexModules(backendFile.modulePool());
|
||||
for (final var syntheticFunction : backendFile.syntheticFunctions()) {
|
||||
syntheticFunctions.add(new IRSyntheticFunction(
|
||||
remapModuleId(syntheticFunction.moduleId(), moduleRemap, "synthetic function"),
|
||||
syntheticFunction.callableName(),
|
||||
syntheticFunction.kind(),
|
||||
syntheticFunction.origin()));
|
||||
}
|
||||
for (final var global : backendFile.globals()) {
|
||||
globals.add(new IRGlobal(
|
||||
global.fileId(),
|
||||
remapModuleId(global.moduleId(), moduleRemap, "global"),
|
||||
global.name(),
|
||||
global.declaredTypeSurface(),
|
||||
global.span()));
|
||||
}
|
||||
final var callableRemap = reindexCallables(backendFile.callableSignatures(), moduleRemap);
|
||||
final var intrinsicRemap = reindexIntrinsics(backendFile.intrinsicPool());
|
||||
for (final var function : backendFile.executableFunctions()) {
|
||||
@ -248,6 +269,8 @@ public class IRBackend {
|
||||
.entryPointCallableName(resolveEntryPointCallableName())
|
||||
.entryPointModuleId(resolveEntryPointModuleId())
|
||||
.functions(ReadOnlyList.wrap(functions))
|
||||
.syntheticFunctions(ReadOnlyList.wrap(syntheticFunctions))
|
||||
.globals(ReadOnlyList.wrap(globals))
|
||||
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
|
||||
.modulePool(emitModulePool())
|
||||
.callableSignatures(emitCallableSignatures())
|
||||
@ -278,6 +301,8 @@ public class IRBackend {
|
||||
.append('@')
|
||||
.append(entryPointModuleId == null || entryPointModuleId.isNone() ? "-" : entryPointModuleId.getIndex())
|
||||
.append(", functions=").append(functions.size())
|
||||
.append(", syntheticFunctions=").append(syntheticFunctions.size())
|
||||
.append(", globals=").append(globals.size())
|
||||
.append(", executableFunctions=").append(executableFunctions.size())
|
||||
.append(", modulePool=").append(modulePool.size())
|
||||
.append(", callableSignatures=").append(callableSignatures.size())
|
||||
|
||||
@ -11,6 +11,8 @@ import java.util.Objects;
|
||||
public record IRBackendFile(
|
||||
FileId fileId,
|
||||
ReadOnlyList<IRFunction> functions,
|
||||
ReadOnlyList<IRSyntheticFunction> syntheticFunctions,
|
||||
ReadOnlyList<IRGlobal> globals,
|
||||
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
||||
IRReservedMetadata reservedMetadata,
|
||||
ReadOnlyList<ModuleReference> modulePool,
|
||||
@ -19,6 +21,8 @@ public record IRBackendFile(
|
||||
public IRBackendFile {
|
||||
fileId = Objects.requireNonNull(fileId, "fileId");
|
||||
functions = functions == null ? ReadOnlyList.empty() : functions;
|
||||
syntheticFunctions = syntheticFunctions == null ? ReadOnlyList.empty() : syntheticFunctions;
|
||||
globals = globals == null ? ReadOnlyList.empty() : globals;
|
||||
executableFunctions = executableFunctions == null ? ReadOnlyList.empty() : executableFunctions;
|
||||
reservedMetadata = reservedMetadata == null ? IRReservedMetadata.empty() : reservedMetadata;
|
||||
modulePool = modulePool == null ? ReadOnlyList.empty() : modulePool;
|
||||
@ -33,6 +37,8 @@ public record IRBackendFile(
|
||||
fileId,
|
||||
functions,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
IRReservedMetadata.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
@ -44,6 +50,8 @@ public record IRBackendFile(
|
||||
fileId,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
IRReservedMetadata.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
@ -58,6 +66,8 @@ public record IRBackendFile(
|
||||
fileId,
|
||||
functions,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
reservedMetadata,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
@ -74,10 +84,32 @@ public record IRBackendFile(
|
||||
this(
|
||||
fileId,
|
||||
functions,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
executableFunctions,
|
||||
reservedMetadata,
|
||||
ReadOnlyList.empty(),
|
||||
callableSignatures,
|
||||
intrinsicPool);
|
||||
}
|
||||
|
||||
public IRBackendFile(
|
||||
final FileId fileId,
|
||||
final ReadOnlyList<IRFunction> functions,
|
||||
final ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
||||
final IRReservedMetadata reservedMetadata,
|
||||
final ReadOnlyList<ModuleReference> modulePool,
|
||||
final ReadOnlyList<CallableSignatureRef> callableSignatures,
|
||||
final ReadOnlyList<IntrinsicReference> intrinsicPool) {
|
||||
this(
|
||||
fileId,
|
||||
functions,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
executableFunctions,
|
||||
reservedMetadata,
|
||||
modulePool,
|
||||
callableSignatures,
|
||||
intrinsicPool);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
package p.studio.compiler.models;
|
||||
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
import p.studio.compiler.source.identifiers.ModuleId;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record IRGlobal(
|
||||
FileId fileId,
|
||||
ModuleId moduleId,
|
||||
String name,
|
||||
String declaredTypeSurface,
|
||||
Span span) {
|
||||
public IRGlobal {
|
||||
fileId = Objects.requireNonNull(fileId, "fileId");
|
||||
moduleId = moduleId == null ? ModuleId.none() : moduleId;
|
||||
name = Objects.requireNonNull(name, "name");
|
||||
declaredTypeSurface = declaredTypeSurface == null ? "" : declaredTypeSurface;
|
||||
span = span == null ? Span.none() : span;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package p.studio.compiler.models;
|
||||
|
||||
public enum IRSyntheticCallableKind {
|
||||
FILE_INIT_FRAGMENT,
|
||||
MODULE_INIT,
|
||||
PROJECT_INIT,
|
||||
PUBLISHED_FRAME_WRAPPER
|
||||
}
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package p.studio.compiler.models;
|
||||
|
||||
import p.studio.compiler.source.identifiers.ModuleId;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record IRSyntheticFunction(
|
||||
ModuleId moduleId,
|
||||
String callableName,
|
||||
IRSyntheticCallableKind kind,
|
||||
IRSyntheticOrigin origin) {
|
||||
public IRSyntheticFunction {
|
||||
moduleId = moduleId == null ? ModuleId.none() : moduleId;
|
||||
callableName = Objects.requireNonNull(callableName, "callableName");
|
||||
kind = Objects.requireNonNull(kind, "kind");
|
||||
origin = Objects.requireNonNull(origin, "origin");
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
package p.studio.compiler.models;
|
||||
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record IRSyntheticOrigin(
|
||||
FileId fileId,
|
||||
String anchorCallableName,
|
||||
Span span) {
|
||||
public IRSyntheticOrigin {
|
||||
fileId = Objects.requireNonNull(fileId, "fileId");
|
||||
anchorCallableName = anchorCallableName == null ? "" : anchorCallableName;
|
||||
span = span == null ? Span.none() : span;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user