Skip to content

Commit db07738

Browse files
authored
Fix Refinements After Early Return (#277)
1 parent d5ae6be commit db07738

3 files changed

Lines changed: 35 additions & 6 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.Refinement;
4+
5+
public class CorrectEarlyReturn {
6+
7+
public static int divide(int a, @Refinement("b != 0") int b) {
8+
return a / b;
9+
}
10+
11+
public static void divideUnlessZero(int x, int y) {
12+
if (y == 0) return;
13+
divide(x, y);
14+
}
15+
}

liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/RefinementTypeChecker.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public <T> void visitCtConstructor(CtConstructor<T> constructor) {
115115
}
116116
contextHistory.saveContext(constructor, context);
117117
context.exitContext();
118+
vcChecker.clearPathVariables();
118119
}
119120

120121
public <R> void visitCtMethod(CtMethod<R> method) {
@@ -130,6 +131,7 @@ public <R> void visitCtMethod(CtMethod<R> method) {
130131
}
131132
contextHistory.saveContext(method, context);
132133
context.exitContext();
134+
vcChecker.clearPathVariables();
133135
}
134136

135137
@Override
@@ -411,30 +413,38 @@ public void visitCtIf(CtIf ifElement) {
411413
// VISIT THEN
412414
context.enterContext();
413415
visitCtBlock(ifElement.getThenStatement());
414-
if (canCompleteNormally(ifElement.getThenStatement())) {
416+
boolean thenCompletes = canCompleteNormally(ifElement.getThenStatement());
417+
if (thenCompletes) {
415418
context.variablesSetThenIf();
416419
}
417420
contextHistory.saveContext(ifElement.getThenStatement(), context);
418421
context.exitContext();
419422

420423
// VISIT ELSE
424+
boolean elseCompletes = true;
421425
if (ifElement.getElseStatement() != null) {
422426
context.getVariableByName(pathVarName);
423427
context.newRefinementToVariableInContext(pathVarName, elseRefs);
424428

425429
context.enterContext();
426430
visitCtBlock(ifElement.getElseStatement());
427-
if (canCompleteNormally(ifElement.getElseStatement())) {
431+
elseCompletes = canCompleteNormally(ifElement.getElseStatement());
432+
if (elseCompletes) {
428433
context.variablesSetElseIf();
429434
}
430435
contextHistory.saveContext(ifElement.getElseStatement(), context);
431436
context.exitContext();
432437
}
433438
// end
434-
// Reset the path variable's refinement to the original condition after the if,
435-
// so branch-local truth assertions (and any typestate they imply) don't leak past the join.
436-
context.newRefinementToVariableInContext(pathVarName, expRefs);
437-
vcChecker.removePathVariable(freshRV);
439+
if (thenCompletes == elseCompletes) {
440+
// Reset the path variable's refinement to the original condition after the if,
441+
// so branch-local truth assertions (and any typestate they imply) don't leak past the join.
442+
context.newRefinementToVariableInContext(pathVarName, expRefs);
443+
vcChecker.removePathVariable(freshRV);
444+
} else {
445+
// Keep the refinement of the only branch that reaches the code after the if.
446+
context.newRefinementToVariableInContext(pathVarName, thenCompletes ? thenRefs : elseRefs);
447+
}
438448
context.exitContext();
439449
context.variablesCombineFromIf(expRefs);
440450
context.variablesFinishIfCombination();

liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/VCChecker.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,10 @@ public void removePathVariable(RefinedVariable rv) {
353353
pathVariables.remove(rv);
354354
}
355355

356+
void clearPathVariables() {
357+
pathVariables.clear();
358+
}
359+
356360
void removePathVariableThatIncludes(String otherVar) {
357361
pathVariables.stream().filter(rv -> rv.getRefinement().getVariableNames().contains(otherVar)).toList()
358362
.forEach(pathVariables::remove);

0 commit comments

Comments
 (0)