implements PR-08.4

This commit is contained in:
bQUARKz 2026-03-09 14:27:06 +00:00
parent 2a68d27eca
commit 505a4f72ce
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
2 changed files with 97 additions and 3 deletions

View File

@ -389,6 +389,10 @@ public final class PbsFrontendCompiler {
.add(callableId);
returnSlotsByCallableId.put(callableId, importedCallable.returnSlots());
}
final var callableSignatureByCallableId = new HashMap<CallableId, CallableSignatureRef>();
for (final var callableId : callableIdTable.identifiers()) {
callableSignatureByCallableId.put(callableId, callableIdTable.get(callableId));
}
final var executableFunctions = new ArrayList<IRBackendExecutableFunction>(localCallables.size());
for (final var callable : localCallables) {
@ -411,6 +415,7 @@ public final class PbsFrontendCompiler {
intrinsicReturnOwnerByCanonical,
builtinConstOwnerByNameId,
callableIdsByNameAndArity,
callableSignatureByCallableId,
returnSlotsByCallableId,
intrinsicIdTable,
localSlotByNameId);
@ -964,11 +969,16 @@ public final class PbsFrontendCompiler {
return;
}
final var calleeCallableId = callableCandidates.getFirst();
final var calleeSignature = context.callableSignatureByCallableId().get(calleeCallableId);
if (calleeSignature == null) {
reportUnsupportedLowering("executable lowering resolved callable without signature metadata", callExpr.span(), context);
return;
}
context.instructions().add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
context.moduleId(),
context.moduleKey(),
calleeIdentity.primaryCallableDisplayName(),
calleeSignature.moduleId(),
calleeSignature.moduleKey(),
calleeSignature.callableName(),
calleeCallableId,
null,
null,
@ -1347,6 +1357,7 @@ public final class PbsFrontendCompiler {
private final Map<IntrinsicCanonicalKey, String> intrinsicReturnOwnerByCanonical;
private final Map<NameId, String> builtinConstOwnerByNameId;
private final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity;
private final Map<CallableId, CallableSignatureRef> callableSignatureByCallableId;
private final Map<CallableId, Integer> returnSlotsByCallableId;
private final IntrinsicTable intrinsicIdTable;
private final Map<NameId, Integer> localSlotByNameId;
@ -1364,6 +1375,7 @@ public final class PbsFrontendCompiler {
final Map<IntrinsicCanonicalKey, String> intrinsicReturnOwnerByCanonical,
final Map<NameId, String> builtinConstOwnerByNameId,
final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
final Map<CallableId, CallableSignatureRef> callableSignatureByCallableId,
final Map<CallableId, Integer> returnSlotsByCallableId,
final IntrinsicTable intrinsicIdTable,
final Map<NameId, Integer> localSlotByNameId) {
@ -1376,6 +1388,7 @@ public final class PbsFrontendCompiler {
this.intrinsicReturnOwnerByCanonical = intrinsicReturnOwnerByCanonical;
this.builtinConstOwnerByNameId = builtinConstOwnerByNameId;
this.callableIdsByNameAndArity = callableIdsByNameAndArity;
this.callableSignatureByCallableId = callableSignatureByCallableId;
this.returnSlotsByCallableId = returnSlotsByCallableId;
this.intrinsicIdTable = intrinsicIdTable;
this.localSlotByNameId = localSlotByNameId == null ? Map.of() : localSlotByNameId;
@ -1417,6 +1430,10 @@ public final class PbsFrontendCompiler {
return callableIdsByNameAndArity;
}
private Map<CallableId, CallableSignatureRef> callableSignatureByCallableId() {
return callableSignatureByCallableId;
}
private Map<CallableId, Integer> returnSlotsByCallableId() {
return returnSlotsByCallableId;
}

View File

@ -33,6 +33,7 @@ import java.util.Map;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PBSFrontendPhaseServiceTest {
@ -138,6 +139,82 @@ class PBSFrontendPhaseServiceTest {
assertEquals("good", irBackend.getFunctions().getFirst().name());
}
@Test
void shouldEmitCallFuncWithCalleeModuleIdentityFromResolvedTarget() throws IOException {
final var projectRoot = tempDir.resolve("project-callsite-module-identity");
final var sourceRoot = projectRoot.resolve("src");
final var moduleAPath = sourceRoot.resolve("a");
final var moduleBPath = sourceRoot.resolve("b");
Files.createDirectories(moduleAPath);
Files.createDirectories(moduleBPath);
final var sourceA = moduleAPath.resolve("source.pbs");
final var barrelA = moduleAPath.resolve("mod.barrel");
Files.writeString(sourceA, """
fn target() -> int {
return 1;
}
""");
Files.writeString(barrelA, "pub fn target() -> int;");
final var sourceB = moduleBPath.resolve("source.pbs");
final var barrelB = moduleBPath.resolve("mod.barrel");
Files.writeString(sourceB, """
import { target } from @app:a;
fn caller() -> int {
return target();
}
""");
Files.writeString(barrelB, "pub fn caller() -> int;");
final var projectTable = new ProjectTable();
final var fileTable = new FileTable(1);
final var projectId = projectTable.register(ProjectDescriptor.builder()
.rootPath(projectRoot)
.name("app")
.version("1.0.0")
.sourceRoots(ReadOnlyList.wrap(List.of(sourceRoot)))
.build());
registerFile(projectId, projectRoot, sourceA, fileTable);
registerFile(projectId, projectRoot, barrelA, fileTable);
registerFile(projectId, projectRoot, sourceB, fileTable);
registerFile(projectId, projectRoot, barrelB, fileTable);
final var ctx = new FrontendPhaseContext(
projectTable,
fileTable,
new BuildStack(ReadOnlyList.wrap(List.of(projectId))));
final var diagnostics = DiagnosticSink.empty();
final var irBackend = new PBSFrontendPhaseService().compile(
ctx,
diagnostics,
LogAggregator.empty(),
BuildingIssueSink.empty());
assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
final var targetFn = irBackend.getExecutableFunctions().stream()
.filter(function -> "target".equals(function.callableName()))
.findFirst()
.orElseThrow();
final var callerFn = irBackend.getExecutableFunctions().stream()
.filter(function -> "caller".equals(function.callableName()))
.findFirst()
.orElseThrow();
final var callInstruction = callerFn.instructions().stream()
.filter(instruction -> instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC)
.findFirst()
.orElseThrow();
assertNotNull(callInstruction.calleeModuleId());
assertEquals(targetFn.moduleId().getIndex(), callInstruction.calleeModuleId().getIndex());
assertEquals(targetFn.moduleKey(), callInstruction.calleeModuleKey());
assertTrue(callerFn.moduleId().getIndex() != callInstruction.calleeModuleId().getIndex());
}
@Test
void shouldLowerAllSourcesWhenNoAdmissionErrorsExist() throws IOException {
final var projectRoot = tempDir.resolve("project-valid");