Skip to content

Commit 76a7c99

Browse files
Improve Readability of Ghosts in Counterexamples
Co-Authored-By: Catarina Gamboa <52540187+CatarinaGamboa@users.noreply.github.com>
1 parent b3f1112 commit 76a7c99

File tree

1 file changed

+32
-8
lines changed

1 file changed

+32
-8
lines changed

liquidjava-verifier/src/main/java/liquidjava/smt/TranslatorToZ3.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
import com.microsoft.z3.Model;
1616

1717
import java.util.ArrayList;
18+
import java.util.Arrays;
1819
import java.util.HashMap;
1920
import java.util.List;
2021
import java.util.Map;
22+
import java.util.stream.Collectors;
2123

2224
import liquidjava.diagnostics.Counterexample;
2325
import liquidjava.diagnostics.errors.LJError;
@@ -35,6 +37,8 @@ public class TranslatorToZ3 implements AutoCloseable {
3537
private final Map<String, List<Expr<?>>> varSuperTypes = new HashMap<>();
3638
private final Map<String, AliasWrapper> aliasTranslation = new HashMap<>(); // this is not being used
3739
private final Map<String, FuncDecl<?>> funcTranslation = new HashMap<>();
40+
private final Map<String, Expr<?>> funcAppTranslation = new HashMap<>();
41+
private final Map<Expr<?>, String> exprToNameTranslation = new HashMap<>();
3842

3943
public TranslatorToZ3(liquidjava.processor.context.Context c) {
4044
TranslatorContextToZ3.translateVariables(z3, c.getContext(), varTranslation);
@@ -56,14 +60,22 @@ public Solver makeSolverForExpression(Expr<?> e) {
5660
*/
5761
public Counterexample getCounterexample(Model model) {
5862
List<String> assignments = new ArrayList<>();
63+
// Extract constant variable assignments
5964
for (FuncDecl<?> decl : model.getDecls()) {
60-
// extract variable assignments with constants
6165
if (decl.getArity() == 0) {
6266
String name = decl.getName().toString();
6367
String value = model.getConstInterp(decl).toString();
64-
assignments.add(name + " == " + value);
68+
// Skip opaque Z3 object values
69+
if (!value.contains("!val!"))
70+
assignments.add(name + " == " + value);
6571
}
66-
// TODO: extract function assignments (arity > 0)?
72+
}
73+
// Extract function application values
74+
for (Map.Entry<String, Expr<?>> entry : funcAppTranslation.entrySet()) {
75+
String label = entry.getKey();
76+
Expr<?> application = entry.getValue();
77+
Expr<?> value = model.eval(application, true);
78+
assignments.add(label + " = " + value);
6779
}
6880
return new Counterexample(assignments);
6981
}
@@ -101,7 +113,9 @@ private Expr<?> getVariableTranslation(String name) throws LJError {
101113
}
102114

103115
public Expr<?> makeVariable(String name) throws LJError {
104-
return getVariableTranslation(name); // int[] not in varTranslation
116+
Expr<?> expr = getVariableTranslation(name); // int[] not in varTranslation
117+
exprToNameTranslation.put(expr, name); // Track for readable labels
118+
return expr;
105119
}
106120

107121
public Expr<?> makeFunctionInvocation(String name, Expr<?>[] params) throws LJError {
@@ -111,7 +125,7 @@ public Expr<?> makeFunctionInvocation(String name, Expr<?>[] params) throws LJEr
111125
return makeSelect(params);
112126
FuncDecl<?> fd = funcTranslation.get(name);
113127
if (fd == null)
114-
fd = resolveFunctionDeclFallback(name, params);
128+
fd = resolveFunctionDecl(name, params);
115129

116130
Sort[] s = fd.getDomain();
117131
for (int i = 0; i < s.length; i++) {
@@ -125,15 +139,18 @@ public Expr<?> makeFunctionInvocation(String name, Expr<?>[] params) throws LJEr
125139
params[i] = e;
126140
}
127141
}
128-
return z3.mkApp(fd, params);
142+
String label = buildFunctionLabel(name, params);
143+
Expr<?> app = z3.mkApp(fd, params);
144+
funcAppTranslation.put(label, app);
145+
return app;
129146
}
130147

131148
/**
132-
* Fallback resolver for function declarations when an exact qualified name lookup fails. Tries to match by simple
149+
* Gets function declarations when an exact qualified name lookup fails. Tries to match by simple
133150
* name and number of parameters, preferring an exact qualified-name match if found among candidates; otherwise
134151
* returns the first compatible candidate and relies on later coercion via var supertypes.
135152
*/
136-
private FuncDecl<?> resolveFunctionDeclFallback(String name, Expr<?>[] params) throws LJError {
153+
private FuncDecl<?> resolveFunctionDecl(String name, Expr<?>[] params) throws LJError {
137154
String simple = Utils.getSimpleName(name);
138155
FuncDecl<?> candidate = null;
139156
for (Map.Entry<String, FuncDecl<?>> entry : funcTranslation.entrySet()) {
@@ -330,6 +347,13 @@ public Expr<?> makeIte(Expr<?> c, Expr<?> t, Expr<?> e) {
330347
throw new RuntimeException("Condition is not a boolean expression");
331348
}
332349

350+
private String buildFunctionLabel(String functionName, Expr<?>[] params) {
351+
String simpleName = Utils.getSimpleName(functionName);
352+
String argsString = Arrays.stream(params).map(p -> exprToNameTranslation.getOrDefault(p, p.toString()))
353+
.collect(Collectors.joining(", "));
354+
return simpleName + "(" + argsString + ")";
355+
}
356+
333357
@Override
334358
public void close() throws Exception {
335359
z3.close();

0 commit comments

Comments
 (0)