11package liquidjava .rj_language .opt ;
22
33import static liquidjava .rj_language .opt .VCSimplificationUtils .copyWithRefinement ;
4+ import static liquidjava .rj_language .opt .VCSimplificationUtils .containsExpression ;
45
6+ import java .util .ArrayList ;
7+ import java .util .List ;
58import java .util .Optional ;
69
710import liquidjava .processor .VCImplication ;
@@ -18,7 +21,8 @@ public class VCFunctionSubstitution implements VCSimplificationPass {
1821 /**
1922 * A substitution discovered from a function invocation equality
2023 */
21- private record Substitution (VCImplication node , FunctionInvocation invocation , Expression replacement ) {
24+ private record Substitution (VCImplication node , FunctionInvocation invocation , Expression replacement ,
25+ Expression sourceEquality ) {
2226 }
2327
2428 /**
@@ -31,32 +35,52 @@ public VCImplication apply(VCImplication implication) {
3135
3236 if (substitutionOpt .isPresent ()) {
3337 Substitution substitution = substitutionOpt .get ();
34- result = substitute (result , substitution .node (), substitution .invocation (), substitution .replacement ());
38+ result = substitute (result , substitution .node (), substitution .invocation (), substitution .replacement (),
39+ substitution .sourceEquality ());
3540 }
3641 return result ;
3742 }
3843
3944 /**
40- * Preserves nodes before the source equality and starts rewriting at the source suffix
45+ * Rewrites one VC chain with a single substitution and removes its source equality
4146 */
4247 private VCImplication substitute (VCImplication implication , VCImplication node , FunctionInvocation invocation ,
43- Expression replacement ) {
48+ Expression replacement , Expression sourceEquality ) {
4449 if (implication == null )
4550 return null ;
4651
47- // skip the source node to remove it from the chain and start substitution from the next node
52+ // consume the source equality and start substitution from the next node
4853 if (implication == node ) {
49- VCImplication result = copyWithRefinement (implication , implication .getRefinement ().clone ());
50- result .setNext (substituteSuffix (implication .getNext (), invocation , replacement ));
51- return result ;
54+ VCImplication suffix = substituteSuffix (implication .getNext (), invocation , replacement );
55+ VCImplication source = removeSourceEquality (implication , sourceEquality );
56+ if (source == null )
57+ return suffix ;
58+ source .setNext (suffix );
59+ return source ;
5260 }
5361
5462 // preserve the current node and continue rewriting the suffix
5563 VCImplication result = copyWithRefinement (implication , implication .getRefinement ().clone ());
56- result .setNext (substitute (implication .getNext (), node , invocation , replacement ));
64+ result .setNext (substitute (implication .getNext (), node , invocation , replacement , sourceEquality ));
5765 return result ;
5866 }
5967
68+ /**
69+ * Removes the equality conjunct that supplied the substitution, preserving any sibling conjuncts
70+ */
71+ private VCImplication removeSourceEquality (VCImplication implication , Expression sourceEquality ) {
72+ List <Expression > remaining = new ArrayList <>(implication .getRefinement ().getExpression ().getConjuncts ());
73+ if (!remaining .remove (sourceEquality ))
74+ return copyWithRefinement (implication , implication .getRefinement ().clone ());
75+ if (remaining .isEmpty ())
76+ return null ;
77+
78+ Predicate refinement = new Predicate ();
79+ for (Expression conjunct : remaining )
80+ refinement = Predicate .createConjunction (refinement , new Predicate (conjunct .clone ()));
81+ return copyWithRefinement (implication , refinement );
82+ }
83+
6084 /**
6185 * Rewrites every node after the source equality with one function substitution
6286 */
@@ -121,33 +145,13 @@ private Optional<Substitution> getSubstitution(VCImplication implication, Expres
121145 Expression left = binary .getFirstOperand ();
122146 Expression right = binary .getSecondOperand ();
123147 if (left instanceof FunctionInvocation invocation && !containsExpression (right , left ))
124- return Optional .of (new Substitution (implication , (FunctionInvocation ) invocation .clone (), right .clone ()));
148+ return Optional .of (new Substitution (implication , (FunctionInvocation ) invocation .clone (), right .clone (),
149+ binary .clone ()));
125150 if (right instanceof FunctionInvocation invocation && !containsExpression (left , right ))
126- return Optional .of (new Substitution (implication , (FunctionInvocation ) invocation .clone (), left .clone ()));
151+ return Optional .of (new Substitution (implication , (FunctionInvocation ) invocation .clone (), left .clone (),
152+ binary .clone ()));
127153
128154 return Optional .empty ();
129155 }
130156
131- /**
132- * Checks whether an expression contains another expression
133- */
134- private boolean containsExpression (Expression expression , Expression target ) {
135- if (expression .equals (target ))
136- return true ;
137-
138- for (Expression child : expression .getChildren ())
139- if (containsExpression (child , target ))
140- return true ;
141- return false ;
142- }
143-
144- /**
145- * Checks whether a VC suffix contains an expression
146- */
147- private boolean containsExpression (VCImplication implication , Expression target ) {
148- for (VCImplication current = implication ; current != null ; current = current .getNext ())
149- if (containsExpression (current .getRefinement ().getExpression (), target ))
150- return true ;
151- return false ;
152- }
153157}
0 commit comments