diff --git a/src/ast/rewriter/regex_range_collapse.cpp b/src/ast/rewriter/regex_range_collapse.cpp index 1db3f60d2c..7b63108367 100644 --- a/src/ast/rewriter/regex_range_collapse.cpp +++ b/src/ast/rewriter/regex_range_collapse.cpp @@ -22,6 +22,17 @@ Authors: namespace seq { bool regex_to_range_predicate(seq_util& u, expr* r, range_predicate& out) { + // The range algebra only models sets of single characters over the + // unsigned character domain [0, max_char]. Guard against any regex + // whose element type is not a sequence of characters (e.g. a regex + // over (Seq Int) or (Seq (Seq Char))): for such regexes the + // re.range/re.union/... matchers below would silently fabricate a + // character-class predicate and change semantics. Reject them up + // front so callers fall back to the generic regex path. + sort* seq_sort = nullptr; + if (!u.is_re(r, seq_sort) || !u.is_string(seq_sort)) + return false; + unsigned const max_char = u.max_char(); auto& re = u.re; diff --git a/src/test/regex_range_collapse.cpp b/src/test/regex_range_collapse.cpp index 9317dc6643..a8ba90d5b6 100644 --- a/src/test/regex_range_collapse.cpp +++ b/src/test/regex_range_collapse.cpp @@ -10,6 +10,7 @@ Module Name: #include "ast/rewriter/regex_range_collapse.h" #include "ast/reg_decl_plugins.h" #include "ast/ast_pp.h" +#include "ast/arith_decl_plugin.h" #include "util/util.h" #include @@ -151,6 +152,20 @@ namespace { "re.* of range not translatable"); } + // Negative: a regex whose element type is NOT a sequence of + // characters (here (Seq Int)) must be rejected outright, even for + // shapes that structurally resemble char-class operators. + { + range_predicate p(M); + arith_util a(m); + sort* int_seq = u.str.mk_seq(a.mk_int()); + sort* int_re = u.re.mk_re(int_seq); + check(!regex_to_range_predicate(u, u.re.mk_empty(int_re), p), + "re.empty over (Seq Int) is NOT a char class"); + check(!regex_to_range_predicate(u, u.re.mk_full_char(int_re), p), + "re.full_char over (Seq Int) is NOT a char class"); + } + // ---- materialization round-trip ---- // empty -> re.empty