mirror of
https://github.com/Z3Prover/z3
synced 2026-06-19 15:16:29 +00:00
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>
102 lines
2.7 KiB
C++
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;
|
|
}
|
|
|
|
}
|