implements PR-05.1

This commit is contained in:
bQUARKz 2026-03-09 06:50:21 +00:00
parent 4e0f3a3532
commit 541ada32a1
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
2 changed files with 408 additions and 147 deletions

View File

@ -223,97 +223,27 @@ public final class PbsFrontendCompiler {
if (functionCallableId == null) {
continue;
}
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
final var callsites = new ArrayList<PbsAst.CallExpr>();
collectCallsFromBlock(fn.body(), callsites);
for (final var callExpr : callsites) {
final var calleeName = extractSimpleCalleeName(callExpr.callee());
if (calleeName == null || calleeName.isBlank()) {
diagnostics.error(
DiagnosticPhase.STATIC_SEMANTICS,
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
Map.of(),
"executable lowering requires resolvable callee identity",
callExpr.span());
continue;
}
final var host = hostByMethodName.get(calleeName);
if (host != null) {
instructions.add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_HOST,
"",
"",
new IRBackendExecutableFunction.HostCallMetadata(
host.abiModule(),
host.abiMethod(),
host.abiVersion(),
callExpr.arguments().size(),
0),
null,
callExpr.span()));
continue;
}
final var intrinsic = intrinsicByMethodName.get(calleeName);
if (intrinsic != null) {
instructions.add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
"",
"",
null,
new IRBackendExecutableFunction.IntrinsicCallMetadata(
intrinsic.canonicalName(),
intrinsic.canonicalVersion(),
intrinsicIdTable
.register(intrinsic.canonicalName(), intrinsic.canonicalVersion())
),
callExpr.span()));
continue;
}
final var candidateCallableIds = callableIdsByNameAndArity.get(
new CallableResolutionKey(nameTable.register(calleeName), callExpr.arguments().size()));
if (candidateCallableIds == null || candidateCallableIds.isEmpty()) {
diagnostics.error(
DiagnosticPhase.STATIC_SEMANTICS,
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
Map.of(),
"executable lowering requires resolvable callee identity",
callExpr.span());
continue;
}
if (candidateCallableIds.size() > 1) {
diagnostics.error(
DiagnosticPhase.STATIC_SEMANTICS,
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
Map.of(),
"executable lowering found ambiguous callable identity",
callExpr.span());
continue;
}
final var calleeCallableId = candidateCallableIds.getFirst();
final var loweringContext = new ExecutableLoweringContext(
normalizedModuleKey,
diagnostics,
nameTable,
hostByMethodName,
intrinsicByMethodName,
callableIdsByNameAndArity,
returnSlotsByCallableId,
intrinsicIdTable);
final var terminated = lowerBlock(fn.body(), loweringContext);
final var instructions = loweringContext.instructions();
if (!terminated) {
instructions.add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
normalizedModuleKey,
calleeName,
calleeCallableId,
IRBackendExecutableFunction.InstructionKind.RET,
"",
"",
null,
null,
callExpr.arguments().size(),
returnSlotsByCallableId.get(calleeCallableId),
callExpr.span()));
null,
fn.span()));
}
instructions.add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.RET,
"",
"",
null,
null,
null,
fn.span()));
final var returnSlots = returnSlotsFor(fn);
final int maxStackSlots;
@ -449,127 +379,192 @@ public final class PbsFrontendCompiler {
return (int) value;
}
private void collectCallsFromBlock(
private boolean lowerBlock(
final PbsAst.Block block,
final List<PbsAst.CallExpr> output) {
final ExecutableLoweringContext context) {
if (block == null) {
return;
return false;
}
var terminated = false;
for (final var statement : block.statements()) {
collectCallsFromStatement(statement, output);
if (terminated) {
break;
}
terminated = lowerStatement(statement, context);
}
if (block.tailExpression() != null) {
collectCallsFromExpression(block.tailExpression(), output);
if (!terminated && block.tailExpression() != null) {
lowerExpression(block.tailExpression(), context);
}
return terminated;
}
private void collectCallsFromStatement(
private boolean lowerStatement(
final PbsAst.Statement statement,
final List<PbsAst.CallExpr> output) {
final ExecutableLoweringContext context) {
if (statement == null) {
return;
return false;
}
switch (statement) {
case PbsAst.LetStatement letStatement -> collectCallsFromExpression(letStatement.initializer(), output);
case PbsAst.AssignStatement assignStatement -> collectCallsFromExpression(assignStatement.value(), output);
case PbsAst.LetStatement letStatement -> lowerExpression(letStatement.initializer(), context);
case PbsAst.AssignStatement assignStatement -> lowerExpression(assignStatement.value(), context);
case PbsAst.ReturnStatement returnStatement -> {
if (returnStatement.value() != null) {
collectCallsFromExpression(returnStatement.value(), output);
lowerExpression(returnStatement.value(), context);
}
context.instructions().add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.RET,
"",
"",
null,
null,
null,
returnStatement.span()));
return true;
}
case PbsAst.IfStatement ifStatement -> {
collectCallsFromExpression(ifStatement.condition(), output);
collectCallsFromBlock(ifStatement.thenBlock(), output);
lowerExpression(ifStatement.condition(), context);
final var elseLabel = context.nextLabel("if_else");
final var endLabel = context.nextLabel("if_end");
emitJump(IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE, elseLabel, ifStatement.span(), context);
final var thenTerminated = lowerBlock(ifStatement.thenBlock(), context);
if (!thenTerminated) {
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, endLabel, ifStatement.span(), context);
}
emitLabel(elseLabel, ifStatement.span(), context);
final boolean elseTerminated;
if (ifStatement.elseIf() != null) {
collectCallsFromStatement(ifStatement.elseIf(), output);
elseTerminated = lowerStatement(ifStatement.elseIf(), context);
} else if (ifStatement.elseBlock() != null) {
elseTerminated = lowerBlock(ifStatement.elseBlock(), context);
} else {
elseTerminated = false;
}
if (ifStatement.elseBlock() != null) {
collectCallsFromBlock(ifStatement.elseBlock(), output);
if (!thenTerminated || !elseTerminated) {
emitLabel(endLabel, ifStatement.span(), context);
}
return (ifStatement.elseIf() != null || ifStatement.elseBlock() != null)
&& thenTerminated
&& elseTerminated;
}
case PbsAst.ForStatement forStatement -> {
collectCallsFromExpression(forStatement.fromExpression(), output);
collectCallsFromExpression(forStatement.untilExpression(), output);
lowerExpression(forStatement.fromExpression(), context);
final var loopStart = context.nextLabel("for_start");
final var loopExit = context.nextLabel("for_exit");
final var loopContinue = context.nextLabel("for_continue");
emitLabel(loopStart, forStatement.span(), context);
lowerExpression(forStatement.untilExpression(), context);
emitJump(IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE, loopExit, forStatement.span(), context);
context.pushLoop(loopContinue, loopExit);
lowerBlock(forStatement.body(), context);
context.popLoop();
emitLabel(loopContinue, forStatement.span(), context);
if (forStatement.stepExpression() != null) {
collectCallsFromExpression(forStatement.stepExpression(), output);
lowerExpression(forStatement.stepExpression(), context);
}
collectCallsFromBlock(forStatement.body(), output);
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, loopStart, forStatement.span(), context);
emitLabel(loopExit, forStatement.span(), context);
}
case PbsAst.WhileStatement whileStatement -> {
collectCallsFromExpression(whileStatement.condition(), output);
collectCallsFromBlock(whileStatement.body(), output);
final var loopStart = context.nextLabel("while_start");
final var loopExit = context.nextLabel("while_exit");
emitLabel(loopStart, whileStatement.span(), context);
lowerExpression(whileStatement.condition(), context);
emitJump(IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE, loopExit, whileStatement.span(), context);
context.pushLoop(loopStart, loopExit);
lowerBlock(whileStatement.body(), context);
context.popLoop();
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, loopStart, whileStatement.span(), context);
emitLabel(loopExit, whileStatement.span(), context);
}
case PbsAst.ExpressionStatement expressionStatement ->
collectCallsFromExpression(expressionStatement.expression(), output);
case PbsAst.BreakStatement ignored -> {
case PbsAst.ExpressionStatement expressionStatement -> lowerExpression(expressionStatement.expression(), context);
case PbsAst.BreakStatement breakStatement -> {
if (context.loopTargets().isEmpty()) {
reportUnsupportedLowering("break statement outside loop", breakStatement.span(), context);
return true;
}
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, context.loopTargets().peek().breakLabel(), breakStatement.span(), context);
return true;
}
case PbsAst.ContinueStatement ignored -> {
case PbsAst.ContinueStatement continueStatement -> {
if (context.loopTargets().isEmpty()) {
reportUnsupportedLowering("continue statement outside loop", continueStatement.span(), context);
return true;
}
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, context.loopTargets().peek().continueLabel(), continueStatement.span(), context);
return true;
}
}
return false;
}
private void collectCallsFromExpression(
private void lowerExpression(
final PbsAst.Expression expression,
final List<PbsAst.CallExpr> output) {
final ExecutableLoweringContext context) {
if (expression == null) {
return;
}
switch (expression) {
case PbsAst.CallExpr callExpr -> {
output.add(callExpr);
collectCallsFromExpression(callExpr.callee(), output);
lowerExpression(callExpr.callee(), context);
for (final var arg : callExpr.arguments()) {
collectCallsFromExpression(arg, output);
lowerExpression(arg, context);
}
lowerCallsite(callExpr, context);
}
case PbsAst.ApplyExpr applyExpr -> {
collectCallsFromExpression(applyExpr.callee(), output);
collectCallsFromExpression(applyExpr.argument(), output);
lowerExpression(applyExpr.callee(), context);
lowerExpression(applyExpr.argument(), context);
}
case PbsAst.BinaryExpr binaryExpr -> {
collectCallsFromExpression(binaryExpr.left(), output);
collectCallsFromExpression(binaryExpr.right(), output);
lowerExpression(binaryExpr.left(), context);
lowerExpression(binaryExpr.right(), context);
}
case PbsAst.UnaryExpr unaryExpr -> collectCallsFromExpression(unaryExpr.expression(), output);
case PbsAst.UnaryExpr unaryExpr -> lowerExpression(unaryExpr.expression(), context);
case PbsAst.ElseExpr elseExpr -> {
collectCallsFromExpression(elseExpr.optionalExpression(), output);
collectCallsFromExpression(elseExpr.fallbackExpression(), output);
lowerExpression(elseExpr.optionalExpression(), context);
lowerExpression(elseExpr.fallbackExpression(), context);
}
case PbsAst.IfExpr ifExpr -> {
collectCallsFromExpression(ifExpr.condition(), output);
collectCallsFromBlock(ifExpr.thenBlock(), output);
collectCallsFromExpression(ifExpr.elseExpression(), output);
lowerExpression(ifExpr.condition(), context);
final var elseLabel = context.nextLabel("ifexpr_else");
final var endLabel = context.nextLabel("ifexpr_end");
emitJump(IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE, elseLabel, ifExpr.span(), context);
final var thenTerminated = lowerBlock(ifExpr.thenBlock(), context);
if (!thenTerminated) {
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, endLabel, ifExpr.span(), context);
}
emitLabel(elseLabel, ifExpr.span(), context);
lowerExpression(ifExpr.elseExpression(), context);
if (!thenTerminated) {
emitLabel(endLabel, ifExpr.span(), context);
}
}
case PbsAst.SwitchExpr switchExpr -> {
collectCallsFromExpression(switchExpr.selector(), output);
for (final var arm : switchExpr.arms()) {
collectCallsFromBlock(arm.block(), output);
}
lowerExpression(switchExpr.selector(), context);
reportUnsupportedLowering("switch expression is not lowerable in executable lowering v1", switchExpr.span(), context);
}
case PbsAst.HandleExpr handleExpr -> {
collectCallsFromExpression(handleExpr.value(), output);
for (final var arm : handleExpr.arms()) {
collectCallsFromBlock(arm.block(), output);
}
lowerExpression(handleExpr.value(), context);
reportUnsupportedLowering("handle expression is not lowerable in executable lowering v1", handleExpr.span(), context);
}
case PbsAst.AsExpr asExpr -> collectCallsFromExpression(asExpr.expression(), output);
case PbsAst.MemberExpr memberExpr -> collectCallsFromExpression(memberExpr.receiver(), output);
case PbsAst.PropagateExpr propagateExpr -> collectCallsFromExpression(propagateExpr.expression(), output);
case PbsAst.GroupExpr groupExpr -> collectCallsFromExpression(groupExpr.expression(), output);
case PbsAst.AsExpr asExpr -> lowerExpression(asExpr.expression(), context);
case PbsAst.MemberExpr memberExpr -> lowerExpression(memberExpr.receiver(), context);
case PbsAst.PropagateExpr propagateExpr -> lowerExpression(propagateExpr.expression(), context);
case PbsAst.GroupExpr groupExpr -> lowerExpression(groupExpr.expression(), context);
case PbsAst.NewExpr newExpr -> {
for (final var arg : newExpr.arguments()) {
collectCallsFromExpression(arg, output);
lowerExpression(arg, context);
}
}
case PbsAst.BindExpr bindExpr -> collectCallsFromExpression(bindExpr.contextExpression(), output);
case PbsAst.SomeExpr someExpr -> collectCallsFromExpression(someExpr.value(), output);
case PbsAst.OkExpr okExpr -> collectCallsFromExpression(okExpr.value(), output);
case PbsAst.BindExpr bindExpr -> lowerExpression(bindExpr.contextExpression(), context);
case PbsAst.SomeExpr someExpr -> lowerExpression(someExpr.value(), context);
case PbsAst.OkExpr okExpr -> lowerExpression(okExpr.value(), context);
case PbsAst.TupleExpr tupleExpr -> {
for (final var item : tupleExpr.items()) {
collectCallsFromExpression(item.expression(), output);
lowerExpression(item.expression(), context);
}
}
case PbsAst.BlockExpr blockExpr -> collectCallsFromBlock(blockExpr.block(), output);
case PbsAst.BlockExpr blockExpr -> lowerBlock(blockExpr.block(), context);
case PbsAst.IdentifierExpr ignored -> {
}
case PbsAst.IntLiteralExpr ignored -> {
@ -593,6 +588,119 @@ public final class PbsFrontendCompiler {
}
}
private void lowerCallsite(
final PbsAst.CallExpr callExpr,
final ExecutableLoweringContext context) {
final var calleeName = extractSimpleCalleeName(callExpr.callee());
if (calleeName == null || calleeName.isBlank()) {
reportUnsupportedLowering("executable lowering requires resolvable callee identity", callExpr.span(), context);
return;
}
final var host = context.hostByMethodName().get(calleeName);
if (host != null) {
context.instructions().add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_HOST,
"",
"",
new IRBackendExecutableFunction.HostCallMetadata(
host.abiModule(),
host.abiMethod(),
host.abiVersion(),
callExpr.arguments().size(),
0),
null,
callExpr.span()));
return;
}
final var intrinsic = context.intrinsicByMethodName().get(calleeName);
if (intrinsic != null) {
context.instructions().add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
"",
"",
null,
new IRBackendExecutableFunction.IntrinsicCallMetadata(
intrinsic.canonicalName(),
intrinsic.canonicalVersion(),
context.intrinsicIdTable().register(intrinsic.canonicalName(), intrinsic.canonicalVersion())),
callExpr.span()));
return;
}
final var candidateCallableIds = context.callableIdsByNameAndArity().get(
new CallableResolutionKey(context.nameTable().register(calleeName), callExpr.arguments().size()));
if (candidateCallableIds == null || candidateCallableIds.isEmpty()) {
reportUnsupportedLowering("executable lowering requires resolvable callee identity", callExpr.span(), context);
return;
}
if (candidateCallableIds.size() > 1) {
reportUnsupportedLowering("executable lowering found ambiguous callable identity", callExpr.span(), context);
return;
}
final var calleeCallableId = candidateCallableIds.getFirst();
context.instructions().add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
context.moduleKey(),
calleeName,
calleeCallableId,
null,
null,
callExpr.arguments().size(),
context.returnSlotsByCallableId().get(calleeCallableId),
callExpr.span()));
}
private void emitJump(
final IRBackendExecutableFunction.InstructionKind jumpKind,
final String targetLabel,
final p.studio.compiler.source.Span span,
final ExecutableLoweringContext context) {
context.instructions().add(new IRBackendExecutableFunction.Instruction(
jumpKind,
"",
"",
null,
null,
null,
"",
targetLabel,
null,
null,
span));
}
private void emitLabel(
final String label,
final p.studio.compiler.source.Span span,
final ExecutableLoweringContext context) {
context.instructions().add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.LABEL,
"",
"",
null,
null,
null,
label,
"",
null,
null,
span));
}
private void reportUnsupportedLowering(
final String message,
final p.studio.compiler.source.Span span,
final ExecutableLoweringContext context) {
context.diagnostics().error(
DiagnosticPhase.STATIC_SEMANTICS,
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
Map.of(),
message,
span);
}
private String extractSimpleCalleeName(final PbsAst.Expression callee) {
return switch (callee) {
case PbsAst.IdentifierExpr identifierExpr -> identifierExpr.name();
@ -700,6 +808,98 @@ public final class PbsFrontendCompiler {
}
}
private static final class ExecutableLoweringContext {
private final String moduleKey;
private final DiagnosticSink diagnostics;
private final NameTable nameTable;
private final Map<String, IRReservedMetadata.HostMethodBinding> hostByMethodName;
private final Map<String, IRReservedMetadata.IntrinsicSurface> intrinsicByMethodName;
private final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity;
private final Map<CallableId, Integer> returnSlotsByCallableId;
private final IntrinsicTable intrinsicIdTable;
private final ArrayList<IRBackendExecutableFunction.Instruction> instructions = new ArrayList<>();
private final ArrayDeque<LoopTargets> loopTargets = new ArrayDeque<>();
private int nextLabelId = 0;
private ExecutableLoweringContext(
final String moduleKey,
final DiagnosticSink diagnostics,
final NameTable nameTable,
final Map<String, IRReservedMetadata.HostMethodBinding> hostByMethodName,
final Map<String, IRReservedMetadata.IntrinsicSurface> intrinsicByMethodName,
final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
final Map<CallableId, Integer> returnSlotsByCallableId,
final IntrinsicTable intrinsicIdTable) {
this.moduleKey = moduleKey;
this.diagnostics = diagnostics;
this.nameTable = nameTable;
this.hostByMethodName = hostByMethodName;
this.intrinsicByMethodName = intrinsicByMethodName;
this.callableIdsByNameAndArity = callableIdsByNameAndArity;
this.returnSlotsByCallableId = returnSlotsByCallableId;
this.intrinsicIdTable = intrinsicIdTable;
}
private String moduleKey() {
return moduleKey;
}
private DiagnosticSink diagnostics() {
return diagnostics;
}
private NameTable nameTable() {
return nameTable;
}
private Map<String, IRReservedMetadata.HostMethodBinding> hostByMethodName() {
return hostByMethodName;
}
private Map<String, IRReservedMetadata.IntrinsicSurface> intrinsicByMethodName() {
return intrinsicByMethodName;
}
private Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity() {
return callableIdsByNameAndArity;
}
private Map<CallableId, Integer> returnSlotsByCallableId() {
return returnSlotsByCallableId;
}
private IntrinsicTable intrinsicIdTable() {
return intrinsicIdTable;
}
private ArrayList<IRBackendExecutableFunction.Instruction> instructions() {
return instructions;
}
private ArrayDeque<LoopTargets> loopTargets() {
return loopTargets;
}
private void pushLoop(
final String continueLabel,
final String breakLabel) {
loopTargets.push(new LoopTargets(continueLabel, breakLabel));
}
private void popLoop() {
loopTargets.pop();
}
private String nextLabel(final String prefix) {
return "__" + prefix + "_" + nextLabelId++;
}
}
private record LoopTargets(
String continueLabel,
String breakLabel) {
}
private record ExecutableLoweringResult(
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
ReadOnlyList<CallableSignatureRef> callableSignatures,

View File

@ -123,6 +123,67 @@ class PbsFrontendCompilerTest {
assertEquals("c", executableA.instructions().get(1).calleeCallableName());
}
@Test
void shouldStopLoweringReachablePathAfterReturn() {
final var source = """
fn a() -> int { return 1; }
fn b() -> int { return 2; }
fn main() -> int {
return a();
b();
}
""";
final var diagnostics = DiagnosticSink.empty();
final var compiler = new PbsFrontendCompiler();
final var fileBackend = compiler.compileFile(new FileId(100), 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();
assertEquals(2, executableMain.instructions().size());
assertEquals("a", executableMain.instructions().get(0).calleeCallableName());
assertEquals(p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.RET, executableMain.instructions().get(1).kind());
}
@Test
void shouldEmitControlFlowInstructionsForIfAndWhile() {
final var source = """
fn cond() -> bool { return true; }
fn t() -> int { return 1; }
fn f() -> int { return 2; }
fn loopBody() -> void { return; }
fn main() -> int {
while cond() {
break;
}
if cond() {
return t();
} else {
return f();
}
}
""";
final var diagnostics = DiagnosticSink.empty();
final var compiler = new PbsFrontendCompiler();
final var fileBackend = compiler.compileFile(new FileId(101), 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();
assertTrue(executableMain.instructions().stream().anyMatch(i ->
i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.LABEL));
assertTrue(executableMain.instructions().stream().anyMatch(i ->
i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.JMP));
assertTrue(executableMain.instructions().stream().anyMatch(i ->
i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE));
}
@Test
void shouldNotLowerWhenSyntaxErrorsExist() {
final var source = """