implements PR-07.2

This commit is contained in:
bQUARKz 2026-03-09 09:10:55 +00:00
parent b91a3cea34
commit 14f9b5dd26
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
3 changed files with 86 additions and 3 deletions

View File

@ -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 | |

View File

@ -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);

View File

@ -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 = """