From 14f9b5dd2662488a85140d4c4abe32534b731077 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 9 Mar 2026 09:10:55 +0000 Subject: [PATCH] implements PR-07.2 --- ...Backend Spec-to-Test Conformance Matrix.md | 2 +- .../compiler/pbs/PbsFrontendCompiler.java | 4 +- .../compiler/pbs/PbsFrontendCompilerTest.java | 83 +++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/docs/general/specs/22. Backend Spec-to-Test Conformance Matrix.md b/docs/general/specs/22. Backend Spec-to-Test Conformance Matrix.md index adfcdf5d..fc3737b2 100644 --- a/docs/general/specs/22. Backend Spec-to-Test Conformance Matrix.md +++ b/docs/general/specs/22. Backend Spec-to-Test Conformance Matrix.md @@ -73,7 +73,7 @@ to concrete positive/negative test evidence and current status. | PBS13-12.1.4 | Source anchor (`fileId/start/end`) MUST be preserved. | `IRBackendExecutableContractTest#aggregatorMustPreserveExecutableSourceAttribution`; `IRBackendExecutableContractTest#functionContractMustRejectInvalidSlotAndSpanBounds` | N/A | pass | Handoff preserves function and instruction source anchors after aggregation/reindexing. | | PBS13-12.1.5 | Executable body representation MUST be backend-lowerable. | `LowerToIRVMServiceTest#lowerMustMapHostAndIntrinsicCallsites` | `LowerToIRVMServiceTest#lowerMustRejectMissingCallee` | pass | | | PBS13-12.2.1 | Callsite MUST be exactly one of `CALL_FUNC/CALL_HOST/CALL_INTRINSIC`. | `IRBackendExecutableContractTest#callInstructionMustRequireCategorySpecificMetadata` | `IRBackendExecutableContractTest#instructionContractMustRejectMixedMetadataKinds` | pass | | -| PBS13-12.2.2 | Backend MUST NOT infer callsite category by textual heuristics. | `PbsFrontendCompilerTest#shouldLowerExecutableFunctionsWithCallsiteCategories`; `IRBackendExecutableContractTest#callInstructionMustRequireCategorySpecificMetadata` | N/A | partial | Semantic-identity coverage exists; dedicated ambiguous-identity regression fixture is still missing. | +| PBS13-12.2.2 | Backend MUST NOT infer callsite category by textual heuristics. | `PbsFrontendCompilerTest#shouldClassifyCallableCallEvenWhenNameLooksHostLike`; `PbsFrontendCompilerTest#shouldNotInferCallableCategoryFromMemberCallText`; `PbsFrontendCompilerTest#shouldRejectAmbiguousCallableIdentityInsteadOfGuessingByText`; `IRBackendExecutableContractTest#callInstructionMustRequireCategorySpecificMetadata` | N/A | pass | Dedicated regression fixtures now cover textually misleading names and ambiguous callable identity without category guessing. | | PBS13-12.3.1 | Host-backed callsite MUST preserve canonical `(module,name,version)`. | `LowerToIRVMServiceTest#lowerMustMapHostAndIntrinsicCallsites` | N/A | pass | | | PBS13-12.3.2 | Host ABI shape (`arg_slots`,`ret_slots`) MUST be preserved when available. | `LowerToIRVMServiceTest#lowerMustMapHostAndIntrinsicCallsites` | `BytecodeEmitterTest#emitMustRejectAbiMismatch` | pass | | | PBS13-12.4.1 | Intrinsic callsite MUST preserve canonical intrinsic identity. | `IRBackendExecutableContractTest#aggregatorMustReindexIntrinsicsToBuildGlobalPool` | N/A | pass | | diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java index 71195bb5..fe04b8ad 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java @@ -693,8 +693,8 @@ public final class PbsFrontendCompiler { return switch (callee) { case PbsAst.IdentifierExpr identifierExpr -> new CalleeIdentity(nameTable.register(identifierExpr.name()), identifierExpr.name()); - case PbsAst.MemberExpr memberExpr -> - new CalleeIdentity(nameTable.register(memberExpr.memberName()), memberExpr.memberName()); + // Member access does not carry executable callsite identity in v1 lowering. + case PbsAst.MemberExpr ignored -> null; case PbsAst.BindExpr bindExpr -> new CalleeIdentity(nameTable.register(bindExpr.functionName()), bindExpr.functionName()); case PbsAst.GroupExpr groupExpr -> resolveCalleeIdentity(groupExpr.expression(), nameTable); diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java index 0e0c09dc..74e1c175 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/PbsFrontendCompilerTest.java @@ -98,6 +98,89 @@ class PbsFrontendCompilerTest { i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC)); } + @Test + void shouldClassifyCallableCallEvenWhenNameLooksHostLike() { + final var source = """ + fn draw_pixel(x: int, y: int) -> int { + return x; + } + + fn main() -> int { + return draw_pixel(1, 2); + } + """; + + final var diagnostics = DiagnosticSink.empty(); + final var compiler = new PbsFrontendCompiler(); + final var fileBackend = compiler.compileFile(new FileId(43), source, diagnostics); + + assertTrue(diagnostics.isEmpty(), "Valid program should not report diagnostics"); + final var executableMain = fileBackend.executableFunctions().stream() + .filter(fn -> fn.callableName().equals("main")) + .findFirst() + .orElseThrow(); + final var callInstruction = executableMain.instructions().stream() + .filter(i -> i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC) + .findFirst() + .orElseThrow(); + assertEquals("draw_pixel", callInstruction.calleeCallableName()); + } + + @Test + void shouldNotInferCallableCategoryFromMemberCallText() { + final var source = """ + fn draw_pixel(x: int, y: int) -> int { + return x; + } + + fn main() -> int { + return gfx.draw_pixel(1, 2); + } + """; + + final var diagnostics = DiagnosticSink.empty(); + final var compiler = new PbsFrontendCompiler(); + final var fileBackend = compiler.compileFile(new FileId(44), source, diagnostics); + + assertTrue(fileBackend.executableFunctions().stream() + .filter(fn -> fn.callableName().equals("main")) + .findFirst() + .map(fn -> fn.instructions().stream().noneMatch(i -> + i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC + || i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_HOST + || i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC)) + .orElse(true)); + } + + @Test + void shouldRejectAmbiguousCallableIdentityInsteadOfGuessingByText() { + final var source = """ + fn draw_pixel(x: int) -> int { + return x; + } + + fn draw_pixel(x: float) -> int { + return 0; + } + + fn main() -> int { + return draw_pixel(1); + } + """; + + final var diagnostics = DiagnosticSink.empty(); + final var compiler = new PbsFrontendCompiler(); + final var fileBackend = compiler.compileFile(new FileId(45), source, diagnostics); + + final var loweringDiagnostic = diagnostics.stream() + .filter(d -> d.getCode().equals(PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name())) + .findFirst() + .orElseThrow(); + assertEquals(DiagnosticPhase.STATIC_SEMANTICS, loweringDiagnostic.getPhase()); + assertTrue(loweringDiagnostic.getMessage().contains("ambiguous callable identity")); + assertEquals(0, fileBackend.executableFunctions().size()); + } + @Test void shouldLowerExecutableCallsitesInDeterministicSourceOrder() { final var source = """