Skip to content

Commit 9a1ebae

Browse files
committed
Fix Refinements After Early Return
1 parent d91f256 commit 9a1ebae

2 files changed

Lines changed: 26 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: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -409,30 +409,35 @@ public void visitCtIf(CtIf ifElement) {
409409
// VISIT THEN
410410
context.enterContext();
411411
visitCtBlock(ifElement.getThenStatement());
412-
if (canCompleteNormally(ifElement.getThenStatement())) {
412+
boolean thenCompletes = canCompleteNormally(ifElement.getThenStatement());
413+
if (thenCompletes) {
413414
context.variablesSetThenIf();
414415
}
415416
contextHistory.saveContext(ifElement.getThenStatement(), context);
416417
context.exitContext();
417418

418419
// VISIT ELSE
420+
boolean elseCompletes = true;
419421
if (ifElement.getElseStatement() != null) {
420422
context.getVariableByName(pathVarName);
421423
context.newRefinementToVariableInContext(pathVarName, elseRefs);
422424

423425
context.enterContext();
424426
visitCtBlock(ifElement.getElseStatement());
425-
if (canCompleteNormally(ifElement.getElseStatement())) {
427+
elseCompletes = canCompleteNormally(ifElement.getElseStatement());
428+
if (elseCompletes) {
426429
context.variablesSetElseIf();
427430
}
428431
contextHistory.saveContext(ifElement.getElseStatement(), context);
429432
context.exitContext();
430433
}
431434
// end
432-
// Reset the path variable's refinement to the original condition after the if,
433-
// so branch-local truth assertions (and any typestate they imply) don't leak past the join.
434-
context.newRefinementToVariableInContext(pathVarName, expRefs);
435-
vcChecker.removePathVariable(freshRV);
435+
if (thenCompletes == elseCompletes) {
436+
context.newRefinementToVariableInContext(pathVarName, expRefs);
437+
vcChecker.removePathVariable(freshRV);
438+
} else {
439+
context.newRefinementToVariableInContext(pathVarName, thenCompletes ? thenRefs : elseRefs);
440+
}
436441
context.exitContext();
437442
context.variablesCombineFromIf(expRefs);
438443
context.variablesFinishIfCombination();

0 commit comments

Comments
 (0)