mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-04 13:29:11 +00:00 
			
		
		
		
	Track whether pob is in pob_queue
pob_queue is a priority queue. Changing a pob while it is in the queue might change the priority. This is a source of subtle bugs. The flag is ment to help defend against this issues in the future. As a side-effect, a pob that is already in the queue will be silently not added to it, and a new version of a pob might be created if a version being looked for is already in the queue.
This commit is contained in:
		
							parent
							
								
									c00c6b4285
								
							
						
					
					
						commit
						5bc57238a6
					
				
					 2 changed files with 85 additions and 67 deletions
				
			
		| 
						 | 
				
			
			@ -66,8 +66,8 @@ pob::pob (pob* parent, pred_transformer& pt,
 | 
			
		|||
    m_binding(m_pt.get_ast_manager()),
 | 
			
		||||
    m_new_post (m_pt.get_ast_manager ()),
 | 
			
		||||
    m_level (level), m_depth (depth),
 | 
			
		||||
    m_open (true), m_use_farkas (true), m_weakness(0),
 | 
			
		||||
    m_blocked_lvl(0) {
 | 
			
		||||
    m_open (true), m_use_farkas (true), m_in_queue(false),
 | 
			
		||||
    m_weakness(0), m_blocked_lvl(0) {
 | 
			
		||||
    if (add_to_parent && m_parent) {
 | 
			
		||||
        m_parent->add_child(*this);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +79,7 @@ void pob::set_post(expr* post) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void pob::set_post(expr* post, app_ref_vector const &binding) {
 | 
			
		||||
    SASSERT(!is_in_queue());
 | 
			
		||||
    normalize(post, m_post,
 | 
			
		||||
              m_pt.get_context().simplify_pob(),
 | 
			
		||||
              m_pt.get_context().use_euf_gen());
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +89,7 @@ void pob::set_post(expr* post, app_ref_vector const &binding) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void pob::inherit(pob const &p) {
 | 
			
		||||
    SASSERT(!is_in_queue());
 | 
			
		||||
    SASSERT(m_parent == p.m_parent);
 | 
			
		||||
    SASSERT(&m_pt == &p.m_pt);
 | 
			
		||||
    SASSERT(m_post == p.m_post);
 | 
			
		||||
| 
						 | 
				
			
			@ -105,17 +107,10 @@ void pob::inherit(pob const &p) {
 | 
			
		|||
    m_derivation = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pob::clean () {
 | 
			
		||||
    if (m_new_post) {
 | 
			
		||||
        m_post = m_new_post;
 | 
			
		||||
        m_new_post.reset();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pob::close () {
 | 
			
		||||
    if (!m_open) { return; }
 | 
			
		||||
 | 
			
		||||
    reset ();
 | 
			
		||||
    m_derivation = nullptr;
 | 
			
		||||
    m_open = false;
 | 
			
		||||
    for (unsigned i = 0, sz = m_kids.size (); i < sz; ++i)
 | 
			
		||||
    { m_kids [i]->close(); }
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +143,13 @@ pob* pob_queue::top ()
 | 
			
		|||
    return m_data.top ();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pob_queue::pop() {
 | 
			
		||||
    pob *p = m_data.top();
 | 
			
		||||
    p->set_in_queue(false);
 | 
			
		||||
    m_data.pop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void pob_queue::set_root(pob& root)
 | 
			
		||||
{
 | 
			
		||||
    m_root = &root;
 | 
			
		||||
| 
						 | 
				
			
			@ -156,19 +158,28 @@ void pob_queue::set_root(pob& root)
 | 
			
		|||
    reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pob_queue::~pob_queue() {}
 | 
			
		||||
 | 
			
		||||
void pob_queue::reset()
 | 
			
		||||
{
 | 
			
		||||
    while (!m_data.empty()) { m_data.pop(); }
 | 
			
		||||
    if (m_root) { m_data.push(m_root.get()); }
 | 
			
		||||
    while (!m_data.empty()) {
 | 
			
		||||
        pob *p = m_data.top();
 | 
			
		||||
        m_data.pop();
 | 
			
		||||
        p->set_in_queue(false);
 | 
			
		||||
    }
 | 
			
		||||
    if (m_root) {
 | 
			
		||||
        SASSERT(!m_root->is_in_queue());
 | 
			
		||||
        m_root->set_in_queue(true);
 | 
			
		||||
        m_data.push(m_root.get());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pob_queue::push(pob &n) {
 | 
			
		||||
    TRACE("pob_queue",
 | 
			
		||||
          tout << "pob_queue::push(" << n.post()->get_id() << ")\n";);
 | 
			
		||||
    m_data.push (&n);
 | 
			
		||||
    n.get_context().new_pob_eh(&n);
 | 
			
		||||
    if (!n.is_in_queue()) {
 | 
			
		||||
        TRACE("pob_queue",
 | 
			
		||||
              tout << "pob_queue::push(" << n.post()->get_id() << ")\n";);
 | 
			
		||||
        n.set_in_queue(true);
 | 
			
		||||
        m_data.push (&n);
 | 
			
		||||
        n.get_context().new_pob_eh(&n);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----------------
 | 
			
		||||
| 
						 | 
				
			
			@ -2135,7 +2146,7 @@ pob* pred_transformer::pob_manager::mk_pob(pob *parent,
 | 
			
		|||
        auto &buf = m_pobs[p.post()];
 | 
			
		||||
        for (unsigned i = 0, sz = buf.size(); i < sz; ++i) {
 | 
			
		||||
            pob *f = buf.get(i);
 | 
			
		||||
            if (f->parent() == parent) {
 | 
			
		||||
            if (f->parent() == parent && !f->is_in_queue()) {
 | 
			
		||||
                f->inherit(p);
 | 
			
		||||
                return f;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -3051,27 +3062,19 @@ bool context::check_reachability ()
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        SASSERT (m_pob_queue.top ());
 | 
			
		||||
        // -- remove all closed nodes and updated all dirty nodes
 | 
			
		||||
        // -- remove all closed nodes
 | 
			
		||||
        // -- this is necessary because there is no easy way to
 | 
			
		||||
        // -- remove nodes from the priority queue.
 | 
			
		||||
        while (m_pob_queue.top ()->is_closed () ||
 | 
			
		||||
               m_pob_queue.top()->is_dirty()) {
 | 
			
		||||
            pob_ref n = m_pob_queue.top ();
 | 
			
		||||
            m_pob_queue.pop ();
 | 
			
		||||
            if (n->is_closed()) {
 | 
			
		||||
                IF_VERBOSE (1,
 | 
			
		||||
                            verbose_stream () << "Deleting closed node: "
 | 
			
		||||
                            << n->pt ().head ()->get_name ()
 | 
			
		||||
                            << "(" << n->level () << ", " << n->depth () << ")"
 | 
			
		||||
                            << " " << n->post ()->get_id () << "\n";);
 | 
			
		||||
                if (m_pob_queue.is_root(*n)) { return true; }
 | 
			
		||||
                SASSERT (m_pob_queue.top ());
 | 
			
		||||
            } else if (n->is_dirty()) {
 | 
			
		||||
                n->clean ();
 | 
			
		||||
                // -- the node n might not be at the top after it is cleaned
 | 
			
		||||
                m_pob_queue.push (*n);
 | 
			
		||||
            } else
 | 
			
		||||
            { UNREACHABLE(); }
 | 
			
		||||
        while (m_pob_queue.top ()->is_closed ()) {
 | 
			
		||||
            pob_ref n = m_pob_queue.top();
 | 
			
		||||
            m_pob_queue.pop();
 | 
			
		||||
            IF_VERBOSE (1,
 | 
			
		||||
                        verbose_stream () << "Deleting closed node: "
 | 
			
		||||
                        << n->pt ().head ()->get_name ()
 | 
			
		||||
                        << "(" << n->level () << ", " << n->depth () << ")"
 | 
			
		||||
                        << " " << n->post ()->get_id () << "\n";);
 | 
			
		||||
            if (m_pob_queue.is_root(*n)) {return true;}
 | 
			
		||||
            SASSERT (m_pob_queue.top ());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SASSERT (m_pob_queue.top ());
 | 
			
		||||
| 
						 | 
				
			
			@ -3165,6 +3168,7 @@ bool context::is_reachable(pob &n)
 | 
			
		|||
    unsigned num_reuse_reach = 0;
 | 
			
		||||
 | 
			
		||||
    unsigned saved = n.level ();
 | 
			
		||||
    // TBD: don't expose private field
 | 
			
		||||
    n.m_level = infty_level ();
 | 
			
		||||
    lbool res = n.pt().is_reachable(n, nullptr, &mdl,
 | 
			
		||||
                                    uses_level, is_concrete, r,
 | 
			
		||||
| 
						 | 
				
			
			@ -3409,10 +3413,11 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out)
 | 
			
		|||
 | 
			
		||||
        // Optionally update the node to be the negation of the lemma
 | 
			
		||||
        if (v && m_use_lemma_as_pob) {
 | 
			
		||||
            n.new_post (mk_and(lemma->get_cube()));
 | 
			
		||||
            n.set_farkas_generalizer (false);
 | 
			
		||||
            // XXX hack while refactoring is in progress
 | 
			
		||||
            n.clean();
 | 
			
		||||
            NOT_IMPLEMENTED_YET();
 | 
			
		||||
            // n.new_post (mk_and(lemma->get_cube()));
 | 
			
		||||
            // n.set_farkas_generalizer (false);
 | 
			
		||||
            // // XXX hack while refactoring is in progress
 | 
			
		||||
            // n.clean();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // schedule the node to be placed back in the queue
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -562,6 +562,7 @@ public:
 | 
			
		|||
 * A proof obligation.
 | 
			
		||||
 */
 | 
			
		||||
class pob {
 | 
			
		||||
    // TBD: remove this
 | 
			
		||||
    friend class context;
 | 
			
		||||
    unsigned m_ref_count;
 | 
			
		||||
    /// parent node
 | 
			
		||||
| 
						 | 
				
			
			@ -576,14 +577,15 @@ class pob {
 | 
			
		|||
    /// new post to be swapped in for m_post
 | 
			
		||||
    expr_ref                m_new_post;
 | 
			
		||||
    /// level at which to decide the post
 | 
			
		||||
    unsigned                m_level;
 | 
			
		||||
 | 
			
		||||
    unsigned                m_depth;
 | 
			
		||||
    unsigned                m_level:16;
 | 
			
		||||
    unsigned                m_depth:16;
 | 
			
		||||
 | 
			
		||||
    /// whether a concrete answer to the post is found
 | 
			
		||||
    bool                    m_open;
 | 
			
		||||
    unsigned                m_open:1;
 | 
			
		||||
    /// whether to use farkas generalizer to construct a lemma blocking this node
 | 
			
		||||
    bool                    m_use_farkas;
 | 
			
		||||
    unsigned                m_use_farkas:1;
 | 
			
		||||
    /// true if this pob is in pob_queue
 | 
			
		||||
    unsigned                m_in_queue:1;
 | 
			
		||||
 | 
			
		||||
    unsigned                m_weakness;
 | 
			
		||||
    /// derivation representing the position of this node in the parent's rule
 | 
			
		||||
| 
						 | 
				
			
			@ -598,17 +600,25 @@ class pob {
 | 
			
		|||
    // depth -> watch
 | 
			
		||||
    std::map<unsigned, stopwatch> m_expand_watches;
 | 
			
		||||
    unsigned m_blocked_lvl;
 | 
			
		||||
 | 
			
		||||
    void set_post(expr *post);
 | 
			
		||||
public:
 | 
			
		||||
    pob (pob* parent, pred_transformer& pt,
 | 
			
		||||
         unsigned level, unsigned depth=0, bool add_to_parent=true);
 | 
			
		||||
 | 
			
		||||
    ~pob() {if (m_parent) { m_parent->erase_child(*this); }}
 | 
			
		||||
 | 
			
		||||
    // TBD: move into constructor and make private
 | 
			
		||||
    void set_post(expr *post, app_ref_vector const &binding);
 | 
			
		||||
 | 
			
		||||
    unsigned weakness() {return m_weakness;}
 | 
			
		||||
    void bump_weakness() {m_weakness++;}
 | 
			
		||||
    void reset_weakness() {m_weakness=0;}
 | 
			
		||||
 | 
			
		||||
    void inc_level () {m_level++; m_depth++;reset_weakness();}
 | 
			
		||||
    void inc_level () {
 | 
			
		||||
        SASSERT(!is_in_queue());
 | 
			
		||||
        m_level++; m_depth++;reset_weakness();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void inherit(pob const &p);
 | 
			
		||||
    void set_derivation (derivation *d) {m_derivation = d;}
 | 
			
		||||
| 
						 | 
				
			
			@ -628,32 +638,25 @@ public:
 | 
			
		|||
    unsigned level () const { return m_level; }
 | 
			
		||||
    unsigned depth () const {return m_depth;}
 | 
			
		||||
    unsigned width () const {return m_kids.size();}
 | 
			
		||||
    unsigned blocked_at(unsigned lvl=0){return (m_blocked_lvl = std::max(lvl, m_blocked_lvl)); }
 | 
			
		||||
    unsigned blocked_at(unsigned lvl=0){
 | 
			
		||||
        return (m_blocked_lvl = std::max(lvl, m_blocked_lvl));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool is_in_queue() const {return m_in_queue;}
 | 
			
		||||
    void set_in_queue(bool v) {m_in_queue = v;}
 | 
			
		||||
    bool use_farkas_generalizer () const {return m_use_farkas;}
 | 
			
		||||
    void set_farkas_generalizer (bool v) {m_use_farkas = v;}
 | 
			
		||||
 | 
			
		||||
    expr* post() const { return m_post.get (); }
 | 
			
		||||
    void set_post(expr *post);
 | 
			
		||||
    void set_post(expr *post, app_ref_vector const &binding);
 | 
			
		||||
 | 
			
		||||
    /// indicate that a new post should be set for the node
 | 
			
		||||
    void new_post(expr *post) {if (post != m_post) {m_new_post = post;}}
 | 
			
		||||
    /// true if the node needs to be updated outside of the priority queue
 | 
			
		||||
    bool is_dirty () {return m_new_post;}
 | 
			
		||||
    /// clean a dirty node
 | 
			
		||||
    void clean();
 | 
			
		||||
 | 
			
		||||
    void reset () {clean (); m_derivation = nullptr; m_open = true;}
 | 
			
		||||
 | 
			
		||||
    bool is_closed () const { return !m_open; }
 | 
			
		||||
    void close();
 | 
			
		||||
 | 
			
		||||
    const ptr_vector<pob> &children() {return m_kids;}
 | 
			
		||||
    const ptr_vector<pob> &children() const {return m_kids;}
 | 
			
		||||
    void add_child (pob &v) {m_kids.push_back (&v);}
 | 
			
		||||
    void erase_child (pob &v) {m_kids.erase (&v);}
 | 
			
		||||
 | 
			
		||||
    const ptr_vector<lemma> &lemmas() {return m_lemmas;}
 | 
			
		||||
    const ptr_vector<lemma> &lemmas() const {return m_lemmas;}
 | 
			
		||||
    void add_lemma(lemma* new_lemma) {m_lemmas.push_back(new_lemma);}
 | 
			
		||||
 | 
			
		||||
    bool is_ground () const { return m_binding.empty (); }
 | 
			
		||||
| 
						 | 
				
			
			@ -666,8 +669,14 @@ public:
 | 
			
		|||
     */
 | 
			
		||||
    void get_skolems(app_ref_vector& v);
 | 
			
		||||
 | 
			
		||||
    void on_expand() { m_expand_watches[m_depth].start(); if (m_parent.get()){m_parent.get()->on_expand();} }
 | 
			
		||||
    void off_expand() { m_expand_watches[m_depth].stop(); if (m_parent.get()){m_parent.get()->off_expand();} };
 | 
			
		||||
    void on_expand() {
 | 
			
		||||
        m_expand_watches[m_depth].start();
 | 
			
		||||
        if (m_parent.get()){m_parent.get()->on_expand();}
 | 
			
		||||
    }
 | 
			
		||||
    void off_expand() {
 | 
			
		||||
        m_expand_watches[m_depth].stop();
 | 
			
		||||
        if (m_parent.get()){m_parent.get()->off_expand();}
 | 
			
		||||
    }
 | 
			
		||||
    double get_expand_time(unsigned depth) { return m_expand_watches[depth].get_seconds();}
 | 
			
		||||
 | 
			
		||||
    void inc_ref () {++m_ref_count;}
 | 
			
		||||
| 
						 | 
				
			
			@ -782,18 +791,22 @@ class pob_queue {
 | 
			
		|||
 | 
			
		||||
public:
 | 
			
		||||
    pob_queue(): m_root(nullptr), m_max_level(0), m_min_depth(0) {}
 | 
			
		||||
    ~pob_queue();
 | 
			
		||||
    ~pob_queue() {}
 | 
			
		||||
 | 
			
		||||
    void reset();
 | 
			
		||||
    pob* top();
 | 
			
		||||
    void pop() {m_data.pop ();}
 | 
			
		||||
    void pop();
 | 
			
		||||
    void push (pob &n);
 | 
			
		||||
 | 
			
		||||
    void inc_level () {
 | 
			
		||||
        SASSERT (!m_data.empty () || m_root);
 | 
			
		||||
        m_max_level++;
 | 
			
		||||
        m_min_depth++;
 | 
			
		||||
        if (m_root && m_data.empty()) { m_data.push(m_root.get()); }
 | 
			
		||||
        if (m_root && m_data.empty()) {
 | 
			
		||||
            SASSERT(!m_root->is_in_queue());
 | 
			
		||||
            m_root->set_in_queue(true);
 | 
			
		||||
            m_data.push(m_root.get());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pob& get_root() const {return *m_root.get ();}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue