implements PR-O1.3

This commit is contained in:
bQUARKz 2026-03-07 17:49:38 +00:00
parent 9e3d9ccb93
commit 3e14c13979
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
3 changed files with 53 additions and 3 deletions

View File

@ -12,7 +12,9 @@ import p.studio.compiler.pbs.metadata.PbsReservedMetadataExtractor;
import p.studio.compiler.pbs.parser.PbsParser;
import p.studio.compiler.pbs.semantics.PbsDeclarationSemanticsValidator;
import p.studio.compiler.pbs.semantics.PbsFlowSemanticsValidator;
import p.studio.compiler.pbs.semantics.PbsSemanticsErrors;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.diagnostics.DiagnosticPhase;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.utilities.structures.ReadOnlyList;
@ -117,9 +119,13 @@ public final class PbsFrontendCompiler {
final ReadOnlyList<IRFunction> functions = sourceKind == SourceKind.SDK_INTERFACE
? ReadOnlyList.empty()
: lowerFunctions(fileId, ast);
final var loweringErrorBaseline = diagnostics.errorCount();
final ReadOnlyList<IRBackendExecutableFunction> executableFunctions = sourceKind == SourceKind.SDK_INTERFACE
? ReadOnlyList.empty()
: lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata);
: lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata, diagnostics);
if (diagnostics.errorCount() > loweringErrorBaseline) {
return IRBackendFile.empty(fileId);
}
return new IRBackendFile(fileId, functions, executableFunctions, reservedMetadata);
}
@ -140,7 +146,8 @@ public final class PbsFrontendCompiler {
final FileId fileId,
final PbsAst.File ast,
final String moduleKey,
final IRReservedMetadata reservedMetadata) {
final IRReservedMetadata reservedMetadata,
final DiagnosticSink diagnostics) {
final var hostByMethodName = new HashMap<String, IRReservedMetadata.HostMethodBinding>();
for (final var hostBinding : reservedMetadata.hostMethodBindings()) {
hostByMethodName.put(hostBinding.sourceMethodName(), hostBinding);
@ -157,8 +164,25 @@ public final class PbsFrontendCompiler {
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
final var callsites = new ArrayList<PbsAst.CallExpr>();
collectCallsFromBlock(fn.body(), callsites);
callsites.sort((a, b) -> {
final var deltaStart = Long.compare(a.span().getStart(), b.span().getStart());
if (deltaStart != 0) {
return deltaStart;
}
return Long.compare(a.span().getEnd(), b.span().getEnd());
});
for (final var callExpr : callsites) {
final var calleeName = extractSimpleCalleeName(callExpr.callee());
if (calleeName == null || calleeName.isBlank()) {
diagnostics.error(
DiagnosticPhase.STATIC_SEMANTICS,
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
Map.of(),
"executable lowering requires resolvable callee identity",
callExpr.span());
continue;
}
final var host = hostByMethodName.get(calleeName);
if (host != null) {
instructions.add(new IRBackendExecutableFunction.Instruction(
@ -395,7 +419,7 @@ public final class PbsFrontendCompiler {
case PbsAst.MemberExpr memberExpr -> memberExpr.memberName();
case PbsAst.BindExpr bindExpr -> bindExpr.functionName();
case PbsAst.GroupExpr groupExpr -> extractSimpleCalleeName(groupExpr.expression());
default -> "<unknown>";
default -> null;
};
}
}

View File

@ -59,4 +59,5 @@ public enum PbsSemanticsErrors {
E_SEM_HANDLE_NON_RESULT,
E_SEM_HANDLE_ERROR_MISMATCH,
E_SEM_HANDLE_ARM_TERMINAL_INVALID,
E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE,
}

View File

@ -98,6 +98,31 @@ class PbsFrontendCompilerTest {
i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC));
}
@Test
void shouldLowerExecutableCallsitesInDeterministicSourceOrder() {
final var source = """
fn b() -> int { return 1; }
fn c() -> int { return 2; }
fn a() -> int {
b();
c();
return 0;
}
""";
final var diagnostics = DiagnosticSink.empty();
final var compiler = new PbsFrontendCompiler();
final var fileBackend = compiler.compileFile(new FileId(99), source, diagnostics);
assertTrue(diagnostics.isEmpty(), "Valid program should not report diagnostics");
final var executableA = fileBackend.executableFunctions().stream()
.filter(fn -> fn.callableName().equals("a"))
.findFirst()
.orElseThrow();
assertEquals("b", executableA.instructions().get(0).calleeCallableName());
assertEquals("c", executableA.instructions().get(1).calleeCallableName());
}
@Test
void shouldNotLowerWhenSyntaxErrorsExist() {
final var source = """