mirror of
https://github.com/Z3Prover/z3
synced 2026-07-15 03:25:43 +00:00
Fix unsound collapse of symbolic re.range bounds to empty language
mk_re_range collapsed a re.range to the empty language whenever either bound was not a constant single character. For a symbolic bound such as (re.range "a" (ite (= a "") "z" "")) this is unsound: the bound may evaluate to a valid character, so the range is not empty. This caused z3 to return unsat on a satisfiable benchmark (iss-5142/small.smt2). Only decide the range here when both bounds are concrete characters (applying the exact empty/singleton/range semantics). When either bound is a non-constant term, return BR_FAILED and leave the range for the theory solver / derivative engine, which already handles symbolic bounds. Provably-empty constant bounds are still caught by the existing min_length/max_length checks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
652402fa1f
commit
a979580852
1 changed files with 32 additions and 23 deletions
|
|
@ -4126,35 +4126,44 @@ br_status seq_rewriter::mk_re_range(expr* lo, expr* hi, expr_ref& result) {
|
|||
is_empty = true;
|
||||
if (max_length(hi) == std::make_pair(true, rational(0)))
|
||||
is_empty = true;
|
||||
if (!is_empty) {
|
||||
if (str().is_string(lo, slo) && slo.length() == 1)
|
||||
clo = slo[0];
|
||||
else if (str().is_unit(lo, lo1) && m_util.is_const_char(lo1, clo))
|
||||
;
|
||||
else
|
||||
is_empty = true;
|
||||
}
|
||||
if (!is_empty) {
|
||||
if (str().is_string(hi, shi) && shi.length() == 1)
|
||||
chi = shi[0];
|
||||
else if (str().is_unit(hi, hi1) && m_util.is_const_char(hi1, chi))
|
||||
;
|
||||
else
|
||||
is_empty = true;
|
||||
}
|
||||
|
||||
// clo/chi are only meaningful once both bounds were extracted; an early
|
||||
// is_empty (from the length checks) leaves them at their default 0, so the
|
||||
// is_empty return must come before the singleton/ordering checks below.
|
||||
if (!is_empty && clo > chi)
|
||||
is_empty = true;
|
||||
|
||||
// A bound whose length is provably not 1 forces the whole range to denote
|
||||
// the empty language, regardless of the other endpoint.
|
||||
if (is_empty) {
|
||||
sort* srt = re().mk_re(lo->get_sort());
|
||||
result = re().mk_empty(srt);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
// Resolve each bound to a concrete character. A bound that is not a
|
||||
// constant single character (e.g. a symbolic term such as an ite) cannot
|
||||
// be decided here: leave the range untouched for the theory solver rather
|
||||
// than assuming it denotes the empty language. Collapsing a symbolic range
|
||||
// to empty is unsound, since the bound may evaluate to a valid character.
|
||||
bool lo_is_char = false, hi_is_char = false;
|
||||
if (str().is_string(lo, slo) && slo.length() == 1) {
|
||||
clo = slo[0];
|
||||
lo_is_char = true;
|
||||
}
|
||||
else if (str().is_unit(lo, lo1) && m_util.is_const_char(lo1, clo))
|
||||
lo_is_char = true;
|
||||
|
||||
if (str().is_string(hi, shi) && shi.length() == 1) {
|
||||
chi = shi[0];
|
||||
hi_is_char = true;
|
||||
}
|
||||
else if (str().is_unit(hi, hi1) && m_util.is_const_char(hi1, chi))
|
||||
hi_is_char = true;
|
||||
|
||||
if (!lo_is_char || !hi_is_char)
|
||||
return BR_FAILED;
|
||||
|
||||
// Both endpoints are concrete characters: apply the exact semantics.
|
||||
if (clo > chi) {
|
||||
sort* srt = re().mk_re(lo->get_sort());
|
||||
result = re().mk_empty(srt);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
// Singleton: re.range "a" "a" → str.to_re "a"
|
||||
if (clo == chi) {
|
||||
result = re().mk_to_re(str().mk_string(zstring(clo)));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue