Skip to content

Commit 72c5df6

Browse files
add tests and a valderivation node for this
1 parent 039c2a9 commit 72c5df6

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,18 @@ private static void extractVarOrigins(ValDerivationNode node, Map<String, Deriva
149149
}
150150
}
151151

152+
// Mirror the resolver's bare-internal-var rule (`#x` ⇒ true, `!#x` ⇒ false): point #x's
153+
// back-link at the conjunct that justified the substitution, so subsequent passes preserve
154+
// provenance through these substitutions too.
155+
if (value instanceof Var var && var.getName().startsWith("#")) {
156+
DerivationNode back = origin != null ? origin : node;
157+
varOrigins.putIfAbsent(var.getName(), back);
158+
} else if (value instanceof UnaryExpression u && "!".equals(u.getOp()) && u.getExpression()instanceof Var inner
159+
&& inner.getName().startsWith("#")) {
160+
DerivationNode back = origin != null ? origin : node;
161+
varOrigins.putIfAbsent(inner.getName(), back);
162+
}
163+
152164
// recursively process the origin tree
153165
if (origin instanceof BinaryDerivationNode binOrigin) {
154166
extractVarOrigins(binOrigin.getLeft(), varOrigins);

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,33 @@ void testUnusedFunctionInvocationEqualityIsIgnored() {
214214

215215
assertTrue(result.isEmpty(), "Function invocation definitions with no usage should be ignored");
216216
}
217+
218+
@Test
219+
void testBareNegatedInternalVarSubstitutesFalse() {
220+
Expression expression = parse("!#fresh_1 && #fresh_1 == #hasNext_2");
221+
Map<String, Expression> result = VariableResolver.resolve(expression);
222+
223+
assertEquals(1, result.size(), "Should extract only the bare-conjunct substitution");
224+
assertEquals("false", result.get("#fresh_1").toString(),
225+
"!#fresh_1 as top-level conjunct should bind #fresh_1 to false");
226+
}
227+
228+
@Test
229+
void testBareInternalVarSubstitutesTrue() {
230+
Expression expression = parse("#fresh_1 && #fresh_1 == #hasNext_2");
231+
Map<String, Expression> result = VariableResolver.resolve(expression);
232+
233+
assertEquals(1, result.size(), "Should extract only the bare-conjunct substitution");
234+
assertEquals("true", result.get("#fresh_1").toString(),
235+
"Bare #fresh_1 as top-level conjunct should bind #fresh_1 to true");
236+
}
237+
238+
@Test
239+
void testBareUserVarNotSubstituted() {
240+
Expression expression = parse("x && y == x");
241+
Map<String, Expression> result = VariableResolver.resolve(expression);
242+
243+
assertTrue(result.isEmpty(),
244+
"Bare user-facing variables should not be substituted, to preserve their names in error messages");
245+
}
217246
}

0 commit comments

Comments
 (0)