implements PR-05.0.4

This commit is contained in:
bQUARKz 2026-03-09 06:36:21 +00:00
parent 0149b85e7d
commit 19f293d8ea
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
10 changed files with 221 additions and 51 deletions

View File

@ -15,14 +15,19 @@ 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.CallableShapeId;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.source.identifiers.IntrinsicId;
import p.studio.compiler.source.identifiers.NameId;
import p.studio.compiler.source.identifiers.TypeSurfaceId;
import p.studio.compiler.source.tables.CallableShapeTable;
import p.studio.compiler.source.tables.CallableSignatureRef;
import p.studio.compiler.source.tables.CallableTable;
import p.studio.compiler.source.tables.IntrinsicReference;
import p.studio.compiler.source.tables.IntrinsicTable;
import p.studio.compiler.source.tables.NameTable;
import p.studio.compiler.source.tables.TypeSurfaceTable;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList;
@ -144,7 +149,7 @@ public final class PbsFrontendCompiler {
final var loweringErrorBaseline = diagnostics.errorCount();
final var executableLowering = sourceKind == SourceKind.SDK_INTERFACE
? new ExecutableLoweringResult(ReadOnlyList.empty(), ReadOnlyList.empty(), ReadOnlyList.empty())
: lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata, diagnostics);
: lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata, effectiveNameTable, diagnostics);
if (diagnostics.errorCount() > loweringErrorBaseline) {
return IRBackendFile.empty(fileId);
}
@ -175,6 +180,7 @@ public final class PbsFrontendCompiler {
final PbsAst.File ast,
final String moduleKey,
final IRReservedMetadata reservedMetadata,
final NameTable nameTable,
final DiagnosticSink diagnostics) {
final var normalizedModuleKey = moduleKey == null ? "" : moduleKey;
final var hostByMethodName = new HashMap<String, IRReservedMetadata.HostMethodBinding>();
@ -188,19 +194,24 @@ public final class PbsFrontendCompiler {
}
}
final var callableIdTable = new CallableTable();
final var callableIdsByNameAndArity = new HashMap<String, List<CallableId>>();
final var callableIdsByNameAndArity = new HashMap<CallableResolutionKey, List<CallableId>>();
final var callableIdByDeclaration = new HashMap<PbsAst.FunctionDecl, CallableId>();
final var returnSlotsByCallableId = new HashMap<CallableId, Integer>();
final var intrinsicIdTable = new IntrinsicTable();
final var typeSurfaceTable = new TypeSurfaceTable();
final var callableShapeTable = new CallableShapeTable();
for (final var declaredFn : ast.functions()) {
final var callableShapeId = callableShapeId(declaredFn, typeSurfaceTable, callableShapeTable);
final var callableId = callableIdTable.register(
normalizedModuleKey,
declaredFn.name(),
declaredFn.parameters().size(),
callableShapeKey(declaredFn));
callableShapeSurface(callableShapeId, typeSurfaceTable, callableShapeTable));
callableIdByDeclaration.put(declaredFn, callableId);
callableIdsByNameAndArity
.computeIfAbsent(callableArityKey(declaredFn.name(), declaredFn.parameters().size()), ignored -> new ArrayList<>())
.computeIfAbsent(
new CallableResolutionKey(nameTable.register(declaredFn.name()), declaredFn.parameters().size()),
ignored -> new ArrayList<>())
.add(callableId);
final var retSlots = returnSlotsFor(declaredFn);
returnSlotsByCallableId.put(callableId, retSlots);
@ -261,7 +272,8 @@ public final class PbsFrontendCompiler {
continue;
}
final var candidateCallableIds = callableIdsByNameAndArity.get(callableArityKey(calleeName, callExpr.arguments().size()));
final var candidateCallableIds = callableIdsByNameAndArity.get(
new CallableResolutionKey(nameTable.register(calleeName), callExpr.arguments().size()));
if (candidateCallableIds == null || candidateCallableIds.isEmpty()) {
diagnostics.error(
DiagnosticPhase.STATIC_SEMANTICS,
@ -354,38 +366,51 @@ public final class PbsFrontendCompiler {
};
}
private String callableArityKey(
final String callableName,
final int arity) {
return callableName + "#" + arity;
private CallableShapeId callableShapeId(
final PbsAst.FunctionDecl functionDecl,
final TypeSurfaceTable typeSurfaceTable,
final CallableShapeTable callableShapeTable) {
final var parameterSurfaceIds = new ArrayList<TypeSurfaceId>(functionDecl.parameters().size());
for (final var parameter : functionDecl.parameters()) {
parameterSurfaceIds.add(typeSurfaceTable.register(typeSurfaceKey(parameter.typeRef())));
}
final var outputSurfaceId = typeSurfaceTable.register(outputShapeSurface(
functionDecl.returnKind(),
functionDecl.returnType(),
functionDecl.resultErrorType()));
return callableShapeTable.register(ReadOnlyList.wrap(parameterSurfaceIds), outputSurfaceId);
}
private String callableShapeKey(final PbsAst.FunctionDecl functionDecl) {
private String callableShapeSurface(
final CallableShapeId callableShapeId,
final TypeSurfaceTable typeSurfaceTable,
final CallableShapeTable callableShapeTable) {
final var shape = callableShapeTable.get(callableShapeId);
final var builder = new StringBuilder();
builder.append('(');
for (var i = 0; i < functionDecl.parameters().size(); i++) {
for (var i = 0; i < shape.parameterTypeSurfaces().size(); i++) {
if (i > 0) {
builder.append(',');
}
builder.append(typeKey(functionDecl.parameters().get(i).typeRef()));
builder.append(typeSurfaceTable.get(shape.parameterTypeSurfaces().get(i)).surface());
}
builder.append(")->");
builder.append(outputShapeKey(functionDecl.returnKind(), functionDecl.returnType(), functionDecl.resultErrorType()));
builder.append(typeSurfaceTable.get(shape.outputTypeSurface()).surface());
return builder.toString();
}
private String outputShapeKey(
private String outputShapeSurface(
final PbsAst.ReturnKind returnKind,
final PbsAst.TypeRef returnType,
final PbsAst.TypeRef resultErrorType) {
return switch (returnKind) {
case INFERRED_UNIT, EXPLICIT_UNIT -> "unit";
case PLAIN -> typeKey(returnType);
case RESULT -> "result<" + typeKey(resultErrorType) + ">" + typeKey(returnType);
case PLAIN -> typeSurfaceKey(returnType);
case RESULT -> "result<" + typeSurfaceKey(resultErrorType) + ">" + typeSurfaceKey(returnType);
};
}
private String typeKey(final PbsAst.TypeRef typeRef) {
private String typeSurfaceKey(final PbsAst.TypeRef typeRef) {
final var unwrapped = unwrapGroup(typeRef);
if (unwrapped == null) {
return "unit";
@ -393,11 +418,11 @@ public final class PbsFrontendCompiler {
return switch (unwrapped.kind()) {
case SIMPLE -> "simple:" + unwrapped.name();
case SELF -> "self";
case OPTIONAL -> "optional(" + typeKey(unwrapped.inner()) + ")";
case OPTIONAL -> "optional(" + typeSurfaceKey(unwrapped.inner()) + ")";
case UNIT -> "unit";
case GROUP -> typeKey(unwrapped.inner());
case GROUP -> typeSurfaceKey(unwrapped.inner());
case NAMED_TUPLE -> "tuple(" + unwrapped.fields().stream()
.map(field -> typeKey(field.typeRef()))
.map(field -> typeSurfaceKey(field.typeRef()))
.reduce((left, right) -> left + "," + right)
.orElse("") + ")";
case ERROR -> "error";
@ -681,6 +706,11 @@ public final class PbsFrontendCompiler {
ReadOnlyList<IntrinsicReference> intrinsicPool) {
}
private record CallableResolutionKey(
NameId callableNameId,
int arity) {
}
private static final class ExecutableLoweringAnalysisException extends RuntimeException {
private ExecutableLoweringAnalysisException(final String message) {
super(message);

View File

@ -6,8 +6,12 @@ import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.diagnostics.RelatedSpan;
import p.studio.compiler.source.diagnostics.Severity;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.source.identifiers.CallableShapeId;
import p.studio.compiler.source.identifiers.NameId;
import p.studio.compiler.source.identifiers.TypeSurfaceId;
import p.studio.compiler.source.tables.CallableShapeTable;
import p.studio.compiler.source.tables.NameTable;
import p.studio.compiler.source.tables.TypeSurfaceTable;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList;
@ -29,10 +33,11 @@ public final class PbsModuleVisibilityValidator {
final ReadOnlyList<ModuleUnit> modules,
final NameTable nameTable,
final DiagnosticSink diagnostics) {
final var signatureInterning = new SignatureInterning(new TypeSurfaceTable(), new CallableShapeTable());
final var exportsByModule = new HashMap<ModuleRefKey, ModuleExports>();
for (final var module : modules) {
final var exports = validateModule(module, nameTable, diagnostics);
final var exports = validateModule(module, nameTable, signatureInterning, diagnostics);
exportsByModule.put(toModuleRef(module.coordinates()), exports);
}
@ -44,6 +49,7 @@ public final class PbsModuleVisibilityValidator {
private ModuleExports validateModule(
final ModuleUnit module,
final NameTable nameTable,
final SignatureInterning signatureInterning,
final DiagnosticSink diagnostics) {
final var exports = new ModuleExports();
@ -79,7 +85,7 @@ public final class PbsModuleVisibilityValidator {
}
final var barrel = module.barrelFiles().getFirst();
final var declarations = collectDeclarations(module, nameTable);
final var declarations = collectDeclarations(module, nameTable, signatureInterning);
exports.declaredNameIds.addAll(declarations.declaredNameIds);
final Map<NonFunctionSymbolKey, Span> seenNonFunctionEntries = new HashMap<>();
final Map<FunctionSymbolKey, Span> seenFunctionEntries = new HashMap<>();
@ -92,7 +98,8 @@ public final class PbsModuleVisibilityValidator {
functionItem.returnKind(),
functionItem.returnType(),
functionItem.resultErrorType(),
nameTable);
nameTable,
signatureInterning);
final var firstFunctionEntry = seenFunctionEntries.putIfAbsent(functionKey, functionItem.span());
if (firstFunctionEntry != null) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
@ -231,7 +238,8 @@ public final class PbsModuleVisibilityValidator {
private ModuleDeclarations collectDeclarations(
final ModuleUnit module,
final NameTable nameTable) {
final NameTable nameTable,
final SignatureInterning signatureInterning) {
final var declarations = new ModuleDeclarations();
for (final var sourceFile : module.sourceFiles()) {
@ -243,7 +251,8 @@ public final class PbsModuleVisibilityValidator {
functionDecl.returnKind(),
functionDecl.returnType(),
functionDecl.resultErrorType(),
nameTable);
nameTable,
signatureInterning);
declarations.functionsBySignature
.computeIfAbsent(key, ignored -> new ArrayList<>())
.add(functionDecl.span());
@ -295,37 +304,37 @@ public final class PbsModuleVisibilityValidator {
final PbsAst.ReturnKind returnKind,
final PbsAst.TypeRef returnType,
final PbsAst.TypeRef resultErrorType,
final NameTable nameTable) {
final var signatureSurface = signatureSurfaceKey(parameterTypes, returnKind, returnType, resultErrorType);
return new FunctionSymbolKey(nameTable.register(name), signatureSurface);
final NameTable nameTable,
final SignatureInterning signatureInterning) {
return new FunctionSymbolKey(
nameTable.register(name),
callableShapeId(parameterTypes, returnKind, returnType, resultErrorType, signatureInterning));
}
private String signatureSurfaceKey(
private CallableShapeId callableShapeId(
final List<PbsAst.TypeRef> parameterTypes,
final PbsAst.ReturnKind returnKind,
final PbsAst.TypeRef returnType,
final PbsAst.TypeRef resultErrorType,
final SignatureInterning signatureInterning) {
final var parameterSurfaceIds = new ArrayList<TypeSurfaceId>(parameterTypes.size());
for (final var parameterType : parameterTypes) {
parameterSurfaceIds.add(signatureInterning.typeSurfaces().register(typeSurfaceKey(parameterType)));
}
final var outputSurfaceId = signatureInterning.typeSurfaces().register(outputSurfaceKey(returnKind, returnType, resultErrorType));
return signatureInterning.callableShapes().register(ReadOnlyList.wrap(parameterSurfaceIds), outputSurfaceId);
}
private String outputSurfaceKey(
final PbsAst.ReturnKind returnKind,
final PbsAst.TypeRef returnType,
final PbsAst.TypeRef resultErrorType) {
final var builder = new StringBuilder();
builder.append('(');
for (int i = 0; i < parameterTypes.size(); i++) {
if (i > 0) {
builder.append(',');
}
builder.append(typeSurfaceKey(parameterTypes.get(i)));
}
builder.append(")->");
switch (returnKind) {
case INFERRED_UNIT -> builder.append("infer_unit");
case EXPLICIT_UNIT -> builder.append("unit");
case PLAIN -> builder.append(typeSurfaceKey(returnType));
case RESULT -> builder.append("result<")
.append(typeSurfaceKey(resultErrorType))
.append('>')
.append(typeSurfaceKey(returnType));
}
return builder.toString();
return switch (returnKind) {
case INFERRED_UNIT -> "infer_unit";
case EXPLICIT_UNIT -> "unit";
case PLAIN -> typeSurfaceKey(returnType);
case RESULT -> "result<" + typeSurfaceKey(resultErrorType) + ">" + typeSurfaceKey(returnType);
};
}
private String typeSurfaceKey(final PbsAst.TypeRef typeRef) {
@ -489,7 +498,12 @@ public final class PbsModuleVisibilityValidator {
private record FunctionSymbolKey(
NameId nameId,
String signatureSurface) {
CallableShapeId callableShapeId) {
}
private record SignatureInterning(
TypeSurfaceTable typeSurfaces,
CallableShapeTable callableShapes) {
}
private static final class ModuleDeclarations {

View File

@ -0,0 +1,7 @@
package p.studio.compiler.source.identifiers;
public class CallableShapeId extends SourceIdentifier {
public CallableShapeId(final int id) {
super(id);
}
}

View File

@ -0,0 +1,7 @@
package p.studio.compiler.source.identifiers;
public class TypeSurfaceId extends SourceIdentifier {
public TypeSurfaceId(final int id) {
super(id);
}
}

View File

@ -0,0 +1,15 @@
package p.studio.compiler.source.tables;
import p.studio.compiler.source.identifiers.TypeSurfaceId;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.Objects;
public record CallableShapeRef(
ReadOnlyList<TypeSurfaceId> parameterTypeSurfaces,
TypeSurfaceId outputTypeSurface) {
public CallableShapeRef {
parameterTypeSurfaces = parameterTypeSurfaces == null ? ReadOnlyList.empty() : parameterTypeSurfaces;
outputTypeSurface = Objects.requireNonNull(outputTypeSurface, "outputTypeSurface");
}
}

View File

@ -0,0 +1,18 @@
package p.studio.compiler.source.tables;
import p.studio.compiler.source.identifiers.CallableShapeId;
import p.studio.compiler.source.identifiers.TypeSurfaceId;
import p.studio.utilities.structures.ReadOnlyList;
public class CallableShapeTable extends InternTable<CallableShapeId, CallableShapeRef> {
public CallableShapeTable() {
super(CallableShapeId::new);
}
public CallableShapeId register(
final ReadOnlyList<TypeSurfaceId> parameterTypeSurfaces,
final TypeSurfaceId outputTypeSurface) {
return register(new CallableShapeRef(parameterTypeSurfaces, outputTypeSurface));
}
}

View File

@ -0,0 +1,13 @@
package p.studio.compiler.source.tables;
import java.util.Objects;
public record TypeSurfaceRef(
String surface) {
public TypeSurfaceRef {
surface = Objects.requireNonNull(surface, "surface");
if (surface.isBlank()) {
throw new IllegalArgumentException("surface must not be blank");
}
}
}

View File

@ -0,0 +1,14 @@
package p.studio.compiler.source.tables;
import p.studio.compiler.source.identifiers.TypeSurfaceId;
public class TypeSurfaceTable extends InternTable<TypeSurfaceId, TypeSurfaceRef> {
public TypeSurfaceTable() {
super(TypeSurfaceId::new);
}
public TypeSurfaceId register(final String surface) {
return register(new TypeSurfaceRef(surface));
}
}

View File

@ -0,0 +1,25 @@
package p.studio.compiler.source.tables;
import org.junit.jupiter.api.Test;
import p.studio.utilities.structures.ReadOnlyList;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CallableShapeTableTest {
@Test
void shouldInternEqualShapesToSameIdentifier() {
final var surfaces = new TypeSurfaceTable();
final var shapes = new CallableShapeTable();
final var intType = surfaces.register("simple:int");
final var unitType = surfaces.register("unit");
final var first = shapes.register(ReadOnlyList.from(intType, intType), unitType);
final var second = shapes.register(ReadOnlyList.from(intType, intType), unitType);
final var third = shapes.register(ReadOnlyList.from(intType), unitType);
assertEquals(first, second);
assertEquals(2, shapes.size());
assertEquals(1, shapes.get(third).parameterTypeSurfaces().size());
}
}

View File

@ -0,0 +1,27 @@
package p.studio.compiler.source.tables;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class TypeSurfaceTableTest {
@Test
void shouldInternEqualSurfacesToSameIdentifier() {
final var table = new TypeSurfaceTable();
final var first = table.register("simple:int");
final var second = table.register("simple:int");
final var third = table.register("result<error>simple:int");
assertEquals(first, second);
assertEquals(2, table.size());
assertEquals("result<error>simple:int", table.get(third).surface());
}
@Test
void shouldRejectInvalidSurfaceContract() {
assertThrows(IllegalArgumentException.class, () -> new TypeSurfaceRef(""));
}
}