when printing counterexamples, try to prove which variables are undef for sure
MODIFY
smt/expr.cpp
smt/expr.cpp
#include <limits>
#include <memory>
#include <string>
+#include <unordered_set>
#include <z3.h>
#define DEBUG_Z3_RC 0
@@ -1268,6 +1269,56 @@ expr expr::subst(const expr &from, const expr &to) const {
return Z3_substitute(ctx(), ast(), 1, &f, &t);
}
+set<expr> expr::vars() const {
+ C();
+ set<expr> result;
+ unordered_set<Z3_ast> todo{ ast() };
+ unordered_set<Z3_ast> seen = todo;
+
+ do {
+ auto I = todo.begin();
+ auto ast = *I;
+ todo.erase(I);
+
+ switch (Z3_get_ast_kind(ctx(), ast)) {
+ case Z3_VAR_AST:
+ case Z3_NUMERAL_AST:
+ break;
+
+ case Z3_QUANTIFIER_AST: {
+ auto body = Z3_get_quantifier_body(ctx(), ast);
+ if (seen.emplace(body).second)
+ todo.emplace(body);
+ break;
+ }
+
+ case Z3_APP_AST: {
+ // Z3_NUMERAL_AST above only catches real numbers
+ if (Z3_is_numeral_ast(ctx(), ast))
+ continue;
+
+ auto app = Z3_to_app(ctx(), ast);
+ auto num_args = Z3_get_app_num_args(ctx(), app);
+ if (num_args == 0) { // it's a variable
+ result.emplace(expr(ast));
+ continue;
+ }
+
+ for (unsigned i = 0; i < num_args; ++i) {
+ auto arg = Z3_get_app_arg(ctx(), app, i);
+ if (seen.emplace(arg).second)
+ todo.emplace(arg);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ } while (!todo.empty());
+
+ return result;
+}
+
void expr::printUnsigned(ostream &os) const {
os << numeral_string();
}

MODIFY
smt/expr.h
smt/expr.h
@@ -248,6 +248,8 @@ public:
expr subst(const std::vector<std::pair<expr, expr>> &repls) const;
expr subst(const expr &from, const expr &to) const;
+ std::set<expr> vars() const;
+
void printUnsigned(std::ostream &os) const;
void printSigned(std::ostream &os) const;
void printHexadecimal(std::ostream &os) const;

MODIFY
tools/transform.cpp
tools/transform.cpp
@@ -20,6 +20,12 @@ using namespace util;
using namespace std;
+static bool is_undef(const expr &e) {
+ Solver s;
+ s.add(expr::mkForAll(e.vars(), expr::mkVar("#undef", e) != e));
+ return s.check().isUnsat();
+}
+
static void print_varval(ostream &s, const Model &m, const Value *var,
const Type &type, const StateValue &val) {
// if the model is partial, we don't know for sure if it's poison or not
@@ -41,15 +47,19 @@ static void print_varval(ostream &s, const Model &m, const Value *var,
assert(n == 0);
}
- expr e = m.eval(val.value);
- // undef variables may not have a model since each read uses a copy
- if (!e.isConst()) {
- // FIXME
- s << "?";
+ expr partial = m.eval(val.value);
+ if (is_undef(partial)) {
+ s << "undef";
return;
}
- type.printVal(s, e);
+ type.printVal(s, m.eval(val.value, true));
+
+ // undef variables may not have a model since each read uses a copy
+ // TODO: add intervals of possible values for ints at least?
+ if (!partial.isConst()) {
+ s << "\t[based on undef value]";
+ }
}

when printing counterexamples, try to prove which variables are undef for sure