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 375dbdc4..1f68eeef 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 @@ -158,6 +158,17 @@ public final class PbsFrontendCompiler { intrinsicByMethodName.put(intrinsicSurface.sourceMethodName(), intrinsicSurface); } } + final var returnSlotsByCallableAndArity = new HashMap(); + for (final var declaredFn : ast.functions()) { + final var key = callableArityKey(declaredFn.name(), declaredFn.parameters().size()); + final var retSlots = returnSlotsFor(declaredFn); + if (returnSlotsByCallableAndArity.containsKey(key) + && !returnSlotsByCallableAndArity.get(key).equals(retSlots)) { + returnSlotsByCallableAndArity.put(key, null); + } else { + returnSlotsByCallableAndArity.put(key, retSlots); + } + } final var executableFunctions = new ArrayList(ast.functions().size()); for (final var fn : ast.functions()) { @@ -221,6 +232,8 @@ public final class PbsFrontendCompiler { calleeName, null, null, + callExpr.arguments().size(), + returnSlotsByCallableAndArity.get(callableArityKey(calleeName, callExpr.arguments().size())), callExpr.span())); } instructions.add(new IRBackendExecutableFunction.Instruction( @@ -231,10 +244,7 @@ public final class PbsFrontendCompiler { null, fn.span())); - final var returnSlots = switch (fn.returnKind()) { - case INFERRED_UNIT, EXPLICIT_UNIT -> 0; - case PLAIN, RESULT -> 1; - }; + final var returnSlots = returnSlotsFor(fn); final var start = safeToInt(fn.span().getStart()); final var end = safeToInt(fn.span().getEnd()); executableFunctions.add(new IRBackendExecutableFunction( @@ -259,6 +269,19 @@ public final class PbsFrontendCompiler { return (canonicalName + "#" + canonicalVersion).hashCode(); } + private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) { + return switch (functionDecl.returnKind()) { + case INFERRED_UNIT, EXPLICIT_UNIT -> 0; + case PLAIN, RESULT -> 1; + }; + } + + private String callableArityKey( + final String callableName, + final int arity) { + return callableName + "#" + arity; + } + private int safeToInt(final long value) { if (value > Integer.MAX_VALUE) { return Integer.MAX_VALUE; diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMLoweringErrorCode.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMLoweringErrorCode.java index c27ac894..c5b977d3 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMLoweringErrorCode.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMLoweringErrorCode.java @@ -3,5 +3,6 @@ package p.studio.compiler.backend.irvm; public enum IRVMLoweringErrorCode { LOWER_IRVM_EMPTY_EXECUTABLE_INPUT, LOWER_IRVM_MISSING_CALLEE, + LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH, + LOWER_IRVM_CALL_RET_SLOTS_MISMATCH, } - diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/LowerToIRVMService.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/LowerToIRVMService.java index 2f9e23d4..327ed287 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/LowerToIRVMService.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/LowerToIRVMService.java @@ -65,6 +65,25 @@ public class LowerToIRVMService { IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE, "missing callee function: " + key); } + final var calleeFunction = ordered.get(calleeId); + if (instr.expectedArgSlots() != null + && instr.expectedArgSlots() != calleeFunction.paramSlots()) { + throw new IRVMLoweringException( + IRVMLoweringErrorCode.LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH, + "call arg_slots mismatch for %s. expected=%d actual=%d".formatted( + key, + instr.expectedArgSlots(), + calleeFunction.paramSlots())); + } + if (instr.expectedRetSlots() != null + && instr.expectedRetSlots() != calleeFunction.returnSlots()) { + throw new IRVMLoweringException( + IRVMLoweringErrorCode.LOWER_IRVM_CALL_RET_SLOTS_MISMATCH, + "call ret_slots mismatch for %s. expected=%d actual=%d".formatted( + key, + instr.expectedRetSlots(), + calleeFunction.returnSlots())); + } instructions.add(new IRVMInstruction(IRVMOp.CALL, calleeId)); operations.add(BytecodeEmitter.Operation.callFunc(calleeId)); } @@ -142,4 +161,3 @@ public class LowerToIRVMService { return (moduleKey == null ? "" : moduleKey) + "::" + callableName; } } - diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java index 20e887ac..dae3f3f3 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java @@ -79,6 +79,20 @@ class LowerToIRVMServiceTest { assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE, thrown.code()); } + @Test + void lowerMustRejectCallArgSlotMismatch() { + final var backend = IRBackend.builder() + .executableFunctions(ReadOnlyList.from( + fn("callee", "app", ReadOnlyList.from(ret())), + fn("main", "app", ReadOnlyList.from( + callFuncWithExpected("app", "callee", 2, 0), + ret())))) + .build(); + + final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend)); + assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH, thrown.code()); + } + private static IRBackendExecutableFunction fn( final String name, final String moduleKey, @@ -117,6 +131,22 @@ class LowerToIRVMServiceTest { Span.none()); } + private static IRBackendExecutableFunction.Instruction callFuncWithExpected( + final String moduleKey, + final String name, + final int expectedArgSlots, + final int expectedRetSlots) { + return new IRBackendExecutableFunction.Instruction( + IRBackendExecutableFunction.InstructionKind.CALL_FUNC, + moduleKey, + name, + null, + null, + expectedArgSlots, + expectedRetSlots, + Span.none()); + } + private static IRBackendExecutableFunction.Instruction callHost( final String module, final String name, @@ -145,4 +175,3 @@ class LowerToIRVMServiceTest { Span.none()); } } -