implements PR-08.1
This commit is contained in:
parent
e11cfee9a4
commit
40188b67ab
@ -186,6 +186,7 @@ public final class PbsFrontendCompiler {
|
||||
final var executableLowering = lowerExecutableFunctions(
|
||||
fileId,
|
||||
ast,
|
||||
effectiveSupplementalTopDecls,
|
||||
moduleKey,
|
||||
effectiveReservedMetadata,
|
||||
effectiveNameTable,
|
||||
@ -219,6 +220,7 @@ public final class PbsFrontendCompiler {
|
||||
private ExecutableLoweringResult lowerExecutableFunctions(
|
||||
final FileId fileId,
|
||||
final PbsAst.File ast,
|
||||
final ReadOnlyList<PbsAst.TopDecl> supplementalTopDecls,
|
||||
final String moduleKey,
|
||||
final IRReservedMetadata reservedMetadata,
|
||||
final NameTable nameTable,
|
||||
@ -231,12 +233,77 @@ public final class PbsFrontendCompiler {
|
||||
.computeIfAbsent(nameTable.register(hostBinding.sourceMethodName()), ignored -> new ArrayList<>())
|
||||
.add(hostBinding);
|
||||
}
|
||||
final var intrinsicByMethodName = new HashMap<NameId, List<IRReservedMetadata.IntrinsicSurface>>();
|
||||
final var builtinCanonicalBySourceType = new HashMap<String, String>();
|
||||
for (final var builtinType : reservedMetadata.builtinTypeSurfaces()) {
|
||||
builtinCanonicalBySourceType.putIfAbsent(builtinType.sourceTypeName(), builtinType.canonicalTypeName());
|
||||
}
|
||||
final var builtinConstOwnerByNameId = new HashMap<NameId, String>();
|
||||
for (final var builtinConst : reservedMetadata.builtinConstSurfaces()) {
|
||||
if (builtinConst.sourceConstName().isBlank() || builtinConst.canonicalTarget().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
builtinConstOwnerByNameId.put(nameTable.register(builtinConst.sourceConstName()), builtinConst.canonicalTarget());
|
||||
}
|
||||
|
||||
final var builtinSignatureByOwnerAndMethod = new HashMap<IntrinsicOwnerMethodKey, PbsAst.FunctionSignature>();
|
||||
final var topDeclsForIntrinsicInference = new ArrayList<PbsAst.TopDecl>(ast.topDecls().size() + supplementalTopDecls.size());
|
||||
topDeclsForIntrinsicInference.addAll(ast.topDecls().asList());
|
||||
topDeclsForIntrinsicInference.addAll(supplementalTopDecls.asList());
|
||||
for (final var topDecl : topDeclsForIntrinsicInference) {
|
||||
if (!(topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl)) {
|
||||
continue;
|
||||
}
|
||||
final var ownerCanonical = builtinCanonicalBySourceType.getOrDefault(builtinTypeDecl.name(), "");
|
||||
if (ownerCanonical.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
for (final var signature : builtinTypeDecl.signatures()) {
|
||||
builtinSignatureByOwnerAndMethod.putIfAbsent(
|
||||
new IntrinsicOwnerMethodKey(ownerCanonical, signature.name()),
|
||||
signature);
|
||||
}
|
||||
}
|
||||
|
||||
final var intrinsicByOwnerAndMethod = new HashMap<IntrinsicOwnerMethodKey, List<IRReservedMetadata.IntrinsicSurface>>();
|
||||
final var intrinsicReturnOwnerByCanonical = new HashMap<IntrinsicCanonicalKey, String>();
|
||||
for (final var builtinType : reservedMetadata.builtinTypeSurfaces()) {
|
||||
final var ownerCanonical = builtinType.canonicalTypeName();
|
||||
for (final var intrinsicSurface : builtinType.intrinsics()) {
|
||||
intrinsicByMethodName
|
||||
.computeIfAbsent(nameTable.register(intrinsicSurface.sourceMethodName()), ignored -> new ArrayList<>())
|
||||
intrinsicByOwnerAndMethod
|
||||
.computeIfAbsent(
|
||||
new IntrinsicOwnerMethodKey(ownerCanonical, intrinsicSurface.sourceMethodName()),
|
||||
ignored -> new ArrayList<>())
|
||||
.add(intrinsicSurface);
|
||||
|
||||
final var signature = builtinSignatureByOwnerAndMethod.get(
|
||||
new IntrinsicOwnerMethodKey(ownerCanonical, intrinsicSurface.sourceMethodName()));
|
||||
if (signature == null) {
|
||||
continue;
|
||||
}
|
||||
final var returnOwner = inferIntrinsicReturnOwner(
|
||||
signature,
|
||||
ownerCanonical,
|
||||
builtinCanonicalBySourceType);
|
||||
if (returnOwner.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
final var key = new IntrinsicCanonicalKey(
|
||||
intrinsicSurface.canonicalName(),
|
||||
intrinsicSurface.canonicalVersion());
|
||||
final var previous = intrinsicReturnOwnerByCanonical.putIfAbsent(key, returnOwner);
|
||||
if (previous != null && !previous.equals(returnOwner)) {
|
||||
diagnostics.error(
|
||||
DiagnosticPhase.STATIC_SEMANTICS,
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
Map.of(),
|
||||
"inconsistent intrinsic return owner metadata for %s@%d: %s vs %s".formatted(
|
||||
intrinsicSurface.canonicalName(),
|
||||
intrinsicSurface.canonicalVersion(),
|
||||
previous,
|
||||
returnOwner),
|
||||
intrinsicSurface.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
final var callableIdTable = new CallableTable();
|
||||
@ -296,7 +363,9 @@ public final class PbsFrontendCompiler {
|
||||
diagnostics,
|
||||
nameTable,
|
||||
hostByMethodName,
|
||||
intrinsicByMethodName,
|
||||
intrinsicByOwnerAndMethod,
|
||||
intrinsicReturnOwnerByCanonical,
|
||||
builtinConstOwnerByNameId,
|
||||
callableIdsByNameAndArity,
|
||||
returnSlotsByCallableId,
|
||||
intrinsicIdTable,
|
||||
@ -458,6 +527,25 @@ public final class PbsFrontendCompiler {
|
||||
};
|
||||
}
|
||||
|
||||
private String inferIntrinsicReturnOwner(
|
||||
final PbsAst.FunctionSignature signature,
|
||||
final String intrinsicOwnerCanonical,
|
||||
final Map<String, String> builtinCanonicalBySourceType) {
|
||||
if (signature.returnKind() == PbsAst.ReturnKind.INFERRED_UNIT
|
||||
|| signature.returnKind() == PbsAst.ReturnKind.EXPLICIT_UNIT) {
|
||||
return "";
|
||||
}
|
||||
final var returnType = unwrapGroup(signature.returnType());
|
||||
if (returnType == null) {
|
||||
return "";
|
||||
}
|
||||
return switch (returnType.kind()) {
|
||||
case SELF -> intrinsicOwnerCanonical == null ? "" : intrinsicOwnerCanonical;
|
||||
case SIMPLE -> builtinCanonicalBySourceType.getOrDefault(returnType.name(), "");
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
private String typeSurfaceKey(final PbsAst.TypeRef typeRef) {
|
||||
final var unwrapped = unwrapGroup(typeRef);
|
||||
if (unwrapped == null) {
|
||||
@ -758,7 +846,7 @@ public final class PbsFrontendCompiler {
|
||||
}
|
||||
|
||||
final var hostCandidates = context.hostByMethodName().getOrDefault(calleeIdentity.memberNameId(), List.of());
|
||||
final var intrinsicCandidates = context.intrinsicByMethodName().getOrDefault(calleeIdentity.memberNameId(), List.of());
|
||||
final var intrinsicCandidates = resolveIntrinsicCandidates(callExpr, calleeIdentity, context);
|
||||
|
||||
var categoryCount = 0;
|
||||
if (callableCandidates != null && !callableCandidates.isEmpty()) {
|
||||
@ -843,6 +931,66 @@ public final class PbsFrontendCompiler {
|
||||
callExpr.span()));
|
||||
}
|
||||
|
||||
private List<IRReservedMetadata.IntrinsicSurface> resolveIntrinsicCandidates(
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final CalleeIdentity calleeIdentity,
|
||||
final ExecutableLoweringContext context) {
|
||||
final var receiverOwner = resolveReceiverOwnerForCallee(callExpr.callee(), context);
|
||||
if (receiverOwner.isBlank()) {
|
||||
return List.of();
|
||||
}
|
||||
final var key = new IntrinsicOwnerMethodKey(receiverOwner, calleeIdentity.memberSourceMethodName());
|
||||
return context.intrinsicByOwnerAndMethod().getOrDefault(key, List.of());
|
||||
}
|
||||
|
||||
private String resolveReceiverOwnerForCallee(
|
||||
final PbsAst.Expression callee,
|
||||
final ExecutableLoweringContext context) {
|
||||
return switch (callee) {
|
||||
case PbsAst.MemberExpr memberExpr -> resolveExpressionOwner(memberExpr.receiver(), context);
|
||||
case PbsAst.GroupExpr groupExpr -> resolveReceiverOwnerForCallee(groupExpr.expression(), context);
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
private String resolveExpressionOwner(
|
||||
final PbsAst.Expression expression,
|
||||
final ExecutableLoweringContext context) {
|
||||
if (expression == null) {
|
||||
return "";
|
||||
}
|
||||
return switch (expression) {
|
||||
case PbsAst.IdentifierExpr identifierExpr ->
|
||||
context.builtinConstOwnerByNameId().getOrDefault(context.nameTable().register(identifierExpr.name()), "");
|
||||
case PbsAst.CallExpr callExpr -> resolveCallReturnOwner(callExpr, context);
|
||||
case PbsAst.MemberExpr memberExpr -> resolveExpressionOwner(memberExpr.receiver(), context);
|
||||
case PbsAst.GroupExpr groupExpr -> resolveExpressionOwner(groupExpr.expression(), context);
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
private String resolveCallReturnOwner(
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final ExecutableLoweringContext context) {
|
||||
final var calleeIdentity = resolveCalleeIdentity(callExpr.callee(), context.nameTable());
|
||||
if (calleeIdentity == null) {
|
||||
return "";
|
||||
}
|
||||
final var receiverOwner = resolveReceiverOwnerForCallee(callExpr.callee(), context);
|
||||
if (receiverOwner.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
final var key = new IntrinsicOwnerMethodKey(receiverOwner, calleeIdentity.memberSourceMethodName());
|
||||
final var intrinsicCandidates = context.intrinsicByOwnerAndMethod().getOrDefault(key, List.of());
|
||||
if (intrinsicCandidates.size() != 1) {
|
||||
return "";
|
||||
}
|
||||
final var intrinsic = intrinsicCandidates.getFirst();
|
||||
return context.intrinsicReturnOwnerByCanonical().getOrDefault(
|
||||
new IntrinsicCanonicalKey(intrinsic.canonicalName(), intrinsic.canonicalVersion()),
|
||||
"");
|
||||
}
|
||||
|
||||
private CalleeIdentity resolveCalleeIdentity(
|
||||
final PbsAst.Expression callee,
|
||||
final NameTable nameTable) {
|
||||
@ -853,6 +1001,7 @@ public final class PbsFrontendCompiler {
|
||||
identifierExpr.name(),
|
||||
null,
|
||||
"",
|
||||
identifierExpr.name(),
|
||||
nameTable.register(identifierExpr.name()));
|
||||
case PbsAst.MemberExpr memberExpr -> {
|
||||
final var memberName = memberExpr.memberName();
|
||||
@ -874,6 +1023,7 @@ public final class PbsFrontendCompiler {
|
||||
qualified,
|
||||
secondaryNameId,
|
||||
secondaryDisplayName,
|
||||
memberName,
|
||||
nameTable.register(memberName));
|
||||
}
|
||||
case PbsAst.BindExpr bindExpr ->
|
||||
@ -882,6 +1032,7 @@ public final class PbsFrontendCompiler {
|
||||
bindExpr.functionName(),
|
||||
null,
|
||||
"",
|
||||
bindExpr.functionName(),
|
||||
nameTable.register(bindExpr.functionName()));
|
||||
case PbsAst.GroupExpr groupExpr -> resolveCalleeIdentity(groupExpr.expression(), nameTable);
|
||||
default -> null;
|
||||
@ -1145,7 +1296,9 @@ public final class PbsFrontendCompiler {
|
||||
private final DiagnosticSink diagnostics;
|
||||
private final NameTable nameTable;
|
||||
private final Map<NameId, List<IRReservedMetadata.HostMethodBinding>> hostByMethodName;
|
||||
private final Map<NameId, List<IRReservedMetadata.IntrinsicSurface>> intrinsicByMethodName;
|
||||
private final Map<IntrinsicOwnerMethodKey, List<IRReservedMetadata.IntrinsicSurface>> intrinsicByOwnerAndMethod;
|
||||
private final Map<IntrinsicCanonicalKey, String> intrinsicReturnOwnerByCanonical;
|
||||
private final Map<NameId, String> builtinConstOwnerByNameId;
|
||||
private final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity;
|
||||
private final Map<CallableId, Integer> returnSlotsByCallableId;
|
||||
private final IntrinsicTable intrinsicIdTable;
|
||||
@ -1159,7 +1312,9 @@ public final class PbsFrontendCompiler {
|
||||
final DiagnosticSink diagnostics,
|
||||
final NameTable nameTable,
|
||||
final Map<NameId, List<IRReservedMetadata.HostMethodBinding>> hostByMethodName,
|
||||
final Map<NameId, List<IRReservedMetadata.IntrinsicSurface>> intrinsicByMethodName,
|
||||
final Map<IntrinsicOwnerMethodKey, List<IRReservedMetadata.IntrinsicSurface>> intrinsicByOwnerAndMethod,
|
||||
final Map<IntrinsicCanonicalKey, String> intrinsicReturnOwnerByCanonical,
|
||||
final Map<NameId, String> builtinConstOwnerByNameId,
|
||||
final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
|
||||
final Map<CallableId, Integer> returnSlotsByCallableId,
|
||||
final IntrinsicTable intrinsicIdTable,
|
||||
@ -1168,7 +1323,9 @@ public final class PbsFrontendCompiler {
|
||||
this.diagnostics = diagnostics;
|
||||
this.nameTable = nameTable;
|
||||
this.hostByMethodName = hostByMethodName;
|
||||
this.intrinsicByMethodName = intrinsicByMethodName;
|
||||
this.intrinsicByOwnerAndMethod = intrinsicByOwnerAndMethod;
|
||||
this.intrinsicReturnOwnerByCanonical = intrinsicReturnOwnerByCanonical;
|
||||
this.builtinConstOwnerByNameId = builtinConstOwnerByNameId;
|
||||
this.callableIdsByNameAndArity = callableIdsByNameAndArity;
|
||||
this.returnSlotsByCallableId = returnSlotsByCallableId;
|
||||
this.intrinsicIdTable = intrinsicIdTable;
|
||||
@ -1191,8 +1348,16 @@ public final class PbsFrontendCompiler {
|
||||
return hostByMethodName;
|
||||
}
|
||||
|
||||
private Map<NameId, List<IRReservedMetadata.IntrinsicSurface>> intrinsicByMethodName() {
|
||||
return intrinsicByMethodName;
|
||||
private Map<IntrinsicOwnerMethodKey, List<IRReservedMetadata.IntrinsicSurface>> intrinsicByOwnerAndMethod() {
|
||||
return intrinsicByOwnerAndMethod;
|
||||
}
|
||||
|
||||
private Map<IntrinsicCanonicalKey, String> intrinsicReturnOwnerByCanonical() {
|
||||
return intrinsicReturnOwnerByCanonical;
|
||||
}
|
||||
|
||||
private Map<NameId, String> builtinConstOwnerByNameId() {
|
||||
return builtinConstOwnerByNameId;
|
||||
}
|
||||
|
||||
private Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity() {
|
||||
@ -1255,11 +1420,22 @@ public final class PbsFrontendCompiler {
|
||||
int arity) {
|
||||
}
|
||||
|
||||
private record IntrinsicOwnerMethodKey(
|
||||
String ownerCanonicalName,
|
||||
String sourceMethodName) {
|
||||
}
|
||||
|
||||
private record IntrinsicCanonicalKey(
|
||||
String canonicalName,
|
||||
long canonicalVersion) {
|
||||
}
|
||||
|
||||
private record CalleeIdentity(
|
||||
NameId primaryCallableNameId,
|
||||
String primaryCallableDisplayName,
|
||||
NameId secondaryCallableNameId,
|
||||
String secondaryCallableDisplayName,
|
||||
String memberSourceMethodName,
|
||||
NameId memberNameId) {
|
||||
}
|
||||
|
||||
|
||||
@ -867,6 +867,70 @@ class PBSFrontendPhaseServiceTest {
|
||||
+ executableNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveIntrinsicChainsWithMethodNameCollisionsByOwnerIdentity() throws IOException {
|
||||
final var projectRoot = tempDir.resolve("project-intrinsic-owner-collision");
|
||||
final var sourceRoot = projectRoot.resolve("src");
|
||||
Files.createDirectories(sourceRoot);
|
||||
|
||||
final var sourceFile = sourceRoot.resolve("main.pbs");
|
||||
final var modBarrel = sourceRoot.resolve("mod.barrel");
|
||||
Files.writeString(sourceFile, """
|
||||
import { Input } from @sdk:input;
|
||||
|
||||
fn frame() -> void
|
||||
{
|
||||
Input.pad().x().pressed();
|
||||
Input.touch().x();
|
||||
}
|
||||
""");
|
||||
Files.writeString(modBarrel, "pub fn frame() -> void;");
|
||||
|
||||
final var projectTable = new ProjectTable();
|
||||
final var fileTable = new FileTable(1);
|
||||
final var projectId = projectTable.register(ProjectDescriptor.builder()
|
||||
.rootPath(projectRoot)
|
||||
.name("main")
|
||||
.version("1.0.0")
|
||||
.sourceRoots(ReadOnlyList.wrap(List.of(sourceRoot)))
|
||||
.build());
|
||||
|
||||
registerFile(projectId, projectRoot, sourceFile, fileTable);
|
||||
registerFile(projectId, projectRoot, modBarrel, fileTable);
|
||||
|
||||
final var ctx = new FrontendPhaseContext(
|
||||
projectTable,
|
||||
fileTable,
|
||||
new BuildStack(ReadOnlyList.wrap(List.of(projectId))),
|
||||
1);
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
|
||||
final var irBackend = new PBSFrontendPhaseService().compile(
|
||||
ctx,
|
||||
diagnostics,
|
||||
LogAggregator.empty(),
|
||||
BuildingIssueSink.empty());
|
||||
|
||||
assertTrue(diagnostics.stream().noneMatch(d ->
|
||||
d.getCode().equals(PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name())),
|
||||
diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
|
||||
final var frameExecutable = irBackend.getExecutableFunctions().stream()
|
||||
.filter(function -> "frame".equals(function.callableName()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
final var intrinsicCalls = frameExecutable.instructions().stream()
|
||||
.filter(instruction ->
|
||||
instruction.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC
|
||||
&& instruction.intrinsicCall() != null)
|
||||
.map(instruction -> instruction.intrinsicCall().canonicalName())
|
||||
.toList();
|
||||
assertTrue(intrinsicCalls.contains("input.pad"));
|
||||
assertTrue(intrinsicCalls.contains("input.pad.x"));
|
||||
assertTrue(intrinsicCalls.contains("input.button.pressed"));
|
||||
assertTrue(intrinsicCalls.contains("input.touch"));
|
||||
assertTrue(intrinsicCalls.contains("input.touch.x"));
|
||||
}
|
||||
|
||||
private void registerFile(
|
||||
final ProjectId projectId,
|
||||
final Path projectRoot,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user