Skip to content

Commit ebe2dcd

Browse files
examples and changes to use refinements in if conds
1 parent cd523df commit ebe2dcd

5 files changed

Lines changed: 127 additions & 7 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package testSuite.classes.state_test_method_correct;
2+
3+
import liquidjava.specification.ExternalRefinementsFor;
4+
import liquidjava.specification.Refinement;
5+
import liquidjava.specification.StateRefinement;
6+
import liquidjava.specification.StateSet;
7+
8+
@StateSet({"aliveDone", "aliveNotDone", "notAlive"})
9+
@ExternalRefinementsFor("javax.swing.undo.AbstractUndoableEdit")
10+
public interface AbstractUndoableEditRefinements {
11+
12+
@StateRefinement(to = "aliveDone(this)")
13+
void AbstractUndoableEdit();
14+
15+
@StateRefinement(from = "aliveNotDone(this)", to = "aliveDone(this)")
16+
void redo();
17+
18+
@StateRefinement(from = "aliveDone(this)", to = "aliveNotDone(this)")
19+
void undo();
20+
21+
@StateRefinement(from = "!notAlive(this)", to = "notAlive(this)")
22+
void die();
23+
24+
@Refinement("_ == true --> aliveDone(this)")
25+
boolean canUndo();
26+
27+
@Refinement("_ == true --> aliveNotDone(this)")
28+
boolean canRedo();
29+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package testSuite.classes.state_test_method_correct;
2+
3+
import javax.swing.undo.AbstractUndoableEdit;
4+
5+
public class EditCycler {
6+
public static void undoIfPossible(AbstractUndoableEdit edit) {
7+
if (edit.canUndo()) {
8+
edit.undo();
9+
}
10+
}
11+
12+
public static void redoIfPossible(AbstractUndoableEdit edit) {
13+
if (edit.canRedo()) {
14+
edit.redo();
15+
}
16+
}
17+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package testSuite.classes.state_test_method_error;
2+
3+
import liquidjava.specification.ExternalRefinementsFor;
4+
import liquidjava.specification.Refinement;
5+
import liquidjava.specification.StateRefinement;
6+
import liquidjava.specification.StateSet;
7+
8+
@StateSet({"aliveDone", "aliveNotDone", "notAlive"})
9+
@ExternalRefinementsFor("javax.swing.undo.AbstractUndoableEdit")
10+
public interface AbstractUndoableEditRefinements {
11+
12+
@StateRefinement(to = "aliveDone(this)")
13+
void AbstractUndoableEdit();
14+
15+
@StateRefinement(from = "aliveNotDone(this)", to = "aliveDone(this)")
16+
void redo();
17+
18+
@StateRefinement(from = "aliveDone(this)", to = "aliveNotDone(this)")
19+
void undo();
20+
21+
@Refinement("_ == true --> aliveDone(this)")
22+
boolean canUndo();
23+
24+
@Refinement("_ == true --> aliveNotDone(this)")
25+
boolean canRedo();
26+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package testSuite.classes.state_test_method_error;
2+
3+
import javax.swing.undo.AbstractUndoableEdit;
4+
5+
public class EditMisuse {
6+
7+
public static void undoInElseBranch(AbstractUndoableEdit edit) {
8+
if (edit.canUndo()) {
9+
} else {
10+
edit.undo(); // State Refinement Error
11+
}
12+
}
13+
14+
public static void undoNotInElse(AbstractUndoableEdit edit) {
15+
if (!edit.canUndo()) {
16+
edit.undo(); // State Refinement Error
17+
}
18+
}
19+
public static void wrongTesterForRedo(AbstractUndoableEdit edit) {
20+
if (edit.canUndo()) {
21+
edit.redo(); // State Refinement Error
22+
}
23+
}
24+
}

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

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -343,24 +343,47 @@ public void visitCtIf(CtIf ifElement) {
343343

344344
String pathVarName = String.format(Formats.FRESH, context.getCounter());
345345
RefinedVariable freshRV;
346+
// The condition's predicate may use Keys.WILDCARD as a stand-in for the boolean value of the condition
347+
// (e.g. invocation post-conditions of the form `_ == true --> state(this)`, or variable reads `_ == k`).
348+
// When that's the case the fresh path variable IS that boolean value, so we assert it true in the then
349+
// branch and false in the else branch — instead of negating the whole predicate, which is unsound for
350+
// implications and equality forms.
351+
boolean valueIsCondition = false;
352+
Predicate thenRefs;
353+
Predicate elseRefs;
346354
if (isUninformativeCondition(expRefs, exp)) {
347355
// No refinement means the condition is unknown, not true: model it as a fresh
348356
// boolean so the SMT solver may pick either truth value for each branch.
349357
expRefs = Predicate.createVar(pathVarName);
358+
thenRefs = expRefs;
359+
elseRefs = expRefs.negate();
350360
freshRV = context.addInstanceToContext(pathVarName, factory.Type().BOOLEAN_PRIMITIVE, new Predicate(), exp);
351361
} else {
362+
valueIsCondition = expRefs.getVariableNames().contains(Keys.WILDCARD);
352363
expRefs = expRefs.substituteVariable(Keys.WILDCARD, pathVarName);
353364
Predicate lastExpRefs = substituteAllVariablesForLastInstance(expRefs);
354365
expRefs = Predicate.createConjunction(expRefs, lastExpRefs);
355366

356-
freshRV = context.addInstanceToContext(pathVarName, factory.Type().INTEGER_PRIMITIVE, expRefs, exp);
357-
}
367+
// TODO Change in future
368+
if (expRefs.getVariableNames().contains("null")) {
369+
expRefs = new Predicate();
370+
valueIsCondition = false;
371+
}
358372

359-
// TODO Change in future
360-
if (expRefs.getVariableNames().contains("null")) {
361-
expRefs = new Predicate();
373+
thenRefs = expRefs;
374+
elseRefs = expRefs.negate();
375+
if (valueIsCondition) {
376+
Predicate freshIsTrue = Predicate.createEquals(Predicate.createVar(pathVarName),
377+
Predicate.createLit("true", Types.BOOLEAN));
378+
Predicate freshIsFalse = Predicate.createEquals(Predicate.createVar(pathVarName),
379+
Predicate.createLit("false", Types.BOOLEAN));
380+
thenRefs = Predicate.createConjunction(expRefs, freshIsTrue);
381+
elseRefs = Predicate.createConjunction(expRefs, freshIsFalse);
382+
freshRV = context.addInstanceToContext(pathVarName, factory.Type().BOOLEAN_PRIMITIVE, thenRefs, exp);
383+
} else {
384+
freshRV = context.addInstanceToContext(pathVarName, factory.Type().INTEGER_PRIMITIVE, expRefs, exp);
385+
}
362386
}
363-
364387
vcChecker.addPathVariable(freshRV);
365388

366389
context.variablesNewIfCombination();
@@ -378,7 +401,8 @@ public void visitCtIf(CtIf ifElement) {
378401

379402
// VISIT ELSE
380403
if (ifElement.getElseStatement() != null) {
381-
context.newRefinementToVariableInContext(pathVarName, expRefs.negate());
404+
context.getVariableByName(pathVarName);
405+
context.newRefinementToVariableInContext(pathVarName, elseRefs);
382406

383407
context.enterContext();
384408
visitCtBlock(ifElement.getElseStatement());

0 commit comments

Comments
 (0)