mirror of
https://github.com/Z3Prover/z3
synced 2026-07-14 19:15:41 +00:00
Regex factorization needs special care when resuming after finding a sat node in-between
This commit is contained in:
parent
b7ef51ca04
commit
d1b0cbee34
2 changed files with 22 additions and 5 deletions
|
|
@ -2329,7 +2329,7 @@ namespace seq {
|
||||||
// differs), so it would alias the parent's signature, yet it still has
|
// differs), so it would alias the parent's signature, yet it still has
|
||||||
// pending splits to explore — it is not a true recurrence.
|
// pending splits to explore — it is not a true recurrence.
|
||||||
{
|
{
|
||||||
if (!node->rf_cont() && m_unsat_node_cache.contains(node)) {
|
if (!node->is_rf_cont() && m_unsat_node_cache.contains(node)) {
|
||||||
node->set_conflict(backtrack_reason::sibling, nullptr /*we use the one of the sibling*/);
|
node->set_conflict(backtrack_reason::sibling, nullptr /*we use the one of the sibling*/);
|
||||||
node->set_general_conflict();
|
node->set_general_conflict();
|
||||||
node->m_unsat_cacheable = true;
|
node->m_unsat_cacheable = true;
|
||||||
|
|
@ -2429,12 +2429,17 @@ namespace seq {
|
||||||
// with string-only conflicts and self-contained cuts (see the epilogue).
|
// with string-only conflicts and self-contained cuts (see the epilogue).
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
node->canonize_and_compute_final_node_hash();
|
node->canonize_and_compute_final_node_hash();
|
||||||
// A lazy-factorization continuation node (rf_cont set) is EXEMPT from the
|
// A lazy-factorization continuation node (is_rf_cont) is EXEMPT from the
|
||||||
// loop-cut: it aliases its parent's string signature (only the suspended
|
// loop-cut: it aliases its parent's string signature (only the suspended
|
||||||
// split iterator differs) but is not a true recurrence — it still has
|
// split iterator differs) but is not a true recurrence — it still has
|
||||||
// pending splits. The iterator is finite, so the continuation chain
|
// pending splits. The iterator is finite, so the continuation chain
|
||||||
// terminates on its own (exhaustion → regex conflict).
|
// terminates on its own (exhaustion → regex conflict). The exemption uses
|
||||||
if (!node->rf_cont()) {
|
// the STICKY is_rf_cont() marker, not the live rf_cont() pointer: the
|
||||||
|
// pointer is nulled once the node is extended, but on a hot-restart the
|
||||||
|
// node is re-traversed without re-extending, and it must stay exempt (else
|
||||||
|
// it is wrongly cut as a sibling of the ancestor it aliases, pruning a
|
||||||
|
// branch that may still lead to SAT).
|
||||||
|
if (!node->is_rf_cont()) {
|
||||||
auto it = m_siblings.find(node);
|
auto it = m_siblings.find(node);
|
||||||
if (it != m_siblings.end() && !it->second.empty()) {
|
if (it != m_siblings.end() && !it->second.empty()) {
|
||||||
nielsen_node* anc = it->second.back(); // deepest sibling still on the path
|
nielsen_node* anc = it->second.back(); // deepest sibling still on the path
|
||||||
|
|
|
||||||
|
|
@ -600,6 +600,14 @@ namespace seq {
|
||||||
// split iterator so factorization resumes from the next split when this
|
// split iterator so factorization resumes from the next split when this
|
||||||
// node is extended. Owned by nielsen_graph::m_rf_states (raw pointer here).
|
// node is extended. Owned by nielsen_graph::m_rf_states (raw pointer here).
|
||||||
rf_state* m_rf_cont = nullptr;
|
rf_state* m_rf_cont = nullptr;
|
||||||
|
// Sticky marker: true once this node was ever created as a factorization
|
||||||
|
// continuation (child B), even after its iterator has been consumed
|
||||||
|
// (m_rf_cont reset to null when the node is extended). The subsumption
|
||||||
|
// loop-cut and unsat-cache exemptions (§1.6b) are a PERMANENT structural
|
||||||
|
// property of a continuation node — it aliases its parent's string
|
||||||
|
// signature yet is not a recurrence — so they must survive the hot-restart
|
||||||
|
// re-traversal, where m_rf_cont is already null. See search_dfs.
|
||||||
|
bool m_is_rf_cont = false;
|
||||||
// number of constraints inherited from the parent node at clone time.
|
// number of constraints inherited from the parent node at clone time.
|
||||||
// constraints[0..m_parent_ic_count) are already asserted at the
|
// constraints[0..m_parent_ic_count) are already asserted at the
|
||||||
// parent's solver scope; only [m_parent_ic_count..end) need to be
|
// parent's solver scope; only [m_parent_ic_count..end) need to be
|
||||||
|
|
@ -657,7 +665,11 @@ namespace seq {
|
||||||
|
|
||||||
// lazy regex factorization continuation (see m_rf_cont).
|
// lazy regex factorization continuation (see m_rf_cont).
|
||||||
rf_state* rf_cont() const { return m_rf_cont; }
|
rf_state* rf_cont() const { return m_rf_cont; }
|
||||||
void set_rf_cont(rf_state* s) { m_rf_cont = s; }
|
void set_rf_cont(rf_state* s) { m_rf_cont = s; if (s) m_is_rf_cont = true; }
|
||||||
|
// Sticky: true if this node was ever a factorization continuation, even
|
||||||
|
// after its iterator has been consumed. Drives the loop-cut / unsat-cache
|
||||||
|
// exemptions so they persist across the hot-restart re-traversal.
|
||||||
|
bool is_rf_cont() const { return m_is_rf_cont; }
|
||||||
|
|
||||||
// returns 0 if hash is unknown
|
// returns 0 if hash is unknown
|
||||||
unsigned hash() const {
|
unsigned hash() const {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue