Skip to content

Commit 9c54833

Browse files
committed
Replace SimplifiedPredicate with SimplifiedVCImplication
1 parent c42edc8 commit 9c54833

6 files changed

Lines changed: 119 additions & 189 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package liquidjava.processor;
2+
3+
import java.util.Objects;
4+
5+
import liquidjava.rj_language.Predicate;
6+
7+
/**
8+
* Represents a VC implication node whose refinement was simplified from another quantified VC shape.
9+
*/
10+
public class SimplifiedVCImplication extends VCImplication {
11+
12+
private final VCImplication origin;
13+
14+
public SimplifiedVCImplication(VCImplication implication, Predicate refinement, VCImplication origin) {
15+
super(implication, refinement);
16+
this.origin = origin.clone();
17+
}
18+
19+
public VCImplication getOrigin() {
20+
return origin;
21+
}
22+
23+
@Override
24+
public VCImplication copyWithRefinement(Predicate refinement) {
25+
return new SimplifiedVCImplication(this, refinement, origin);
26+
}
27+
28+
@Override
29+
public SimplifiedVCImplication clone() {
30+
SimplifiedVCImplication vc = new SimplifiedVCImplication(this, this.refinement.clone(), origin);
31+
if (this.next != null)
32+
vc.next = this.next.clone();
33+
return vc;
34+
}
35+
36+
@Override
37+
public int hashCode() {
38+
return Objects.hash(super.hashCode(), origin);
39+
}
40+
41+
@Override
42+
public boolean equals(Object obj) {
43+
if (this == obj)
44+
return true;
45+
if (!(obj instanceof SimplifiedVCImplication))
46+
return false;
47+
if (!super.equals(obj))
48+
return false;
49+
SimplifiedVCImplication other = (SimplifiedVCImplication) obj;
50+
return origin.equals(other.origin);
51+
}
52+
}

liquidjava-verifier/src/main/java/liquidjava/processor/VCImplication.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public VCImplication(VCImplication implication, Predicate ref) {
3131
this.refinement = ref;
3232
}
3333

34+
public VCImplication copyWithRefinement(Predicate refinement) {
35+
return new VCImplication(this, refinement);
36+
}
37+
3438
public void setNext(VCImplication c) {
3539
next = c;
3640
}

liquidjava-verifier/src/main/java/liquidjava/rj_language/SimplifiedPredicate.java

Lines changed: 0 additions & 114 deletions
This file was deleted.

liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/VCSubstitution.java

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
import java.util.List;
55
import java.util.Optional;
66

7+
import liquidjava.processor.SimplifiedVCImplication;
78
import liquidjava.processor.VCImplication;
89
import liquidjava.rj_language.Predicate;
9-
import liquidjava.rj_language.SimplifiedPredicate;
10-
import liquidjava.rj_language.SimplifiedPredicate.Binder;
1110
import liquidjava.rj_language.ast.BinaryExpression;
1211
import liquidjava.rj_language.ast.Expression;
1312
import liquidjava.rj_language.ast.Var;
@@ -20,7 +19,7 @@ public class VCSubstitution {
2019
/**
2120
* A substitution discovered from an implication node
2221
*/
23-
private record Substitution(VCImplication source, Expression value) {
22+
private record Substitution(VCImplication node, Expression replacement) {
2423
}
2524

2625
/**
@@ -36,50 +35,47 @@ public static VCImplication applyOnce(VCImplication implication) {
3635
// apply only the first available substitution
3736
if (substitutionOpt.isPresent()) {
3837
VCSubstitution.Substitution substitution = substitutionOpt.get();
39-
result = VCSubstitution.substitute(result, substitution.source(), substitution.value());
38+
result = VCSubstitution.substitute(result, substitution.node(), substitution.replacement());
4039
}
4140
return result;
4241
}
4342

4443
/**
4544
* Rewrites one VC chain with a single substitution and removes its source node
4645
*/
47-
private static VCImplication substitute(VCImplication implication, VCImplication source, Expression value) {
46+
private static VCImplication substitute(VCImplication implication, VCImplication node, Expression replacement) {
4847
if (implication == null)
4948
return null;
5049

5150
// skip the source node to remove it from the chain and start substitution from the next node
52-
if (implication == source)
53-
return substitute(implication.getNext(), source, value);
51+
if (implication == node)
52+
return substitute(implication.getNext(), node, replacement);
5453

55-
Predicate refinement = substituteRefinement(implication.getRefinement(), source, value);
56-
VCImplication result = new VCImplication(implication, refinement);
57-
result.setNext(substitute(implication.getNext(), source, value));
54+
VCImplication result = substituteNode(implication, node, replacement);
55+
result.setNext(substitute(implication.getNext(), node, replacement));
5856
return result;
5957
}
6058

6159
/**
62-
* Substitutes a source binder inside one predicate while preserving simplification metadata
60+
* Substitutes a source binder inside one VC node while preserving simplification metadata
6361
*/
64-
private static Predicate substituteRefinement(Predicate refinement, VCImplication source, Expression value) {
65-
Expression exp = refinement.getExpression().clone();
66-
Binder binder = new Binder(source.getName(), source.getType());
67-
Expression substituted = exp.substitute(new Var(binder.getName()), value.clone());
68-
69-
return new SimplifiedPredicate(new Predicate(substituted), refinement.getOrigin().clone(),
70-
bindersAfterSubstitution(refinement, exp, binder));
62+
private static VCImplication substituteNode(VCImplication implication, VCImplication node, Expression replacement) {
63+
Expression exp = implication.getRefinement().getExpression().clone();
64+
if (!containsVar(exp, node.getName()))
65+
return implication.copyWithRefinement(new Predicate(exp));
66+
67+
Expression substituted = exp.substitute(new Var(node.getName()), replacement.clone());
68+
VCImplication origin = new VCImplication(node.getName(), node.getType(), origin(implication));
69+
return new SimplifiedVCImplication(implication, new Predicate(substituted), origin);
7170
}
7271

7372
/**
74-
* Builds the binder metadata after one substitution
73+
* Uses the earliest original predicate available when simplifying an already-simplified node
7574
*/
76-
private static List<Binder> bindersAfterSubstitution(Predicate refinement, Expression exp,
77-
SimplifiedPredicate.Binder binder) {
78-
List<SimplifiedPredicate.Binder> binders = refinement instanceof SimplifiedPredicate previous
79-
? new ArrayList<>(previous.getBinders()) : new ArrayList<>();
80-
if (containsVariable(exp, binder.getName()) && !binders.contains(binder))
81-
binders.add(binder);
82-
return binders;
75+
private static Predicate origin(VCImplication implication) {
76+
if (implication instanceof SimplifiedVCImplication simplified)
77+
return simplified.getOrigin().getRefinement().clone();
78+
return implication.getRefinement().clone();
8379
}
8480

8581
/**
@@ -111,9 +107,9 @@ private static Optional<Substitution> getSubstitution(VCImplication implication)
111107
Expression left = binary.getFirstOperand();
112108
Expression right = binary.getSecondOperand();
113109

114-
if (isVar(left, name) && !containsVariable(right, name))
110+
if (isVar(left, name) && !containsVar(right, name))
115111
return Optional.of(new Substitution(implication, right.clone()));
116-
if (isVar(right, name) && !containsVariable(left, name))
112+
if (isVar(right, name) && !containsVar(left, name))
117113
return Optional.of(new Substitution(implication, left.clone()));
118114

119115
return Optional.empty();
@@ -129,7 +125,7 @@ public static boolean isVar(Expression expression, String name) {
129125
/**
130126
* Checks whether an expression contains a variable name
131127
*/
132-
public static boolean containsVariable(Expression expression, String name) {
128+
public static boolean containsVar(Expression expression, String name) {
133129
List<String> names = new ArrayList<>();
134130
expression.getVariableNames(names);
135131
return names.contains(name);

liquidjava-verifier/src/test/java/liquidjava/rj_language/opt/VCSubstitutionTest.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package liquidjava.rj_language.opt;
22

33
import static liquidjava.utils.VCTestUtils.*;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
45
import static org.junit.jupiter.api.Assertions.assertNotSame;
56
import static org.junit.jupiter.api.Assertions.assertNull;
67

@@ -20,7 +21,7 @@ void substitutesBinderEqualityIntoWholeChain() {
2021

2122
VCImplication result = VCSubstitution.applyOnce(implication);
2223

23-
assertSimplifiedVC(result, simplified("3 > 0", "x > 0", "x:int"));
24+
assertSimplifiedVC(result, simplified("3 > 0", "∀x:int. x > 0"));
2425
}
2526

2627
@Test
@@ -29,7 +30,7 @@ void substitutesReverseBinderEquality() {
2930

3031
VCImplication result = VCSubstitution.applyOnce(implication);
3132

32-
assertSimplifiedVC(result, simplified("3 > 0", "x > 0", "x:int"));
33+
assertSimplifiedVC(result, simplified("3 > 0", "∀x:int. x > 0"));
3334
}
3435

3536
@Test
@@ -38,7 +39,7 @@ void substitutesCompoundKnownValue() {
3839

3940
VCImplication result = VCSubstitution.applyOnce(implication);
4041

41-
assertSimplifiedVC(result, simplified("y + 1 > y", "x > y", "x:int"));
42+
assertSimplifiedVC(result, simplified("y + 1 > y", "∀x:int. x > y"));
4243
}
4344

4445
@Test
@@ -47,7 +48,9 @@ void usesFirstSubstitutionFoundInChain() {
4748

4849
VCImplication result = VCSubstitution.applyOnce(implication);
4950

50-
assertSimplifiedVC(result, simplified("x > 0", "x > 0", ""), simplified("x + 4 > 0", "x + y > 0", "y:int"));
51+
assertVC(result, "x > 0", "x + 4 > 0");
52+
assertEquals(VCImplication.class, result.getClass());
53+
assertSimplifiedVC(result.getNext(), simplified("x + 4 > 0", "∀y:int. x + y > 0"));
5154
}
5255

5356
@Test
@@ -56,8 +59,10 @@ void substitutesInnerKnownValueAcrossNestedImplications() {
5659

5760
VCImplication result = VCSubstitution.applyOnce(implication);
5861

59-
assertSimplifiedVC(result, simplified("true", "true", ""), simplified("z > 1", "z > y", "y:int"),
60-
simplified("1 + z > 0", "y + z > 0", "y:int"));
62+
assertVC(result, "true", "z > 1", "1 + z > 0");
63+
assertEquals(VCImplication.class, result.getClass());
64+
assertSimplifiedVC(result.getNext(), simplified("z > 1", "∀y:int. z > y"),
65+
simplified("1 + z > 0", "∀y:int. y + z > 0"));
6166
}
6267

6368
@Test
@@ -66,8 +71,8 @@ void substitutesOuterKnownValueIntoNestedBinderRefinements() {
6671

6772
VCImplication result = VCSubstitution.applyOnce(implication);
6873

69-
assertSimplifiedVC(result, simplified("y == 3 + 1", "y == x + 1", "x:int"),
70-
simplified("y > 3", "y > x", "x:int"));
74+
assertSimplifiedVC(result, simplified("y == 3 + 1", "∀x:int. y == x + 1"),
75+
simplified("y > 3", "∀x:int. y > x"));
7176
}
7277

7378
@Test

0 commit comments

Comments
 (0)