3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-06-19 15:16:29 +00:00
z3/src/ast/rewriter/range_predicate_translator.cpp
Margus Veanes 4bef7d513c seq::derive — Stage 2: range_predicate-based path state
Replace seq::derive's m_intervals + m_intervals_start append-only
char-range stack and the imperative intersect_intervals /
exclude_interval helpers with a single canonical range_predicate
m_path_pred that tracks the feasible character set under the
current path.

* Add range_predicate_translator: pure AST -> range_predicate
  translator for the boolean-over-char_le fragment
  (true/false, eq with const, char_le with const, not/and/or any
  nesting). Returns false on the first sub-term outside the
  fragment so the caller can fall back to other reasoning.

* push_intervals_impl: translate the candidate predicate to a
  range_predicate and reduce path tracking to set arithmetic
  (intersection + subset/empty checks). The legacy top-level
  and/or descent is preserved for mixed char / non-char
  conditions.

* eval_range_cond: implication becomes subset_of and contradiction
  becomes !intersects, both linear in the number of ranges with no
  AST allocation.

* range_predicate gains subset_of / intersects / disjoint_from to
  support allocation-free path queries.

* path_save now stores the saved range_predicate by value; the
  stack switches from svector (CallDestructors=false) to vector
  because range_predicate owns an inner svector.

Tests: 91/91 pass with /a, including the new
range_predicate_translator unit test exercising true/false, eq,
char_le, and/or/not, and De Morgan agreement.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 03:27:54 -07:00

102 lines
2.7 KiB
C++

/*++
Copyright (c) 2026 Microsoft Corporation
Module Name:
range_predicate_translator.cpp
Abstract:
Implementation of seq::range_predicate_translator. See header for the
algebraic specification of the translatable fragment.
Authors:
Margus Veanes (veanes) 2026
--*/
#include "ast/rewriter/range_predicate_translator.h"
namespace seq {
bool range_predicate_translator::translate(expr* ele, expr* cond,
range_predicate& out) const {
unsigned const max_char = m_u.max_char();
if (m.is_true(cond)) {
out = range_predicate::top(max_char);
return true;
}
if (m.is_false(cond)) {
out = range_predicate::empty(max_char);
return true;
}
expr* a = nullptr;
expr* b = nullptr;
unsigned c = 0;
if (m.is_eq(cond, a, b)) {
if (a == ele && m_u.is_const_char(b, c)) {
out = range_predicate::singleton(c, max_char);
return true;
}
if (b == ele && m_u.is_const_char(a, c)) {
out = range_predicate::singleton(c, max_char);
return true;
}
return false;
}
if (m_u.is_char_le(cond, a, b)) {
// ele <= c --> [0, c]
if (a == ele && m_u.is_const_char(b, c)) {
out = range_predicate::range(0, c, max_char);
return true;
}
// c <= ele --> [c, max_char]
if (b == ele && m_u.is_const_char(a, c)) {
out = range_predicate::range(c, max_char, max_char);
return true;
}
return false;
}
expr* arg = nullptr;
if (m.is_not(cond, arg)) {
range_predicate tmp(max_char);
if (!translate(ele, arg, tmp))
return false;
out = ~tmp;
return true;
}
if (m.is_and(cond)) {
range_predicate acc = range_predicate::top(max_char);
for (expr* sub : *to_app(cond)) {
range_predicate p(max_char);
if (!translate(ele, sub, p))
return false;
acc = acc & p;
}
out = acc;
return true;
}
if (m.is_or(cond)) {
range_predicate acc = range_predicate::empty(max_char);
for (expr* sub : *to_app(cond)) {
range_predicate p(max_char);
if (!translate(ele, sub, p))
return false;
acc = acc | p;
}
out = acc;
return true;
}
return false;
}
}