From 43e47271f7e935764070db3572cd16ca590f0832 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Sep 2017 15:58:09 -0700 Subject: [PATCH 001/148] have quantified tactics work with bound Boolean variables. Adding stubs for match Signed-off-by: Nikolaj Bjorner --- src/parsers/smt2/smt2parser.cpp | 58 +++++++++++++++++++++++++++++++- src/tactic/arith/probe_arith.cpp | 31 +++++++++-------- 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 681c3d597..fd32a63cc 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -108,6 +108,7 @@ namespace smt2 { symbol m_check_sat_assuming; symbol m_define_fun_rec; symbol m_define_funs_rec; + symbol m_match; symbol m_underscore; typedef std::pair named_expr; @@ -135,7 +136,7 @@ namespace smt2 { typedef psort_frame sort_frame; - enum expr_frame_kind { EF_APP, EF_LET, EF_LET_DECL, EF_QUANT, EF_ATTR_EXPR, EF_PATTERN }; + enum expr_frame_kind { EF_APP, EF_LET, EF_LET_DECL, EF_QUANT, EF_MATCH, EF_ATTR_EXPR, EF_PATTERN }; struct expr_frame { expr_frame_kind m_kind; @@ -172,6 +173,12 @@ namespace smt2 { m_expr_spos(expr_spos) {} }; + struct match_frame : public expr_frame { + match_frame(): + expr_frame(EF_MATCH) + {} + }; + struct let_frame : public expr_frame { bool m_in_decls; unsigned m_sym_spos; @@ -389,6 +396,7 @@ namespace smt2 { bool curr_id_is_underscore() const { SASSERT(curr_is_identifier()); return curr_id() == m_underscore; } bool curr_id_is_as() const { SASSERT(curr_is_identifier()); return curr_id() == m_as; } + bool curr_id_is_match() const { SASSERT(curr_is_identifier()); return curr_id() == m_match; } bool curr_id_is_forall() const { SASSERT(curr_is_identifier()); return curr_id() == m_forall; } bool curr_id_is_exists() const { SASSERT(curr_is_identifier()); return curr_id() == m_exists; } bool curr_id_is_bang() const { SASSERT(curr_is_identifier()); return curr_id() == m_bang; } @@ -1273,6 +1281,47 @@ namespace smt2 { throw parser_exception("invalid quantifier, list of sorted variables is empty"); } + /** + * SMT-LIB 2.6 pattern matches are of the form + * (match t ((p1 t1) ··· (pm+1 tm+1))) + */ + void push_match_frame() { + next(); +#if 0 + // just use the stack for parsing these for now. + void * mem = m_stack.allocate(sizeof(match_frame)); + new (mem) match_frame(); + m_num_expr_frames++; +#endif + parse_expr(); + expr_ref t(expr_stack().back(), m()); + expr_stack().pop_back(); + expr_ref_vector patterns(m()), cases(m()); + + check_lparen_next("pattern bindings should be enclosed in a parenthesis"); + while (!curr_is_rparen()) { + check_lparen_next("invalid pattern binding, '(' expected"); + parse_expr(); // TBD need to parse a pattern here. The sort of 't' provides context for how to interpret _. + patterns.push_back(expr_stack().back()); + expr_stack().pop_back(); + parse_expr(); + cases.push_back(expr_stack().back()); + expr_stack().pop_back(); + check_rparen_next("invalid pattern binding, ')' expected"); + } + next(); + expr_stack().push_back(compile_patterns(t, patterns, cases)); + } + + expr_ref compile_patterns(expr* t, expr_ref_vector const& patterns, expr_ref_vector const& cases) { + NOT_IMPLEMENTED_YET(); + return expr_ref(m()); + } + + void pop_match_frame(match_frame * fr) { + + } + symbol parse_indexed_identifier_core() { check_underscore_next("invalid indexed identifier, '_' expected"); check_identifier("invalid indexed identifier, symbol expected"); @@ -1599,6 +1648,9 @@ namespace smt2 { else if (curr_id_is_root_obj()) { parse_root_obj(); } + else if (curr_id_is_match()) { + push_match_frame(); + } else { push_app_frame(); } @@ -1776,6 +1828,9 @@ namespace smt2 { case EF_QUANT: pop_quant_frame(static_cast(fr)); break; + case EF_MATCH: + pop_match_frame(static_cast(fr)); + break; case EF_ATTR_EXPR: pop_attr_expr_frame(static_cast(fr)); break; @@ -2730,6 +2785,7 @@ namespace smt2 { m_check_sat_assuming("check-sat-assuming"), m_define_fun_rec("define-fun-rec"), m_define_funs_rec("define-funs-rec"), + m_match("match"), m_underscore("_"), m_num_open_paren(0), m_current_file(filename) { diff --git a/src/tactic/arith/probe_arith.cpp b/src/tactic/arith/probe_arith.cpp index c2de64484..edd4483e7 100644 --- a/src/tactic/arith/probe_arith.cpp +++ b/src/tactic/arith/probe_arith.cpp @@ -392,24 +392,27 @@ struct is_non_nira_functor { is_non_nira_functor(ast_manager & _m, bool _int, bool _real, bool _quant, bool linear):m(_m), u(m), m_int(_int), m_real(_real), m_quant(_quant), m_linear(linear) {} - void throw_found() { + void throw_found(expr* e) { + TRACE("probe", tout << expr_ref(e, m) << ": " << sort_ref(m.get_sort(e), m) << "\n";); throw found(); } void operator()(var * x) { if (!m_quant) - throw_found(); + throw_found(x); sort * s = x->get_sort(); if (m_int && u.is_int(s)) return; if (m_real && u.is_real(s)) return; - throw_found(); + if (m.is_bool(s)) + return; + throw_found(x); } - void operator()(quantifier *) { + void operator()(quantifier * q) { if (!m_quant) - throw_found(); + throw_found(q); } bool compatible_sort(app * n) const { @@ -424,7 +427,7 @@ struct is_non_nira_functor { void operator()(app * n) { if (!compatible_sort(n)) - throw_found(); + throw_found(n); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; @@ -437,39 +440,39 @@ struct is_non_nira_functor { case OP_MUL: if (m_linear) { if (n->get_num_args() != 2) - throw_found(); + throw_found(n); if (!u.is_numeral(n->get_arg(0))) - throw_found(); + throw_found(n); } return; case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: if (m_linear && !u.is_numeral(n->get_arg(1))) - throw_found(); + throw_found(n); return; case OP_IS_INT: if (m_real) - throw_found(); + throw_found(n); return; case OP_TO_INT: case OP_TO_REAL: return; case OP_POWER: if (m_linear) - throw_found(); + throw_found(n); return; case OP_IRRATIONAL_ALGEBRAIC_NUM: if (m_linear || !m_real) - throw_found(); + throw_found(n); return; default: - throw_found(); + throw_found(n); } return; } if (is_uninterp_const(n)) return; - throw_found(); + throw_found(n); } }; From caa02c3c0296fb531488b8549f36424a1656cc1a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Sep 2017 19:39:02 -0700 Subject: [PATCH 002/148] add match expression construct to SMT-LIB2.6 frontend Signed-off-by: Nikolaj Bjorner --- src/ast/ast_smt2_pp.cpp | 2 + src/ast/rewriter/rewriter_def.h | 4 + src/cmd_context/cmd_context.cpp | 6 +- src/model/func_interp.cpp | 1 - src/parsers/smt2/smt2parser.cpp | 216 +++++++++++++++++++++++++++----- src/smt/smt_context.cpp | 10 +- 6 files changed, 203 insertions(+), 36 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index abb4ae959..cad2265c8 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -584,6 +584,8 @@ class smt2_printer { string_buffer<> buf; buf.append("(:var "); buf.append(v->get_idx()); + buf.append(" "); + buf.append(v->get_sort()->get_name().str().c_str()); buf.append(")"); f = mk_string(m(), buf.c_str()); } diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index 42f268379..f5f72674d 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -42,6 +42,10 @@ void rewriter_tpl::process_var(var * v) { unsigned index = m_bindings.size() - idx - 1; var * r = (var*)(m_bindings[index]); if (r != 0) { + CTRACE("rewriter", v->get_sort() != m().get_sort(r), + tout << expr_ref(v, m()) << ":" << sort_ref(v->get_sort(), m()) << " != " << expr_ref(r, m()) << ":" << sort_ref(m().get_sort(r), m()); + tout << "index " << index << " bindings " << m_bindings.size() << "\n"; + display_bindings(tout);); SASSERT(v->get_sort() == m().get_sort(r)); if (!is_ground(r) && m_shifts[index] != m_bindings.size()) { diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 14618887a..3889d6205 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -202,7 +202,7 @@ func_decl * func_decls::find(unsigned arity, sort * const * domain, sort * range if (f->get_arity() != arity) continue; unsigned i = 0; - for (i = 0; i < arity; i++) { + for (i = 0; domain && i < arity; i++) { if (f->get_domain(i) != domain[i]) break; } @@ -937,7 +937,7 @@ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, unsigned arity, sort * const * domain, sort * range) const { builtin_decl d; - if (m_builtin_decls.find(s, d)) { + if (domain && m_builtin_decls.find(s, d)) { family_id fid = d.m_fid; decl_kind k = d.m_decl; // Hack: if d.m_next != 0, we use domain[0] (if available) to decide which plugin we use. @@ -961,7 +961,7 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, return f; } - if (contains_macro(s, arity, domain)) + if (domain && contains_macro(s, arity, domain)) throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); if (num_indices > 0) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 2ead63e0a..6699ef0e2 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -140,7 +140,6 @@ void func_interp::set_else(expr * e) { return; reset_interp_cache(); - ptr_vector args; while (e && is_fi_entry_expr(e, args)) { TRACE("func_interp", tout << "fi entry expr: " << mk_ismt2_pp(e, m()) << std::endl;); diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index fd32a63cc..6b62edcbd 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -24,6 +24,7 @@ Revision History: #include "ast/ast_pp.h" #include "ast/well_sorted.h" #include "ast/rewriter/rewriter.h" +#include "ast/rewriter/var_subst.h" #include "ast/has_free_vars.h" #include "ast/ast_smt2_pp.h" #include "parsers/smt2/smt2parser.h" @@ -68,6 +69,7 @@ namespace smt2 { scoped_ptr m_bv_util; scoped_ptr m_arith_util; + scoped_ptr m_datatype_util; scoped_ptr m_seq_util; scoped_ptr m_pattern_validator; scoped_ptr m_var_shifter; @@ -136,7 +138,7 @@ namespace smt2 { typedef psort_frame sort_frame; - enum expr_frame_kind { EF_APP, EF_LET, EF_LET_DECL, EF_QUANT, EF_MATCH, EF_ATTR_EXPR, EF_PATTERN }; + enum expr_frame_kind { EF_APP, EF_LET, EF_LET_DECL, EF_MATCH, EF_QUANT, EF_ATTR_EXPR, EF_PATTERN }; struct expr_frame { expr_frame_kind m_kind; @@ -174,9 +176,7 @@ namespace smt2 { }; struct match_frame : public expr_frame { - match_frame(): - expr_frame(EF_MATCH) - {} + match_frame():expr_frame(EF_MATCH) {} }; struct let_frame : public expr_frame { @@ -282,6 +282,12 @@ namespace smt2 { return *(m_arith_util.get()); } + datatype_util & dtutil() { + if (m_datatype_util.get() == 0) + m_datatype_util = alloc(datatype_util, m()); + return *(m_datatype_util.get()); + } + seq_util & sutil() { if (m_seq_util.get() == 0) m_seq_util = alloc(seq_util, m()); @@ -1266,6 +1272,23 @@ namespace smt2 { return num; } + void push_let_frame() { + next(); + check_lparen_next("invalid let declaration, '(' expected"); + void * mem = m_stack.allocate(sizeof(let_frame)); + new (mem) let_frame(symbol_stack().size(), expr_stack().size()); + m_num_expr_frames++; + } + + void push_bang_frame(expr_frame * curr) { + TRACE("consume_attributes", tout << "begin bang, expr_stack.size(): " << expr_stack().size() << "\n";); + next(); + void * mem = m_stack.allocate(sizeof(attr_expr_frame)); + new (mem) attr_expr_frame(curr, symbol_stack().size(), expr_stack().size()); + m_num_expr_frames++; + } + + void push_quant_frame(bool is_forall) { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_forall() || curr_id_is_exists()); @@ -1286,40 +1309,179 @@ namespace smt2 { * (match t ((p1 t1) ··· (pm+1 tm+1))) */ void push_match_frame() { + SASSERT(curr_is_identifier()); + SASSERT(curr_id() == m_match); next(); -#if 0 - // just use the stack for parsing these for now. - void * mem = m_stack.allocate(sizeof(match_frame)); + void * mem = m_stack.allocate(sizeof(match_frame)); new (mem) match_frame(); - m_num_expr_frames++; -#endif + unsigned num_frames = m_num_expr_frames; + parse_expr(); expr_ref t(expr_stack().back(), m()); expr_stack().pop_back(); expr_ref_vector patterns(m()), cases(m()); + sort* srt = m().get_sort(t); check_lparen_next("pattern bindings should be enclosed in a parenthesis"); while (!curr_is_rparen()) { + m_env.begin_scope(); + unsigned num_bindings = m_num_bindings; check_lparen_next("invalid pattern binding, '(' expected"); - parse_expr(); // TBD need to parse a pattern here. The sort of 't' provides context for how to interpret _. + parse_match_pattern(srt); patterns.push_back(expr_stack().back()); expr_stack().pop_back(); parse_expr(); cases.push_back(expr_stack().back()); expr_stack().pop_back(); + m_num_bindings = num_bindings; + m_env.end_scope(); check_rparen_next("invalid pattern binding, ')' expected"); } next(); + m_num_expr_frames = num_frames + 1; expr_stack().push_back(compile_patterns(t, patterns, cases)); } - expr_ref compile_patterns(expr* t, expr_ref_vector const& patterns, expr_ref_vector const& cases) { - NOT_IMPLEMENTED_YET(); - return expr_ref(m()); + void pop_match_frame(match_frame* fr) { + m_stack.deallocate(fr); + m_num_expr_frames--; } - void pop_match_frame(match_frame * fr) { + expr_ref compile_patterns(expr* t, expr_ref_vector const& patterns, expr_ref_vector const& cases) { + expr_ref result(m()); + var_subst sub(m(), false); + TRACE("parse_expr", tout << "term\n" << expr_ref(t, m()) << "\npatterns\n" << patterns << "\ncases\n" << cases << "\n";); + for (unsigned i = patterns.size(); i > 0; ) { + --i; + expr_ref_vector subst(m()); + expr_ref cond = bind_match(t, patterns[i], subst); + expr_ref new_case(m()); + if (subst.empty()) { + new_case = cases[i]; + } + else { + sub(cases[i], subst.size(), subst.c_ptr(), new_case); + inv_var_shifter inv(m()); + inv(new_case, subst.size(), new_case); + } + if (result) { + result = m().mk_ite(cond, new_case, result); + } + else { + // pattern match binding is ignored. + result = new_case; + } + } + TRACE("parse_expr", tout << result << "\n";); + return result; + } + // compute match condition and substitution + // t is shifted by size of subst. + expr_ref bind_match(expr* t, expr* pattern, expr_ref_vector& subst) { + expr_ref tsh(m()); + if (is_var(pattern)) { + shifter()(t, 1, tsh); + subst.push_back(tsh); + return expr_ref(m().mk_true(), m()); + } + else { + SASSERT(is_app(pattern)); + func_decl * f = to_app(pattern)->get_decl(); + func_decl * r = dtutil().get_constructor_recognizer(f); + ptr_vector const * acc = dtutil().get_constructor_accessors(f); + shifter()(t, acc->size(), tsh); + for (func_decl* a : *acc) { + subst.push_back(m().mk_app(a, tsh)); + } + return expr_ref(m().mk_app(r, t), m()); + } + } + + /** + * parse a match pattern + * (C x1 .... xn) + * C + * _ + * x + */ + + bool parse_constructor_pattern(sort * srt) { + if (!curr_is_lparen()) { + return false; + } + next(); + svector vars; + expr_ref_vector args(m()); + symbol C(check_identifier_next("constructor symbol expected")); + while (!curr_is_rparen()) { + symbol v(check_identifier_next("variable symbol expected")); + if (v != m_underscore && vars.contains(v)) { + throw parser_exception("unexpected repeated variable in pattern expression"); + } + vars.push_back(v); + } + next(); + + // now have C, vars + // look up constructor C, + // create bound variables based on constructor type. + // store expression in expr_stack(). + // ensure that bound variables are adjusted to vars + + func_decl* f = m_ctx.find_func_decl(C, 0, nullptr, vars.size(), nullptr, srt); + if (!f) { + throw parser_exception("expecting a constructor that has been declared"); + } + if (!dtutil().is_constructor(f)) { + throw parser_exception("expecting a constructor"); + } + if (f->get_arity() != vars.size()) { + throw parser_exception("mismatching number of variables supplied to constructor"); + } + m_num_bindings += vars.size(); + for (unsigned i = 0; i < vars.size(); ++i) { + var * v = m().mk_var(i, f->get_domain(i)); + args.push_back(v); + if (vars[i] != m_underscore) { + m_env.insert(vars[i], local(v, m_num_bindings)); + } + } + expr_stack().push_back(m().mk_app(f, args.size(), args.c_ptr())); + return true; + } + + void parse_match_pattern(sort* srt) { + if (parse_constructor_pattern(srt)) { + // done + } + else if (curr_id() == m_underscore) { + // we have a wild-card. + // store dummy variable in expr_stack() + next(); + var* v = m().mk_var(0, srt); + expr_stack().push_back(v); + } + else { + symbol xC(check_identifier_next("constructor symbol or variable expected")); + // check if xC is a constructor, otherwise make it a variable + // of sort srt. + try { + func_decl* f = m_ctx.find_func_decl(xC, 0, nullptr, 0, nullptr, srt); + if (!dtutil().is_constructor(f)) { + throw parser_exception("expecting a constructor, got a previously declared function"); + } + if (f->get_arity() > 0) { + throw parser_exception("constructor expects arguments, but no arguments were supplied in pattern"); + } + expr_stack().push_back(m().mk_const(f)); + } + catch (cmd_exception &) { + var* v = m().mk_var(0, srt); + expr_stack().push_back(v); + m_env.insert(xC, local(v, m_num_bindings++)); + } + } } symbol parse_indexed_identifier_core() { @@ -1613,8 +1775,7 @@ namespace smt2 { new (mem) app_frame(f, expr_spos, param_spos, has_as); m_num_expr_frames++; } - - // return true if a new frame was created. + void push_expr_frame(expr_frame * curr) { SASSERT(curr_is_lparen()); next(); @@ -1622,11 +1783,7 @@ namespace smt2 { if (curr_is_identifier()) { TRACE("push_expr_frame", tout << "push_expr_frame(), curr_id(): " << curr_id() << "\n";); if (curr_id_is_let()) { - next(); - check_lparen_next("invalid let declaration, '(' expected"); - void * mem = m_stack.allocate(sizeof(let_frame)); - new (mem) let_frame(symbol_stack().size(), expr_stack().size()); - m_num_expr_frames++; + push_let_frame(); } else if (curr_id_is_forall()) { push_quant_frame(true); @@ -1635,14 +1792,9 @@ namespace smt2 { push_quant_frame(false); } else if (curr_id_is_bang()) { - TRACE("consume_attributes", tout << "begin bang, expr_stack.size(): " << expr_stack().size() << "\n";); - next(); - void * mem = m_stack.allocate(sizeof(attr_expr_frame)); - new (mem) attr_expr_frame(curr, symbol_stack().size(), expr_stack().size()); - m_num_expr_frames++; + push_bang_frame(curr); } else if (curr_id_is_as() || curr_id_is_underscore()) { - TRACE("push_expr_frame", tout << "push_expr_frame(): parse_qualified_name\n";); parse_qualified_name(); } else if (curr_id_is_root_obj()) { @@ -1825,12 +1977,12 @@ namespace smt2 { m_stack.deallocate(static_cast(fr)); m_num_expr_frames--; break; - case EF_QUANT: - pop_quant_frame(static_cast(fr)); - break; case EF_MATCH: pop_match_frame(static_cast(fr)); break; + case EF_QUANT: + pop_quant_frame(static_cast(fr)); + break; case EF_ATTR_EXPR: pop_attr_expr_frame(static_cast(fr)); break; @@ -2287,8 +2439,10 @@ namespace smt2 { throw cmd_exception("invalid assert command, expression required as argument"); } expr * f = expr_stack().back(); - if (!m().is_bool(f)) + if (!m().is_bool(f)) { + TRACE("smt2parser", tout << expr_ref(f, m()) << "\n";); throw cmd_exception("invalid assert command, term is not Boolean"); + } if (f == m_last_named_expr.second) { m_ctx.assert_expr(m_last_named_expr.first, f); } diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index eef84f773..901f4b5ac 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -4374,9 +4374,17 @@ namespace smt { expr* fn = to_app(q->get_pattern(0))->get_arg(0); expr* body = to_app(q->get_pattern(1))->get_arg(0); SASSERT(is_app(fn)); + // reverse argument order so that variable 0 starts at the beginning. + expr_ref_vector subst(m); + for (expr* arg : *to_app(fn)) { + subst.push_back(arg); + } + expr_ref bodyr(m); + var_subst sub(m, false); + sub(body, subst.size(), subst.c_ptr(), bodyr); func_decl* f = to_app(fn)->get_decl(); func_interp* fi = alloc(func_interp, m, f->get_arity()); - fi->set_else(body); + fi->set_else(bodyr); m_model->register_decl(f, fi); } } From 93e08d9499c20dd23af35c4225b512b83abd86f2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Sep 2017 19:43:23 -0700 Subject: [PATCH 003/148] fix #1261 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_solver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index e78c6388e..d624a5c9a 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -109,8 +109,10 @@ namespace smt { } virtual void assert_expr(expr * t, expr * a) { + if (m_name2assertion.contains(a)) { + throw default_exception("named assertion defined twice"); + } solver_na2as::assert_expr(t, a); - SASSERT(!m_name2assertion.contains(a)); get_manager().inc_ref(t); m_name2assertion.insert(a, t); } From cb15473d5b759b6973666f98693a5583d4f70cb1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Sep 2017 20:02:41 -0700 Subject: [PATCH 004/148] remove type annotation from var printing Signed-off-by: Nikolaj Bjorner --- src/ast/ast_smt2_pp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index cad2265c8..e4a875005 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -584,8 +584,8 @@ class smt2_printer { string_buffer<> buf; buf.append("(:var "); buf.append(v->get_idx()); - buf.append(" "); - buf.append(v->get_sort()->get_name().str().c_str()); + //buf.append(" "); + //buf.append(v->get_sort()->get_name().str().c_str()); buf.append(")"); f = mk_string(m(), buf.c_str()); } From da2826b55e29317cef6db8bc07bfd0c6f0c0d788 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Wed, 20 Sep 2017 16:13:41 +0200 Subject: [PATCH 005/148] Fix warnings in C++ API When assertions are disabled, the compiler warns about unused function parameters. --- src/api/c++/z3++.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 6d8ff3b35..8938fb1b5 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -354,7 +354,7 @@ namespace z3 { Z3_error_code check_error() const { return m_ctx->check_error(); } friend void check_context(object const & a, object const & b); }; - inline void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } + inline void check_context(object const & a, object const & b) { (void)a; (void)b; assert(a.m_ctx == b.m_ctx); } class symbol : public object { Z3_symbol m_sym; From 936c22a00b6ada5abc238d7b98fdf8b93a5aaaef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Sep 2017 09:44:38 -0700 Subject: [PATCH 006/148] add pattern match validation Signed-off-by: Nikolaj Bjorner --- src/parsers/smt2/smt2parser.cpp | 16 ++++++++++++++++ src/smt/mam.cpp | 4 ---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 6b62edcbd..b4648f0d6 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -1351,6 +1351,7 @@ namespace smt2 { expr_ref result(m()); var_subst sub(m(), false); TRACE("parse_expr", tout << "term\n" << expr_ref(t, m()) << "\npatterns\n" << patterns << "\ncases\n" << cases << "\n";); + check_patterns(patterns, m().get_sort(t)); for (unsigned i = patterns.size(); i > 0; ) { --i; expr_ref_vector subst(m()); @@ -1376,6 +1377,21 @@ namespace smt2 { return result; } + void check_patterns(expr_ref_vector const& patterns, sort* s) { + if (!dtutil().is_datatype(s)) + throw parser_exception("pattern matching is only supported for algebraic datatypes"); + ptr_vector const& cons = *dtutil().get_datatype_constructors(s); + for (expr * arg : patterns) if (is_var(arg)) return; + if (patterns.size() < cons.size()) + throw parser_exception("non-exhaustive pattern match"); + ast_fast_mark1 marked; + for (expr * arg : patterns) + marked.mark(to_app(arg)->get_decl(), true); + for (func_decl * f : cons) + if (!marked.is_marked(f)) + throw parser_exception("a constructor is missing from pattern match"); + } + // compute match condition and substitution // t is shifted by size of subst. expr_ref bind_match(expr* t, expr* pattern, expr_ref_vector& subst) { diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 2aa9d6095..a88ef1e0d 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -1753,10 +1753,6 @@ namespace smt { m_use_filters(use_filters) { } - context & get_context() { - return m_context; - } - /** \brief Create a new code tree for the given quantifier. From cc9f67267d30bf8909adf9894f9f0fba13f10444 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 20 Sep 2017 20:16:09 +0100 Subject: [PATCH 007/148] Eliminated the remaining operator kinds for partially unspecified FP operators. --- src/api/z3_api.h | 17 -- src/ast/fpa/bv2fpa_converter.cpp | 4 +- src/ast/fpa/fpa2bv_converter.cpp | 288 +++++++++++------------------- src/ast/fpa/fpa2bv_converter.h | 19 +- src/ast/fpa/fpa2bv_rewriter.cpp | 12 +- src/ast/fpa_decl_plugin.cpp | 10 -- src/ast/fpa_decl_plugin.h | 18 +- src/ast/rewriter/fpa_rewriter.cpp | 76 ++------ src/smt/theory_fpa.cpp | 18 -- src/smt/theory_fpa.h | 3 - 10 files changed, 125 insertions(+), 340 deletions(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index bfc0b93a2..154b8eb3c 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -993,18 +993,6 @@ typedef enum 3 = 011 = Z3_OP_FPA_RM_TOWARD_NEGATIVE, 4 = 100 = Z3_OP_FPA_RM_TOWARD_ZERO. - - Z3_OP_FPA_MIN_I: The same as Z3_OP_FPA_MIN, but the arguments are - expected not to be zeroes with different signs. - - - Z3_OP_FPA_MAX_I: The same as Z3_OP_FPA_MAX, but the arguments are - expected not to be zeroes with different signs. - - - Z3_OP_FPA_MIN_UNSPECIFIED: The same as Z3_OP_FPA_MIN, but the - arguments are expected to be zeroes with different signs. - - - Z3_OP_FPA_MAX_UNSPECIFIED: The same as Z3_OP_FPA_MAX, but the - arguments are expected to be zeroes with different signs. - - Z3_OP_INTERNAL: internal (often interpreted) symbol, but no additional information is exposed. Tools may use the string representation of the function declaration to obtain more information. @@ -1291,13 +1279,8 @@ typedef enum { Z3_OP_FPA_TO_IEEE_BV, - Z3_OP_FPA_MIN_I, - Z3_OP_FPA_MAX_I, - Z3_OP_FPA_BVWRAP, Z3_OP_FPA_BV2RM, - Z3_OP_FPA_MIN_UNSPECIFIED, - Z3_OP_FPA_MAX_UNSPECIFIED, Z3_OP_INTERNAL, diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index 30d41bc53..93bd79571 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -62,8 +62,8 @@ bv2fpa_converter::bv2fpa_converter(ast_manager & m, fpa2bv_converter & conv) : m.inc_ref(it->m_key); m.inc_ref(it->m_value); } - for (obj_map >::iterator it = conv.m_min_max_specials.begin(); - it != conv.m_min_max_specials.end(); + for (obj_map >::iterator it = conv.m_min_max_ufs.begin(); + it != conv.m_min_max_ufs.end(); it++) { m_specials.insert(it->m_key, it->m_value); m.inc_ref(it->m_key); diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index bc0364994..f09ddcd42 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -230,39 +230,7 @@ void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) result = m_util.mk_fp(sgn, e, s); } -void fpa2bv_converter::mk_function_output(sort * rng, func_decl * fbv, expr * const * new_args, expr_ref & result) { - if (m_util.is_float(rng)) { - unsigned ebits = m_util.get_ebits(rng); - unsigned sbits = m_util.get_sbits(rng); - unsigned bv_sz = ebits + sbits; - - app_ref na(m); - na = m.mk_app(fbv, fbv->get_arity(), new_args); - result = m_util.mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, na), - m_bv_util.mk_extract(bv_sz - 2, sbits - 1, na), - m_bv_util.mk_extract(sbits - 2, 0, na)); - } - else if (m_util.is_rm(rng)) { - app_ref na(m); - na = m.mk_app(fbv, fbv->get_arity(), new_args); - result = m_util.mk_bv2rm(na); - } - else - result = m.mk_app(fbv, fbv->get_arity(), new_args); -} - -func_decl * fpa2bv_converter::get_bv_uf(func_decl * f, sort * bv_rng, unsigned arity) { - func_decl * res; - if (!m_uf2bvuf.find(f, res)) { - res = m.mk_fresh_func_decl(f->get_name(), symbol("bv"), arity, f->get_domain(), bv_rng); - m_uf2bvuf.insert(f, res); - m.inc_ref(f); - m.inc_ref(res); - TRACE("fpa2bv", tout << "New UF func_decl: " << std::endl << mk_ismt2_pp(res, m) << std::endl;); - } - return res; -} -void fpa2bv_converter::mk_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +void fpa2bv_converter::mk_uf(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); @@ -278,7 +246,7 @@ void fpa2bv_converter::mk_function(func_decl * f, unsigned num, expr * const * a unsigned sbits = m_util.get_sbits(rng); unsigned bv_sz = ebits+sbits; bv_rng = m_bv_util.mk_sort(bv_sz); - func_decl * bv_f = get_bv_uf(f, bv_rng, num); + func_decl * bv_f = mk_bv_uf(f, f->get_domain(), bv_rng); bv_app = m.mk_app(bv_f, num, args); flt_app = m_util.mk_fp(m_bv_util.mk_extract(bv_sz-1, bv_sz-1, bv_app), m_bv_util.mk_extract(sbits+ebits-2, sbits-1, bv_app), @@ -291,7 +259,7 @@ void fpa2bv_converter::mk_function(func_decl * f, unsigned num, expr * const * a sort_ref bv_rng(m); expr_ref new_eq(m); bv_rng = m_bv_util.mk_sort(3); - func_decl * bv_f = get_bv_uf(f, bv_rng, num); + func_decl * bv_f = mk_bv_uf(f, f->get_domain(), bv_rng); bv_app = m.mk_app(bv_f, num, args); flt_app = m_util.mk_bv2rm(bv_app); new_eq = m.mk_eq(fapp, flt_app); @@ -1211,61 +1179,89 @@ void fpa2bv_converter::mk_abs(sort * s, expr_ref & x, expr_ref & result) { } void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - expr_ref x(m), y(m); - x = args[0]; - y = args[1]; - - expr_ref x_is_zero(m), y_is_zero(m), both_are_zero(m); - x_is_zero = m_util.mk_is_zero(x); - y_is_zero = m_util.mk_is_zero(y); - both_are_zero = m.mk_and(x_is_zero, y_is_zero); - - expr_ref x_is_positive(m), x_is_negative(m), y_is_positive(m), y_is_negative(m), pn(m), np(m), pn_or_np(m); - x_is_positive = m_util.mk_is_positive(x); - x_is_negative = m_util.mk_is_negative(x); - y_is_positive = m_util.mk_is_positive(y); - y_is_negative = m_util.mk_is_negative(y); - pn = m.mk_and(x_is_positive, y_is_negative); - np = m.mk_and(x_is_negative, y_is_positive); - pn_or_np = m.mk_or(pn, np); - - expr_ref c(m), v(m); - c = m.mk_and(both_are_zero, pn_or_np); - v = m.mk_app(m_util.get_family_id(), OP_FPA_MIN_UNSPECIFIED, x, y); - - // Note: This requires BR_REWRITE_FULL afterwards. - expr_ref min_i(m); - min_i = m.mk_app(m_util.get_family_id(), OP_FPA_MIN_I, x, y); - m_simp.mk_ite(c, v, min_i, result); -} - -void fpa2bv_converter::mk_min_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); expr * x = args[0], * y = args[1]; - expr * x_sgn, * x_sig, * x_exp; - expr * y_sgn, * y_sig, * y_exp; + expr * x_sgn, *x_sig, *x_exp; + expr * y_sgn, *y_sig, *y_exp; split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); - expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), both_zero(m), pzero(m); - mk_is_zero(x, x_is_zero); - mk_is_zero(y, y_is_zero); - m_simp.mk_and(x_is_zero, y_is_zero, both_zero); + expr_ref bv0(m), bv1(m); + bv0 = m_bv_util.mk_numeral(0, 1); + bv1 = m_bv_util.mk_numeral(1, 1); + + expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), xy_are_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); - mk_pzero(f, pzero); + mk_is_zero(x, x_is_zero); + mk_is_zero(y, y_is_zero); + xy_are_zero = m.mk_and(x_is_zero, y_is_zero); - expr_ref sgn_eq(m), sgn_diff(m); - sgn_eq = m.mk_eq(x_sgn, y_sgn); - sgn_diff = m.mk_not(sgn_eq); + expr_ref x_is_pos(m), x_is_neg(m); + expr_ref y_is_pos(m), y_is_neg(m); + expr_ref pn(m), np(m), pn_or_np_zeros(m); + mk_is_pos(x, x_is_pos); + mk_is_pos(y, y_is_pos); + mk_is_neg(x, x_is_neg); + mk_is_neg(y, y_is_neg); + pn_or_np_zeros = m.mk_and(xy_are_zero, m.mk_not(m.mk_eq(x_sgn, y_sgn))); - expr_ref lt(m); - mk_float_lt(f, num, args, lt); + expr_ref unspec(m); + unspec = mk_min_max_unspecified(f, x, y); - mk_ite(lt, x, y, result); - mk_ite(both_zero, y, result, result); + expr_ref x_lt_y(m); + mk_float_lt(f, num, args, x_lt_y); + + mk_ite(x_lt_y, x, y, result); + mk_ite(xy_are_zero, y, result, result); + mk_ite(pn_or_np_zeros, unspec, result, result); + mk_ite(y_is_nan, x, result, result); + mk_ite(x_is_nan, y, result, result); + + SASSERT(is_well_sorted(m, result)); +} + +void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + + expr * x = args[0], *y = args[1]; + + expr * x_sgn, *x_sig, *x_exp; + expr * y_sgn, *y_sig, *y_exp; + split_fp(x, x_sgn, x_exp, x_sig); + split_fp(y, y_sgn, y_exp, y_sig); + + expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), xy_are_zero(m); + mk_is_nan(x, x_is_nan); + mk_is_nan(y, y_is_nan); + mk_is_zero(x, x_is_zero); + mk_is_zero(y, y_is_zero); + xy_are_zero = m.mk_and(x_is_zero, y_is_zero); + + expr_ref x_is_pos(m), x_is_neg(m); + expr_ref y_is_pos(m), y_is_neg(m); + expr_ref pn(m), np(m), pn_or_np_zeros(m); + mk_is_pos(x, x_is_pos); + mk_is_pos(y, y_is_pos); + mk_is_neg(x, x_is_neg); + mk_is_neg(y, y_is_neg); + pn_or_np_zeros = m.mk_and(xy_are_zero, m.mk_not(m.mk_eq(x_sgn, y_sgn))); + + expr_ref unspec(m); + unspec = mk_min_max_unspecified(f, x, y); + + expr_ref x_gt_y(m); + mk_float_gt(f, num, args, x_gt_y); + + mk_ite(x_gt_y, x, y, result); + mk_ite(xy_are_zero, y, result, result); + mk_ite(pn_or_np_zeros, unspec, result, result); mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); @@ -1281,10 +1277,10 @@ expr_ref fpa2bv_converter::mk_min_max_unspecified(func_decl * f, expr * x, expr // There is no "hardware interpretation" for fp.min/fp.max. std::pair decls(0, 0); - if (!m_min_max_specials.find(f, decls)) { + if (!m_min_max_ufs.find(f, decls)) { decls.first = m.mk_fresh_const(0, m_bv_util.mk_sort(1)); decls.second = m.mk_fresh_const(0, m_bv_util.mk_sort(1)); - m_min_max_specials.insert(f, decls); + m_min_max_ufs.insert(f, decls); m.inc_ref(f); m.inc_ref(decls.first); m.inc_ref(decls.second); @@ -1303,68 +1299,6 @@ expr_ref fpa2bv_converter::mk_min_max_unspecified(func_decl * f, expr * x, expr return res; } -void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - expr_ref x(m), y(m); - x = args[0]; - y = args[1]; - - expr_ref x_is_zero(m), y_is_zero(m), both_are_zero(m); - x_is_zero = m_util.mk_is_zero(x); - y_is_zero = m_util.mk_is_zero(y); - both_are_zero = m.mk_and(x_is_zero, y_is_zero); - - expr_ref x_is_positive(m), x_is_negative(m), y_is_positive(m), y_is_negative(m), pn(m), np(m), pn_or_np(m); - x_is_positive = m_util.mk_is_positive(x); - x_is_negative = m_util.mk_is_negative(x); - y_is_positive = m_util.mk_is_positive(y); - y_is_negative = m_util.mk_is_negative(y); - pn = m.mk_and(x_is_positive, y_is_negative); - np = m.mk_and(x_is_negative, y_is_positive); - pn_or_np = m.mk_or(pn, np); - - expr_ref c(m), v(m); - c = m.mk_and(both_are_zero, pn_or_np); - v = m.mk_app(m_util.get_family_id(), OP_FPA_MAX_UNSPECIFIED, x, y); - - // Note: This requires BR_REWRITE_FULL afterwards. - expr_ref max_i(m); - max_i = m.mk_app(m_util.get_family_id(), OP_FPA_MAX_I, x, y); - m_simp.mk_ite(c, v, max_i, result); -} - -void fpa2bv_converter::mk_max_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - SASSERT(num == 2); - - expr * x = args[0], *y = args[1]; - - expr * x_sgn, *x_sig, *x_exp; - expr * y_sgn, *y_sig, *y_exp; - split_fp(x, x_sgn, x_exp, x_sig); - split_fp(y, y_sgn, y_exp, y_sig); - - expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), both_zero(m), pzero(m); - mk_is_zero(x, x_is_zero); - mk_is_zero(y, y_is_zero); - m_simp.mk_and(x_is_zero, y_is_zero, both_zero); - mk_is_nan(x, x_is_nan); - mk_is_nan(y, y_is_nan); - mk_pzero(f, pzero); - - expr_ref sgn_diff(m), sgn_eq(m); - sgn_eq = m.mk_eq(x_sgn, y_sgn); - sgn_diff = m.mk_not(sgn_eq); - - expr_ref gt(m); - mk_float_gt(f, num, args, gt); - - mk_ite(gt, x, y, result); - mk_ite(both_zero, y, result, result); - mk_ite(y_is_nan, x, result, result); - mk_ite(x_is_nan, y, result, result); - - SASSERT(is_well_sorted(m, result)); -} - void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 4); SASSERT(m_util.is_bv2rm(args[0])); @@ -3150,19 +3084,12 @@ void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * unsigned ebits = m_util.get_ebits(fp_srt); unsigned sbits = m_util.get_sbits(fp_srt); - expr_ref nanv(m); - if (m_hi_fp_unspecified) - // The "hardware interpretation" is 01...10...01. - nanv = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 1), - m_bv_util.mk_concat(m_bv_util.mk_numeral(-1, ebits), - m_bv_util.mk_concat(m_bv_util.mk_numeral(0, sbits - 2), - m_bv_util.mk_numeral(1, 1)))); - else - mk_to_ieee_bv_unspecified(f, num, args, nanv); + expr_ref unspec(m); + mk_to_ieee_bv_unspecified(f, num, args, unspec); expr_ref sgn_e_s(m); join_fp(x, sgn_e_s); - m_simp.mk_ite(x_is_nan, nanv, sgn_e_s, result); + m_simp.mk_ite(x_is_nan, unspec, sgn_e_s, result); TRACE("fpa2bv_to_ieee_bv", tout << "result=" << mk_ismt2_pp(result, m) << std::endl;); SASSERT(is_well_sorted(m, result)); @@ -3174,26 +3101,15 @@ void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, ex unsigned ebits = f->get_domain()[0]->get_parameter(0).get_int(); unsigned sbits = f->get_domain()[0]->get_parameter(1).get_int(); - if (m_hi_fp_unspecified) { - result = m_bv_util.mk_concat(m_bv_util.mk_concat( - m_bv_util.mk_numeral(0, 1), - m_bv_util.mk_numeral(-1, ebits)), - m_bv_util.mk_numeral(1, sbits-1)); - } + if (m_hi_fp_unspecified) + mk_nan(f->get_range(), result); else { expr * n = args[0]; expr_ref n_bv(m); join_fp(n, n_bv); - func_decl * f_bv; - if (!m_uf2bvuf.find(f, f_bv)) { - sort * domain[2] = { m.get_sort(n_bv) }; - f_bv = m.mk_fresh_func_decl(0, 1, domain, f->get_range()); - m_uf2bvuf.insert(f, f_bv); - m.inc_ref(f); - m.inc_ref(f_bv); - } - + sort * domain[1] = { m.get_sort(n_bv) }; + func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); result = m.mk_app(f_bv, n_bv); expr_ref exp_bv(m), exp_all_ones(m); @@ -3382,14 +3298,8 @@ void fpa2bv_converter::mk_to_bv_unspecified(func_decl * f, unsigned num, expr * expr_ref n_bv(m); join_fp(n, n_bv); - func_decl * f_bv; - if (!m_uf2bvuf.find(f, f_bv)) { - sort * domain[2] = { m.get_sort(rm_bv), m.get_sort(n_bv) }; - f_bv = m.mk_fresh_func_decl(0, 2, domain, f->get_range()); - m_uf2bvuf.insert(f, f_bv); - m.inc_ref(f); - m.inc_ref(f_bv); - } + sort * domain[2] = { m.get_sort(rm_bv), m.get_sort(n_bv) }; + func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); result = m.mk_app(f_bv, rm_bv, n_bv); } @@ -3407,14 +3317,8 @@ void fpa2bv_converter::mk_to_real_unspecified(func_decl * f, unsigned num, expr expr_ref n_bv(m); join_fp(n, n_bv); - func_decl * f_bv; - if (!m_uf2bvuf.find(f, f_bv)) { - sort * domain[2] = { m.get_sort(n_bv) }; - f_bv = m.mk_fresh_func_decl(0, 1, domain, f->get_range()); - m_uf2bvuf.insert(f, f_bv); - m.inc_ref(f); - m.inc_ref(f_bv); - } + sort * domain[1] = { m.get_sort(n_bv) }; + func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); result = m.mk_app(f_bv, n_bv); } } @@ -4190,13 +4094,25 @@ void fpa2bv_converter::reset(void) { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); dec_ref_map_key_values(m, m_uf2bvuf); - for (obj_map >::iterator it = m_min_max_specials.begin(); - it != m_min_max_specials.end(); + for (obj_map >::iterator it = m_min_max_ufs.begin(); + it != m_min_max_ufs.end(); it++) { m.dec_ref(it->m_key); m.dec_ref(it->m_value.first); m.dec_ref(it->m_value.second); } - m_min_max_specials.reset(); + m_min_max_ufs.reset(); m_extra_assertions.reset(); } + +func_decl * fpa2bv_converter::mk_bv_uf(func_decl * f, sort * const * domain, sort * range) { + func_decl * res; + if (!m_uf2bvuf.find(f, res)) { + res = m.mk_fresh_func_decl(0, f->get_arity(), domain, range); + m_uf2bvuf.insert(f, res); + m.inc_ref(f); + m.inc_ref(res); + TRACE("fpa2bv", tout << "New UF func_decl: " << std::endl << mk_ismt2_pp(res, m) << std::endl;); + } + return res; +} diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index d54adac78..f0e50ba2d 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -53,7 +53,7 @@ protected: const2bv_t m_const2bv; const2bv_t m_rm_const2bv; uf2bvuf_t m_uf2bvuf; - special_t m_min_max_specials; + special_t m_min_max_ufs; friend class fpa2bv_model_converter; friend class bv2fpa_converter; @@ -87,7 +87,7 @@ public: void mk_numeral(sort * s, mpf const & v, expr_ref & result); virtual void mk_const(func_decl * f, expr_ref & result); virtual void mk_rm_const(func_decl * f, expr_ref & result); - virtual void mk_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + virtual void mk_uf(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_var(unsigned base_inx, sort * srt, expr_ref & result); void mk_pinf(func_decl * f, expr_ref & result); @@ -147,18 +147,15 @@ public: void set_unspecified_fp_hi(bool v) { m_hi_fp_unspecified = v; } void mk_min(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_min_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - virtual expr_ref mk_min_max_unspecified(func_decl * f, expr * x, expr * y); - void mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - void mk_max_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + expr_ref mk_min_max_unspecified(func_decl * f, expr * x, expr * y); void reset(void); void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector m_extra_assertions; - special_t const & get_min_max_specials() const { return m_min_max_specials; }; + special_t const & get_min_max_specials() const { return m_min_max_ufs; }; const2bv_t const & get_const2bv() const { return m_const2bv; }; const2bv_t const & get_rm_const2bv() const { return m_rm_const2bv; }; uf2bvuf_t const & get_uf2bvuf() const { return m_uf2bvuf; }; @@ -202,12 +199,6 @@ protected: void mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result); - sort_ref replace_float_sorts(sort * s); - func_decl_ref replace_function(func_decl * f); - expr_ref replace_float_arg(expr * a); - void mk_function_output(sort * rng, func_decl * fbv, expr * const * new_args, expr_ref & result); - func_decl * get_bv_uf(func_decl * f, sort * bv_rng, unsigned arity); - private: void mk_nan(sort * s, expr_ref & result); void mk_nzero(sort * s, expr_ref & result); @@ -226,6 +217,8 @@ private: void mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & x, expr_ref & result); void mk_to_fp_float(sort * s, expr * rm, expr * x, expr_ref & result); + + func_decl * mk_bv_uf(func_decl * f, sort * const * domain, sort * range); }; #endif diff --git a/src/ast/fpa/fpa2bv_rewriter.cpp b/src/ast/fpa/fpa2bv_rewriter.cpp index 07623ec48..6c96d92c1 100644 --- a/src/ast/fpa/fpa2bv_rewriter.cpp +++ b/src/ast/fpa/fpa2bv_rewriter.cpp @@ -124,6 +124,8 @@ br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * co case OP_FPA_DIV: m_conv.mk_div(f, num, args, result); return BR_DONE; case OP_FPA_REM: m_conv.mk_rem(f, num, args, result); return BR_DONE; case OP_FPA_ABS: m_conv.mk_abs(f, num, args, result); return BR_DONE; + case OP_FPA_MIN: m_conv.mk_min(f, num, args, result); return BR_DONE; + case OP_FPA_MAX: m_conv.mk_max(f, num, args, result); return BR_DONE; case OP_FPA_FMA: m_conv.mk_fma(f, num, args, result); return BR_DONE; case OP_FPA_SQRT: m_conv.mk_sqrt(f, num, args, result); return BR_DONE; case OP_FPA_ROUND_TO_INTEGRAL: m_conv.mk_round_to_integral(f, num, args, result); return BR_DONE; @@ -147,14 +149,6 @@ br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * co case OP_FPA_TO_REAL: m_conv.mk_to_real(f, num, args, result); return BR_DONE; case OP_FPA_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; - case OP_FPA_MIN: m_conv.mk_min(f, num, args, result); return BR_REWRITE_FULL; - case OP_FPA_MAX: m_conv.mk_max(f, num, args, result); return BR_REWRITE_FULL; - - case OP_FPA_MIN_UNSPECIFIED: - case OP_FPA_MAX_UNSPECIFIED: result = m_conv.mk_min_max_unspecified(f, args[0], args[1]); return BR_DONE; - case OP_FPA_MIN_I: m_conv.mk_min_i(f, num, args, result); return BR_DONE; - case OP_FPA_MAX_I: m_conv.mk_max_i(f, num, args, result); return BR_DONE; - case OP_FPA_BVWRAP: case OP_FPA_BV2RM: return BR_FAILED; @@ -169,7 +163,7 @@ br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * co { SASSERT(!m_conv.is_float_family(f)); if (m_conv.fu().contains_floats(f)) { - m_conv.mk_function(f, num, args, result); + m_conv.mk_uf(f, num, args, result); return BR_DONE; } } diff --git a/src/ast/fpa_decl_plugin.cpp b/src/ast/fpa_decl_plugin.cpp index 9bee51af7..9d298b413 100644 --- a/src/ast/fpa_decl_plugin.cpp +++ b/src/ast/fpa_decl_plugin.cpp @@ -361,10 +361,6 @@ func_decl * fpa_decl_plugin::mk_binary_decl(decl_kind k, unsigned num_parameters case OP_FPA_REM: name = "fp.rem"; break; case OP_FPA_MIN: name = "fp.min"; break; case OP_FPA_MAX: name = "fp.max"; break; - case OP_FPA_MIN_I: name = "fp.min_i"; break; - case OP_FPA_MAX_I: name = "fp.max_i"; break; - case OP_FPA_MIN_UNSPECIFIED: name = "fp.min_unspecified"; break; - case OP_FPA_MAX_UNSPECIFIED: name = "fp.max_unspecified"; break; default: UNREACHABLE(); break; @@ -781,12 +777,6 @@ func_decl * fpa_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, case OP_FPA_BV2RM: return mk_bv2rm(k, num_parameters, parameters, arity, domain, range); - case OP_FPA_MIN_I: - case OP_FPA_MAX_I: - case OP_FPA_MIN_UNSPECIFIED: - case OP_FPA_MAX_UNSPECIFIED: - return mk_binary_decl(k, num_parameters, parameters, arity, domain, range); - default: m_manager->raise_exception("unsupported floating point operator"); return 0; diff --git a/src/ast/fpa_decl_plugin.h b/src/ast/fpa_decl_plugin.h index f59c8f92f..4e86c9d3f 100644 --- a/src/ast/fpa_decl_plugin.h +++ b/src/ast/fpa_decl_plugin.h @@ -89,11 +89,6 @@ enum fpa_op_kind { OP_FPA_BVWRAP, OP_FPA_BV2RM, - OP_FPA_MIN_I, - OP_FPA_MAX_I, - OP_FPA_MIN_UNSPECIFIED, - OP_FPA_MAX_UNSPECIFIED, - LAST_FLOAT_OP }; @@ -351,21 +346,12 @@ public: } bool is_bvwrap(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_BVWRAP); } - bool is_bvwrap(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_BVWRAP; } bool is_bv2rm(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_BV2RM); } - bool is_bv2rm(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_BV2RM; } - - bool is_min_interpreted(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_MIN_I); } - bool is_min_unspecified(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_MIN_UNSPECIFIED); } - bool is_max_interpreted(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_MAX_I); } - bool is_max_unspecified(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_MAX_UNSPECIFIED); } bool is_to_ubv(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_TO_UBV); } bool is_to_sbv(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_TO_SBV); } - bool is_min_interpreted(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_MIN_I; } - bool is_min_unspecified(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_MIN_UNSPECIFIED; } - bool is_max_interpreted(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_MAX_I; } - bool is_max_unspecified(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_MAX_UNSPECIFIED; } + bool is_bvwrap(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_BVWRAP; } + bool is_bv2rm(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_BV2RM; } bool is_to_ubv(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_UBV; } bool is_to_sbv(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_SBV; } diff --git a/src/ast/rewriter/fpa_rewriter.cpp b/src/ast/rewriter/fpa_rewriter.cpp index 6420dd968..818336c75 100644 --- a/src/ast/rewriter/fpa_rewriter.cpp +++ b/src/ast/rewriter/fpa_rewriter.cpp @@ -94,12 +94,6 @@ br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con case OP_FPA_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(f, args[0], result); break; case OP_FPA_TO_REAL: SASSERT(num_args == 1); st = mk_to_real(args[0], result); break; - case OP_FPA_MIN_I:SASSERT(num_args == 2); st = mk_min_i(f, args[0], args[1], result); break; - case OP_FPA_MAX_I: SASSERT(num_args == 2); st = mk_max_i(f, args[0], args[1], result); break; - case OP_FPA_MIN_UNSPECIFIED: - case OP_FPA_MAX_UNSPECIFIED: - SASSERT(num_args == 2); st = BR_FAILED; break; - case OP_FPA_BVWRAP: SASSERT(num_args == 1); st = mk_bvwrap(args[0], result); break; case OP_FPA_BV2RM: SASSERT(num_args == 1); st = mk_bv2rm(args[0], result); break; @@ -385,38 +379,13 @@ br_status fpa_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) { } scoped_mpf v1(m_fm), v2(m_fm); - if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { - if (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1) != m_fm.sgn(v2)) { - result = m().mk_app(get_fid(), OP_FPA_MIN_UNSPECIFIED, arg1, arg2); - return BR_REWRITE1; - } - else { - scoped_mpf r(m_fm); - m_fm.minimum(v1, v2, r); - result = m_util.mk_value(r); - return BR_DONE; - } - } - else { - expr_ref c(m()), v(m()); - c = m().mk_and(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2)), - m().mk_or(m().mk_and(m_util.mk_is_positive(arg1), m_util.mk_is_negative(arg2)), - m().mk_and(m_util.mk_is_negative(arg1), m_util.mk_is_positive(arg2)))); - v = m().mk_app(get_fid(), OP_FPA_MIN_UNSPECIFIED, arg1, arg2); - - result = m().mk_ite(c, v, m().mk_app(get_fid(), OP_FPA_MIN_I, arg1, arg2)); - return BR_REWRITE_FULL; - } -} - -br_status fpa_rewriter::mk_min_i(func_decl * f, expr * arg1, expr * arg2, expr_ref & result) { - scoped_mpf v1(m_fm), v2(m_fm); - if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { if (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1) != m_fm.sgn(v2)) - result = m().mk_app(get_fid(), OP_FPA_MIN_UNSPECIFIED, arg1, arg2); - else - result = m_util.mk_min(arg1, arg2); + return BR_FAILED; + + scoped_mpf r(m_fm); + m_fm.minimum(v1, v2, r); + result = m_util.mk_value(r); return BR_DONE; } @@ -434,38 +403,13 @@ br_status fpa_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { } scoped_mpf v1(m_fm), v2(m_fm); - if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { - if (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1) != m_fm.sgn(v2)) { - result = m().mk_app(get_fid(), OP_FPA_MAX_UNSPECIFIED, arg1, arg2); - return BR_REWRITE1; - } - else { - scoped_mpf r(m_fm); - m_fm.maximum(v1, v2, r); - result = m_util.mk_value(r); - return BR_DONE; - } - } - else { - expr_ref c(m()), v(m()); - c = m().mk_and(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2)), - m().mk_or(m().mk_and(m_util.mk_is_positive(arg1), m_util.mk_is_negative(arg2)), - m().mk_and(m_util.mk_is_negative(arg1), m_util.mk_is_positive(arg2)))); - v = m().mk_app(get_fid(), OP_FPA_MAX_UNSPECIFIED, arg1, arg2); - - result = m().mk_ite(c, v, m().mk_app(get_fid(), OP_FPA_MAX_I, arg1, arg2)); - return BR_REWRITE_FULL; - } -} - -br_status fpa_rewriter::mk_max_i(func_decl * f, expr * arg1, expr * arg2, expr_ref & result) { - scoped_mpf v1(m_fm), v2(m_fm); - if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { if (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1) != m_fm.sgn(v2)) - result = m().mk_app(get_fid(), OP_FPA_MIN_UNSPECIFIED, arg1, arg2); - else - result = m_util.mk_max(arg1, arg2); + return BR_FAILED; + + scoped_mpf r(m_fm); + m_fm.maximum(v1, v2, r); + result = m_util.mk_value(r); return BR_DONE; } diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index d337cc65d..cc9b0017d 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -884,22 +884,4 @@ namespace smt { out << r->get_id() << " --> " << mk_ismt2_pp(n, m) << std::endl; } } - - bool theory_fpa::include_func_interp(func_decl * f) { - TRACE("t_fpa", tout << "f = " << mk_ismt2_pp(f, get_manager()) << std::endl;); - - if (f->get_family_id() == get_family_id()) { - bool include = - m_fpa_util.is_min_unspecified(f) || - m_fpa_util.is_max_unspecified(f) ; - if (include && !m_is_added_to_model.contains(f)) { - //m_is_added_to_model.insert(f); - //get_manager().inc_ref(f); - return true; - } - return false; - } - else - return true; - } }; diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h index 7be82816e..9e9801ee0 100644 --- a/src/smt/theory_fpa.h +++ b/src/smt/theory_fpa.h @@ -158,7 +158,6 @@ namespace smt { virtual char const * get_name() const { return "fpa"; } virtual model_value_proc * mk_value(enode * n, model_generator & mg); - virtual bool include_func_interp(func_decl * f); void assign_eh(bool_var v, bool is_true); virtual void relevant_eh(app * n); @@ -180,8 +179,6 @@ namespace smt { expr_ref convert_term(expr * e); expr_ref convert_conversion_term(expr * e); - void add_trail(ast * a); - void attach_new_th_var(enode * n); void assert_cnstr(expr * e); From 048ee090b02ac07709ba88979db05c3065835a95 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 20 Sep 2017 20:19:36 +0100 Subject: [PATCH 008/148] Eliminated the remaining operator kinds for partially unspecified FP operators from the AST API. --- src/api/api_ast.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 491e08cf4..e22603225 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -1204,12 +1204,8 @@ extern "C" { case OP_FPA_TO_SBV: return Z3_OP_FPA_TO_SBV; case OP_FPA_TO_REAL: return Z3_OP_FPA_TO_REAL; case OP_FPA_TO_IEEE_BV: return Z3_OP_FPA_TO_IEEE_BV; - case OP_FPA_MIN_I: return Z3_OP_FPA_MIN_I; - case OP_FPA_MAX_I: return Z3_OP_FPA_MAX_I; case OP_FPA_BVWRAP: return Z3_OP_FPA_BVWRAP; case OP_FPA_BV2RM: return Z3_OP_FPA_BV2RM; - case OP_FPA_MIN_UNSPECIFIED: return Z3_OP_FPA_MIN_UNSPECIFIED; - case OP_FPA_MAX_UNSPECIFIED: return Z3_OP_FPA_MAX_UNSPECIFIED; return Z3_OP_UNINTERPRETED; default: return Z3_OP_INTERNAL; From 320105c71406f557a8c7fc470054ac1af5e213db Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Sep 2017 13:30:31 -0700 Subject: [PATCH 009/148] removing iterators Signed-off-by: Nikolaj Bjorner --- src/smt/mam.cpp | 18 +++++------------- src/smt/qi_queue.cpp | 5 +---- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index a88ef1e0d..497aab8db 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -2016,26 +2016,20 @@ namespace smt { void execute(code_tree * t) { TRACE("trigger_bug", tout << "execute for code tree:\n"; t->display(tout);); init(t); - enode_vector::const_iterator it = t->get_candidates().begin(); - enode_vector::const_iterator end = t->get_candidates().end(); if (t->filter_candidates()) { - for (; it != end; ++it) { - enode * app = *it; + for (enode * app : t->get_candidates()) { if (!app->is_marked() && app->is_cgr()) { execute_core(t, app); app->set_mark(); } } - it = t->get_candidates().begin(); - for (; it != end; ++it) { - enode * app = *it; + for (enode * app : t->get_candidates()) { if (app->is_marked()) app->unset_mark(); } } else { - for (; it != end; ++it) { - enode * app = *it; + for (enode * app : t->get_candidates()) { TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_owner(), m_ast_manager) << "\n";); if (app->is_cgr()) { TRACE("trigger_bug", tout << "is_cgr\n";); @@ -2821,15 +2815,13 @@ namespace smt { } // end of execute_core void display_trees(std::ostream & out, const ptr_vector & trees) { - ptr_vector::const_iterator it = trees.begin(); - ptr_vector::const_iterator end = trees.end(); unsigned lbl = 0; - for (; it != end; ++it, ++lbl) { - code_tree * tree = *it; + for (code_tree* tree : trees) { if (tree) { out << "tree for f" << lbl << "\n"; out << *tree; } + ++lbl; } } diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp index f77fde29a..2a8d9d199 100644 --- a/src/smt/qi_queue.cpp +++ b/src/smt/qi_queue.cpp @@ -148,11 +148,8 @@ namespace smt { } void qi_queue::instantiate() { - svector::iterator it = m_new_entries.begin(); - svector::iterator end = m_new_entries.end(); unsigned since_last_check = 0; - for (; it != end; ++it) { - entry & curr = *it; + for (entry & curr : m_new_entries) { fingerprint * f = curr.m_qb; quantifier * qa = static_cast(f->get_data()); From cab4e4b461efc4328a24df3fc3a446662a2fc75a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 21 Sep 2017 18:32:46 -0500 Subject: [PATCH 010/148] add feature to display benchmark in format seen by SAT solver Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 14 ++++++++++++++ src/cmd_context/cmd_context.h | 1 + src/cmd_context/extra_cmds/dbg_cmds.cpp | 15 +++++++++++---- src/sat/sat_config.cpp | 3 ++- src/sat/sat_config.h | 1 + src/sat/sat_params.pyg | 3 ++- src/sat/sat_solver.cpp | 7 +++++++ src/sat/sat_solver/inc_sat_solver.cpp | 1 - src/tactic/portfolio/enum2bv_solver.cpp | 1 - 9 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 3889d6205..fa3b06649 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1545,6 +1545,20 @@ void cmd_context::reset_assertions() { } +void cmd_context::display_dimacs() { + if (m_solver) { + try { + gparams::set("sat.dimacs.display", "true"); + m_solver->check_sat(0, nullptr); + } + catch (...) { + gparams::set("sat.dimacs.display", "false"); + throw; + } + gparams::set("sat.dimacs.display", "false"); + } +} + void cmd_context::display_model(model_ref& mdl) { if (mdl) { model_params p; diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 5883a8d8e..b60f590a9 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -419,6 +419,7 @@ public: void display_assertions(); void display_statistics(bool show_total_time = false, double total_time = 0.0); + void display_dimacs(); void reset(bool finalize = false); void assert_expr(expr * t); void assert_expr(symbol const & name, expr * t); diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 7d20685ac..dfdfa6175 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -31,7 +31,6 @@ Notes: #include "ast/rewriter/var_subst.h" #include "util/gparams.h" -#ifndef _EXTERNAL_RELEASE BINARY_SYM_CMD(get_quantifier_body_cmd, "dbg-get-qbody", @@ -343,10 +342,19 @@ public: } }; -#endif +class print_dimacs_cmd : public cmd { +public: + print_dimacs_cmd():cmd("display-dimacs") {} + virtual char const * get_usage() const { return ""; } + virtual char const * get_descr(cmd_context & ctx) const { return "print benchmark in DIMACS format"; } + virtual unsigned get_arity() const { return 0; } + virtual void prepare(cmd_context & ctx) {} + virtual void execute(cmd_context & ctx) { ctx.display_dimacs(); } +}; + void install_dbg_cmds(cmd_context & ctx) { -#ifndef _EXTERNAL_RELEASE + ctx.insert(alloc(print_dimacs_cmd)); ctx.insert(alloc(get_quantifier_body_cmd)); ctx.insert(alloc(set_cmd)); ctx.insert(alloc(pp_var_cmd)); @@ -369,5 +377,4 @@ void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(instantiate_cmd)); ctx.insert(alloc(instantiate_nested_cmd)); ctx.insert(alloc(set_next_id)); -#endif } diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 9cd343bd3..c67511275 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -35,7 +35,7 @@ namespace sat { m_glue("glue"), m_glue_psm("glue_psm"), m_psm_glue("psm_glue") { - m_num_parallel = 1; + m_num_parallel = 1; updt_params(p); } @@ -114,6 +114,7 @@ namespace sat { m_core_minimize = p.core_minimize(); m_core_minimize_partial = p.core_minimize_partial(); m_dyn_sub_res = p.dyn_sub_res(); + m_dimacs_display = p.dimacs_display(); } void config::collect_param_descrs(param_descrs & r) { diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index edd9f0bfc..36f22e83f 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -74,6 +74,7 @@ namespace sat { bool m_core_minimize; bool m_core_minimize_partial; + bool m_dimacs_display; symbol m_always_true; symbol m_always_false; diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 60708fd5c..2b1dc6646 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -23,4 +23,5 @@ def_module_params('sat', ('core.minimize', BOOL, False, 'minimize computed core'), ('core.minimize_partial', BOOL, False, 'apply partial (cheap) core minimization'), ('parallel_threads', UINT, 1, 'number of parallel threads to use'), - ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'))) + ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), + ('dimacs.display', BOOL, False, 'display SAT instance in DIMACS format and return unknown instead of solving'))) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 77bd2283d..2fa0c46f0 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -724,6 +724,13 @@ namespace sat { pop_to_base_level(); IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); SASSERT(scope_lvl() == 0); + if (m_config.m_dimacs_display) { + display_dimacs(std::cout); + for (unsigned i = 0; i < num_lits; ++lits) { + std::cout << dimacs_lit(lits[i]) << " 0\n"; + } + return l_undef; + } if (m_config.m_num_parallel > 1 && !m_par) { return check_par(num_lits, lits); } diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 21f9f1e8a..69645d705 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -105,7 +105,6 @@ public: virtual void set_progress_callback(progress_callback * callback) {} - void display_weighted(std::ostream& out, unsigned sz, expr * const * assumptions, unsigned const* weights) { if (weights != 0) { for (unsigned i = 0; i < sz; ++i) m_weights.push_back(weights[i]); diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp index 36a178c41..5880302d9 100644 --- a/src/tactic/portfolio/enum2bv_solver.cpp +++ b/src/tactic/portfolio/enum2bv_solver.cpp @@ -97,7 +97,6 @@ public: virtual void get_labels(svector & r) { m_solver->get_labels(r); } virtual ast_manager& get_manager() const { return m; } virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } - virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { datatype_util dt(m); bv_util bv(m); From cd24535e51263d8447efea9b917977863b97d8e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 22 Sep 2017 09:54:56 -0500 Subject: [PATCH 011/148] add newline Signed-off-by: Nikolaj Bjorner --- src/api/z3_ast_containers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/z3_ast_containers.h b/src/api/z3_ast_containers.h index 027976c07..c423a3286 100644 --- a/src/api/z3_ast_containers.h +++ b/src/api/z3_ast_containers.h @@ -197,4 +197,4 @@ extern "C" { } #endif // __cplusplus -#endif \ No newline at end of file +#endif From 95ee4c94f1f8084374d72f0780f9d831569f1621 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2017 11:37:55 -0500 Subject: [PATCH 012/148] remove utf fixes #1265 Signed-off-by: Nikolaj Bjorner --- src/parsers/smt2/smt2parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index b4648f0d6..b8ad7e610 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -1306,7 +1306,7 @@ namespace smt2 { /** * SMT-LIB 2.6 pattern matches are of the form - * (match t ((p1 t1) ··· (pm+1 tm+1))) + * (match t ((p1 t1) ... (pm+1 tm+1))) */ void push_match_frame() { SASSERT(curr_is_identifier()); From 2751cbc2703c49d9d38a624db9baf3a63a71bc6a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Sep 2017 22:36:36 -0500 Subject: [PATCH 013/148] n/a Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 6 +++++ src/sat/sat_solver.cpp | 7 +++--- src/sat/sat_solver/inc_sat_solver.cpp | 13 ++++++---- src/sat/tactic/sat_tactic.cpp | 35 +++++++++++++-------------- src/smt/smt_internalizer.cpp | 12 ++------- src/smt/theory_dense_diff_logic_def.h | 14 ++++++----- src/smt/theory_diff_logic_def.h | 1 - src/test/ddnf.cpp | 16 +++++++++++- src/test/main.cpp | 1 + src/test/sorting_network.cpp | 17 +++++++------ 10 files changed, 70 insertions(+), 52 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index fa3b06649..260d49174 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1549,13 +1549,19 @@ void cmd_context::display_dimacs() { if (m_solver) { try { gparams::set("sat.dimacs.display", "true"); + params_ref p; + m_solver->updt_params(p); m_solver->check_sat(0, nullptr); } catch (...) { gparams::set("sat.dimacs.display", "false"); + params_ref p; + m_solver->updt_params(p); throw; } gparams::set("sat.dimacs.display", "false"); + params_ref p; + m_solver->updt_params(p); } } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 2fa0c46f0..63f1badb7 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -724,6 +724,8 @@ namespace sat { pop_to_base_level(); IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); SASSERT(scope_lvl() == 0); + SASSERT(m_config.m_dimacs_display); + std::cout << "display dimacs: " << m_config.m_dimacs_display << "\n"; if (m_config.m_dimacs_display) { display_dimacs(std::cout); for (unsigned i = 0; i < num_lits; ++lits) { @@ -1249,10 +1251,7 @@ namespace sat { } void solver::sort_watch_lits() { - vector::iterator it = m_watches.begin(); - vector::iterator end = m_watches.end(); - for (; it != end; ++it) { - watch_list & wlist = *it; + for (watch_list & wlist : m_watches) { std::stable_sort(wlist.begin(), wlist.end(), watched_lt()); } } diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 69645d705..be418cbc4 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -162,6 +162,11 @@ public: if (r != l_true) return r; r = m_solver.check(m_asms.size(), m_asms.c_ptr()); + if (r == l_undef && m_solver.get_config().m_dimacs_display) { + for (auto const& kv : m_map) { + std::cout << "c " << kv.m_value << " " << mk_pp(kv.m_key, m) << "\n"; + } + } switch (r) { case l_true: @@ -643,14 +648,12 @@ private: } sat::model const & ll_m = m_solver.get_model(); model_ref md = alloc(model, m); - atom2bool_var::iterator it = m_map.begin(); - atom2bool_var::iterator end = m_map.end(); - for (; it != end; ++it) { - expr * n = it->m_key; + for (auto const& kv : m_map) { + expr * n = kv.m_key; if (is_app(n) && to_app(n)->get_num_args() > 0) { continue; } - sat::bool_var v = it->m_value; + sat::bool_var v = kv.m_value; switch (sat::value_at(v, ll_m)) { case l_true: md->register_decl(to_app(n)->get_decl(), m.mk_true()); diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index 9b706597d..4a6171f70 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -16,11 +16,11 @@ Author: Notes: --*/ +#include "ast/ast_pp.h" #include "tactic/tactical.h" +#include "tactic/filter_model_converter.h" #include "sat/tactic/goal2sat.h" #include "sat/sat_solver.h" -#include "tactic/filter_model_converter.h" -#include "ast/ast_smt2_pp.h" #include "model/model_v2_pp.h" class sat_tactic : public tactic { @@ -56,11 +56,9 @@ class sat_tactic : public tactic { sat::literal_vector assumptions; m_goal2sat(*g, m_params, m_solver, map, dep2asm); TRACE("sat_solver_unknown", tout << "interpreted_atoms: " << map.interpreted_atoms() << "\n"; - atom2bool_var::iterator it = map.begin(); - atom2bool_var::iterator end = map.end(); - for (; it != end; ++it) { - if (!is_uninterp_const(it->m_key)) - tout << mk_ismt2_pp(it->m_key, m) << "\n"; + for (auto const& kv : map) { + if (!is_uninterp_const(kv.m_key)) + tout << mk_ismt2_pp(kv.m_key, m) << "\n"; }); g->reset(); g->m().compact_memory(); @@ -70,6 +68,11 @@ class sat_tactic : public tactic { TRACE("sat_dimacs", m_solver.display_dimacs(tout);); dep2assumptions(dep2asm, assumptions); lbool r = m_solver.check(assumptions.size(), assumptions.c_ptr()); + if (r == l_undef && m_solver.get_config().m_dimacs_display) { + for (auto const& kv : map) { + std::cout << "c " << kv.m_value << " " << mk_pp(kv.m_key, g->m()) << "\n"; + } + } if (r == l_false) { expr_dependency * lcore = 0; if (produce_core) { @@ -90,11 +93,9 @@ class sat_tactic : public tactic { model_ref md = alloc(model, m); sat::model const & ll_m = m_solver.get_model(); TRACE("sat_tactic", for (unsigned i = 0; i < ll_m.size(); i++) tout << i << ":" << ll_m[i] << " "; tout << "\n";); - atom2bool_var::iterator it = map.begin(); - atom2bool_var::iterator end = map.end(); - for (; it != end; ++it) { - expr * n = it->m_key; - sat::bool_var v = it->m_value; + for (auto const& kv : map) { + expr * n = kv.m_key; + sat::bool_var v = kv.m_value; TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";); switch (sat::value_at(v, ll_m)) { case l_true: @@ -126,17 +127,15 @@ class sat_tactic : public tactic { void dep2assumptions(obj_map& dep2asm, sat::literal_vector& assumptions) { - obj_map::iterator it = dep2asm.begin(), end = dep2asm.end(); - for (; it != end; ++it) { - assumptions.push_back(it->m_value); + for (auto const& kv : dep2asm) { + assumptions.push_back(kv.m_value); } } void mk_asm2dep(obj_map& dep2asm, u_map& lit2asm) { - obj_map::iterator it = dep2asm.begin(), end = dep2asm.end(); - for (; it != end; ++it) { - lit2asm.insert(it->m_value.index(), it->m_key); + for (auto const& kv : dep2asm) { + lit2asm.insert(kv.m_value.index(), kv.m_key); } } }; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 58b391ac2..5a8a313ba 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -201,16 +201,8 @@ namespace smt { TRACE("deep_internalize", tout << "expression is deep: #" << n->get_id() << "\n" << mk_ll_pp(n, m_manager);); svector sorted_exprs; top_sort_expr(n, sorted_exprs); - TRACE("deep_internalize", - svector::const_iterator it = sorted_exprs.begin(); - svector::const_iterator end = sorted_exprs.end(); - for (; it != end; ++it) { - tout << "#" << it->first->get_id() << " " << it->second << "\n"; - }); - svector::const_iterator it = sorted_exprs.begin(); - svector::const_iterator end = sorted_exprs.end(); - for (; it != end; ++it) - internalize(it->first, it->second); + TRACE("deep_internalize", for (auto & kv : sorted_exprs) tout << "#" << kv.first->get_id() << " " << kv.second << "\n"; ); + for (auto & kv : sorted_exprs) internalize(kv.first, kv.second); } SASSERT(m_manager.is_bool(n)); if (is_gate(m_manager, n)) { diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index ad7727a89..064bdd433 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -151,14 +151,15 @@ namespace smt { m_autil.is_numeral(rhs, _k); numeral offset(_k); app * s, * t; - if (m_autil.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_times_minus_one(to_app(lhs)->get_arg(1), s)) { - t = to_app(to_app(lhs)->get_arg(0)); + expr *arg1, *arg2; + if (m_autil.is_add(lhs, arg1, arg2) && is_times_minus_one(arg2, s)) { + t = to_app(arg1); } - else if (m_autil.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_times_minus_one(to_app(lhs)->get_arg(0), s)) { - t = to_app(to_app(lhs)->get_arg(1)); + else if (m_autil.is_add(lhs, arg1, arg2) && is_times_minus_one(arg1, s)) { + t = to_app(arg2); } - else if (m_autil.is_mul(lhs) && to_app(lhs)->get_num_args() == 2 && m_autil.is_minus_one(to_app(lhs)->get_arg(0))) { - s = to_app(to_app(lhs)->get_arg(1)); + else if (m_autil.is_mul(lhs, arg1, arg2) && m_autil.is_minus_one(arg1)) { + s = to_app(arg2); t = mk_zero_for(s); } else if (!m_autil.is_arith_expr(lhs)) { @@ -170,6 +171,7 @@ namespace smt { found_non_diff_logic_expr(n); return false; } + TRACE("arith", tout << expr_ref(lhs, get_manager()) << " " << expr_ref(s, get_manager()) << " " << expr_ref(t, get_manager()) << "\n";); source = internalize_term_core(s); target = internalize_term_core(t); if (source == null_theory_var || target == null_theory_var) { diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 02cc8860f..a7153234c 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -733,7 +733,6 @@ theory_var theory_diff_logic::mk_term(app* n) { source = mk_var(a); for (unsigned i = 0; i < n->get_num_args(); ++i) { expr* arg = n->get_arg(i); - std::cout << "internalize: " << mk_pp(arg, get_manager()) << " " << ctx.e_internalized(arg) << "\n"; if (!ctx.e_internalized(arg)) { ctx.internalize(arg, false); } diff --git a/src/test/ddnf.cpp b/src/test/ddnf.cpp index 3f8e748be..09f1a4cf9 100644 --- a/src/test/ddnf.cpp +++ b/src/test/ddnf.cpp @@ -200,6 +200,20 @@ void tst_ddnf(char ** argv, int argc, int& i) { dealloc(ddnf); } - +void tst_ddnf1() { + enable_trace("ddnf"); + unsigned W = 2; + datalog::ddnf_core ddnf(W); + tbv_manager& tbvm = ddnf.get_tbv_manager(); + tbv* tXX = tbvm.allocate("xx"); + tbv* t1X = tbvm.allocate("1x"); + tbv* tX1 = tbvm.allocate("x1"); + tbv* t11 = tbvm.allocate("11"); + ddnf.insert(*tXX); + ddnf.insert(*t11); + ddnf.insert(*tX1); + ddnf.insert(*t1X); + ddnf.display(std::cout); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index d0d0aac5b..03fbba1df 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -237,6 +237,7 @@ int main(int argc, char ** argv) { TST(sat_user_scope); TST(pdr); TST_ARGV(ddnf); + TST(ddnf1); TST(model_evaluator); TST_ARGV(lp); TST(get_consequences); diff --git a/src/test/sorting_network.cpp b/src/test/sorting_network.cpp index 2984e94e2..1062057f6 100644 --- a/src/test/sorting_network.cpp +++ b/src/test/sorting_network.cpp @@ -6,16 +6,14 @@ Copyright (c) 2015 Microsoft Corporation #include "util/trace.h" #include "util/vector.h" +#include "util/sorting_network.h" #include "ast/ast.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" -#include "util/sorting_network.h" -#include "smt/smt_kernel.h" -#include "model/model_smt2_pp.h" -#include "smt/params/smt_params.h" #include "ast/ast_util.h" - - +#include "model/model_smt2_pp.h" +#include "smt/smt_kernel.h" +#include "smt/params/smt_params.h" struct ast_ext { ast_manager& m; @@ -388,7 +386,6 @@ void test_at_most_1(unsigned n, bool full) { std::cout << atom << "\n"; if (is_true) ++k; } - VERIFY(l_false == solver.check()); if (k > 1) { solver.assert_expr(result1); } @@ -427,6 +424,12 @@ void tst_sorting_network() { test_at_most_1(i, true); test_at_most_1(i, false); } + + for (unsigned n = 2; n < 20; ++n) { + std::cout << "verify eq-1 out of " << n << "\n"; + test_sorting_eq(n, 1); + } + test_at_most1(); test_sorting_eq(11,7); From 7a15de374a4893de1224bc07f56b43485f464dc3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 24 Sep 2017 09:19:51 -0700 Subject: [PATCH 014/148] fix #1266 by bypassing topological ordering on theory symbols Signed-off-by: Nikolaj Bjorner --- src/smt/smt_internalizer.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 5a8a313ba..ffaee434f 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -198,11 +198,19 @@ namespace smt { if (get_depth(n) > DEEP_EXPR_THRESHOLD) { // if the expression is deep, then execute topological sort to avoid // stack overflow. + // a caveat is that theory internalizers do rely on recursive descent so + // internalization over these follows top-down TRACE("deep_internalize", tout << "expression is deep: #" << n->get_id() << "\n" << mk_ll_pp(n, m_manager);); svector sorted_exprs; top_sort_expr(n, sorted_exprs); TRACE("deep_internalize", for (auto & kv : sorted_exprs) tout << "#" << kv.first->get_id() << " " << kv.second << "\n"; ); - for (auto & kv : sorted_exprs) internalize(kv.first, kv.second); + for (auto & kv : sorted_exprs) { + expr* e = kv.first; + if (!is_app(e) || + to_app(e)->get_family_id() == null_family_id || + to_app(e)->get_family_id() == m_manager.get_basic_family_id()) + internalize(e, kv.second); + } } SASSERT(m_manager.is_bool(n)); if (is_gate(m_manager, n)) { From 9cd974e3343b83491200071442537d7765f48ad7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 24 Sep 2017 09:40:35 -0700 Subject: [PATCH 015/148] remove display Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 63f1badb7..8782cb462 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -724,8 +724,6 @@ namespace sat { pop_to_base_level(); IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); SASSERT(scope_lvl() == 0); - SASSERT(m_config.m_dimacs_display); - std::cout << "display dimacs: " << m_config.m_dimacs_display << "\n"; if (m_config.m_dimacs_display) { display_dimacs(std::cout); for (unsigned i = 0; i < num_lits; ++lits) { From f179d49f4f4ac90e9515f42ee690426c5a67fa52 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 24 Sep 2017 10:58:39 -0700 Subject: [PATCH 016/148] check for eof, based on testing garbled repro from #1267 Signed-off-by: Nikolaj Bjorner --- src/parsers/smt2/smt2scanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index c1cc40b2b..9a9e69b67 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -124,7 +124,7 @@ namespace smt2 { next(); bool is_float = false; - while (true) { + while (!m_at_eof) { char c = curr(); if ('0' <= c && c <= '9') { m_number = rational(10)*m_number + rational(c - '0'); From c8a67abdd70550cbab22bafd259a21f68433da7c Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 25 Sep 2017 14:33:20 -0700 Subject: [PATCH 017/148] fixing issue [1269] --- src/interp/iz3mgr.cpp | 29 +++++++++++++++++++++++++++++ src/interp/iz3mgr.h | 6 ++++++ src/interp/iz3proof_itp.cpp | 4 ++-- src/interp/iz3translate.cpp | 5 +++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 306807f1f..7314403b0 100755 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -33,9 +33,11 @@ #include #include #include +#include #include "ast/expr_abstract.h" #include "util/params.h" +#include "ast/used_vars.h" using namespace stl_ext; @@ -938,3 +940,30 @@ void iz3mgr::get_bound_substitutes(stl_ext::hash_map &memo, const ast } #endif + +unsigned iz3mgr::num_free_variables(const ast &e){ + used_vars uv; + uv(to_expr(e.raw())); + return uv.get_num_vars(); +} + +iz3mgr::ast iz3mgr::close_universally (ast e){ + used_vars uv; + uv(to_expr(e.raw())); + std::vector bvs; + stl_ext::hash_map subst_memo; + for (unsigned i = 0; i < uv.get_max_found_var_idx_plus_1(); i++){ + if (uv.get(i)) { + std::ostringstream os; + os << "%%" << i; + ast c = make_var(os.str(),uv.get(i)); + ast v = cook(m().mk_var(i,uv.get(i))); + subst_memo[v] = c; + bvs.push_back(c); + } + } + e = subst(subst_memo,e); + for (unsigned i = 0; i < bvs.size(); i++) + e = apply_quant(Forall,bvs[i],e); + return e; +} diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index e6e08f84d..6ca8fae34 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -661,6 +661,12 @@ class iz3mgr { ast apply_quant(opr quantifier, ast var, ast e); + // Universally quantify all the free variables in a formula. + // Makes up names for the quntifiers. + + ast close_universally (ast e); + + unsigned num_free_variables(const ast &e); /** For debugging */ void show(ast); diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index eb7f8e325..fc9d0fac6 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2968,9 +2968,9 @@ class iz3proof_itp_impl : public iz3proof_itp { ast interpolate(const node &pf){ // proof of false must be a formula, with quantified symbols #ifndef BOGUS_QUANTS - return add_quants(z3_simplify(pf)); + return close_universally(add_quants(z3_simplify(pf))); #else - return z3_simplify(pf); + return close_universally(z3_simplify(pf)); #endif } diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index ebbee46ca..c59dd0178 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -234,6 +234,11 @@ public: } } + // if(!range_is_empty(rng)){ + // if (num_free_variables(con) > 0) + // rng = range_empty(); + // } + if(res == INT_MAX){ if(range_is_empty(rng)) res = -1; From 6450ee33c58a9dc5e86b7a7b852af46277ce16d5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2017 08:25:48 -0700 Subject: [PATCH 018/148] disregard model validation when source expression contains uninterpreted theory functions Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 260d49174..3f34f3128 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1763,7 +1763,7 @@ void cmd_context::validate_model() { continue; } try { - for_each_expr(contains_underspecified, r); + for_each_expr(contains_underspecified, a); } catch (contains_underspecified_op_proc::found) { continue; From 2229a2fc1b07e733bbb69bd3f997cfd259134ca7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Sep 2017 08:43:31 -0700 Subject: [PATCH 019/148] model validation update take 2 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 3f34f3128..b2a4fc47f 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1764,6 +1764,7 @@ void cmd_context::validate_model() { } try { for_each_expr(contains_underspecified, a); + for_each_expr(contains_underspecified, r); } catch (contains_underspecified_op_proc::found) { continue; From f07b89df867b29f3dc16633a94f5e204c97fae90 Mon Sep 17 00:00:00 2001 From: Max ulidtko Date: Tue, 26 Sep 2017 17:27:47 +0300 Subject: [PATCH 020/148] fix pydoc part of `make api_docs` --- doc/mk_api_doc.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index 234dd670c..d7e8e2fb1 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -288,8 +288,21 @@ try: # Put z3py at the beginning of the search path to try to avoid picking up # an installed copy of Z3py. sys.path.insert(0, os.path.dirname(Z3PY_PACKAGE_PATH)) - pydoc.writedoc('z3') - shutil.move('z3.html', os.path.join(OUTPUT_DIRECTORY, 'html', 'z3.html')) + for modulename in ( + 'z3', + 'z3.z3consts', + 'z3.z3core', + 'z3.z3num', + 'z3.z3poly', + 'z3.z3printer', + 'z3.z3rcf', + 'z3.z3types', + 'z3.z3util', + ): + pydoc.writedoc(modulename) + doc = modulename + '.html' + shutil.move(doc, os.path.join(OUTPUT_DIRECTORY, 'html', doc)) + print("Generated pydoc Z3Py documentation.") if ML_ENABLED: From ce6e26043af3ec3f1e857ab1cfacd6158a472589 Mon Sep 17 00:00:00 2001 From: Max ulidtko Date: Wed, 27 Sep 2017 14:03:38 +0300 Subject: [PATCH 021/148] fix Python API doxygen (`make api_docs`) --- doc/mk_api_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index d7e8e2fb1..ab4d32d39 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -188,7 +188,7 @@ try: if Z3PY_ENABLED: print("Z3Py documentation enabled") - doxygen_config_substitutions['PYTHON_API_FILES'] = 'z3.py' + doxygen_config_substitutions['PYTHON_API_FILES'] = 'z3*.py' else: print("Z3Py documentation disabled") doxygen_config_substitutions['PYTHON_API_FILES'] = '' From 9a464dded4a0208d29ff604cb91ccf0fbd9e665b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 27 Sep 2017 14:22:59 +0100 Subject: [PATCH 022/148] Removed -std=c++11 from OCaml stubs build command. Fixes #1263. --- scripts/mk_util.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 24f22d8ee..6f3052f6e 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1913,7 +1913,11 @@ class MLComponent(Component): src_dir = self.to_src_dir mk_dir(os.path.join(BUILD_DIR, self.sub_dir)) api_src = get_component(API_COMPONENT).to_src_dir - out.write('CXXFLAGS_OCAML=$(CXXFLAGS:/GL=)\n') # remove /GL; the ocaml tools don't like it. + # remove /GL and -std=c++11; the ocaml tools don't like them. + if IS_WINDOWS: + out.write('CXXFLAGS_OCAML=$(CXXFLAGS:/GL=)\n') + else: + out.write('CXXFLAGS_OCAML=$(subst -std=c++11,,$(CXXFLAGS))\n') if IS_WINDOWS: prefix_lib = '-L' + os.path.abspath(BUILD_DIR).replace('\\', '\\\\') From 8ff8c6433b9c377bae84c5c72084eefa3fbded3a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Sep 2017 10:15:27 -0700 Subject: [PATCH 023/148] fix #1277 fix #1278 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 6 ++++++ src/smt/theory_arith_nl.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index b2a4fc47f..95c030687 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -870,6 +870,12 @@ void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, s lhs = m().mk_app(f, binding.size(), binding.c_ptr()); eq = m().mk_eq(lhs, e); if (!ids.empty()) { + if (is_var(e)) { + ptr_vector domain; + for (expr* b : binding) domain.push_back(m().get_sort(b)); + insert_macro(f->get_name(), domain.size(), domain.c_ptr(), e); + return; + } if (!is_app(e)) { throw cmd_exception("Z3 only supports recursive definitions that are proper terms (not binders or variables)"); } diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index a04c34706..230b6de77 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -780,7 +780,7 @@ namespace smt { of a non linear monomial that is not satisfied by the current assignment. if v >= l, then create the case split v >= l+1 else v <= u, then create the case split v <= u-1 - else do nothing and return false. + else create the bound v = 0 and case split on it. */ template bool theory_arith::branch_nl_int_var(theory_var v) { From bec60f763bbe8daf87413088f19a4b7c23ba7e5c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Sep 2017 12:35:36 -0700 Subject: [PATCH 024/148] add diagnostics to DDNF and fix #1268 Signed-off-by: Nikolaj Bjorner --- src/muz/ddnf/ddnf.cpp | 21 ++++++++++++++++----- src/test/ddnf.cpp | 4 ++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/muz/ddnf/ddnf.cpp b/src/muz/ddnf/ddnf.cpp index 0e2f6b35f..da1f5392b 100644 --- a/src/muz/ddnf/ddnf.cpp +++ b/src/muz/ddnf/ddnf.cpp @@ -192,10 +192,15 @@ namespace datalog { for (unsigned i = 0; i < new_tbvs.size(); ++i) { tbv const& nt = *new_tbvs[i]; IF_VERBOSE(10, m_tbv.display(verbose_stream() << "insert: ", nt); verbose_stream() << "\n";); - if (contains(nt)) continue; - ddnf_node* n = alloc(ddnf_node, *this, m_tbv, nt, m_noderefs.size()); - m_noderefs.push_back(n); - m_nodes.insert(n); + ddnf_node* n; + if (contains(nt)) { + n = find(nt); + } + else { + n = alloc(ddnf_node, *this, m_tbv, nt, m_noderefs.size()); + m_noderefs.push_back(n); + m_nodes.insert(n); + } insert(*m_root, n, new_tbvs); } return find(t); @@ -275,13 +280,17 @@ namespace datalog { void insert(ddnf_node& root, ddnf_node* new_n, ptr_vector& new_intersections) { tbv const& new_tbv = new_n->get_tbv(); + IF_VERBOSE(10, m_tbv.display(verbose_stream() << "root: ", root.get_tbv()); + m_tbv.display(verbose_stream() << " new node ", new_tbv); verbose_stream() << "\n";); SASSERT(m_tbv.contains(root.get_tbv(), new_tbv)); - if (&root == new_n) return; + if (m_eq(&root, new_n)) return; ++m_stats.m_num_inserts; bool inserted = false; for (unsigned i = 0; i < root.num_children(); ++i) { ddnf_node& child = *(root[i]); ++m_stats.m_num_comparisons; + IF_VERBOSE(10, m_tbv.display(verbose_stream() << "child ", child.get_tbv()); + verbose_stream() << " contains: " << m_tbv.contains(child.get_tbv(), new_tbv) << "\n";); if (m_tbv.contains(child.get_tbv(), new_tbv)) { inserted = true; insert(child, new_n, new_intersections); @@ -299,11 +308,13 @@ namespace datalog { // checking for subset if (m_tbv.contains(new_tbv, child.get_tbv())) { subset_children.push_back(&child); + IF_VERBOSE(10, m_tbv.display(verbose_stream() << "contains child", child.get_tbv()); verbose_stream() << "\n";); ++m_stats.m_num_comparisons; } else if (m_tbv.intersect(child.get_tbv(), new_tbv, *intr)) { // this means there is a non-full intersection new_intersections.push_back(intr); + IF_VERBOSE(10, m_tbv.display(verbose_stream() << "intersect child ", child.get_tbv()); verbose_stream() << "\n";); intr = m_tbv.allocate(); m_stats.m_num_comparisons += 2; } diff --git a/src/test/ddnf.cpp b/src/test/ddnf.cpp index 09f1a4cf9..c9eb6aa08 100644 --- a/src/test/ddnf.cpp +++ b/src/test/ddnf.cpp @@ -214,6 +214,10 @@ void tst_ddnf1() { ddnf.insert(*tX1); ddnf.insert(*t1X); ddnf.display(std::cout); + tbvm.deallocate(tXX); + tbvm.deallocate(t1X); + tbvm.deallocate(tX1); + tbvm.deallocate(t11); } From 05428314be0a3b7bd6b682d0467efa0fe892c9d5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 1 Oct 2017 15:13:43 -0700 Subject: [PATCH 025/148] fix #1276 related crashes for re-sumption after cancellation Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/maximize_ac_sharing.cpp | 4 ---- src/smt/asserted_formulas.cpp | 7 ++++--- src/smt/smt_context.cpp | 11 ++++++++++- src/util/rlimit.cpp | 5 +++-- src/util/rlimit.h | 15 ++++++++++++++- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/ast/rewriter/maximize_ac_sharing.cpp b/src/ast/rewriter/maximize_ac_sharing.cpp index b560132db..d7e8df7a2 100644 --- a/src/ast/rewriter/maximize_ac_sharing.cpp +++ b/src/ast/rewriter/maximize_ac_sharing.cpp @@ -151,11 +151,7 @@ void maximize_ac_sharing::restore_entries(unsigned old_lim) { } void maximize_ac_sharing::reset() { - restore_entries(0); - m_entries.reset(); m_cache.reset(); - m_region.reset(); - m_scopes.reset(); } void maximize_bv_sharing::init_core() { diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index c52ad851c..11cc846b8 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -170,7 +170,7 @@ void asserted_formulas::get_assertions(ptr_vector & result) const { void asserted_formulas::push_scope() { SASSERT(inconsistent() || m_qhead == m_formulas.size() || m.canceled()); - TRACE("asserted_formulas_scopes", tout << "push:\n"; display(tout);); + TRACE("asserted_formulas_scopes", tout << "before push: " << m_scopes.size() << "\n";); m_scoped_substitution.push(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); @@ -181,10 +181,11 @@ void asserted_formulas::push_scope() { m_bv_sharing.push_scope(); m_macro_manager.push_scope(); commit(); + TRACE("asserted_formulas_scopes", tout << "after push: " << m_scopes.size() << "\n";); } void asserted_formulas::pop_scope(unsigned num_scopes) { - TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << "\n"; display(tout);); + TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << " of " << m_scopes.size() << "\n";); m_bv_sharing.pop_scope(num_scopes); m_macro_manager.pop_scope(num_scopes); unsigned new_lvl = m_scopes.size() - num_scopes; @@ -196,7 +197,7 @@ void asserted_formulas::pop_scope(unsigned num_scopes) { m_qhead = s.m_formulas_lim; m_scopes.shrink(new_lvl); flush_cache(); - TRACE("asserted_formulas_scopes", tout << "after pop " << num_scopes << "\n"; display(tout);); + TRACE("asserted_formulas_scopes", tout << "after pop " << num_scopes << "\n";); } void asserted_formulas::reset() { diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 901f4b5ac..ac7b1f44b 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -487,6 +487,7 @@ namespace smt { */ void context::add_eq(enode * n1, enode * n2, eq_justification js) { unsigned old_trail_size = m_trail_stack.size(); + scoped_suspend_rlimit _suspend_cancel(m_manager.limit()); try { TRACE("add_eq", tout << "assigning: #" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n";); @@ -541,10 +542,14 @@ namespace smt { mark_as_relevant(r1); } + TRACE("add_eq", tout << "to trail\n";); + push_trail(add_eq_trail(r1, n1, r2->get_num_parents())); + TRACE("add_eq", tout << "qmanager add_eq\n";); m_qmanager->add_eq_eh(r1, r2); + TRACE("add_eq", tout << "merge theory_vars\n";); merge_theory_vars(n2, n1, js); // 'Proof' tree @@ -577,6 +582,7 @@ namespace smt { #endif + TRACE("add_eq", tout << "remove_parents_from_cg_table\n";); remove_parents_from_cg_table(r1); enode * curr = r1; @@ -588,8 +594,10 @@ namespace smt { SASSERT(r1->get_root() == r2); + TRACE("add_eq", tout << "reinsert_parents_into_cg_table\n";); reinsert_parents_into_cg_table(r1, r2, n1, n2, js); + TRACE("add_eq", tout << "propagate_bool_enode_assignment\n";); if (n2->is_bool()) propagate_bool_enode_assignment(r1, r2, n1, n2); @@ -604,6 +612,7 @@ namespace smt { catch (...) { // Restore trail size since procedure was interrupted in the middle. // If the add_eq_trail remains on the trail stack, then Z3 may crash when the destructor is invoked. + TRACE("add_eq", tout << "add_eq interrupted. This is unsafe " << m_manager.limit().get_cancel_flag() << "\n";); m_trail_stack.shrink(old_trail_size); throw; } @@ -972,7 +981,7 @@ namespace smt { enode * parent = *it; if (parent->is_cgc_enabled()) { TRACE("add_eq_parents", tout << "removing: #" << parent->get_owner_id() << "\n";); - CTRACE("add_eq", !parent->is_cgr(), + CTRACE("add_eq", !parent->is_cgr() || !m_cg_table.contains_ptr(parent), tout << "old num_parents: " << r2_num_parents << ", num_parents: " << r2->m_parents.size() << ", parent: #" << parent->get_owner_id() << ", parents: \n"; for (unsigned i = 0; i < r2->m_parents.size(); i++) { diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index f3f45c654..e625cab95 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -21,6 +21,7 @@ Revision History: reslimit::reslimit(): m_cancel(0), + m_suspend(false), m_count(0), m_limit(0) { } @@ -31,12 +32,12 @@ uint64 reslimit::count() const { bool reslimit::inc() { ++m_count; - return m_cancel == 0 && (m_limit == 0 || m_count <= m_limit); + return (m_cancel == 0 && (m_limit == 0 || m_count <= m_limit)) || m_suspend; } bool reslimit::inc(unsigned offset) { m_count += offset; - return m_cancel == 0 && (m_limit == 0 || m_count <= m_limit); + return (m_cancel == 0 && (m_limit == 0 || m_count <= m_limit)) || m_suspend; } void reslimit::push(unsigned delta_limit) { diff --git a/src/util/rlimit.h b/src/util/rlimit.h index 3b278d132..0c81f9449 100644 --- a/src/util/rlimit.h +++ b/src/util/rlimit.h @@ -23,12 +23,14 @@ Revision History: class reslimit { volatile unsigned m_cancel; + bool m_suspend; uint64 m_count; uint64 m_limit; svector m_limits; ptr_vector m_children; void set_cancel(unsigned f); + friend class scoped_suspend_rlimit; public: reslimit(); @@ -42,7 +44,7 @@ public: uint64 count() const; - bool get_cancel_flag() const { return m_cancel > 0; } + bool get_cancel_flag() const { return m_cancel > 0 && !m_suspend; } char const* get_cancel_msg() const; void cancel(); void reset_cancel(); @@ -61,6 +63,17 @@ public: }; +class scoped_suspend_rlimit { + reslimit & m_limit; +public: + scoped_suspend_rlimit(reslimit& r): m_limit(r) { + r.m_suspend = true; + } + ~scoped_suspend_rlimit() { + m_limit.m_suspend = false; + } +}; + struct scoped_limits { reslimit& m_limit; unsigned m_sz; From e0e23975665e4344f853c8f9a51ab34ff50e5f3c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 1 Oct 2017 19:40:30 -0700 Subject: [PATCH 026/148] missing setup datatypes for QF_DT Signed-off-by: Nikolaj Bjorner --- src/smt/smt_model_generator.cpp | 18 ++++++++++-------- src/smt/smt_setup.cpp | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 5848fac62..df6865f1e 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -54,7 +54,7 @@ namespace smt { ptr_vector::const_iterator it = m_context->begin_theories(); ptr_vector::const_iterator end = m_context->end_theories(); for (; it != end; ++it) { - TRACE("model_generator_bug", tout << "init_model for theory: " << (*it)->get_name() << "\n";); + TRACE("model", tout << "init_model for theory: " << (*it)->get_name() << "\n";); (*it)->init_model(*this); } } @@ -91,7 +91,7 @@ namespace smt { sort * s = m_manager.get_sort(r->get_owner()); model_value_proc * proc = 0; if (m_manager.is_bool(s)) { - CTRACE("func_interp_bug", m_context->get_assignment(r) == l_undef, + CTRACE("model", m_context->get_assignment(r) == l_undef, tout << mk_pp(r->get_owner(), m_manager) << "\n";); SASSERT(m_context->get_assignment(r) != l_undef); if (m_context->get_assignment(r) == l_true) @@ -108,7 +108,7 @@ namespace smt { SASSERT(proc); } else { - TRACE("model_bug", tout << "creating fresh value for #" << r->get_owner_id() << "\n";); + TRACE("model", tout << "creating fresh value for #" << r->get_owner_id() << "\n";); proc = alloc(fresh_value_proc, mk_extra_fresh_value(m_manager.get_sort(r->get_owner()))); } } @@ -130,7 +130,7 @@ namespace smt { if (!m_manager.is_model_value(n)) { sort * s = m_manager.get_sort(r->get_owner()); n = m_model->get_fresh_value(s); - CTRACE("model_generator_bug", n == 0, + CTRACE("model", n == 0, tout << mk_pp(r->get_owner(), m_manager) << "\nsort:\n" << mk_pp(s, m_manager) << "\n"; tout << "is_finite: " << m_model->is_finite(s) << "\n";); } @@ -406,9 +406,11 @@ namespace smt { */ bool model_generator::include_func_interp(func_decl * f) const { family_id fid = f->get_family_id(); + TRACE("model", tout << f->get_name() << " " << fid << "\n";); if (fid == null_family_id) return !m_hidden_ufs.contains(f); if (fid == m_manager.get_basic_family_id()) return false; theory * th = m_context->get_theory(fid); + TRACE("model", tout << th << "\n";); if (!th) return true; return th->include_func_interp(f); } @@ -443,7 +445,7 @@ namespace smt { SASSERT(m_model->has_interpretation(f)); SASSERT(m_model->get_func_interp(f) == fi); // The entry must be new because n->get_cg() == n - TRACE("func_interp_bug", + TRACE("model", tout << "insert new entry for:\n" << mk_ismt2_pp(n->get_owner(), m_manager) << "\nargs: "; for (unsigned i = 0; i < num_args; i++) { tout << "#" << n->get_arg(i)->get_owner_id() << " "; @@ -507,20 +509,20 @@ namespace smt { void model_generator::register_macros() { unsigned num = m_context->get_num_macros(); - TRACE("register_macros", tout << "num. macros: " << num << "\n";); + TRACE("model", tout << "num. macros: " << num << "\n";); expr_ref v(m_manager); for (unsigned i = 0; i < num; i++) { func_decl * f = m_context->get_macro_interpretation(i, v); func_interp * fi = alloc(func_interp, m_manager, f->get_arity()); fi->set_else(v); - TRACE("register_macros", tout << f->get_name() << "\n" << mk_pp(v, m_manager) << "\n";); + TRACE("model", tout << f->get_name() << "\n" << mk_pp(v, m_manager) << "\n";); m_model->register_decl(f, fi); } } proto_model * model_generator::mk_model() { SASSERT(!m_model); - TRACE("func_interp_bug", m_context->display(tout);); + TRACE("model", m_context->display(tout);); init_model(); register_existing_model_values(); mk_bool_model(); diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 56b5d541a..631805b4d 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -216,6 +216,7 @@ namespace smt { void setup::setup_QF_DT() { setup_QF_UF(); + setup_datatypes(); } void setup::setup_QF_BVRE() { From 6c7a82edceef5e96476b2e80433d4b1a1a61b103 Mon Sep 17 00:00:00 2001 From: Miguel Angelo Da Terra Neves Date: Mon, 2 Oct 2017 09:20:59 -0700 Subject: [PATCH 027/148] update to _get_args to convert arguments from AstVector to a python list Signed-off-by: Miguel Angelo Da Terra Neves --- src/api/python/z3/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 39af03190..a521c5169 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -120,7 +120,7 @@ def _get_args(args): try: if len(args) == 1 and (isinstance(args[0], tuple) or isinstance(args[0], list)): return args[0] - elif len(args) == 1 and isinstance(args[0], set): + elif len(args) == 1 and (isinstance(args[0], set) or isinstance(args[0], AstVector)): return [arg for arg in args[0]] else: return args From 2828126b725362ee08daf0cf8a774d0b09cc3dcb Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 3 Oct 2017 10:20:49 -0700 Subject: [PATCH 028/148] add cancellation checks Signed-off-by: Lev Nachmanson --- src/util/lp/lar_solver.h | 2 + src/util/lp/lp_primal_core_solver_tableau.h | 45 ++++++++++++--------- src/util/lp/lp_settings.h | 3 +- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 1ed30bd70..6d2cbea7d 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -417,6 +417,8 @@ public: for (unsigned i : m_rows_with_changed_bounds.m_index) { calculate_implied_bounds_for_row(i, bp); + if (settings().get_cancel_flag()) + return; } m_rows_with_changed_bounds.clear(); if (!use_tableau()) { diff --git a/src/util/lp/lp_primal_core_solver_tableau.h b/src/util/lp/lp_primal_core_solver_tableau.h index 5c7d4d2c2..d2b8c1a26 100644 --- a/src/util/lp/lp_primal_core_solver_tableau.h +++ b/src/util/lp/lp_primal_core_solver_tableau.h @@ -176,25 +176,34 @@ unsigned lp_primal_core_solver::solve_with_tableau() { default: break; // do nothing } - } while (this->get_status() != FLOATING_POINT_ERROR - && - this->get_status() != UNBOUNDED - && - this->get_status() != OPTIMAL - && - this->get_status() != INFEASIBLE - && - this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements - && - this->total_iterations() <= this->m_settings.max_total_number_of_iterations - && - !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); + } while (this->get_status() != FLOATING_POINT_ERROR + && + this->get_status() != UNBOUNDED + && + this->get_status() != OPTIMAL + && + this->get_status() != INFEASIBLE + && + this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements + && + this->total_iterations() <= this->m_settings.max_total_number_of_iterations + && + !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) + && + m_settings.get_cancel_flag() == false); + + if (m_settings.get_cancel_flag()) { + this->set_status(CANCELLED); + } - SASSERT(this->get_status() == FLOATING_POINT_ERROR - || - this->current_x_is_feasible() == false - || - this->calc_current_x_is_feasible_include_non_basis()); + SASSERT( + this->get_status() == FLOATING_POINT_ERROR + || + this->get_status() == CANCELLED + || + this->current_x_is_feasible() == false + || + this->calc_current_x_is_feasible_include_non_basis()); return this->total_iterations(); } diff --git a/src/util/lp/lp_settings.h b/src/util/lp/lp_settings.h index 70a9f1504..a7e6e2665 100644 --- a/src/util/lp/lp_settings.h +++ b/src/util/lp/lp_settings.h @@ -61,7 +61,8 @@ enum lp_status { TIME_EXHAUSTED, ITERATIONS_EXHAUSTED, EMPTY, - UNSTABLE + UNSTABLE, + CANCELLED }; // when the ratio of the vector lenth to domain size to is greater than the return value we switch to solve_By_for_T_indexed_only From fd3d785a5b41715a5a1c61ea03038c550483030e Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 4 Oct 2017 14:49:45 -0700 Subject: [PATCH 029/148] add this-> Signed-off-by: Lev Nachmanson --- src/util/lp/lp_primal_core_solver_tableau.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/lp/lp_primal_core_solver_tableau.h b/src/util/lp/lp_primal_core_solver_tableau.h index d2b8c1a26..0c56f0ab9 100644 --- a/src/util/lp/lp_primal_core_solver_tableau.h +++ b/src/util/lp/lp_primal_core_solver_tableau.h @@ -190,10 +190,10 @@ unsigned lp_primal_core_solver::solve_with_tableau() { && !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) && - m_settings.get_cancel_flag() == false); + this->m_settings.get_cancel_flag() == false); - if (m_settings.get_cancel_flag()) { - this->set_status(CANCELLED); + if (this->m_settings.get_cancel_flag()) { + this->set_status(CANCELLED); } SASSERT( From 110d558ee4262721f3ab2c999d789eaf84ac15d6 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 5 Oct 2017 08:53:12 +0100 Subject: [PATCH 030/148] dom_simplify_tactic: micro opt --- src/tactic/core/dom_simplify_tactic.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 595f8f7c6..d4a3031cd 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -225,9 +225,8 @@ expr_ref dom_simplify_tactic::simplify(expr * e0) { } else { expr_dominators::tree_t const& t = m_dominators.get_tree(); - if (t.contains(e)) { - ptr_vector const& children = t[e]; - for (expr * child : children) { + if (auto children = t.find_core(e)) { + for (expr * child : children->get_data().m_value) { simplify(child); } } From 53fc6ac11b25593e3827c943f5d61ce9f28da7ca Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Thu, 5 Oct 2017 14:27:56 +0100 Subject: [PATCH 031/148] [TravisCI] Refactor as many CI default options as possible so that the Docker and "TravisCI macOS" builds share most of the same defaults by sourcing the `ci_defaults.sh` file. --- contrib/ci/Dockerfiles/z3_build.Dockerfile | 50 ++++++++--------- contrib/ci/scripts/ci_defaults.sh | 54 +++++++++++++++++++ .../ci/scripts/travis_ci_linux_entry_point.sh | 4 +- .../ci/scripts/travis_ci_osx_entry_point.sh | 30 ++--------- 4 files changed, 86 insertions(+), 52 deletions(-) create mode 100644 contrib/ci/scripts/ci_defaults.sh diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 2d16d5394..5f7608cf4 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -2,34 +2,33 @@ ARG DOCKER_IMAGE_BASE FROM ${DOCKER_IMAGE_BASE} -# Specify defaults. This can be changed when invoking +# Build arguments. This can be changed when invoking # `docker build`. -ARG ASAN_BUILD=0 -ARG BUILD_DOCS=0 -ARG CC=gcc -ARG CXX=g++ -ARG DOTNET_BINDINGS=1 -ARG JAVA_BINDINGS=1 -ARG NO_SUPPRESS_OUTPUT=0 -ARG PYTHON_BINDINGS=1 -ARG PYTHON_EXECUTABLE=/usr/bin/python2.7 -ARG RUN_SYSTEM_TESTS=1 -ARG RUN_UNIT_TESTS=1 -ARG TARGET_ARCH=x86_64 -ARG TEST_INSTALL=1 -ARG UBSAN_BUILD=0 -ARG USE_LIBGMP=0 -ARG USE_LTO=0 -ARG USE_OPENMP=1 +ARG ASAN_BUILD +ARG BUILD_DOCS +ARG CC +ARG CXX +ARG DOTNET_BINDINGS +ARG JAVA_BINDINGS +ARG NO_SUPPRESS_OUTPUT +ARG PYTHON_BINDINGS +ARG PYTHON_EXECUTABLE +ARG RUN_SYSTEM_TESTS +ARG RUN_UNIT_TESTS +ARG TARGET_ARCH +ARG TEST_INSTALL +ARG UBSAN_BUILD +ARG USE_LIBGMP +ARG USE_LTO +ARG USE_OPENMP ARG Z3_SRC_DIR=/home/user/z3_src -ARG Z3_BUILD_TYPE=RelWithDebInfo -ARG Z3_CMAKE_GENERATOR=Ninja -ARG Z3_INSTALL_PREFIX=/usr -ARG Z3_STATIC_BUILD=0 -# Blank default indicates use latest. +ARG Z3_BUILD_TYPE +ARG Z3_CMAKE_GENERATOR +ARG Z3_INSTALL_PREFIX +ARG Z3_STATIC_BUILD ARG Z3_SYSTEM_TEST_GIT_REVISION -ARG Z3_WARNINGS_AS_ERRORS=SERIOUS_ONLY -ARG Z3_VERBOSE_BUILD_OUTPUT=0 +ARG Z3_WARNINGS_AS_ERRORS +ARG Z3_VERBOSE_BUILD_OUTPUT ENV \ ASAN_BUILD=${ASAN_BUILD} \ @@ -74,6 +73,7 @@ ADD *.txt *.md RELEASE_NOTES ${Z3_SRC_DIR}/ ADD \ /contrib/ci/scripts/build_z3_cmake.sh \ + /contrib/ci/scripts/ci_defaults.sh \ /contrib/ci/scripts/set_compiler_flags.sh \ /contrib/ci/scripts/set_generator_args.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ diff --git a/contrib/ci/scripts/ci_defaults.sh b/contrib/ci/scripts/ci_defaults.sh new file mode 100644 index 000000000..354ea95b7 --- /dev/null +++ b/contrib/ci/scripts/ci_defaults.sh @@ -0,0 +1,54 @@ +# This file should be sourced by other scripts +# and not executed directly + +# Set CI build defaults + +export ASAN_BUILD="${ASAN_BUILD:-0}" +export BUILD_DOCS="${BUILD_DOCS:-0}" +export DOTNET_BINDINGS="${DOTNET_BINDINGS:-1}" +export JAVA_BINDINGS="${JAVA_BINDINGS:-1}" +export NO_SUPPRESS_OUTPUT="${NO_SUPPRESS_OUTPUT:-0}" +export PYTHON_BINDINGS="${PYTHON_BINDINGS:-1}" +export PYTHON_EXECUTABLE="${PYTHON_EXECUTABLE:-$(which python)}" +export RUN_SYSTEM_TESTS="${RUN_SYSTEM_TESTS:-1}" +export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-1}" +export TARGET_ARCH="${TARGET_ARCH:-x86_64}" +export TEST_INSTALL="${TEST_INSTALL:-1}" +export UBSAN_BUILD="${UBSAN_BUILD:-0}" +export USE_LIBGMP="${USE_LIBGMP:-0}" +export USE_LTO="${USE_LTO:-0}" +export USE_OPENMP="${USE_OPENMP:-1}" + +export Z3_BUILD_TYPE="${Z3_BUILD_TYPE:-RelWithDebInfo}" +export Z3_CMAKE_GENERATOR="${Z3_CMAKE_GENERATOR:-Ninja}" +export Z3_STATIC_BUILD="${Z3_STATIC_BUILD:-0}" +# Default is blank which means get latest revision +export Z3_SYSTEM_TEST_GIT_REVISION="${Z3_SYSTEM_TEST_GIT_REVISION:-}" +export Z3_WARNINGS_AS_ERRORS="${Z3_WARNINGS_AS_ERRORS:-SERIOUS_ONLY}" +export Z3_VERBOSE_BUILD_OUTPUT="${Z3_VERBOSE_BUILD_OUTPUT:-0}" + +# Platform specific defaults +PLATFORM="$(uname -s)" +case "${PLATFORM}" in + Linux*) + export C_COMPILER="${C_COMPILER:-gcc}" + export CXX_COMPILER="${CXX_COMPILER:-g++}" + export Z3_INSTALL_PREFIX="${Z3_INSTALL_PREFIX:-/usr}" + ;; + Darwin*) + export C_COMPILER="${C_COMPILER:-clang}" + export CXX_COMPILER="${CXX_COMPILER:-clang++}" + export Z3_INSTALL_PREFIX="${Z3_INSTALL_PREFIX:-/usr/local}" + ;; + *) + echo "Unknown platform \"${PLATFORM}\"" + exit 1 + ;; +esac +unset PLATFORM + +# NOTE: The following variables are not set here because +# they are specific to the CI implementation +# Z3_SRC_DIR +# Z3_BUILD_DIR +# Z3_SYSTEM_TEST_DIR diff --git a/contrib/ci/scripts/travis_ci_linux_entry_point.sh b/contrib/ci/scripts/travis_ci_linux_entry_point.sh index 84b2dd400..c0f856faa 100755 --- a/contrib/ci/scripts/travis_ci_linux_entry_point.sh +++ b/contrib/ci/scripts/travis_ci_linux_entry_point.sh @@ -11,13 +11,15 @@ DOCKER_FILE_DIR="$(cd ${SCRIPT_DIR}/../Dockerfiles; echo $PWD)" : ${LINUX_BASE?"LINUX_BASE must be specified"} - # Sanity check. Current working directory should be repo root if [ ! -f "./README.md" ]; then echo "Current working directory should be repo root" exit 1 fi +# Get defaults +source "${SCRIPT_DIR}/ci_defaults.sh" + BUILD_OPTS=() # Override options if they have been provided. # Otherwise the defaults in the Docker file will be used diff --git a/contrib/ci/scripts/travis_ci_osx_entry_point.sh b/contrib/ci/scripts/travis_ci_osx_entry_point.sh index c5e8b4c02..7dd566877 100755 --- a/contrib/ci/scripts/travis_ci_osx_entry_point.sh +++ b/contrib/ci/scripts/travis_ci_osx_entry_point.sh @@ -6,26 +6,8 @@ set -x set -e set -o pipefail -# Set defaults -# FIXME: Refactor this so we don't need to stay in sync with -# `z3_build.Dockerfile`. -export ASAN_BUILD="${ASAN_BUILD:-0}" -export BUILD_DOCS="${BUILD_DOCS:-0}" -export C_COMPILER="${C_COMPILER:-clang}" -export CXX_COMPILER="${CXX_COMPILER:-clang++}" -export DOTNET_BINDINGS="${DOTNET_BINDINGS:-1}" -export JAVA_BINDINGS="${JAVA_BINDINGS:-1}" -export NO_SUPPRESS_OUTPUT="${NO_SUPPRESS_OUTPUT:-0}" -export PYTHON_BINDINGS="${PYTHON_BINDINGS:-1}" -export PYTHON_EXECUTABLE="$(which python)" -export RUN_SYSTEM_TESTS="${RUN_SYSTEM_TESTS:-1}" -export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-1}" -export TARGET_ARCH="${TARGET_ARCH:-x86_64}" -export TEST_INSTALL="${TEST_INSTALL:-1}" -export UBSAN_BUILD="${UBSAN_BUILD:-0}" -export USE_LIBGMP="${USE_LIBGMP:-0}" -export USE_LTO="${USE_LTO:-0}" -export USE_OPENMP="${USE_OPENMP:-1}" +# Get defaults +source "${SCRIPT_DIR}/ci_defaults.sh" if [ -z "${TRAVIS_BUILD_DIR}" ]; then echo "TRAVIS_BUILD_DIR must be set to root of Z3 repository" @@ -37,15 +19,11 @@ if [ ! -d "${TRAVIS_BUILD_DIR}" ]; then exit 1 fi +# These three variables are specific to the macOS TravisCI +# implementation and are not set in `ci_defaults.sh`. export Z3_SRC_DIR="${TRAVIS_BUILD_DIR}" export Z3_BUILD_DIR="${Z3_SRC_DIR}/build" -export Z3_BUILD_TYPE="${Z3_BUILD_TYPE:-RelWithDebInfo}" -export Z3_CMAKE_GENERATOR="${Z3_CMAKE_GENERATOR:-Ninja}" -export Z3_INSTALL_PREFIX="${Z3_INSTALL_PREFIX:-/usr/local}" -export Z3_STATIC_BUILD="${Z3_STATIC_BUILD:-0}" export Z3_SYSTEM_TEST_DIR="${Z3_SRC_DIR}/z3_system_test" -export Z3_WARNINGS_AS_ERRORS="${Z3_WARNINGS_AS_ERRORS:-SERIOUS_ONLY}" -export Z3_VERBOSE_BUILD_OUTPUT="${Z3_VERBOSE_BUILD_OUTPUT:-0}" # Overwrite whatever what set in TravisCI export CC="${C_COMPILER}" From eb975a49d606bcdb076b523d0506bf855ef8d267 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Thu, 5 Oct 2017 14:44:41 +0100 Subject: [PATCH 032/148] [TravisCI] Fix bug where `Z3_BUILD_TYPE` was not being passed as a Docker build argument. Also update an out of date comment. --- contrib/ci/scripts/travis_ci_linux_entry_point.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/ci/scripts/travis_ci_linux_entry_point.sh b/contrib/ci/scripts/travis_ci_linux_entry_point.sh index c0f856faa..bd2c9d2d1 100755 --- a/contrib/ci/scripts/travis_ci_linux_entry_point.sh +++ b/contrib/ci/scripts/travis_ci_linux_entry_point.sh @@ -21,8 +21,11 @@ fi source "${SCRIPT_DIR}/ci_defaults.sh" BUILD_OPTS=() -# Override options if they have been provided. -# Otherwise the defaults in the Docker file will be used +# Pass Docker build arguments +if [ -n "${Z3_BUILD_TYPE}" ]; then + BUILD_OPTS+=("--build-arg" "Z3_BUILD_TYPE=${Z3_BUILD_TYPE}") +fi + if [ -n "${Z3_CMAKE_GENERATOR}" ]; then BUILD_OPTS+=("--build-arg" "Z3_CMAKE_GENERATOR=${Z3_CMAKE_GENERATOR}") fi From 0633d5819f8b996b3300f177efe6b1406a1285a5 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Thu, 5 Oct 2017 15:09:16 +0100 Subject: [PATCH 033/148] [TravisCI] Fix bug. `PYTHON_EXECUTABLE` should not be in common defaults. The location is dependent on the implementation. This triggered a build failure on TravisCI because the location of the default Python binary is different to what is in the Docker container. --- contrib/ci/Dockerfiles/z3_build.Dockerfile | 2 +- contrib/ci/scripts/ci_defaults.sh | 2 +- contrib/ci/scripts/travis_ci_osx_entry_point.sh | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 5f7608cf4..07504e6b9 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -12,7 +12,7 @@ ARG DOTNET_BINDINGS ARG JAVA_BINDINGS ARG NO_SUPPRESS_OUTPUT ARG PYTHON_BINDINGS -ARG PYTHON_EXECUTABLE +ARG PYTHON_EXECUTABLE=/usr/bin/python2.7 ARG RUN_SYSTEM_TESTS ARG RUN_UNIT_TESTS ARG TARGET_ARCH diff --git a/contrib/ci/scripts/ci_defaults.sh b/contrib/ci/scripts/ci_defaults.sh index 354ea95b7..2fb3fa52a 100644 --- a/contrib/ci/scripts/ci_defaults.sh +++ b/contrib/ci/scripts/ci_defaults.sh @@ -9,7 +9,6 @@ export DOTNET_BINDINGS="${DOTNET_BINDINGS:-1}" export JAVA_BINDINGS="${JAVA_BINDINGS:-1}" export NO_SUPPRESS_OUTPUT="${NO_SUPPRESS_OUTPUT:-0}" export PYTHON_BINDINGS="${PYTHON_BINDINGS:-1}" -export PYTHON_EXECUTABLE="${PYTHON_EXECUTABLE:-$(which python)}" export RUN_SYSTEM_TESTS="${RUN_SYSTEM_TESTS:-1}" export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-1}" export TARGET_ARCH="${TARGET_ARCH:-x86_64}" @@ -49,6 +48,7 @@ unset PLATFORM # NOTE: The following variables are not set here because # they are specific to the CI implementation +# PYTHON_EXECUTABLE # Z3_SRC_DIR # Z3_BUILD_DIR # Z3_SYSTEM_TEST_DIR diff --git a/contrib/ci/scripts/travis_ci_osx_entry_point.sh b/contrib/ci/scripts/travis_ci_osx_entry_point.sh index 7dd566877..ad3b0c7ab 100755 --- a/contrib/ci/scripts/travis_ci_osx_entry_point.sh +++ b/contrib/ci/scripts/travis_ci_osx_entry_point.sh @@ -19,8 +19,9 @@ if [ ! -d "${TRAVIS_BUILD_DIR}" ]; then exit 1 fi -# These three variables are specific to the macOS TravisCI +# These variables are specific to the macOS TravisCI # implementation and are not set in `ci_defaults.sh`. +export PYTHON_EXECUTABLE="${PYTHON_EXECUTABLE:-$(which python)}" export Z3_SRC_DIR="${TRAVIS_BUILD_DIR}" export Z3_BUILD_DIR="${Z3_SRC_DIR}/build" export Z3_SYSTEM_TEST_DIR="${Z3_SRC_DIR}/z3_system_test" From 6268ff1fa13d283c9b4b0461f2b4423509a554ee Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 5 Oct 2017 18:10:20 +0100 Subject: [PATCH 034/148] dom_simplify improvements with Nikolaj --- src/tactic/core/dom_simplify_tactic.cpp | 129 ++++++++++++++++-------- src/tactic/core/dom_simplify_tactic.h | 19 +++- 2 files changed, 102 insertions(+), 46 deletions(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index d4a3031cd..85b7fd9d6 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -95,13 +95,16 @@ void expr_dominators::compute_dominators() { expr * child = m_post2expr[i]; ptr_vector const& p = m_parents[child]; SASSERT(!p.empty()); - expr * new_idom = p[0], * idom2 = 0; - for (unsigned j = 1; j < p.size(); ++j) { - if (m_doms.find(p[j], idom2)) { + expr * new_idom = 0, *idom2 = 0; + for (unsigned j = 0; j < p.size(); ++j) { + if (!new_idom) { + m_doms.find(p[j], new_idom); + } + else if (m_doms.find(p[j], idom2)) { new_idom = intersect(new_idom, idom2); } } - if (!m_doms.find(child, idom2) || idom2 != new_idom) { + if (new_idom && (!m_doms.find(child, idom2) || idom2 != new_idom)) { m_doms.insert(child, new_idom); change = true; } @@ -113,7 +116,7 @@ void expr_dominators::extract_tree() { for (auto const& kv : m_doms) { add_edge(m_tree, kv.m_value, kv.m_key); } -} +} void expr_dominators::compile(expr * e) { reset(); @@ -147,9 +150,9 @@ tactic * dom_simplify_tactic::translate(ast_manager & m) { } void dom_simplify_tactic::operator()( - goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, + goal_ref const & in, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; @@ -162,33 +165,41 @@ void dom_simplify_tactic::operator()( } void dom_simplify_tactic::cleanup() { - m_trail.reset(); - m_args.reset(); - m_args2.reset(); - m_result.reset(); - m_dominators.reset(); + m_trail.reset(); + m_args.reset(); + m_args2.reset(); + m_result.reset(); + m_dominators.reset(); } expr_ref dom_simplify_tactic::simplify_ite(app * ite) { expr_ref r(m); - expr * c = 0, * t = 0, * e = 0; + expr * c = 0, *t = 0, *e = 0; VERIFY(m.is_ite(ite, c, t, e)); unsigned old_lvl = scope_level(); expr_ref new_c = simplify(c); if (m.is_true(new_c)) { r = simplify(t); - } - else if (m.is_false(new_c) || !assert_expr(new_c, false)) { + } else if (m.is_false(new_c) || !assert_expr(new_c, false)) { r = simplify(e); - } - else { - expr_ref new_t = simplify(t); + } else { + for (expr * child : tree(ite)) { + if (is_subexpr(child, t) && !is_subexpr(child, e)) { + simplify(child); + } + } pop(scope_level() - old_lvl); + expr_ref new_t = simplify(t); if (!assert_expr(new_c, true)) { return new_t; } - expr_ref new_e = simplify(e); + for (expr * child : tree(ite)) { + if (is_subexpr(child, e) && !is_subexpr(child, t)) { + simplify(child); + } + } pop(scope_level() - old_lvl); + expr_ref new_e = simplify(e); if (c == new_c && t == new_t && e == new_e) { r = ite; } @@ -197,7 +208,7 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { } else { TRACE("tactic", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";); - r = m.mk_ite(new_c, new_t, new_c); + r = m.mk_ite(new_c, new_t, new_e); } } return r; @@ -224,11 +235,8 @@ expr_ref dom_simplify_tactic::simplify(expr * e0) { r = simplify_or(to_app(e)); } else { - expr_dominators::tree_t const& t = m_dominators.get_tree(); - if (auto children = t.find_core(e)) { - for (expr * child : children->get_data().m_value) { - simplify(child); - } + for (expr * child : tree(e)) { + simplify(child); } if (is_app(e)) { m_args.reset(); @@ -251,27 +259,33 @@ expr_ref dom_simplify_tactic::simplify(expr * e0) { expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { expr_ref r(m); unsigned old_lvl = scope_level(); - m_args.reset(); + + auto is_subexpr_arg = [&](expr * child, expr * except) { + if (!is_subexpr(child, except)) + return false; + for (expr * arg : *e) { + if (arg != except && is_subexpr(child, arg)) + return false; + } + return true; + }; + + expr_ref_vector args(m); for (expr * arg : *e) { - r = simplify(arg); - if (!assert_expr(r, !is_and)) { - r = is_and ? m.mk_false() : m.mk_true(); + for (expr * child : tree(arg)) { + if (is_subexpr_arg(child, arg)) { + simplify(child); + } + } + r = simplify(arg); + args.push_back(r); + if (!assert_expr(simplify(arg), !is_and)) { + r = is_and ? m.mk_false() : m.mk_true(); + return r; } - m_args.push_back(r); } pop(scope_level() - old_lvl); - m_args.reverse(); - m_args2.reset(); - for (expr * arg : m_args) { - r = simplify(arg); - if (!assert_expr(r, !is_and)) { - r = is_and ? m.mk_false() : m.mk_true(); - } - m_args2.push_back(r); - } - pop(scope_level() - old_lvl); - m_args2.reverse(); - r = is_and ? mk_and(m_args2) : mk_or(m_args2); + r = is_and ? mk_and(args) : mk_or(args); return r; } @@ -332,11 +346,36 @@ void dom_simplify_tactic::simplify_goal(goal& g) { SASSERT(scope_level() == 0); } +bool dom_simplify_tactic::is_subexpr(expr * a, expr * b) { + if (a == b) + return true; + + bool r; + if (m_subexpr_cache.find(a, b, r)) + return r; + + for (expr * e : tree(b)) { + if (is_subexpr(a, e)) { + m_subexpr_cache.insert(a, b, true); + return true; + } + } + m_subexpr_cache.insert(a, b, false); + return false; +} + +ptr_vector const & dom_simplify_tactic::tree(expr * e) { + if (auto p = m_dominators.get_tree().find_core(e)) + return p->get_data().get_value(); + return m_empty; +} + // ---------------------- // expr_substitution_simplifier bool expr_substitution_simplifier::assert_expr(expr * t, bool sign) { + m_scoped_substitution.push(); expr* tt; if (!sign) { update_substitution(t, 0); @@ -439,3 +478,7 @@ void expr_substitution_simplifier::compute_depth(expr* e) { m_expr2depth.insert(e, d + 1); } } + +tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(dom_simplify_tactic, m, alloc(expr_substitution_simplifier, m), p)); +} diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 2fa79dd1d..9fe59de23 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -23,6 +23,8 @@ Notes: #include "ast/ast.h" #include "ast/expr_substitution.h" #include "tactic/tactic.h" +#include "tactic/tactical.h" +#include "util/obj_pair_hashtable.h" class expr_dominators { @@ -89,6 +91,8 @@ private: unsigned m_scope_level; unsigned m_depth; unsigned m_max_depth; + ptr_vector m_empty; + obj_pair_map m_subexpr_cache; expr_ref simplify(expr* t); expr_ref simplify_ite(app * ite); @@ -97,9 +101,13 @@ private: expr_ref simplify_and_or(bool is_and, app * ite); void simplify_goal(goal& g); - expr_ref get_cached(expr* t) { expr* r = 0; if (!m_result.find(r, r)) r = t; return expr_ref(r, m); } + bool is_subexpr(expr * a, expr * b); + + expr_ref get_cached(expr* t) { expr* r = 0; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } + ptr_vector const & tree(expr * e); + unsigned scope_level() { return m_scope_level; } void pop(unsigned n) { SASSERT(n <= m_scope_level); m_scope_level -= n; m_simplifier->pop(n); } bool assert_expr(expr* f, bool sign) { m_scope_level++; return m_simplifier->assert_expr(f, sign); } @@ -156,8 +164,13 @@ public: SASSERT(m_subst.empty()); return alloc(expr_substitution_simplifier, m); } - - }; + +tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* +ADD_TACTIC("dom-simplify", "apply dominator simplification rules.", "mk_dom_simplify_tactic(m, p)") +*/ + #endif From f59cf2452d9a335785bd1691dbf04b93973193d1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 5 Oct 2017 22:20:31 +0100 Subject: [PATCH 035/148] #1284 build problems Signed-off-by: Nikolaj Bjorner --- src/tactic/core/CMakeLists.txt | 2 + src/tactic/core/dom_simplify_tactic.cpp | 1 + src/tactic/core/dom_simplify_tactic.h | 53 +++++++++++++------------ 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index f192b4fa6..16778d8dd 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -7,6 +7,7 @@ z3_add_component(core_tactics ctx_simplify_tactic.cpp der_tactic.cpp distribute_forall_tactic.cpp + dom_simplify_tactic.cpp elim_term_ite_tactic.cpp elim_uncnstr_tactic.cpp injectivity_tactic.cpp @@ -32,6 +33,7 @@ z3_add_component(core_tactics ctx_simplify_tactic.h der_tactic.h distribute_forall_tactic.h + dom_simplify_tactic.h elim_term_ite_tactic.h elim_uncnstr_tactic.h injectivity_tactic.h diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 85b7fd9d6..bba27cf46 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -188,6 +188,7 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { simplify(child); } } + pop(scope_level() - old_lvl); expr_ref new_t = simplify(t); if (!assert_expr(new_c, true)) { diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 9fe59de23..825a173bf 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -58,32 +58,35 @@ public: }; +class dom_simplifier { + public: + dom_simplifier() {} + + virtual ~dom_simplifier() {} + /** + \brief assert_expr performs an implicit push + */ + virtual bool assert_expr(expr * t, bool sign) = 0; + + /** + \brief apply simplification. + */ + virtual void operator()(expr_ref& r) = 0; + + /** + \brief pop scopes accumulated from assertions. + */ + virtual void pop(unsigned num_scopes) = 0; + + virtual dom_simplifier * translate(ast_manager & m) = 0; + +}; + class dom_simplify_tactic : public tactic { public: - class simplifier { - public: - virtual ~simplifier() {} - /** - \brief assert_expr performs an implicit push - */ - virtual bool assert_expr(expr * t, bool sign) = 0; - - /** - \brief apply simplification. - */ - virtual void operator()(expr_ref& r) = 0; - - /** - \brief pop scopes accumulated from assertions. - */ - virtual void pop(unsigned num_scopes) = 0; - - virtual simplifier * translate(ast_manager & m); - - }; private: ast_manager& m; - simplifier* m_simplifier; + dom_simplifier* m_simplifier; params_ref m_params; expr_ref_vector m_trail, m_args, m_args2; obj_map m_result; @@ -115,7 +118,7 @@ private: void init(goal& g); public: - dom_simplify_tactic(ast_manager & m, simplifier* s, params_ref const & p = params_ref()): + dom_simplify_tactic(ast_manager & m, dom_simplifier* s, params_ref const & p = params_ref()): m(m), m_simplifier(s), m_params(p), m_trail(m), m_args(m), m_args2(m), m_dominators(m), @@ -138,7 +141,7 @@ public: virtual void cleanup(); }; -class expr_substitution_simplifier : public dom_simplify_tactic::simplifier { +class expr_substitution_simplifier : public dom_simplifier { ast_manager& m; expr_substitution m_subst; scoped_expr_substitution m_scoped_substitution; @@ -160,7 +163,7 @@ public: virtual void pop(unsigned num_scopes) { m_scoped_substitution.pop(num_scopes); } - virtual simplifier * translate(ast_manager & m) { + virtual dom_simplifier * translate(ast_manager & m) { SASSERT(m_subst.empty()); return alloc(expr_substitution_simplifier, m); } From eac659f748fc3209fca947556d94d9b08a3c8c82 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Oct 2017 11:34:14 +0100 Subject: [PATCH 036/148] deal with empty set of post-orders Signed-off-by: Nikolaj Bjorner --- src/muz/rel/karr_relation.cpp | 4 ++-- .../transforms/dl_mk_interp_tail_simplifier.cpp | 4 ++-- src/smt/theory_lra.cpp | 4 ++-- src/tactic/core/dom_simplify_tactic.cpp | 14 ++++++++------ src/tactic/core/dom_simplify_tactic.h | 4 ++-- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/muz/rel/karr_relation.cpp b/src/muz/rel/karr_relation.cpp index 572d5e8d5..c8a489d69 100644 --- a/src/muz/rel/karr_relation.cpp +++ b/src/muz/rel/karr_relation.cpp @@ -111,8 +111,8 @@ namespace datalog { void filter_interpreted(app* cond) { rational one(1), mone(-1); - expr* e1, *e2, *en; - var* v, *w; + expr* e1 = 0, *e2 = 0, *en = 0; + var* v = 0, *w = 0; rational n1, n2; expr_ref_vector conjs(m); flatten_and(cond, conjs); diff --git a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp index 30505a5e8..7bd35b4ef 100644 --- a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp @@ -398,8 +398,8 @@ namespace datalog { } bool mk_interp_tail_simplifier::propagate_variable_equivalences(rule * r, rule_ref& res) { - if (!m_context.get_params ().xform_tail_simplifier_pve ()) - return false; + if (!m_context.get_params ().xform_tail_simplifier_pve ()) + return false; unsigned u_len = r->get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if (u_len == len) { diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 0bb76cd90..7fec6f836 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -721,7 +721,7 @@ namespace smt { SASSERT(!ctx().b_internalized(atom)); bool_var bv = ctx().mk_bool_var(atom); ctx().set_var_theory(bv, get_id()); - expr* n1, *n2; + expr* n1 = 0, *n2 = 0; rational r; lra_lp::bound_kind k; theory_var v = null_theory_var; @@ -862,7 +862,7 @@ namespace smt { void relevant_eh(app* n) { TRACE("arith", tout << mk_pp(n, m) << "\n";); - expr* n1, *n2; + expr* n1 = 0, *n2 = 0; if (a.is_mod(n, n1, n2)) mk_idiv_mod_axioms(n1, n2); else if (a.is_rem(n, n1, n2)) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index bba27cf46..17262a9d6 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -90,8 +90,8 @@ void expr_dominators::compute_dominators() { bool change = true; while (change) { change = false; - SASSERT(m_post2expr.back() == e); - for (unsigned i = 0; i < m_post2expr.size() - 1; ++i) { + SASSERT(m_post2expr.empty() || m_post2expr.back() == e); + for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) { expr * child = m_post2expr[i]; ptr_vector const& p = m_parents[child]; SASSERT(!p.empty()); @@ -167,7 +167,6 @@ void dom_simplify_tactic::operator()( void dom_simplify_tactic::cleanup() { m_trail.reset(); m_args.reset(); - m_args2.reset(); m_result.reset(); m_dominators.reset(); } @@ -180,9 +179,11 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { expr_ref new_c = simplify(c); if (m.is_true(new_c)) { r = simplify(t); - } else if (m.is_false(new_c) || !assert_expr(new_c, false)) { + } + else if (m.is_false(new_c) || !assert_expr(new_c, false)) { r = simplify(e); - } else { + } + else { for (expr * child : tree(ite)) { if (is_subexpr(child, t) && !is_subexpr(child, e)) { simplify(child); @@ -254,6 +255,7 @@ expr_ref dom_simplify_tactic::simplify(expr * e0) { cache(e0, r); TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); --m_depth; + m_subexpr_cache.reset(); return r; } @@ -361,7 +363,7 @@ bool dom_simplify_tactic::is_subexpr(expr * a, expr * b) { return true; } } - m_subexpr_cache.insert(a, b, false); + m_subexpr_cache.insert(a, b, false); return false; } diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 825a173bf..c62651c5c 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -88,7 +88,7 @@ private: ast_manager& m; dom_simplifier* m_simplifier; params_ref m_params; - expr_ref_vector m_trail, m_args, m_args2; + expr_ref_vector m_trail, m_args; obj_map m_result; expr_dominators m_dominators; unsigned m_scope_level; @@ -120,7 +120,7 @@ private: public: dom_simplify_tactic(ast_manager & m, dom_simplifier* s, params_ref const & p = params_ref()): m(m), m_simplifier(s), m_params(p), - m_trail(m), m_args(m), m_args2(m), + m_trail(m), m_args(m), m_dominators(m), m_scope_level(0), m_depth(0), m_max_depth(1024) {} From 6df628edc7043a08dd11eae3d2ab8d44e07a172c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Oct 2017 11:45:29 +0100 Subject: [PATCH 037/148] pin elements in expr2depth Signed-off-by: Nikolaj Bjorner --- src/smt/smt_implied_equalities.cpp | 2 +- src/smt/theory_lra.cpp | 2 +- src/tactic/bv/bv_bounds_tactic.cpp | 6 ++++-- src/tactic/core/dom_simplify_tactic.cpp | 2 ++ src/tactic/core/dom_simplify_tactic.h | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/smt/smt_implied_equalities.cpp b/src/smt/smt_implied_equalities.cpp index 7d54e714e..d021708fc 100644 --- a/src/smt/smt_implied_equalities.cpp +++ b/src/smt/smt_implied_equalities.cpp @@ -284,7 +284,7 @@ namespace smt { } lbool reduce_cond(model_ref& model, expr* e) { - expr* e1, *e2; + expr* e1 = 0, *e2 = 0; if (m.is_eq(e, e1, e2) && m_array_util.is_as_array(e1) && m_array_util.is_as_array(e2)) { if (e1 == e2) { return l_true; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 7fec6f836..292d2ab0d 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -689,7 +689,7 @@ namespace smt { SASSERT(!ctx().b_internalized(atom)); bool_var bv = ctx().mk_bool_var(atom); ctx().set_var_theory(bv, get_id()); - expr* n1, *n2; + expr* n1 = 0, *n2 = 0; rational r; lra_lp::bound_kind k; theory_var v = null_theory_var; diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index d2cd0d66c..1f0bb8473 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -70,11 +70,13 @@ struct interval { if (is_wrapped()) { // l >= b.l >= b.h >= h return b.is_wrapped() && h <= b.h && l >= b.l; - } else if (b.is_wrapped()) { + } + else if (b.is_wrapped()) { // b.l > b.h >= h >= l // h >= l >= b.l > b.h return h <= b.h || l >= b.l; - } else { + } + else { // return l >= b.l && h <= b.h; } diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 17262a9d6..08037fe18 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -430,6 +430,8 @@ void expr_substitution_simplifier::update_substitution(expr* n, proof* pr) { if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) { compute_depth(lhs); compute_depth(rhs); + m_trail.push_back(lhs); + m_trail.push_back(rhs); if (is_gt(lhs, rhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";); m_scoped_substitution.insert(lhs, rhs, pr); diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index c62651c5c..25fb7c24f 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -146,6 +146,7 @@ class expr_substitution_simplifier : public dom_simplifier { expr_substitution m_subst; scoped_expr_substitution m_scoped_substitution; obj_map m_expr2depth; + expr_ref_vector m_trail; // move from asserted_formulas to here.. void compute_depth(expr* e); @@ -153,7 +154,7 @@ class expr_substitution_simplifier : public dom_simplifier { unsigned depth(expr* e) { return m_expr2depth[e]; } public: - expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst) {} + expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst), m_trail(m) {} virtual ~expr_substitution_simplifier() {} virtual bool assert_expr(expr * t, bool sign); From cb548404bc1ad5a55628e1dd23fc4efe69de8856 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Oct 2017 12:08:37 +0100 Subject: [PATCH 038/148] bail out dominators after log number of steps Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_bounds_tactic.cpp | 1227 ++++++++++++++--------- src/tactic/core/dom_simplify_tactic.cpp | 25 +- src/tactic/core/dom_simplify_tactic.h | 8 +- 3 files changed, 792 insertions(+), 468 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 1f0bb8473..2d5eb216a 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -18,6 +18,7 @@ Author: #include "tactic/bv/bv_bounds_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" +#include "tactic/core/dom_simplify_tactic.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" #include @@ -29,509 +30,825 @@ static uint64 uMaxInt(unsigned sz) { namespace { -struct interval { - // l < h: [l, h] - // l > h: [0, h] U [l, UMAX_INT] - uint64 l, h; - unsigned sz; - bool tight; + struct interval { + // l < h: [l, h] + // l > h: [0, h] U [l, UMAX_INT] + uint64 l, h; + unsigned sz; + bool tight; - interval() {} - interval(uint64 l, uint64 h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { - // canonicalize full set - if (is_wrapped() && l == h + 1) { - this->l = 0; - this->h = uMaxInt(sz); - } - SASSERT(invariant()); - } - - bool invariant() const { - return l <= uMaxInt(sz) && h <= uMaxInt(sz) && - (!is_wrapped() || l != h+1); - } - - bool is_full() const { return l == 0 && h == uMaxInt(sz); } - bool is_wrapped() const { return l > h; } - bool is_singleton() const { return l == h; } - - bool operator==(const interval& b) const { - SASSERT(sz == b.sz); - return l == b.l && h == b.h && tight == b.tight; - } - bool operator!=(const interval& b) const { return !(*this == b); } - - bool implies(const interval& b) const { - if (b.is_full()) - return true; - if (is_full()) - return false; - - if (is_wrapped()) { - // l >= b.l >= b.h >= h - return b.is_wrapped() && h <= b.h && l >= b.l; - } - else if (b.is_wrapped()) { - // b.l > b.h >= h >= l - // h >= l >= b.l > b.h - return h <= b.h || l >= b.l; - } - else { - // - return l >= b.l && h <= b.h; - } - } - - /// return false if intersection is unsat - bool intersect(const interval& b, interval& result) const { - if (is_full() || *this == b) { - result = b; - return true; - } - if (b.is_full()) { - result = *this; - return true; + interval() {} + interval(uint64 l, uint64 h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { + // canonicalize full set + if (is_wrapped() && l == h + 1) { + this->l = 0; + this->h = uMaxInt(sz); + } + SASSERT(invariant()); } - if (is_wrapped()) { - if (b.is_wrapped()) { - if (h >= b.l) { - result = b; - } else if (b.h >= l) { - result = *this; + bool invariant() const { + return l <= uMaxInt(sz) && h <= uMaxInt(sz) && + (!is_wrapped() || l != h+1); + } + + bool is_full() const { return l == 0 && h == uMaxInt(sz); } + bool is_wrapped() const { return l > h; } + bool is_singleton() const { return l == h; } + + bool operator==(const interval& b) const { + SASSERT(sz == b.sz); + return l == b.l && h == b.h && tight == b.tight; + } + bool operator!=(const interval& b) const { return !(*this == b); } + + bool implies(const interval& b) const { + if (b.is_full()) + return true; + if (is_full()) + return false; + + if (is_wrapped()) { + // l >= b.l >= b.h >= h + return b.is_wrapped() && h <= b.h && l >= b.l; + } + else if (b.is_wrapped()) { + // b.l > b.h >= h >= l + // h >= l >= b.l > b.h + return h <= b.h || l >= b.l; + } + else { + // + return l >= b.l && h <= b.h; + } + } + + /// return false if intersection is unsat + bool intersect(const interval& b, interval& result) const { + if (is_full() || *this == b) { + result = b; + return true; + } + if (b.is_full()) { + result = *this; + return true; + } + + if (is_wrapped()) { + if (b.is_wrapped()) { + if (h >= b.l) { + result = b; + } else if (b.h >= l) { + result = *this; + } else { + result = interval(std::max(l, b.l), std::min(h, b.h), sz); + } } else { - result = interval(std::max(l, b.l), std::min(h, b.h), sz); + return b.intersect(*this, result); + } + } + else if (b.is_wrapped()) { + // ... b.h ... l ... h ... b.l .. + if (h < b.l && l > b.h) { + return false; + } + // ... l ... b.l ... h ... + if (h >= b.l && l <= b.h) { + result = b; + } else if (h >= b.l) { + result = interval(b.l, h, sz); + } else { + // ... l .. b.h .. h .. b.l ... + SASSERT(l <= b.h); + result = interval(l, std::min(h, b.h), sz); } } else { - return b.intersect(*this, result); - } - } else if (b.is_wrapped()) { - // ... b.h ... l ... h ... b.l .. - if (h < b.l && l > b.h) { - return false; - } - // ... l ... b.l ... h ... - if (h >= b.l && l <= b.h) { - result = b; - } else if (h >= b.l) { - result = interval(b.l, h, sz); - } else { - // ... l .. b.h .. h .. b.l ... - SASSERT(l <= b.h); - result = interval(l, std::min(h, b.h), sz); - } - } else { - if (l > b.h || h < b.l) - return false; + if (l > b.h || h < b.l) + return false; - // 0 .. l.. l' ... h ... h' - result = interval(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); - } - return true; - } - - /// return false if negation is empty - bool negate(interval& result) const { - if (!tight) { - result = interval(0, uMaxInt(sz), true); + // 0 .. l.. l' ... h ... h' + result = interval(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); + } return true; } - if (is_full()) - return false; - if (l == 0) { - result = interval(h + 1, uMaxInt(sz), sz); - } else if (uMaxInt(sz) == h) { - result = interval(0, l - 1, sz); - } else { - result = interval(h + 1, l - 1, sz); + /// return false if negation is empty + bool negate(interval& result) const { + if (!tight) { + result = interval(0, uMaxInt(sz), true); + return true; + } + + if (is_full()) + return false; + if (l == 0) { + result = interval(h + 1, uMaxInt(sz), sz); + } else if (uMaxInt(sz) == h) { + result = interval(0, l - 1, sz); + } else { + result = interval(h + 1, l - 1, sz); + } + return true; } - return true; - } -}; + }; #ifdef _TRACE -std::ostream& operator<<(std::ostream& o, const interval& I) { - o << "[" << I.l << ", " << I.h << "]"; - return o; -} -#endif - - -struct undo_bound { - expr* e; - interval b; - bool fresh; - undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {} -}; - -class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { - typedef obj_map map; - typedef obj_map expr_set; - typedef obj_map expr_cnt; - - ast_manager& m; - params_ref m_params; - bool m_propagate_eq; - bv_util m_bv; - vector m_scopes; - map m_bound; - svector m_expr_vars; - svector m_bound_exprs; - - bool is_number(expr *e, uint64& n, unsigned& sz) const { - rational r; - if (m_bv.is_numeral(e, r, sz) && sz <= 64) { - n = r.get_uint64(); - return true; - } - return false; - } - - bool is_bound(expr *e, expr*& v, interval& b) const { - uint64 n; - expr *lhs, *rhs; - unsigned sz; - - if (m_bv.is_bv_ule(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { // C ule x <=> x uge C - if (m_bv.is_numeral(rhs)) - return false; - b = interval(n, uMaxInt(sz), sz, true); - v = rhs; - return true; - } - if (is_number(rhs, n, sz)) { // x ule C - b = interval(0, n, sz, true); - v = lhs; - return true; - } - } else if (m_bv.is_bv_sle(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { // C sle x <=> x sge C - if (m_bv.is_numeral(rhs)) - return false; - b = interval(n, (1ull << (sz-1)) - 1, sz, true); - v = rhs; - return true; - } - if (is_number(rhs, n, sz)) { // x sle C - b = interval(1ull << (sz-1), n, sz, true); - v = lhs; - return true; - } - } else if (m.is_eq(e, lhs, rhs)) { - if (is_number(lhs, n, sz)) { - if (m_bv.is_numeral(rhs)) - return false; - b = interval(n, n, sz, true); - v = rhs; - return true; - } - if (is_number(rhs, n, sz)) { - b = interval(n, n, sz, true); - v = lhs; - return true; - } - } - return false; - } - -#if 0 - expr_set* get_expr_vars(expr* t) { - unsigned id = t->get_id(); - m_expr_vars.reserve(id + 1); - expr_set*& entry = m_expr_vars[id]; - if (entry) - return entry; - - expr_set* set = alloc(expr_set); - entry = set; - - if (!m_bv.is_numeral(t)) - set->insert(t, true); - - if (!is_app(t)) - return set; - - app* a = to_app(t); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - expr_set* set_arg = get_expr_vars(a->get_arg(i)); - for (expr_set::iterator I = set_arg->begin(), E = set_arg->end(); I != E; ++I) { - set->insert(I->m_key, true); - } - } - return set; + std::ostream& operator<<(std::ostream& o, const interval& I) { + o << "[" << I.l << ", " << I.h << "]"; + return o; } #endif -#if 0 - expr_cnt* get_expr_bounds(expr* t) { - unsigned id = t->get_id(); - m_bound_exprs.reserve(id + 1); - expr_cnt*& entry = m_bound_exprs[id]; - if (entry) - return entry; - expr_cnt* set = alloc(expr_cnt); - entry = set; - - if (!is_app(t)) - return set; - - interval b; + struct undo_bound { expr* e; - if (is_bound(t, e, b)) { - set->insert_if_not_there2(e, 0)->get_data().m_value++; + interval b; + bool fresh; + undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {} + }; + + class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { + typedef obj_map map; + typedef obj_map expr_set; + typedef obj_map expr_cnt; + + ast_manager& m; + params_ref m_params; + bool m_propagate_eq; + bv_util m_bv; + vector m_scopes; + map m_bound; + svector m_expr_vars; + svector m_bound_exprs; + + bool is_number(expr *e, uint64& n, unsigned& sz) const { + rational r; + if (m_bv.is_numeral(e, r, sz) && sz <= 64) { + n = r.get_uint64(); + return true; + } + return false; } - app* a = to_app(t); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - expr_cnt* set_arg = get_expr_bounds(a->get_arg(i)); - for (expr_cnt::iterator I = set_arg->begin(), E = set_arg->end(); I != E; ++I) { - set->insert_if_not_there2(I->m_key, 0)->get_data().m_value += I->m_value; + bool is_bound(expr *e, expr*& v, interval& b) const { + uint64 n; + expr *lhs = 0, *rhs = 0; + unsigned sz; + + if (m_bv.is_bv_ule(e, lhs, rhs)) { + if (is_number(lhs, n, sz)) { // C ule x <=> x uge C + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, uMaxInt(sz), sz, true); + v = rhs; + return true; + } + if (is_number(rhs, n, sz)) { // x ule C + b = interval(0, n, sz, true); + v = lhs; + return true; + } + } + else if (m_bv.is_bv_sle(e, lhs, rhs)) { + if (is_number(lhs, n, sz)) { // C sle x <=> x sge C + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, (1ull << (sz-1)) - 1, sz, true); + v = rhs; + return true; + } + if (is_number(rhs, n, sz)) { // x sle C + b = interval(1ull << (sz-1), n, sz, true); + v = lhs; + return true; + } + } else if (m.is_eq(e, lhs, rhs)) { + if (is_number(lhs, n, sz)) { + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, n, sz, true); + v = rhs; + return true; + } + if (is_number(rhs, n, sz)) { + b = interval(n, n, sz, true); + v = lhs; + return true; + } } + return false; + } + +#if 0 + expr_set* get_expr_vars(expr* t) { + unsigned id = t->get_id(); + m_expr_vars.reserve(id + 1); + expr_set*& entry = m_expr_vars[id]; + if (entry) + return entry; + + expr_set* set = alloc(expr_set); + entry = set; + + if (!m_bv.is_numeral(t)) + set->insert(t, true); + + if (!is_app(t)) + return set; + + app* a = to_app(t); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + expr_set* set_arg = get_expr_vars(a->get_arg(i)); + for (expr_set::iterator I = set_arg->begin(), E = set_arg->end(); I != E; ++I) { + set->insert(I->m_key, true); + } + } + return set; } - return set; - } #endif -public: - bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { - updt_params(p); - } +#if 0 + expr_cnt* get_expr_bounds(expr* t) { + unsigned id = t->get_id(); + m_bound_exprs.reserve(id + 1); + expr_cnt*& entry = m_bound_exprs[id]; + if (entry) + return entry; - virtual void updt_params(params_ref const & p) { - m_propagate_eq = p.get_bool("propagate_eq", false); - } + expr_cnt* set = alloc(expr_cnt); + entry = set; - static void get_param_descrs(param_descrs& r) { - r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); - } + if (!is_app(t)) + return set; - virtual ~bv_bounds_simplifier() { - for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { - dealloc(m_expr_vars[i]); - } - for (unsigned i = 0, e = m_bound_exprs.size(); i < e; ++i) { - dealloc(m_bound_exprs[i]); - } - } - - virtual bool assert_expr(expr * t, bool sign) { - while (m.is_not(t, t)) { - sign = !sign; - } - - interval b; - expr* t1; - if (is_bound(t, t1, b)) { - SASSERT(!m_bv.is_numeral(t1)); - if (sign) - VERIFY(b.negate(b)); - - TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); - map::obj_map_entry* e = m_bound.find_core(t1); - if (e) { - interval& old = e->get_data().m_value; - interval intr; - if (!old.intersect(b, intr)) - return false; - if (old == intr) - return true; - m_scopes.insert(undo_bound(t1, old, false)); - old = intr; - } else { - m_bound.insert(t1, b); - m_scopes.insert(undo_bound(t1, interval(), true)); - } - } - return true; - } - - virtual bool simplify(expr* t, expr_ref& result) { - expr* t1; - interval b; - - if (m_bound.find(t, b) && b.is_singleton()) { - result = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); - return true; - } - - if (!m.is_bool(t)) - return false; - - bool sign = false; - while (m.is_not(t, t)) { - sign = !sign; - } - - if (!is_bound(t, t1, b)) - return false; - - if (sign && b.tight) { - sign = false; - if (!b.negate(b)) { - result = m.mk_false(); - return true; - } - } - - interval ctx, intr; - result = 0; - - if (b.is_full() && b.tight) { - result = m.mk_true(); - } else if (m_bound.find(t1, ctx)) { - if (ctx.implies(b)) { - result = m.mk_true(); - } else if (!b.intersect(ctx, intr)) { - result = m.mk_false(); - } else if (m_propagate_eq && intr.is_singleton()) { - result = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), - m.get_sort(t1))); - } - } - - CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); - if (sign && result != 0) - result = m.mk_not(result); - return result != 0; - } - - // check if t contains v - ptr_vector todo; - bool contains(expr* t, expr* v) { - ast_fast_mark1 mark; - todo.push_back(t); - while (!todo.empty()) { - t = todo.back(); - todo.pop_back(); - if (mark.is_marked(t)) { - continue; - } - if (t == v) { - todo.reset(); - return true; - } - mark.mark(t); - - if (!is_app(t)) { - continue; - } - app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); - } - return false; - } - - bool contains_bound(expr* t) { - ast_fast_mark1 mark1; - ast_fast_mark2 mark2; - - todo.push_back(t); - while (!todo.empty()) { - t = todo.back(); - todo.pop_back(); - if (mark1.is_marked(t)) { - continue; - } - mark1.mark(t); - - if (!is_app(t)) { - continue; - } interval b; expr* e; if (is_bound(t, e, b)) { - if (mark2.is_marked(e)) { - todo.reset(); - return true; - } - mark2.mark(e); - if (m_bound.contains(e)) { - todo.reset(); - return true; - } + set->insert_if_not_there2(e, 0)->get_data().m_value++; } app* a = to_app(t); - todo.append(a->get_num_args(), a->get_args()); - } - return false; - } - - virtual bool may_simplify(expr* t) { - if (m_bv.is_numeral(t)) - return false; - - while (m.is_not(t, t)); - - for (auto & v : m_bound) { - if (contains(t, v.m_key)) return true; - } - -#if 0 - expr_set* used_exprs = get_expr_vars(t); - for (map::iterator I = m_bound.begin(), E = m_bound.end(); I != E; ++I) { - if (contains(t, I->m_key)) return true; - if (I->m_value.is_singleton() && used_exprs->contains(I->m_key)) - return true; + for (unsigned i = 0; i < a->get_num_args(); ++i) { + expr_cnt* set_arg = get_expr_bounds(a->get_arg(i)); + for (expr_cnt::iterator I = set_arg->begin(), E = set_arg->end(); I != E; ++I) { + set->insert_if_not_there2(I->m_key, 0)->get_data().m_value += I->m_value; + } + } + return set; } #endif - expr* t1; - interval b; - // skip common case: single bound constraint without any context for simplification - if (is_bound(t, t1, b)) { - return b.is_full() || m_bound.contains(t1); + public: + bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { + updt_params(p); } - if (contains_bound(t)) { - return true; + virtual void updt_params(params_ref const & p) { + m_propagate_eq = p.get_bool("propagate_eq", false); } -#if 0 - expr_cnt* bounds = get_expr_bounds(t); - for (expr_cnt::iterator I = bounds->begin(), E = bounds->end(); I != E; ++I) { - if (I->m_value > 1 || m_bound.contains(I->m_key)) - return true; - } -#endif - return false; - } - virtual void pop(unsigned num_scopes) { - TRACE("bv", tout << "pop: " << num_scopes << "\n";); - if (m_scopes.empty()) - return; - unsigned target = m_scopes.size() - num_scopes; - if (target == 0) { - m_bound.reset(); - m_scopes.reset(); - return; + static void get_param_descrs(param_descrs& r) { + r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); } - for (unsigned i = m_scopes.size()-1; i >= target; --i) { - undo_bound& undo = m_scopes[i]; - SASSERT(m_bound.contains(undo.e)); - if (undo.fresh) { - m_bound.erase(undo.e); - } else { - m_bound.insert(undo.e, undo.b); + + virtual ~bv_bounds_simplifier() { + for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { + dealloc(m_expr_vars[i]); + } + for (unsigned i = 0, e = m_bound_exprs.size(); i < e; ++i) { + dealloc(m_bound_exprs[i]); } } - m_scopes.shrink(target); - } - virtual simplifier * translate(ast_manager & m) { - return alloc(bv_bounds_simplifier, m, m_params); - } + virtual bool assert_expr(expr * t, bool sign) { + while (m.is_not(t, t)) { + sign = !sign; + } - virtual unsigned scope_level() const { - return m_scopes.size(); - } -}; + interval b; + expr* t1; + if (is_bound(t, t1, b)) { + SASSERT(!m_bv.is_numeral(t1)); + if (sign) + VERIFY(b.negate(b)); + + TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); + map::obj_map_entry* e = m_bound.find_core(t1); + if (e) { + interval& old = e->get_data().m_value; + interval intr; + if (!old.intersect(b, intr)) + return false; + if (old == intr) + return true; + m_scopes.insert(undo_bound(t1, old, false)); + old = intr; + } else { + m_bound.insert(t1, b); + m_scopes.insert(undo_bound(t1, interval(), true)); + } + } + return true; + } + + virtual bool simplify(expr* t, expr_ref& result) { + expr* t1; + interval b; + + if (m_bound.find(t, b) && b.is_singleton()) { + result = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); + return true; + } + + if (!m.is_bool(t)) + return false; + + bool sign = false; + while (m.is_not(t, t)) { + sign = !sign; + } + + if (!is_bound(t, t1, b)) + return false; + + if (sign && b.tight) { + sign = false; + if (!b.negate(b)) { + result = m.mk_false(); + return true; + } + } + + interval ctx, intr; + result = 0; + + if (b.is_full() && b.tight) { + result = m.mk_true(); + } else if (m_bound.find(t1, ctx)) { + if (ctx.implies(b)) { + result = m.mk_true(); + } else if (!b.intersect(ctx, intr)) { + result = m.mk_false(); + } else if (m_propagate_eq && intr.is_singleton()) { + result = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), + m.get_sort(t1))); + } + } + + CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); + if (sign && result != 0) + result = m.mk_not(result); + return result != 0; + } + + // check if t contains v + ptr_vector todo; + bool contains(expr* t, expr* v) { + ast_fast_mark1 mark; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (mark.is_marked(t)) { + continue; + } + if (t == v) { + todo.reset(); + return true; + } + mark.mark(t); + + if (!is_app(t)) { + continue; + } + app* a = to_app(t); + todo.append(a->get_num_args(), a->get_args()); + } + return false; + } + + bool contains_bound(expr* t) { + ast_fast_mark1 mark1; + ast_fast_mark2 mark2; + + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (mark1.is_marked(t)) { + continue; + } + mark1.mark(t); + + if (!is_app(t)) { + continue; + } + interval b; + expr* e; + if (is_bound(t, e, b)) { + if (mark2.is_marked(e)) { + todo.reset(); + return true; + } + mark2.mark(e); + if (m_bound.contains(e)) { + todo.reset(); + return true; + } + } + + app* a = to_app(t); + todo.append(a->get_num_args(), a->get_args()); + } + return false; + } + + virtual bool may_simplify(expr* t) { + if (m_bv.is_numeral(t)) + return false; + + while (m.is_not(t, t)); + + for (auto & v : m_bound) { + if (contains(t, v.m_key)) return true; + } + +#if 0 + expr_set* used_exprs = get_expr_vars(t); + for (map::iterator I = m_bound.begin(), E = m_bound.end(); I != E; ++I) { + if (contains(t, I->m_key)) return true; + if (I->m_value.is_singleton() && used_exprs->contains(I->m_key)) + return true; + } +#endif + + expr* t1; + interval b; + // skip common case: single bound constraint without any context for simplification + if (is_bound(t, t1, b)) { + return b.is_full() || m_bound.contains(t1); + } + + if (contains_bound(t)) { + return true; + } +#if 0 + expr_cnt* bounds = get_expr_bounds(t); + for (expr_cnt::iterator I = bounds->begin(), E = bounds->end(); I != E; ++I) { + if (I->m_value > 1 || m_bound.contains(I->m_key)) + return true; + } +#endif + return false; + } + + virtual void pop(unsigned num_scopes) { + TRACE("bv", tout << "pop: " << num_scopes << "\n";); + if (m_scopes.empty()) + return; + unsigned target = m_scopes.size() - num_scopes; + if (target == 0) { + m_bound.reset(); + m_scopes.reset(); + return; + } + for (unsigned i = m_scopes.size()-1; i >= target; --i) { + undo_bound& undo = m_scopes[i]; + SASSERT(m_bound.contains(undo.e)); + if (undo.fresh) { + m_bound.erase(undo.e); + } else { + m_bound.insert(undo.e, undo.b); + } + } + m_scopes.shrink(target); + } + + virtual simplifier * translate(ast_manager & m) { + return alloc(bv_bounds_simplifier, m, m_params); + } + + virtual unsigned scope_level() const { + return m_scopes.size(); + } + }; + + + class dom_bv_bounds_simplifier : public dom_simplifier { + typedef obj_map map; + typedef obj_map expr_set; + typedef obj_map expr_cnt; + + ast_manager& m; + params_ref m_params; + bool m_propagate_eq; + bv_util m_bv; + vector m_scopes; + map m_bound; + svector m_expr_vars; + svector m_bound_exprs; + + bool is_number(expr *e, uint64& n, unsigned& sz) const { + rational r; + if (m_bv.is_numeral(e, r, sz) && sz <= 64) { + n = r.get_uint64(); + return true; + } + return false; + } + + bool is_bound(expr *e, expr*& v, interval& b) const { + uint64 n; + expr *lhs = 0, *rhs = 0; + unsigned sz; + + if (m_bv.is_bv_ule(e, lhs, rhs)) { + if (is_number(lhs, n, sz)) { // C ule x <=> x uge C + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, uMaxInt(sz), sz, true); + v = rhs; + return true; + } + if (is_number(rhs, n, sz)) { // x ule C + b = interval(0, n, sz, true); + v = lhs; + return true; + } + } + else if (m_bv.is_bv_sle(e, lhs, rhs)) { + if (is_number(lhs, n, sz)) { // C sle x <=> x sge C + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, (1ull << (sz-1)) - 1, sz, true); + v = rhs; + return true; + } + if (is_number(rhs, n, sz)) { // x sle C + b = interval(1ull << (sz-1), n, sz, true); + v = lhs; + return true; + } + } else if (m.is_eq(e, lhs, rhs)) { + if (is_number(lhs, n, sz)) { + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, n, sz, true); + v = rhs; + return true; + } + if (is_number(rhs, n, sz)) { + b = interval(n, n, sz, true); + v = lhs; + return true; + } + } + return false; + } + + + public: + dom_bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { + updt_params(p); + } + + virtual void updt_params(params_ref const & p) { + m_propagate_eq = p.get_bool("propagate_eq", false); + } + + static void get_param_descrs(param_descrs& r) { + r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); + } + + virtual ~dom_bv_bounds_simplifier() { + for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { + dealloc(m_expr_vars[i]); + } + for (unsigned i = 0, e = m_bound_exprs.size(); i < e; ++i) { + dealloc(m_bound_exprs[i]); + } + } + + virtual bool assert_expr(expr * t, bool sign) { + while (m.is_not(t, t)) { + sign = !sign; + } + + interval b; + expr* t1; + if (is_bound(t, t1, b)) { + SASSERT(!m_bv.is_numeral(t1)); + if (sign) + VERIFY(b.negate(b)); + + TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); + map::obj_map_entry* e = m_bound.find_core(t1); + if (e) { + interval& old = e->get_data().m_value; + interval intr; + if (!old.intersect(b, intr)) + return false; + if (old == intr) + return true; + m_scopes.insert(undo_bound(t1, old, false)); + old = intr; + } else { + m_bound.insert(t1, b); + m_scopes.insert(undo_bound(t1, interval(), true)); + } + } + return true; + } + + virtual void operator()(expr_ref& r) { + expr* t1, * t = r; + interval b; + + if (m_bound.find(t, b) && b.is_singleton()) { + r = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); + return; + } + + if (!m.is_bool(t)) + return; + + bool sign = false; + while (m.is_not(t, t)) { + sign = !sign; + } + + if (!is_bound(t, t1, b)) + return; + + if (sign && b.tight) { + sign = false; + if (!b.negate(b)) { + r = m.mk_false(); + return; + } + } + + interval ctx, intr; + bool unchanged = false; + if (b.is_full() && b.tight) { + r = m.mk_true(); + } else if (m_bound.find(t1, ctx)) { + if (ctx.implies(b)) { + r = m.mk_true(); + } else if (!b.intersect(ctx, intr)) { + r = m.mk_false(); + } else if (m_propagate_eq && intr.is_singleton()) { + r = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), + m.get_sort(t1))); + } + } + else { + unchanged = true; + } + + CTRACE("bv", !unchanged, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); + if (sign && unchanged) + r = m.mk_not(r); + } + + // check if t contains v + ptr_vector todo; + bool contains(expr* t, expr* v) { + ast_fast_mark1 mark; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (mark.is_marked(t)) { + continue; + } + if (t == v) { + todo.reset(); + return true; + } + mark.mark(t); + + if (!is_app(t)) { + continue; + } + app* a = to_app(t); + todo.append(a->get_num_args(), a->get_args()); + } + return false; + } + + bool contains_bound(expr* t) { + ast_fast_mark1 mark1; + ast_fast_mark2 mark2; + + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + todo.pop_back(); + if (mark1.is_marked(t)) { + continue; + } + mark1.mark(t); + + if (!is_app(t)) { + continue; + } + interval b; + expr* e; + if (is_bound(t, e, b)) { + if (mark2.is_marked(e)) { + todo.reset(); + return true; + } + mark2.mark(e); + if (m_bound.contains(e)) { + todo.reset(); + return true; + } + } + + app* a = to_app(t); + todo.append(a->get_num_args(), a->get_args()); + } + return false; + } + + virtual bool may_simplify(expr* t) { + if (m_bv.is_numeral(t)) + return false; + + while (m.is_not(t, t)); + + for (auto & v : m_bound) { + if (contains(t, v.m_key)) return true; + } + +#if 0 + expr_set* used_exprs = get_expr_vars(t); + for (map::iterator I = m_bound.begin(), E = m_bound.end(); I != E; ++I) { + if (contains(t, I->m_key)) return true; + if (I->m_value.is_singleton() && used_exprs->contains(I->m_key)) + return true; + } +#endif + + expr* t1; + interval b; + // skip common case: single bound constraint without any context for simplification + if (is_bound(t, t1, b)) { + return b.is_full() || m_bound.contains(t1); + } + + if (contains_bound(t)) { + return true; + } +#if 0 + expr_cnt* bounds = get_expr_bounds(t); + for (expr_cnt::iterator I = bounds->begin(), E = bounds->end(); I != E; ++I) { + if (I->m_value > 1 || m_bound.contains(I->m_key)) + return true; + } +#endif + return false; + } + + virtual void pop(unsigned num_scopes) { + TRACE("bv", tout << "pop: " << num_scopes << "\n";); + if (m_scopes.empty()) + return; + unsigned target = m_scopes.size() - num_scopes; + if (target == 0) { + m_bound.reset(); + m_scopes.reset(); + return; + } + for (unsigned i = m_scopes.size()-1; i >= target; --i) { + undo_bound& undo = m_scopes[i]; + SASSERT(m_bound.contains(undo.e)); + if (undo.fresh) { + m_bound.erase(undo.e); + } else { + m_bound.insert(undo.e, undo.b); + } + } + m_scopes.shrink(target); + } + + virtual dom_simplifier * translate(ast_manager & m) { + return alloc(dom_bv_bounds_simplifier, m, m_params); + } + + virtual unsigned scope_level() const { + return m_scopes.size(); + } + }; } tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(ctx_simplify_tactic, m, alloc(bv_bounds_simplifier, m, p), p)); } + +tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(dom_simplify_tactic, m, alloc(dom_bv_bounds_simplifier, m, p), p)); +} diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 08037fe18..d38324db9 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -83,11 +83,12 @@ expr* expr_dominators::intersect(expr* x, expr * y) { return x; } -void expr_dominators::compute_dominators() { +bool expr_dominators::compute_dominators() { expr * e = m_root; SASSERT(m_doms.empty()); m_doms.insert(e, e); bool change = true; + unsigned iterations = 1; while (change) { change = false; SASSERT(m_post2expr.empty() || m_post2expr.back() == e); @@ -109,7 +110,12 @@ void expr_dominators::compute_dominators() { change = true; } } + iterations *= 2; + if (change && iterations > m_post2expr.size()) { + return false; + } } + return true; } void expr_dominators::extract_tree() { @@ -118,17 +124,18 @@ void expr_dominators::extract_tree() { } } -void expr_dominators::compile(expr * e) { +bool expr_dominators::compile(expr * e) { reset(); m_root = e; compute_post_order(); - compute_dominators(); + if (!compute_dominators()) return false; extract_tree(); + return true; } -void expr_dominators::compile(unsigned sz, expr * const* es) { +bool expr_dominators::compile(unsigned sz, expr * const* es) { expr_ref e(m.mk_and(sz, es), m); - compile(e); + return compile(e); } void expr_dominators::reset() { @@ -293,14 +300,14 @@ expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { } -void dom_simplify_tactic::init(goal& g) { +bool dom_simplify_tactic::init(goal& g) { expr_ref_vector args(m); unsigned sz = g.size(); for (unsigned i = 0; i < sz; ++i) args.push_back(g.form(i)); expr_ref fml = mk_and(args); m_result.reset(); m_trail.reset(); - m_dominators.compile(fml); + return m_dominators.compile(fml); } void dom_simplify_tactic::simplify_goal(goal& g) { @@ -312,7 +319,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { change = false; // go forwards - init(g); + if (!init(g)) return; unsigned sz = g.size(); for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { expr_ref r = simplify(g.form(i)); @@ -329,7 +336,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { pop(scope_level()); // go backwards - init(g); + if (!init(g)) return; sz = g.size(); for (unsigned i = sz; !g.inconsistent() && i > 0; ) { --i; diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 25fb7c24f..6e5833cdb 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -45,14 +45,14 @@ private: void compute_post_order(); expr* intersect(expr* x, expr * y); - void compute_dominators(); + bool compute_dominators(); void extract_tree(); public: expr_dominators(ast_manager& m): m(m), m_root(m) {} - void compile(expr * e); - void compile(unsigned sz, expr * const* es); + bool compile(expr * e); + bool compile(unsigned sz, expr * const* es); tree_t const& get_tree() { return m_tree; } void reset(); @@ -115,7 +115,7 @@ private: void pop(unsigned n) { SASSERT(n <= m_scope_level); m_scope_level -= n; m_simplifier->pop(n); } bool assert_expr(expr* f, bool sign) { m_scope_level++; return m_simplifier->assert_expr(f, sign); } - void init(goal& g); + bool init(goal& g); public: dom_simplify_tactic(ast_manager & m, dom_simplifier* s, params_ref const & p = params_ref()): From 755ca46df65209f275966886acc968ea02533992 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Oct 2017 12:15:41 +0100 Subject: [PATCH 039/148] adding bv_bounds tactic dominator style Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_bounds_tactic.cpp | 58 +++++------------------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 2d5eb216a..44f920840 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -560,7 +560,7 @@ namespace { bool is_bound(expr *e, expr*& v, interval& b) const { uint64 n; expr *lhs = 0, *rhs = 0; - unsigned sz; + unsigned sz = 0; if (m_bv.is_bv_ule(e, lhs, rhs)) { if (is_number(lhs, n, sz)) { // C ule x <=> x uge C @@ -689,25 +689,27 @@ namespace { } interval ctx, intr; - bool unchanged = false; + bool was_updated = true; if (b.is_full() && b.tight) { r = m.mk_true(); } else if (m_bound.find(t1, ctx)) { if (ctx.implies(b)) { r = m.mk_true(); - } else if (!b.intersect(ctx, intr)) { + } + else if (!b.intersect(ctx, intr)) { r = m.mk_false(); - } else if (m_propagate_eq && intr.is_singleton()) { + } + else if (m_propagate_eq && intr.is_singleton()) { r = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), m.get_sort(t1))); } } else { - unchanged = true; + was_updated = false; } - CTRACE("bv", !unchanged, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); - if (sign && unchanged) + CTRACE("bv", was_updated, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); + if (sign && was_updated) r = m.mk_not(r); } @@ -773,45 +775,6 @@ namespace { return false; } - virtual bool may_simplify(expr* t) { - if (m_bv.is_numeral(t)) - return false; - - while (m.is_not(t, t)); - - for (auto & v : m_bound) { - if (contains(t, v.m_key)) return true; - } - -#if 0 - expr_set* used_exprs = get_expr_vars(t); - for (map::iterator I = m_bound.begin(), E = m_bound.end(); I != E; ++I) { - if (contains(t, I->m_key)) return true; - if (I->m_value.is_singleton() && used_exprs->contains(I->m_key)) - return true; - } -#endif - - expr* t1; - interval b; - // skip common case: single bound constraint without any context for simplification - if (is_bound(t, t1, b)) { - return b.is_full() || m_bound.contains(t1); - } - - if (contains_bound(t)) { - return true; - } -#if 0 - expr_cnt* bounds = get_expr_bounds(t); - for (expr_cnt::iterator I = bounds->begin(), E = bounds->end(); I != E; ++I) { - if (I->m_value > 1 || m_bound.contains(I->m_key)) - return true; - } -#endif - return false; - } - virtual void pop(unsigned num_scopes) { TRACE("bv", tout << "pop: " << num_scopes << "\n";); if (m_scopes.empty()) @@ -838,9 +801,6 @@ namespace { return alloc(dom_bv_bounds_simplifier, m, m_params); } - virtual unsigned scope_level() const { - return m_scopes.size(); - } }; } From 690b17fc25d8b8bac8af63ae4788e37b29b20cee Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 6 Oct 2017 12:33:47 +0100 Subject: [PATCH 040/148] Removed Ubuntu x86 VSTS/CI build (not supported by VSTS anymore). --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 197928877..701556cc8 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z ## Build status -| Windows x86 | Windows x64 | Ubuntu x64 | Ubuntu x86 | Debian x64 | OSX | TravisCI | -| ----------- | ----------- | ---------- | ---------- | ---------- | --- | -------- | -[![win32-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/4/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=4) | [![win64-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/7/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=7) | [![ubuntu-x64-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/3/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=3) | [![ubuntu-x86-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/6/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=6) | [![debian-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/5/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=5) | [![osx-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/2/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=2) | [![Build Status](https://travis-ci.org/Z3Prover/z3.svg?branch=master)](https://travis-ci.org/Z3Prover/z3) +| Windows x86 | Windows x64 | Ubuntu x64 | Debian x64 | OSX | TravisCI | +| ----------- | ----------- | ---------- | ---------- | --- | -------- | +[![win32-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/4/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=4) | [![win64-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/7/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=7) | [![ubuntu-x64-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/3/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=3) | [![debian-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/5/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=5) | [![osx-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/2/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=2) | [![Build Status](https://travis-ci.org/Z3Prover/z3.svg?branch=master)](https://travis-ci.org/Z3Prover/z3) [1]: #building-z3-on-windows-using-visual-studio-command-prompt [2]: #building-z3-using-make-and-gccclang From d47aea398730634444bd31172fd2a04739a9efa7 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 11:23:44 +0100 Subject: [PATCH 041/148] [TravisCI] Workaround slow unit test execution for Debug builds. Unit tests execute very slowly in Debug (i.e. unoptimized) builds. This causes TravisCI to terminate the job due to no console output being seen. To workaround this for the debug builds the tests are just compiled but not executed. To implement this the `RUN_UNIT_TESTS` environment variable now can take on the values `BUILD_ONLY`, `BUILD_AND_RUN`, and `SKIP` rather than `0` or `1`. --- .travis.yml | 11 ++++-- contrib/ci/README.md | 2 +- contrib/ci/scripts/ci_defaults.sh | 2 +- .../ci/scripts/test_z3_unit_tests_cmake.sh | 36 ++++++++++++++----- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 50d63e593..ac00ef918 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,10 +22,17 @@ env: # 64-bit Clang 3.9 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo + # Debug builds + # + # Note the unit tests for the debug builds are compiled but **not** + # executed. This is because the debug build of unit tests takes a large + # amount of time to execute compared to the optimized builds. The hope is + # that just running the optimized unit tests is sufficient. + # # 64-bit GCC 5.4 Debug - - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug RUN_UNIT_TESTS=BUILD_ONLY # 64-bit Clang Debug - - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug RUN_UNIT_TESTS=BUILD_ONLY # 32-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu32_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=i686 Z3_BUILD_TYPE=RelWithDebInfo diff --git a/contrib/ci/README.md b/contrib/ci/README.md index 2e117c8b1..e4c9aecfd 100644 --- a/contrib/ci/README.md +++ b/contrib/ci/README.md @@ -31,7 +31,7 @@ the future. * `NO_SUPPRESS_OUTPUT` - Don't suppress output of some commands (`0` or `1`) * `PYTHON_BINDINGS` - Build and test Python API bindings (`0` or `1`) * `RUN_SYSTEM_TESTS` - Run system tests (`0` or `1`) -* `RUN_UNIT_TESTS` - Run unit tests (`0` or `1`) +* `RUN_UNIT_TESTS` - Run unit tests (`BUILD_ONLY` or `BUILD_AND_RUN` or `SKIP`) * `TARGET_ARCH` - Target architecture (`x86_64` or `i686`) * `TEST_INSTALL` - Test running `install` target (`0` or `1`) * `UBSAN_BUILD` - Do [UndefinedBehaviourSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) build (`0` or `1`) diff --git a/contrib/ci/scripts/ci_defaults.sh b/contrib/ci/scripts/ci_defaults.sh index 2fb3fa52a..d8e9376d8 100644 --- a/contrib/ci/scripts/ci_defaults.sh +++ b/contrib/ci/scripts/ci_defaults.sh @@ -10,7 +10,7 @@ export JAVA_BINDINGS="${JAVA_BINDINGS:-1}" export NO_SUPPRESS_OUTPUT="${NO_SUPPRESS_OUTPUT:-0}" export PYTHON_BINDINGS="${PYTHON_BINDINGS:-1}" export RUN_SYSTEM_TESTS="${RUN_SYSTEM_TESTS:-1}" -export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-1}" +export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-BUILD_AND_RUN}" export TARGET_ARCH="${TARGET_ARCH:-x86_64}" export TEST_INSTALL="${TEST_INSTALL:-1}" export UBSAN_BUILD="${UBSAN_BUILD:-0}" diff --git a/contrib/ci/scripts/test_z3_unit_tests_cmake.sh b/contrib/ci/scripts/test_z3_unit_tests_cmake.sh index 666673328..e1c927f58 100755 --- a/contrib/ci/scripts/test_z3_unit_tests_cmake.sh +++ b/contrib/ci/scripts/test_z3_unit_tests_cmake.sh @@ -10,17 +10,35 @@ set -o pipefail : ${Z3_BUILD_DIR?"Z3_BUILD_DIR must be specified"} : ${RUN_UNIT_TESTS?"RUN_UNIT_TESTS must be specified"} -if [ "X${RUN_UNIT_TESTS}" != "X1" ]; then - echo "Skipping unit tests" - exit 0 -fi - # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh cd "${Z3_BUILD_DIR}" -# Build and run internal tests -cmake --build $(pwd) --target test-z3 "${GENERATOR_ARGS[@]}" -# Run all tests that don't require arguments -run_quiet ./test-z3 /a +function build_unit_tests() { + # Build internal tests + cmake --build $(pwd) --target test-z3 "${GENERATOR_ARGS[@]}" +} + +function run_unit_tests() { + # Run all tests that don't require arguments + run_quiet ./test-z3 /a +} + +case "${RUN_UNIT_TESTS}" in + BUILD_AND_RUN) + build_unit_tests + run_unit_tests + ;; + BUILD_ONLY) + build_unit_tests + ;; + SKIP) + echo "RUN_UNIT_TESTS set to \"${RUN_UNIT_TESTS}\" so skipping build and run" + exit 0 + ;; + *) + echo "Error: RUN_UNIT_TESTS set to unhandled value \"${RUN_UNIT_TESTS}\"" + exit 1 + ;; +esac From 2519719d3f452eb2cbbaf0db822e58b671e4e74f Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 11:30:06 +0100 Subject: [PATCH 042/148] Fix typo --- contrib/ci/scripts/set_generator_args.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ci/scripts/set_generator_args.sh b/contrib/ci/scripts/set_generator_args.sh index 0ef7b76aa..a704c518b 100644 --- a/contrib/ci/scripts/set_generator_args.sh +++ b/contrib/ci/scripts/set_generator_args.sh @@ -1,4 +1,4 @@ -# This script should is intended to be included by other +# This script is intended to be included by other # scripts and should not be executed directly : ${Z3_CMAKE_GENERATOR?"Z3_CMAKE_GENERATOR must be specified"} From 0be28b66fe3319be01492a0391514727e099916c Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 11:48:20 +0100 Subject: [PATCH 043/148] [TravisCI] Update out of date `README.md` file. --- contrib/ci/README.md | 46 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/contrib/ci/README.md b/contrib/ci/README.md index e4c9aecfd..99bbcd7a6 100644 --- a/contrib/ci/README.md +++ b/contrib/ci/README.md @@ -58,8 +58,9 @@ The `scripts/travis_ci_linux_entry_point.sh` script variables (if set) into the build using the `--build-arg` argument of the `docker run` command. -If an environemnt variable is not set a defaults value is used which can be -found in `Dockerfiles/z3_build.Dockerfile`. +The default values of the configuration environment variables +can be found in +[`scripts/ci_defaults.sh`](scripts/ci_defaults.sh). #### Linux specific configuration variables @@ -67,8 +68,9 @@ found in `Dockerfiles/z3_build.Dockerfile`. #### Reproducing a build locally -A build can be reproduced locally by using the `scripts/travis_ci_linux_entry_point.sh` -script and setting the appropriate environment variable. +A build can be reproduced locally by using the +`scripts/travis_ci_linux_entry_point.sh` script and setting the appropriate +environment variable. For example lets say we wanted to reproduce the build below. @@ -104,11 +106,43 @@ feature might be removed in the future. It may be better to just build the base image once (outside of TravisCI), upload it to [DockerHub](https://hub.docker.com/) and have the build pull down the pre-built -image everytime. +image every time. An [organization](https://hub.docker.com/u/z3prover/) has been created on DockerHub for this. ### macOS -Not yet implemented. +For macOS we execute directly on TravisCI's macOS environment. The entry point +for the TravisCI builds is the +[`scripts/travis_ci_osx_entry_point.sh`](scripts/travis_ci_osx_entry_point.sh) +scripts. + +#### macOS specific configuration variables + +* `MACOS_SKIP_DEPS_UPDATE` - If set to `1` installing the necessary build dependencies + is skipped. This is useful for local testing if the dependencies are already installed. +* `MACOS_UPDATE_CMAKE` - If set to `1` the installed version of CMake will be upgraded. + +#### Reproducing a build locally + +To reproduce a build (e.g. like the one shown below) + +```yaml +- os: osx + osx_image: xcode8.3 + # Note: Apple Clang does not support OpenMP + env: Z3_BUILD_TYPE=RelWithDebInfo USE_OPENMP=0 +``` + +Run the following: + +```bash +TRAVIS_BUILD_DIR=$(pwd) \ +Z3_BUILD_TYPE=RelWithDebInfo \ +USE_OPEN_MP=0 \ +contrib/ci/scripts/travis_ci_osx_entry_point.sh +``` + +Note this assumes that the current working directory is the root of the Z3 +git repository. From 50042ab6388f5bd4bb7b8ba23622b96b7ef59131 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 6 Oct 2017 13:00:09 +0100 Subject: [PATCH 044/148] removed unused variables --- src/ast/fpa/fpa2bv_converter.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index f09ddcd42..13a7599a9 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -1180,8 +1180,6 @@ void fpa2bv_converter::mk_abs(sort * s, expr_ref & x, expr_ref & result) { void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); expr * x = args[0], * y = args[1]; @@ -1227,8 +1225,6 @@ void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); expr * x = args[0], *y = args[1]; @@ -3081,8 +3077,6 @@ void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * mk_is_nan(x, x_is_nan); sort * fp_srt = m.get_sort(x); - unsigned ebits = m_util.get_ebits(fp_srt); - unsigned sbits = m_util.get_sbits(fp_srt); expr_ref unspec(m); mk_to_ieee_bv_unspecified(f, num, args, unspec); From 2634be01aa0c615e3b8b1362ce317f783b7109fc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Oct 2017 13:43:01 +0100 Subject: [PATCH 045/148] adding backwards pass Signed-off-by: Nikolaj Bjorner --- src/tactic/core/dom_simplify_tactic.cpp | 35 +++++++++++++++++-------- src/tactic/core/dom_simplify_tactic.h | 3 ++- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index d38324db9..da704bd0b 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -281,17 +281,28 @@ expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { }; expr_ref_vector args(m); - for (expr * arg : *e) { - for (expr * child : tree(arg)) { - if (is_subexpr_arg(child, arg)) { - simplify(child); - } - } - r = simplify(arg); - args.push_back(r); - if (!assert_expr(simplify(arg), !is_and)) { - r = is_and ? m.mk_false() : m.mk_true(); - return r; + if (m_forward) { + for (expr * arg : *e) { +#define _SIMP_ARG(arg) \ + for (expr * child : tree(arg)) { \ + if (is_subexpr_arg(child, arg)) { \ + simplify(child); \ + } \ + } \ + r = simplify(arg); \ + args.push_back(r); \ + if (!assert_expr(simplify(arg), !is_and)) { \ + r = is_and ? m.mk_false() : m.mk_true(); \ + return r; \ + } + _SIMP_ARG(arg); + } + } + else { + for (unsigned i = e->get_num_args(); i > 0; ) { + --i; + expr* arg = e->get_arg(i); + _SIMP_ARG(arg); } } pop(scope_level() - old_lvl); @@ -319,6 +330,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { change = false; // go forwards + m_forward = true; if (!init(g)) return; unsigned sz = g.size(); for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { @@ -336,6 +348,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { pop(scope_level()); // go backwards + m_forward = false; if (!init(g)) return; sz = g.size(); for (unsigned i = sz; !g.inconsistent() && i > 0; ) { diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 6e5833cdb..581d40cd0 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -96,6 +96,7 @@ private: unsigned m_max_depth; ptr_vector m_empty; obj_pair_map m_subexpr_cache; + bool m_forward; expr_ref simplify(expr* t); expr_ref simplify_ite(app * ite); @@ -122,7 +123,7 @@ public: m(m), m_simplifier(s), m_params(p), m_trail(m), m_args(m), m_dominators(m), - m_scope_level(0), m_depth(0), m_max_depth(1024) {} + m_scope_level(0), m_depth(0), m_max_depth(1024), m_forward(true) {} virtual ~dom_simplify_tactic() {} From 9aa6386be94e96651b5c12065cb95a9e4dea74b8 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 6 Oct 2017 15:27:16 +0100 Subject: [PATCH 046/148] fix debug build --- src/util/lp/indexed_vector_instances.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/lp/indexed_vector_instances.cpp b/src/util/lp/indexed_vector_instances.cpp index ee078f021..79c3ee1a1 100644 --- a/src/util/lp/indexed_vector_instances.cpp +++ b/src/util/lp/indexed_vector_instances.cpp @@ -33,6 +33,7 @@ template void indexed_vector::resize(unsigned int); template void indexed_vector::set_value(const mpq&, unsigned int); template void indexed_vector::set_value(const unsigned&, unsigned int); #ifdef Z3DEBUG +template bool indexed_vector::is_OK() const; template bool indexed_vector::is_OK() const; template bool indexed_vector::is_OK() const; template bool indexed_vector >::is_OK() const; From ba8bff76ae9a68bef78c88913cbc2c62a193b299 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 15:52:01 +0100 Subject: [PATCH 047/148] [TravisCI] Modify Debug configuration that I forgot to change with `RUN_UNIT_TESTS=BUILD_ONLY`. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ac00ef918..81ddefb8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,7 +64,7 @@ env: # 64-bit GCC 4.8 RelWithDebInfo - LINUX_BASE=ubuntu_14.04 C_COMPILER=/usr/bin/gcc-4.8 CXX_COMPILER=/usr/bin/g++-4.8 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo # 64-bit GCC 4.8 Debug - - LINUX_BASE=ubuntu_14.04 C_COMPILER=/usr/bin/gcc-4.8 CXX_COMPILER=/usr/bin/g++-4.8 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug + - LINUX_BASE=ubuntu_14.04 C_COMPILER=/usr/bin/gcc-4.8 CXX_COMPILER=/usr/bin/g++-4.8 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug RUN_UNIT_TESTS=BUILD_ONLY # macOS (a.k.a OSX) support matrix: From c3f615dbfcf1436a142d1112598f358ed6c27bbd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Oct 2017 16:03:43 +0100 Subject: [PATCH 048/148] reverse arguments Signed-off-by: Nikolaj Bjorner --- src/tactic/core/dom_simplify_tactic.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index da704bd0b..67b039814 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -291,7 +291,7 @@ expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { } \ r = simplify(arg); \ args.push_back(r); \ - if (!assert_expr(simplify(arg), !is_and)) { \ + if (!assert_expr(r, !is_and)) { \ r = is_and ? m.mk_false() : m.mk_true(); \ return r; \ } @@ -304,6 +304,7 @@ expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { expr* arg = e->get_arg(i); _SIMP_ARG(arg); } + args.reverse(); } pop(scope_level() - old_lvl); r = is_and ? mk_and(args) : mk_or(args); @@ -338,6 +339,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { r = m.mk_false(); } + CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";); change |= r != g.form(i); proof* new_pr = 0; if (g.proofs_enabled()) { @@ -358,6 +360,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { r = m.mk_false(); } change |= r != g.form(i); + CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";); proof* new_pr = 0; if (g.proofs_enabled()) { new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite_star(g.form(i), r, 0, 0)); From 31c6b3eb5b94053c8c40f2939ad429c31471edaa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 6 Oct 2017 16:07:25 +0100 Subject: [PATCH 049/148] fix leak Signed-off-by: Nikolaj Bjorner --- src/tactic/core/dom_simplify_tactic.cpp | 4 ++++ src/tactic/core/dom_simplify_tactic.h | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 67b039814..99f83799b 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -152,6 +152,10 @@ void expr_dominators::reset() { // ----------------------- // dom_simplify_tactic +dom_simplify_tactic::~dom_simplify_tactic() { + dealloc(m_simplifier); +} + tactic * dom_simplify_tactic::translate(ast_manager & m) { return alloc(dom_simplify_tactic, m, m_simplifier->translate(m), m_params); } diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 581d40cd0..c1660a941 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -83,8 +83,6 @@ class dom_simplifier { }; class dom_simplify_tactic : public tactic { -public: -private: ast_manager& m; dom_simplifier* m_simplifier; params_ref m_params; @@ -126,7 +124,7 @@ public: m_scope_level(0), m_depth(0), m_max_depth(1024), m_forward(true) {} - virtual ~dom_simplify_tactic() {} + virtual ~dom_simplify_tactic(); virtual tactic * translate(ast_manager & m); virtual void updt_params(params_ref const & p) {} From 1d12a9c86de07feafe1261fa52b8248fe1d494ef Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 6 Oct 2017 18:19:37 +0100 Subject: [PATCH 050/148] dom_simplifier: fix dominator computation --- src/tactic/core/dom_simplify_tactic.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 99f83799b..16d8500a4 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -95,17 +95,17 @@ bool expr_dominators::compute_dominators() { for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) { expr * child = m_post2expr[i]; ptr_vector const& p = m_parents[child]; - SASSERT(!p.empty()); expr * new_idom = 0, *idom2 = 0; - for (unsigned j = 0; j < p.size(); ++j) { - if (!new_idom) { - m_doms.find(p[j], new_idom); - } - else if (m_doms.find(p[j], idom2)) { - new_idom = intersect(new_idom, idom2); + for (auto& pred : p) { + if (m_doms.contains(pred)) { + new_idom = !new_idom ? pred : intersect(new_idom, pred); } } - if (new_idom && (!m_doms.find(child, idom2) || idom2 != new_idom)) { + if (!new_idom) { + m_doms.insert(child, p[0]); + change = true; + } + else if (!m_doms.find(child, idom2) || idom2 != new_idom) { m_doms.insert(child, new_idom); change = true; } From a18236bc7f2a9037ccd1d7d078cb54c132328474 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 7 Oct 2017 00:07:53 +0100 Subject: [PATCH 051/148] have quantifier equality take names into account Signed-off-by: Nikolaj Bjorner --- src/ast/ast.cpp | 3 ++ src/ast/expr_abstract.cpp | 9 ++++ src/ast/expr_substitution.cpp | 7 +++ src/ast/expr_substitution.h | 3 ++ src/tactic/core/dom_simplify_tactic.cpp | 65 ++++++++++++++++++++----- src/tactic/core/dom_simplify_tactic.h | 5 +- 6 files changed, 78 insertions(+), 14 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index c98307ee0..cac12413e 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -471,6 +471,9 @@ bool compare_nodes(ast const * n1, ast const * n2) { compare_arrays(to_quantifier(n1)->get_decl_sorts(), to_quantifier(n2)->get_decl_sorts(), to_quantifier(n1)->get_num_decls()) && + compare_arrays(to_quantifier(n1)->get_decl_names(), + to_quantifier(n2)->get_decl_names(), + to_quantifier(n1)->get_num_decls()) && to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() && to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() && to_quantifier(n1)->get_num_patterns() == to_quantifier(n2)->get_num_patterns() && diff --git a/src/ast/expr_abstract.cpp b/src/ast/expr_abstract.cpp index 46682eed6..43035e203 100644 --- a/src/ast/expr_abstract.cpp +++ b/src/ast/expr_abstract.cpp @@ -19,6 +19,7 @@ Notes: #include "ast/expr_abstract.h" #include "util/map.h" +#include "ast/ast_pp.h" void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { @@ -109,6 +110,9 @@ void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { expr_abstractor abs(m); abs(base, num_bound, bound, n, result); + TRACE("expr_abstract", + tout << expr_ref(n, m) << "\n"; + tout << result << "\n";); } expr_ref mk_quantifier(bool is_forall, ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { @@ -123,6 +127,11 @@ expr_ref mk_quantifier(bool is_forall, ast_manager& m, unsigned num_bound, app* } result = m.mk_quantifier(is_forall, num_bound, sorts.c_ptr(), names.c_ptr(), result); } + TRACE("expr_abstract", + tout << expr_ref(n, m) << "\n"; + for (unsigned i = 0; i < num_bound; ++i) tout << expr_ref(bound[i], m) << " "; + tout << "\n"; + tout << result << "\n";); return result; } diff --git a/src/ast/expr_substitution.cpp b/src/ast/expr_substitution.cpp index 149a59ce5..3ce038250 100644 --- a/src/ast/expr_substitution.cpp +++ b/src/ast/expr_substitution.cpp @@ -56,6 +56,13 @@ expr_substitution::~expr_substitution() { reset(); } +std::ostream& expr_substitution::display(std::ostream& out) { + for (auto & kv : m_subst) { + out << expr_ref(kv.m_key, m()) << " |-> " << expr_ref(kv.m_value, m()) << "\n"; + } + return out; +} + void expr_substitution::insert(expr * c, expr * def, proof * def_pr, expr_dependency * def_dep) { obj_map::obj_map_entry * entry = m_subst.insert_if_not_there2(c, 0); if (entry->get_data().m_value == 0) { diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h index d360356eb..90154d163 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -50,6 +50,8 @@ public: bool contains(expr * s); void reset(); void cleanup(); + + std::ostream& display(std::ostream& out); }; class scoped_expr_substitution { @@ -84,6 +86,7 @@ public: bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep) { return m_subst.find(s, def, def_pr, def_dep); } bool contains(expr * s) { return m_subst.contains(s); } void cleanup() { m_subst.cleanup(); } + std::ostream& display(std::ostream& out) { return m_subst.display(out); } }; #endif diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 99f83799b..60207e823 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -91,23 +91,42 @@ bool expr_dominators::compute_dominators() { unsigned iterations = 1; while (change) { change = false; + TRACE("simplify", + for (auto & kv : m_doms) { + tout << expr_ref(kv.m_key, m) << " |-> " << expr_ref(kv.m_value, m) << "\n"; + }); + SASSERT(m_post2expr.empty() || m_post2expr.back() == e); for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) { expr * child = m_post2expr[i]; ptr_vector const& p = m_parents[child]; SASSERT(!p.empty()); - expr * new_idom = 0, *idom2 = 0; - for (unsigned j = 0; j < p.size(); ++j) { - if (!new_idom) { - m_doms.find(p[j], new_idom); - } - else if (m_doms.find(p[j], idom2)) { - new_idom = intersect(new_idom, idom2); - } + if (p.size() == 1) { + if (!m_doms.contains(child)) { + m_doms.insert(child, p[0]); + change = true; + } } - if (new_idom && (!m_doms.find(child, idom2) || idom2 != new_idom)) { - m_doms.insert(child, new_idom); - change = true; + else { + expr * new_idom = 0, *idom2 = 0; + for (unsigned j = 0; j < p.size(); ++j) { + if (!new_idom) { + m_doms.find(p[j], new_idom); + } + else if (m_doms.find(p[j], idom2)) { + new_idom = intersect(new_idom, idom2); + } + } + if (!new_idom) { + m_doms.insert(child, p[0]); + TRACE("simplify", tout << expr_ref(child, m) << " |-> " << expr_ref(p[0], m) << "\n";); + change = true; + } + else if (!m_doms.find(child, idom2) || idom2 != new_idom) { + m_doms.insert(child, new_idom); + TRACE("simplify", tout << expr_ref(child, m) << " |-> " << expr_ref(new_idom, m) << "\n";); + change = true; + } } } iterations *= 2; @@ -130,6 +149,7 @@ bool expr_dominators::compile(expr * e) { compute_post_order(); if (!compute_dominators()) return false; extract_tree(); + TRACE("simplify", display(tout);); return true; } @@ -147,6 +167,22 @@ void expr_dominators::reset() { m_root.reset(); } +std::ostream& expr_dominators::display(std::ostream& out) { + return display(out, 0, m_root); +} + +std::ostream& expr_dominators::display(std::ostream& out, unsigned indent, expr* r) { + for (unsigned i = 0; i < indent; ++i) out << " "; + out << expr_ref(r, m); + if (m_tree.contains(r)) { + for (expr* child : m_tree[r]) { + if (child != r) + display(out, indent + 1, child); + } + } + out << "\n"; + return out; +} // ----------------------- @@ -200,7 +236,6 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { simplify(child); } } - pop(scope_level() - old_lvl); expr_ref new_t = simplify(t); if (!assert_expr(new_c, true)) { @@ -230,6 +265,8 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { expr_ref dom_simplify_tactic::simplify(expr * e0) { expr_ref r(m); expr* e = 0; + + TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); if (!m_result.find(e0, e)) { e = e0; } @@ -254,7 +291,9 @@ expr_ref dom_simplify_tactic::simplify(expr * e0) { if (is_app(e)) { m_args.reset(); for (expr* arg : *to_app(e)) { - m_args.push_back(get_cached(arg)); // TBD is cache really applied to all sub-terms? + r = get_cached(arg); + (*m_simplifier)(r); + m_args.push_back(r); } r = m.mk_app(to_app(e)->get_decl(), m_args.size(), m_args.c_ptr()); } diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index c1660a941..d99f799b4 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -48,6 +48,8 @@ private: bool compute_dominators(); void extract_tree(); + std::ostream& display(std::ostream& out, unsigned indent, expr* r); + public: expr_dominators(ast_manager& m): m(m), m_root(m) {} @@ -55,7 +57,8 @@ public: bool compile(unsigned sz, expr * const* es); tree_t const& get_tree() { return m_tree; } void reset(); - + + std::ostream& display(std::ostream& out); }; class dom_simplifier { From 76c309a595639cbbde974f8efe2b42f24a9d7e8f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 7 Oct 2017 00:20:58 +0100 Subject: [PATCH 052/148] disable caching of simplifier when applied to direct arguments of terms. Caching is only valid when applied to dominator children Signed-off-by: Nikolaj Bjorner --- src/tactic/core/dom_simplify_tactic.cpp | 31 +++++++++++++------------ src/tactic/core/dom_simplify_tactic.h | 2 +- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 830d44c7a..1bd685bb1 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -101,7 +101,8 @@ bool expr_dominators::compute_dominators() { expr * child = m_post2expr[i]; ptr_vector const& p = m_parents[child]; expr * new_idom = 0, *idom2 = 0; - for (auto& pred : p) { + + for (expr * pred : p) { if (m_doms.contains(pred)) { new_idom = !new_idom ? pred : intersect(new_idom, pred); } @@ -209,31 +210,31 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { expr * c = 0, *t = 0, *e = 0; VERIFY(m.is_ite(ite, c, t, e)); unsigned old_lvl = scope_level(); - expr_ref new_c = simplify(c); + expr_ref new_c = simplify(c, false); if (m.is_true(new_c)) { - r = simplify(t); + r = simplify(t, false); } else if (m.is_false(new_c) || !assert_expr(new_c, false)) { - r = simplify(e); + r = simplify(e, false); } else { for (expr * child : tree(ite)) { if (is_subexpr(child, t) && !is_subexpr(child, e)) { - simplify(child); + simplify(child, true); } } pop(scope_level() - old_lvl); - expr_ref new_t = simplify(t); + expr_ref new_t = simplify(t, false); if (!assert_expr(new_c, true)) { return new_t; } for (expr * child : tree(ite)) { if (is_subexpr(child, e) && !is_subexpr(child, t)) { - simplify(child); + simplify(child, true); } } pop(scope_level() - old_lvl); - expr_ref new_e = simplify(e); + expr_ref new_e = simplify(e, false); if (c == new_c && t == new_t && e == new_e) { r = ite; } @@ -248,7 +249,7 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { return r; } -expr_ref dom_simplify_tactic::simplify(expr * e0) { +expr_ref dom_simplify_tactic::simplify(expr * e0, bool cache_result) { expr_ref r(m); expr* e = 0; @@ -272,7 +273,7 @@ expr_ref dom_simplify_tactic::simplify(expr * e0) { } else { for (expr * child : tree(e)) { - simplify(child); + simplify(child, true); } if (is_app(e)) { m_args.reset(); @@ -288,7 +289,7 @@ expr_ref dom_simplify_tactic::simplify(expr * e0) { } } (*m_simplifier)(r); - cache(e0, r); + if (cache_result) cache(e0, r); TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); --m_depth; m_subexpr_cache.reset(); @@ -315,10 +316,10 @@ expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { #define _SIMP_ARG(arg) \ for (expr * child : tree(arg)) { \ if (is_subexpr_arg(child, arg)) { \ - simplify(child); \ + simplify(child, true); \ } \ } \ - r = simplify(arg); \ + r = simplify(arg, false); \ args.push_back(r); \ if (!assert_expr(r, !is_and)) { \ r = is_and ? m.mk_false() : m.mk_true(); \ @@ -364,7 +365,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { if (!init(g)) return; unsigned sz = g.size(); for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { - expr_ref r = simplify(g.form(i)); + expr_ref r = simplify(g.form(i), true); if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { r = m.mk_false(); } @@ -384,7 +385,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { sz = g.size(); for (unsigned i = sz; !g.inconsistent() && i > 0; ) { --i; - expr_ref r = simplify(g.form(i)); + expr_ref r = simplify(g.form(i), true); if (i > 0 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { r = m.mk_false(); } diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index d99f799b4..e79777215 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -99,7 +99,7 @@ class dom_simplify_tactic : public tactic { obj_pair_map m_subexpr_cache; bool m_forward; - expr_ref simplify(expr* t); + expr_ref simplify(expr* t, bool cache); expr_ref simplify_ite(app * ite); expr_ref simplify_and(app * ite) { return simplify_and_or(true, ite); } expr_ref simplify_or(app * ite) { return simplify_and_or(false, ite); } From 7e4f5322023ddf5d1a4ae485d710c4f8c59a65f6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 7 Oct 2017 00:37:44 +0100 Subject: [PATCH 053/148] fix build by including mk_pp Signed-off-by: Nikolaj Bjorner --- src/ast/expr_substitution.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ast/expr_substitution.cpp b/src/ast/expr_substitution.cpp index 3ce038250..f9333c443 100644 --- a/src/ast/expr_substitution.cpp +++ b/src/ast/expr_substitution.cpp @@ -16,8 +16,9 @@ Author: Notes: --*/ -#include "ast/expr_substitution.h" #include "util/ref_util.h" +#include "ast/expr_substitution.h" +#include "ast/ast_pp.h" typedef obj_map expr2proof; typedef obj_map expr2expr_dependency; @@ -58,7 +59,7 @@ expr_substitution::~expr_substitution() { std::ostream& expr_substitution::display(std::ostream& out) { for (auto & kv : m_subst) { - out << expr_ref(kv.m_key, m()) << " |-> " << expr_ref(kv.m_value, m()) << "\n"; + out << mk_pp(kv.m_key, m()) << " |-> " << mk_pp(kv.m_value, m()) << "\n"; } return out; } From b898b077953c8e4a18d9b49567af37952e889eb8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 7 Oct 2017 11:12:09 +0100 Subject: [PATCH 054/148] distinguish simplify_rec from simplify immediate argument Signed-off-by: Nikolaj Bjorner --- src/tactic/core/dom_simplify_tactic.cpp | 40 ++++++++++++++----------- src/tactic/core/dom_simplify_tactic.h | 3 +- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 1bd685bb1..366e1edb0 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -210,31 +210,31 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { expr * c = 0, *t = 0, *e = 0; VERIFY(m.is_ite(ite, c, t, e)); unsigned old_lvl = scope_level(); - expr_ref new_c = simplify(c, false); + expr_ref new_c = simplify_arg(c); if (m.is_true(new_c)) { - r = simplify(t, false); + r = simplify_arg(t); } else if (m.is_false(new_c) || !assert_expr(new_c, false)) { - r = simplify(e, false); + r = simplify_arg(e); } else { for (expr * child : tree(ite)) { if (is_subexpr(child, t) && !is_subexpr(child, e)) { - simplify(child, true); + simplify_rec(child); } } pop(scope_level() - old_lvl); - expr_ref new_t = simplify(t, false); + expr_ref new_t = simplify_arg(t); if (!assert_expr(new_c, true)) { return new_t; } for (expr * child : tree(ite)) { if (is_subexpr(child, e) && !is_subexpr(child, t)) { - simplify(child, true); + simplify_rec(child); } } pop(scope_level() - old_lvl); - expr_ref new_e = simplify(e, false); + expr_ref new_e = simplify_arg(e); if (c == new_c && t == new_t && e == new_e) { r = ite; } @@ -249,7 +249,15 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { return r; } -expr_ref dom_simplify_tactic::simplify(expr * e0, bool cache_result) { +expr_ref dom_simplify_tactic::simplify_arg(expr * e) { + expr_ref r(m); + r = get_cached(e); + (*m_simplifier)(r); + TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e, m) << " -> " << r << "\n";); + return r; +} + +expr_ref dom_simplify_tactic::simplify_rec(expr * e0) { expr_ref r(m); expr* e = 0; @@ -273,14 +281,12 @@ expr_ref dom_simplify_tactic::simplify(expr * e0, bool cache_result) { } else { for (expr * child : tree(e)) { - simplify(child, true); + simplify_rec(child); } if (is_app(e)) { m_args.reset(); for (expr* arg : *to_app(e)) { - r = get_cached(arg); - (*m_simplifier)(r); - m_args.push_back(r); + m_args.push_back(simplify_arg(arg)); } r = m.mk_app(to_app(e)->get_decl(), m_args.size(), m_args.c_ptr()); } @@ -289,7 +295,7 @@ expr_ref dom_simplify_tactic::simplify(expr * e0, bool cache_result) { } } (*m_simplifier)(r); - if (cache_result) cache(e0, r); + cache(e0, r); TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); --m_depth; m_subexpr_cache.reset(); @@ -316,10 +322,10 @@ expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { #define _SIMP_ARG(arg) \ for (expr * child : tree(arg)) { \ if (is_subexpr_arg(child, arg)) { \ - simplify(child, true); \ + simplify_rec(child); \ } \ } \ - r = simplify(arg, false); \ + r = simplify_arg(arg); \ args.push_back(r); \ if (!assert_expr(r, !is_and)) { \ r = is_and ? m.mk_false() : m.mk_true(); \ @@ -365,7 +371,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { if (!init(g)) return; unsigned sz = g.size(); for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { - expr_ref r = simplify(g.form(i), true); + expr_ref r = simplify_rec(g.form(i)); if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { r = m.mk_false(); } @@ -385,7 +391,7 @@ void dom_simplify_tactic::simplify_goal(goal& g) { sz = g.size(); for (unsigned i = sz; !g.inconsistent() && i > 0; ) { --i; - expr_ref r = simplify(g.form(i), true); + expr_ref r = simplify_rec(g.form(i)); if (i > 0 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { r = m.mk_false(); } diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index e79777215..4f9851514 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -99,7 +99,8 @@ class dom_simplify_tactic : public tactic { obj_pair_map m_subexpr_cache; bool m_forward; - expr_ref simplify(expr* t, bool cache); + expr_ref simplify_rec(expr* t); + expr_ref simplify_arg(expr* t); expr_ref simplify_ite(app * ite); expr_ref simplify_and(app * ite) { return simplify_and_or(true, ite); } expr_ref simplify_or(app * ite) { return simplify_and_or(false, ite); } From deba7d4d6e74c2896b2f75aeeacc205618157f05 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 7 Oct 2017 14:35:44 +0100 Subject: [PATCH 055/148] use idom for checking dominator relationships Signed-off-by: Nikolaj Bjorner --- src/tactic/core/dom_simplify_tactic.cpp | 24 ++++++++++++++++-------- src/tactic/core/dom_simplify_tactic.h | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 366e1edb0..3a6d55569 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -257,11 +257,14 @@ expr_ref dom_simplify_tactic::simplify_arg(expr * e) { return r; } +/** + \brief simplify e recursively. +*/ expr_ref dom_simplify_tactic::simplify_rec(expr * e0) { expr_ref r(m); expr* e = 0; - TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); + TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << "\n";); if (!m_result.find(e0, e)) { e = e0; } @@ -408,6 +411,12 @@ void dom_simplify_tactic::simplify_goal(goal& g) { SASSERT(scope_level() == 0); } +/** + \brief determine if a is dominated by b. + Walk the immediate dominators of a upwards until hitting b or a term that is deeper than b. + Save intermediary results in a cache to avoid recomputations. +*/ + bool dom_simplify_tactic::is_subexpr(expr * a, expr * b) { if (a == b) return true; @@ -416,14 +425,13 @@ bool dom_simplify_tactic::is_subexpr(expr * a, expr * b) { if (m_subexpr_cache.find(a, b, r)) return r; - for (expr * e : tree(b)) { - if (is_subexpr(a, e)) { - m_subexpr_cache.insert(a, b, true); - return true; - } + if (get_depth(a) >= get_depth(b)) { + return false; } - m_subexpr_cache.insert(a, b, false); - return false; + SASSERT(a != idom(a) && get_depth(idom(a)) > get_depth(a)); + r = is_subexpr(idom(a), b); + m_subexpr_cache.insert(a, b, r); + return r; } ptr_vector const & dom_simplify_tactic::tree(expr * e) { diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 4f9851514..79bc9728c 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -57,6 +57,7 @@ public: bool compile(unsigned sz, expr * const* es); tree_t const& get_tree() { return m_tree; } void reset(); + expr* idom(expr *e) const { return m_doms[e]; } std::ostream& display(std::ostream& out); }; @@ -113,6 +114,7 @@ class dom_simplify_tactic : public tactic { void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } ptr_vector const & tree(expr * e); + expr* idom(expr *e) const { return m_dominators.idom(e); } unsigned scope_level() { return m_scope_level; } void pop(unsigned n) { SASSERT(n <= m_scope_level); m_scope_level -= n; m_simplifier->pop(n); } From a5ecf87ab82c86b01eef01646b70fdcbb30b1f5d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 10:32:38 +0100 Subject: [PATCH 056/148] fix #1288 Signed-off-by: Nikolaj Bjorner --- src/muz/rel/tbv.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/muz/rel/tbv.cpp b/src/muz/rel/tbv.cpp index 7475750db..69cc4819a 100644 --- a/src/muz/rel/tbv.cpp +++ b/src/muz/rel/tbv.cpp @@ -74,8 +74,7 @@ tbv* tbv_manager::allocate(tbv const& bv) { } tbv* tbv_manager::allocate(uint64 val) { tbv* v = allocate0(); - for (unsigned bit = num_tbits(); bit > 0;) { - --bit; + for (unsigned bit = std::min(64u, num_tbits()); bit-- > 0;) { if (val & (1ULL << bit)) { set(*v, bit, BIT_1); } else { From 6f7f957a266d8a22f628aaee29303ac650529f73 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 10:38:02 +0100 Subject: [PATCH 057/148] likely fix for #1287 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 8938fb1b5..41f3f0e06 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -140,7 +140,7 @@ namespace z3 { class context { bool m_enable_exceptions; Z3_context m_ctx; - static void error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } + static void __cdecl error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; From c72b3356c16778fe21223f374f7679c1c0664e65 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 10:41:02 +0100 Subject: [PATCH 058/148] fix #1286 Signed-off-by: Nikolaj Bjorner --- src/interp/iz3mgr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 6ca8fae34..e4c294059 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -96,7 +96,7 @@ class ast_r : public ast_i { ast_r(const ast_r &other) : ast_i(other) { _m = other._m; - _m->inc_ref(_ast); + if (_m) _m->inc_ref(_ast); } ast_r &operator=(const ast_r &other) { @@ -104,7 +104,7 @@ class ast_r : public ast_i { _m->dec_ref(_ast); _ast = other._ast; _m = other._m; - _m->inc_ref(_ast); + if (_m) _m->inc_ref(_ast); return *this; } From 52217f0600aaa2b2961f971d8648b8e3c7bf4f2f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 10:56:05 +0100 Subject: [PATCH 059/148] fix #1290 Signed-off-by: Nikolaj Bjorner --- src/model/model_core.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index f94558097..4290700d4 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -86,18 +86,18 @@ void model_core::register_decl(func_decl * d, func_interp * fi) { void model_core::unregister_decl(func_decl * d) { decl2expr::obj_map_entry * ec = m_interp.find_core(d); if (ec && ec->get_data().m_value != 0) { - m_manager.dec_ref(ec->get_data().m_key); - m_manager.dec_ref(ec->get_data().m_value); m_interp.remove(d); m_const_decls.erase(d); + m_manager.dec_ref(ec->get_data().m_key); + m_manager.dec_ref(ec->get_data().m_value); return; } decl2finterp::obj_map_entry * ef = m_finterp.find_core(d); if (ef && ef->get_data().m_value != 0) { - m_manager.dec_ref(ef->get_data().m_key); - dealloc(ef->get_data().m_value); m_finterp.remove(d); m_func_decls.erase(d); + m_manager.dec_ref(ef->get_data().m_key); + dealloc(ef->get_data().m_value); } } From 1371caace28097b8d86ba4cd47be22492d419784 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 11:05:57 +0100 Subject: [PATCH 060/148] fix #1287, again Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 41f3f0e06..2200874d3 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -140,7 +140,7 @@ namespace z3 { class context { bool m_enable_exceptions; Z3_context m_ctx; - static void __cdecl error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } + static void Z3_API error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; From 22fa108ffd91834eb5b4a98a3e14434750b7b8f5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 11:07:22 +0100 Subject: [PATCH 061/148] fix #1288, again Signed-off-by: Nikolaj Bjorner --- src/muz/rel/tbv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/rel/tbv.cpp b/src/muz/rel/tbv.cpp index 69cc4819a..5ef15303e 100644 --- a/src/muz/rel/tbv.cpp +++ b/src/muz/rel/tbv.cpp @@ -74,7 +74,7 @@ tbv* tbv_manager::allocate(tbv const& bv) { } tbv* tbv_manager::allocate(uint64 val) { tbv* v = allocate0(); - for (unsigned bit = std::min(64u, num_tbits()); bit-- > 0;) { + for (unsigned bit = std::min(63u, num_tbits()); bit-- > 0;) { if (val & (1ULL << bit)) { set(*v, bit, BIT_1); } else { From 06d75a616f6246eaa717ca34bb2719726d6858d7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 11:40:17 +0100 Subject: [PATCH 062/148] fix #1288, again Signed-off-by: Nikolaj Bjorner --- src/muz/rel/tbv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/rel/tbv.cpp b/src/muz/rel/tbv.cpp index 5ef15303e..69cc4819a 100644 --- a/src/muz/rel/tbv.cpp +++ b/src/muz/rel/tbv.cpp @@ -74,7 +74,7 @@ tbv* tbv_manager::allocate(tbv const& bv) { } tbv* tbv_manager::allocate(uint64 val) { tbv* v = allocate0(); - for (unsigned bit = std::min(63u, num_tbits()); bit-- > 0;) { + for (unsigned bit = std::min(64u, num_tbits()); bit-- > 0;) { if (val & (1ULL << bit)) { set(*v, bit, BIT_1); } else { From d2ec927844120a4c26236e80b9bd8ae302de51f3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 12:34:08 +0100 Subject: [PATCH 063/148] fix build break Signed-off-by: Nikolaj Bjorner --- src/model/model_core.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index 4290700d4..833c254c3 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -86,18 +86,22 @@ void model_core::register_decl(func_decl * d, func_interp * fi) { void model_core::unregister_decl(func_decl * d) { decl2expr::obj_map_entry * ec = m_interp.find_core(d); if (ec && ec->get_data().m_value != 0) { + auto k = ec->get_data().m_key; + auto v = ec->get_data().m_value; m_interp.remove(d); m_const_decls.erase(d); - m_manager.dec_ref(ec->get_data().m_key); - m_manager.dec_ref(ec->get_data().m_value); + m_manager.dec_ref(k); + m_manager.dec_ref(v); return; } decl2finterp::obj_map_entry * ef = m_finterp.find_core(d); if (ef && ef->get_data().m_value != 0) { + auto k = ef->get_data().m_key; + auto v = ef->get_data().m_value; m_finterp.remove(d); m_func_decls.erase(d); - m_manager.dec_ref(ef->get_data().m_key); - dealloc(ef->get_data().m_value); + m_manager.dec_ref(k); + dealloc(v); } } From f359f238851d8c1a5db11a95d4ded3639813db9a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 8 Oct 2017 15:47:06 -0700 Subject: [PATCH 064/148] another fix for #1288 Signed-off-by: Nikolaj Bjorner --- src/util/mpz.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 9569ac280..7cf87b24b 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -558,14 +558,13 @@ void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { - if (is_small(a) && is_small(b)) { + COMPILE_TIME_ASSERT(sizeof(a.m_val) == sizeof(int)); + if (is_small(a) && is_small(b) && a.m_val != INT_MIN && b.m_val != INT_MIN) { int _a = a.m_val; int _b = b.m_val; if (_a < 0) _a = -_a; if (_b < 0) _b = -_b; unsigned r = u_gcd(_a, _b); - // Remark: r is (INT_MAX + 1) - // If a == b == INT_MIN set(c, r); } else { From 5819e386064e51037e1d748fd842ad2a656e17ff Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 9 Oct 2017 19:17:44 +0100 Subject: [PATCH 065/148] whitespace --- src/smt/asserted_formulas.cpp | 90 +++++++++++++++++------------------ 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index 11cc846b8..2c9d1c668 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -38,7 +38,7 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): m_qhead(0), m_macro_manager(m), m_bv_sharing(m), - m_inconsistent(false), + m_inconsistent(false), m_has_quantifiers(false), m_reduce_asserted_formulas(*this), m_distribute_forall(*this), @@ -54,8 +54,8 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): m_lift_ite(*this), m_ng_lift_ite(*this), m_find_macros(*this), - m_propagate_values(*this), - m_nnf_cnf(*this), + m_propagate_values(*this), + m_nnf_cnf(*this), m_apply_quasi_macros(*this) { m_macro_finder = alloc(macro_finder, m, m_macro_manager); @@ -68,7 +68,7 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): void asserted_formulas::setup() { switch (m_params.m_lift_ite) { case LI_FULL: - m_params.m_ng_lift_ite = LI_NONE; + m_params.m_ng_lift_ite = LI_NONE; break; case LI_CONSERVATIVE: if (m_params.m_ng_lift_ite == LI_CONSERVATIVE) @@ -77,7 +77,7 @@ void asserted_formulas::setup() { default: break; } - + if (m_params.m_relevancy_lvl == 0) m_params.m_relevancy_lemma = false; } @@ -93,7 +93,7 @@ void asserted_formulas::push_assertion(expr * e, proof * pr, vectorget_arg(i); proof_ref _pr(m.mk_not_or_elim(pr, i), m); expr_ref narg(mk_not(m, arg), m); - push_assertion(narg, _pr, result); - } + push_assertion(narg, _pr, result); + } } else { result.push_back(justified_expr(m, e, pr)); @@ -139,7 +139,7 @@ void asserted_formulas::assert_expr(expr * e, proof * _in_pr) { proof_ref in_pr(_in_pr, m), pr(_in_pr, m); expr_ref r(e, m); - if (inconsistent()) + if (inconsistent()) return; m_has_quantifiers |= ::has_quantifiers(e); @@ -183,7 +183,7 @@ void asserted_formulas::push_scope() { commit(); TRACE("asserted_formulas_scopes", tout << "after push: " << m_scopes.size() << "\n";); } - + void asserted_formulas::pop_scope(unsigned num_scopes) { TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << " of " << m_scopes.size() << "\n";); m_bv_sharing.pop_scope(num_scopes); @@ -212,7 +212,7 @@ void asserted_formulas::reset() { bool asserted_formulas::check_well_sorted() const { for (justified_expr const& je : m_formulas) { - if (!is_well_sorted(m, je.get_fml())) return false; + if (!is_well_sorted(m, je.get_fml())) return false; } return true; } @@ -224,20 +224,20 @@ void asserted_formulas::reduce() { return; if (m_qhead == m_formulas.size()) return; - if (!m_params.m_preprocess) - return; - if (m_macro_manager.has_macros()) + if (!m_params.m_preprocess) + return; + if (m_macro_manager.has_macros()) invoke(m_find_macros); - + TRACE("before_reduce", display(tout);); CASSERT("well_sorted", check_well_sorted()); - + set_eliminate_and(false); // do not eliminate and before nnf. if (!invoke(m_propagate_values)) return; if (!invoke(m_find_macros)) return; if (!invoke(m_nnf_cnf)) return; set_eliminate_and(true); - if (!invoke(m_reduce_asserted_formulas)) return; + if (!invoke(m_reduce_asserted_formulas)) return; if (!invoke(m_pull_cheap_ite_trees)) return; if (!invoke(m_pull_nested_quantifiers)) return; if (!invoke(m_lift_ite)) return; @@ -276,14 +276,14 @@ bool asserted_formulas::invoke(simplify_fmls& s) { if (!s.should_apply()) return true; IF_VERBOSE(10, verbose_stream() << "(smt." << s.id() << ")\n";); s(); - IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); - TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); - CASSERT("well_sorted",check_well_sorted()); - if (inconsistent() || canceled()) { - TRACE("after_reduce", display(tout);); - TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); + IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); + TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); + CASSERT("well_sorted",check_well_sorted()); + if (inconsistent() || canceled()) { + TRACE("after_reduce", display(tout);); + TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); return false; - } + } else { return true; } @@ -301,10 +301,10 @@ void asserted_formulas::display(std::ostream & out) const { void asserted_formulas::display_ll(std::ostream & out, ast_mark & pp_visited) const { if (!m_formulas.empty()) { - for (justified_expr const& f : m_formulas) + for (justified_expr const& f : m_formulas) ast_def_ll_pp(out, m, f.get_fml(), pp_visited, true, false); out << "asserted formulas:\n"; - for (justified_expr const& f : m_formulas) + for (justified_expr const& f : m_formulas) out << "#" << f.get_fml()->get_id() << " "; out << "\n"; } @@ -332,9 +332,9 @@ void asserted_formulas::find_macros_core() { void asserted_formulas::apply_quasi_macros() { TRACE("before_quasi_macros", display(tout);); vector new_fmls; - quasi_macros proc(m, m_macro_manager); - while (proc(m_formulas.size() - m_qhead, - m_formulas.c_ptr() + m_qhead, + quasi_macros proc(m, m_macro_manager); + while (proc(m_formulas.size() - m_qhead, + m_formulas.c_ptr() + m_qhead, new_fmls)) { swap_asserted_formulas(new_fmls); new_fmls.reset(); @@ -348,7 +348,7 @@ void asserted_formulas::nnf_cnf() { vector new_fmls; expr_ref_vector push_todo(m); proof_ref_vector push_todo_prs(m); - + unsigned i = m_qhead; unsigned sz = m_formulas.size(); TRACE("nnf_bug", tout << "i: " << i << " sz: " << sz << "\n";); @@ -378,7 +378,7 @@ void asserted_formulas::nnf_cnf() { CASSERT("well_sorted",is_well_sorted(m, r1)); if (canceled()) { return; - } + } if (m.proofs_enabled()) pr = m.mk_modus_ponens(push_todo_prs.get(k), pr1); push_assertion(r1, pr, new_fmls); @@ -389,8 +389,8 @@ void asserted_formulas::nnf_cnf() { void asserted_formulas::simplify_fmls::operator()() { vector new_fmls; - unsigned sz = af.m_formulas.size(); - for (unsigned i = af.m_qhead; i < sz; i++) { + unsigned sz = af.m_formulas.size(); + for (unsigned i = af.m_qhead; i < sz; i++) { auto& j = af.m_formulas[i]; expr_ref result(m); proof_ref result_pr(m); @@ -406,8 +406,8 @@ void asserted_formulas::simplify_fmls::operator()() { af.push_assertion(result, result_pr, new_fmls); } if (af.canceled()) return; - } - af.swap_asserted_formulas(new_fmls); + } + af.swap_asserted_formulas(new_fmls); TRACE("asserted_formulas", af.display(tout);); post_op(); } @@ -473,12 +473,12 @@ void asserted_formulas::propagate_values() { unsigned asserted_formulas::propagate_values(unsigned i) { expr_ref n(m_formulas[i].get_fml(), m); - expr_ref new_n(m); - proof_ref new_pr(m); - m_rewriter(n, new_n, new_pr); - if (m.proofs_enabled()) { + expr_ref new_n(m); + proof_ref new_pr(m); + m_rewriter(n, new_n, new_pr); + if (m.proofs_enabled()) { proof * pr = m_formulas[i].get_proof(); - new_pr = m.mk_modus_ponens(pr, new_pr); + new_pr = m.mk_modus_ponens(pr, new_pr); } justified_expr j(m, new_n, new_pr); m_formulas[i] = j; @@ -510,10 +510,10 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); } if (m.is_not(n, n1)) { - m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); + m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); } else { - m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr)); + m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr)); } } @@ -554,13 +554,13 @@ bool asserted_formulas::is_gt(expr* lhs, expr* rhs) { } UNREACHABLE(); } - + return false; } void asserted_formulas::compute_depth(expr* e) { ptr_vector todo; - todo.push_back(e); + todo.push_back(e); while (!todo.empty()) { e = todo.back(); unsigned d = 0; @@ -603,7 +603,7 @@ proof * asserted_formulas::get_inconsistency_proof() const { return 0; } -void asserted_formulas::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { +void asserted_formulas::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { expr* f = j.get_fml(); if (is_quantifier(f) && simplify_inj_axiom(m, to_quantifier(f), n)) { TRACE("inj_axiom", tout << "simplifying...\n" << mk_pp(f, m) << "\n" << n << "\n";); From 800fa3d246a93c0e3dba3bfe18e8ea49fce2b203 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 9 Oct 2017 19:18:41 +0100 Subject: [PATCH 066/148] Added bv_sort_ac=true to asserted_formulas::m_rewriter --- src/smt/asserted_formulas.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index 2c9d1c668..1ed5f2fe1 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -130,6 +130,7 @@ void asserted_formulas::set_eliminate_and(bool flag) { p.set_bool("eq2ineq", m_params.m_arith_eq2ineq); p.set_bool("gcd_rounding", true); p.set_bool("expand_select_store", true); + p.set_bool("bv_sort_ac", true); m_rewriter.updt_params(p); flush_cache(); } @@ -142,8 +143,6 @@ void asserted_formulas::assert_expr(expr * e, proof * _in_pr) { if (inconsistent()) return; - m_has_quantifiers |= ::has_quantifiers(e); - if (m_params.m_preprocess) { TRACE("assert_expr_bug", tout << r << "\n";); set_eliminate_and(false); // do not eliminate and before nnf. @@ -156,6 +155,9 @@ void asserted_formulas::assert_expr(expr * e, proof * _in_pr) { } TRACE("assert_expr_bug", tout << "after...\n" << r << "\n";); } + + m_has_quantifiers |= ::has_quantifiers(e); + push_assertion(r, pr, m_formulas); TRACE("asserted_formulas_bug", tout << "after assert_expr\n"; display(tout);); } From cae414e575299d976899b764041195ff36f484e9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 9 Oct 2017 13:59:44 -0700 Subject: [PATCH 067/148] fixes for #1296, removing COMPILE_TIME_ASSERT Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/maximize_ac_sharing.cpp | 5 +++-- src/sat/sat_watched.h | 8 ++++---- src/smt/smt_theory_var_list.h | 4 ++-- src/util/approx_set.h | 4 ++-- src/util/bit_vector.h | 2 +- src/util/debug.h | 1 - src/util/double_manager.h | 2 +- src/util/mpf.cpp | 6 +++--- src/util/mpff.cpp | 4 ++-- src/util/mpn.cpp | 2 +- src/util/mpz.cpp | 10 +++++----- src/util/uint_set.h | 1 - src/util/util.h | 6 +++--- 13 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/ast/rewriter/maximize_ac_sharing.cpp b/src/ast/rewriter/maximize_ac_sharing.cpp index d7e8df7a2..a838f59fa 100644 --- a/src/ast/rewriter/maximize_ac_sharing.cpp +++ b/src/ast/rewriter/maximize_ac_sharing.cpp @@ -54,13 +54,13 @@ br_status maximize_ac_sharing::reduce_app(func_decl * f, unsigned num_args, expr TRACE("ac_sharing_detail", tout << "args: "; for (unsigned i = 0; i < num_args; i++) tout << mk_pp(_args[i], m) << "\n";); try_to_reuse: if (num_args > 1 && num_args < MAX_NUM_ARGS_FOR_OPT) { - for (unsigned i = 0; i < num_args - 1; i++) { + for (unsigned i = 0; i + 1 < num_args; i++) { for (unsigned j = i + 1; j < num_args; j++) { if (contains(f, _args[i], _args[j])) { TRACE("ac_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";); _args[i] = m.mk_app(f, _args[i], _args[j]); SASSERT(num_args > 1); - for (unsigned w = j; w < num_args - 1; w++) { + for (unsigned w = j; w + 1 < num_args; w++) { _args[w] = _args[w+1]; } num_args--; @@ -144,6 +144,7 @@ void maximize_ac_sharing::restore_entries(unsigned old_lim) { while (i != old_lim) { --i; entry * e = m_entries[i]; + m_cache.remove(e); m.dec_ref(e->m_arg1); m.dec_ref(e->m_arg2); } diff --git a/src/sat/sat_watched.h b/src/sat/sat_watched.h index e5a02953b..639d3e6a8 100644 --- a/src/sat/sat_watched.h +++ b/src/sat/sat_watched.h @@ -109,10 +109,10 @@ namespace sat { bool operator!=(watched const & w) const { return !operator==(w); } }; - COMPILE_TIME_ASSERT(0 <= watched::BINARY && watched::BINARY <= 3); - COMPILE_TIME_ASSERT(0 <= watched::TERNARY && watched::TERNARY <= 3); - COMPILE_TIME_ASSERT(0 <= watched::CLAUSE && watched::CLAUSE <= 3); - COMPILE_TIME_ASSERT(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 3); + static_assert(0 <= watched::BINARY && watched::BINARY <= 3, ""); + static_assert(0 <= watched::TERNARY && watched::TERNARY <= 3, ""); + static_assert(0 <= watched::CLAUSE && watched::CLAUSE <= 3, ""); + static_assert(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 3, ""); struct watched_lt { bool operator()(watched const & w1, watched const & w2) const { diff --git a/src/smt/smt_theory_var_list.h b/src/smt/smt_theory_var_list.h index d7e246824..aa2816786 100644 --- a/src/smt/smt_theory_var_list.h +++ b/src/smt/smt_theory_var_list.h @@ -67,9 +67,9 @@ namespace smt { }; // 32 bit machine - COMPILE_TIME_ASSERT(sizeof(expr*) != 4 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int)); + static_assert(sizeof(expr*) != 4 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int), "32 bit"); // 64 bit machine - COMPILE_TIME_ASSERT(sizeof(expr*) != 8 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int) + /* a structure must be aligned */ sizeof(int)); + static_assert(sizeof(expr*) != 8 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int) + /* a structure must be aligned */ sizeof(int), "64 bit"); }; #endif /* SMT_THEORY_VAR_LIST_H_ */ diff --git a/src/util/approx_set.h b/src/util/approx_set.h index e696d52ee..1cb7ae9f2 100644 --- a/src/util/approx_set.h +++ b/src/util/approx_set.h @@ -29,7 +29,7 @@ public: static const unsigned long long zero = 0ull; static const unsigned long long one = 1ull; }; -COMPILE_TIME_ASSERT(sizeof(unsigned long long) == 8); +static_assert(sizeof(unsigned long long) == 8, ""); template <> class approx_set_traits { public: @@ -37,7 +37,7 @@ public: static const unsigned zero = 0; static const unsigned one = 1; }; -COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); +static_assert(sizeof(unsigned) == 4, "unsigned are 4 bytes"); template class approx_set_tpl : private T2U_Proc { diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index 6a254e399..2d42e35a2 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -24,7 +24,7 @@ Revision History: #include "util/vector.h" #include "util/memory_manager.h" -COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); +static_assert(sizeof(unsigned) == 4, "unsigned are 4 bytes"); #define BV_DEFAULT_CAPACITY 2 class bit_vector { diff --git a/src/util/debug.h b/src/util/debug.h index e0ceb9a64..536df4588 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -90,7 +90,6 @@ bool is_debug_enabled(const char * tag); exit(-1); \ } -#define COMPILE_TIME_ASSERT(expr) static_assert(expr, "") void finalize_debug(); /* diff --git a/src/util/double_manager.h b/src/util/double_manager.h index 33cccf2af..7532a3b8b 100644 --- a/src/util/double_manager.h +++ b/src/util/double_manager.h @@ -97,7 +97,7 @@ public: } }; -COMPILE_TIME_ASSERT(sizeof(uint64) == sizeof(double)); +static_assert(sizeof(uint64) == sizeof(double), ""); #endif /* DOUBLE_MANAGER_H_ */ diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 5e7233110..3218419a9 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -73,7 +73,7 @@ mpf_manager::~mpf_manager() { } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, int value) { - COMPILE_TIME_ASSERT(sizeof(int) == 4); + static_assert(sizeof(int) == 4, "assume integers are 4 bytes"); o.sign = false; o.ebits = ebits; @@ -119,7 +119,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, double value) { // double === mpf(11, 53) - COMPILE_TIME_ASSERT(sizeof(double) == 8); + static_assert(sizeof(double) == 8, "doubles are 8 bytes"); uint64 raw; memcpy(&raw, &value, sizeof(double)); @@ -155,7 +155,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, double value) { void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, float value) { // single === mpf(8, 24) - COMPILE_TIME_ASSERT(sizeof(float) == 4); + static_assert(sizeof(float) == 4, "floats are 4 bytes"); unsigned int raw; memcpy(&raw, &value, sizeof(float)); diff --git a/src/util/mpff.cpp b/src/util/mpff.cpp index 459b0691c..eac9cc80c 100644 --- a/src/util/mpff.cpp +++ b/src/util/mpff.cpp @@ -27,8 +27,8 @@ Revision History: #include "util/bit_util.h" #include "util/trace.h" -COMPILE_TIME_ASSERT(sizeof(mpn_digit) == sizeof(unsigned)); -COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); +static_assert(sizeof(mpn_digit) == sizeof(unsigned), ""); +static_assert(sizeof(unsigned) == 4, "unsigned haven't changed size for a while"); // MIN_MSW is an shorthand for 0x8000..00, i.e., the minimal most significand word. #define MIN_MSW (1u << (sizeof(unsigned) * 8 - 1)) diff --git a/src/util/mpn.cpp b/src/util/mpn.cpp index 65223133f..2059ea6fd 100644 --- a/src/util/mpn.cpp +++ b/src/util/mpn.cpp @@ -24,7 +24,7 @@ Revision History: #define max(a,b) (((a) > (b)) ? (a) : (b)) typedef uint64 mpn_double_digit; -COMPILE_TIME_ASSERT(sizeof(mpn_double_digit) == 2 * sizeof(mpn_digit)); +static_assert(sizeof(mpn_double_digit) == 2 * sizeof(mpn_digit), "size alignment"); const mpn_digit mpn_manager::zero = 0; diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 7cf87b24b..7ad472ef1 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -558,7 +558,7 @@ void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { - COMPILE_TIME_ASSERT(sizeof(a.m_val) == sizeof(int)); + static_assert(sizeof(a.m_val) == sizeof(int), "size mismatch"); if (is_small(a) && is_small(b) && a.m_val != INT_MIN && b.m_val != INT_MIN) { int _a = a.m_val; int _b = b.m_val; @@ -724,7 +724,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { #ifdef LEHMER_GCD // For now, it only works if sizeof(digit_t) == sizeof(unsigned) - COMPILE_TIME_ASSERT(sizeof(digit_t) == sizeof(unsigned)); + static_assert(sizeof(digit_t) == sizeof(unsigned), ""); int64 a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; mpz a1, b1, t, r, tmp; @@ -1754,7 +1754,7 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { } #ifndef _MP_GMP -COMPILE_TIME_ASSERT(sizeof(digit_t) == 4 || sizeof(digit_t) == 8); +static_assert(sizeof(digit_t) == 4 || sizeof(digit_t) == 8, ""); #endif template @@ -1821,7 +1821,7 @@ unsigned mpz_manager::log2(mpz const & a) { if (is_small(a)) return ::log2((unsigned)a.m_val); #ifndef _MP_GMP - COMPILE_TIME_ASSERT(sizeof(digit_t) == 8 || sizeof(digit_t) == 4); + static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; digit_t * ds = c->m_digits; @@ -1843,7 +1843,7 @@ unsigned mpz_manager::mlog2(mpz const & a) { if (is_small(a)) return ::log2((unsigned)-a.m_val); #ifndef _MP_GMP - COMPILE_TIME_ASSERT(sizeof(digit_t) == 8 || sizeof(digit_t) == 4); + static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; digit_t * ds = c->m_digits; diff --git a/src/util/uint_set.h b/src/util/uint_set.h index 33c39eeb2..352189ef1 100644 --- a/src/util/uint_set.h +++ b/src/util/uint_set.h @@ -22,7 +22,6 @@ Revision History: #include "util/util.h" #include "util/vector.h" -COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); class uint_set : unsigned_vector { diff --git a/src/util/util.h b/src/util/util.h index 23c2c1657..1f753099c 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -33,13 +33,13 @@ Revision History: typedef unsigned long long uint64; #endif -COMPILE_TIME_ASSERT(sizeof(uint64) == 8); +static_assert(sizeof(uint64) == 8, "64 bits please"); #ifndef int64 typedef long long int64; #endif -COMPILE_TIME_ASSERT(sizeof(int64) == 8); +static_assert(sizeof(int64) == 8, "64 bits"); #ifndef INT64_MIN #define INT64_MIN static_cast(0x8000000000000000ull) @@ -111,7 +111,7 @@ inline unsigned next_power_of_two(unsigned v) { unsigned log2(unsigned v); unsigned uint64_log2(uint64 v); -COMPILE_TIME_ASSERT(sizeof(unsigned) == 4); +static_assert(sizeof(unsigned) == 4, "unsigned are 32 bits"); // Return the number of 1 bits in v. static inline unsigned get_num_1bits(unsigned v) { From 7f693186a0c0ca9cc0d15ff3fd8fbc1734e95ee1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2017 07:10:04 -0700 Subject: [PATCH 068/148] trying to address leak reported in #1297 Signed-off-by: Nikolaj Bjorner --- src/api/api_parsers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index bef31e9f1..7a68efbd8 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -69,6 +69,7 @@ extern "C" { ok = false; } mk_c(c)->m_smtlib_error_buffer = outs.str(); + outs.clear(); if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); @@ -98,6 +99,7 @@ extern "C" { ok = false; } mk_c(c)->m_smtlib_error_buffer = outs.str(); + outs.clear(); if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); From 09ea370ea33f64e54f9cd837cd4e9f66ebe933df Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 10 Oct 2017 12:06:19 -0700 Subject: [PATCH 069/148] update C-example that fails to not use longjumps. Issue #1297 Signed-off-by: Nikolaj Bjorner --- examples/c/test_capi.c | 35 ++++++++++++++++++++++++----------- src/api/api_parsers.cpp | 16 ++++++++-------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index 88fdaa1cf..6327ad40f 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -65,6 +65,15 @@ void throw_z3_error(Z3_context c, Z3_error_code e) longjmp(g_catch_buffer, e); } +/** + \brief Error handling that depends on checking an error code on the context. + +*/ + +void nothrow_z3_error(Z3_context c, Z3_error_code e) { + // no-op +} + /** \brief Create a logical context. @@ -1592,18 +1601,16 @@ void error_code_example1() void error_code_example2() { Z3_config cfg; Z3_context ctx = NULL; - int r; + Z3_error_code e; printf("\nerror_code_example2\n"); LOG_MSG("error_code_example2"); - /* low tech try&catch */ - r = setjmp(g_catch_buffer); - if (r == 0) { + if (1) { Z3_ast x, y, app; cfg = Z3_mk_config(); - ctx = mk_context_custom(cfg, throw_z3_error); + ctx = mk_context_custom(cfg, nothrow_z3_error); Z3_del_config(cfg); x = mk_int_var(ctx, "x"); @@ -1611,11 +1618,14 @@ void error_code_example2() { printf("before Z3_mk_iff\n"); /* the next call will produce an error */ app = Z3_mk_iff(ctx, x, y); + e = Z3_get_error_code(ctx); + if (e != Z3_OK) goto err; unreachable(); Z3_del_context(ctx); } else { - printf("Z3 error: %s.\n", Z3_get_error_msg(ctx, (Z3_error_code)r)); + err: + printf("Z3 error: %s.\n", Z3_get_error_msg(ctx, e)); if (ctx != NULL) { Z3_del_context(ctx); } @@ -1781,15 +1791,14 @@ void parser_example5() { Z3_config cfg; Z3_context ctx = NULL; Z3_solver s = NULL; - int r; + Z3_error_code e; printf("\nparser_example5\n"); LOG_MSG("parser_example5"); - r = setjmp(g_catch_buffer); - if (r == 0) { + if (1) { cfg = Z3_mk_config(); - ctx = mk_context_custom(cfg, throw_z3_error); + ctx = mk_context_custom(cfg, nothrow_z3_error); s = mk_solver(ctx); Z3_del_config(cfg); @@ -1798,12 +1807,15 @@ void parser_example5() { "(benchmark tst :extrafuns ((x Int (y Int)) :formula (> x y) :formula (> x 0))", 0, 0, 0, 0, 0, 0); + e = Z3_get_error_code(ctx); + if (e != Z3_OK) goto err; unreachable(); del_solver(ctx, s); Z3_del_context(ctx); } else { - printf("Z3 error: %s.\n", Z3_get_error_msg(ctx, (Z3_error_code)r)); + err: + printf("Z3 error: %s.\n", Z3_get_error_msg(ctx, e)); if (ctx != NULL) { printf("Error message: '%s'.\n",Z3_get_smtlib_error(ctx)); del_solver(ctx, s); @@ -2639,6 +2651,7 @@ void smt2parser_example() { ctx = mk_context(); fs = Z3_parse_smtlib2_string(ctx, "(declare-fun a () (_ BitVec 8)) (assert (bvuge a #x10)) (assert (bvule a #xf0))", 0, 0, 0, 0, 0, 0); printf("formulas: %s\n", Z3_ast_to_string(ctx, fs)); + Z3_del_context(ctx); } diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index 7a68efbd8..71fa945d3 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -56,20 +56,20 @@ extern "C" { Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); - std::ostringstream outs; + std::ostringstream* outs = alloc(std::ostringstream); bool ok = false; RESET_ERROR_CODE(); init_smtlib_parser(c, num_sorts, sort_names, sorts, num_decls, decl_names, decls); - mk_c(c)->m_smtlib_parser->set_error_stream(outs); + mk_c(c)->m_smtlib_parser->set_error_stream(*outs); try { ok = mk_c(c)->m_smtlib_parser->parse_string(str); } catch (...) { ok = false; } - mk_c(c)->m_smtlib_error_buffer = outs.str(); - outs.clear(); + mk_c(c)->m_smtlib_error_buffer = outs->str(); + dealloc(outs); if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); @@ -89,17 +89,17 @@ extern "C" { LOG_Z3_parse_smtlib_file(c, file_name, num_sorts, sort_names, types, num_decls, decl_names, decls); bool ok = false; RESET_ERROR_CODE(); - std::ostringstream outs; + std::ostringstream* outs = alloc(std::ostringstream); init_smtlib_parser(c, num_sorts, sort_names, types, num_decls, decl_names, decls); - mk_c(c)->m_smtlib_parser->set_error_stream(outs); + mk_c(c)->m_smtlib_parser->set_error_stream(*outs); try { ok = mk_c(c)->m_smtlib_parser->parse_file(file_name); } catch(...) { ok = false; } - mk_c(c)->m_smtlib_error_buffer = outs.str(); - outs.clear(); + mk_c(c)->m_smtlib_error_buffer = outs->str(); + dealloc(outs); if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); From c093e6d4b9c1a9a3d97099fab492e89227fc3715 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 11 Oct 2017 09:53:02 -0700 Subject: [PATCH 070/148] harden a few API methods against longjumps in set_error. Memory leak exposed in #1297 Signed-off-by: Nikolaj Bjorner --- src/api/api_opt.cpp | 16 +++++++--------- src/api/api_parsers.cpp | 25 +++++++++++++------------ src/api/api_quant.cpp | 4 +++- src/api/api_solver.cpp | 3 +++ 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index a967dfb2b..9f5f7dd5d 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -283,15 +283,16 @@ extern "C" { Z3_optimize opt, std::istream& s) { ast_manager& m = mk_c(c)->m(); - cmd_context ctx(false, &m); - install_opt_cmds(ctx, to_optimize_ptr(opt)); - ctx.set_ignore_check(true); - if (!parse_smt2_commands(ctx, s)) { + scoped_ptr ctx = alloc(cmd_context, false, &m); + install_opt_cmds(*ctx.get(), to_optimize_ptr(opt)); + ctx->set_ignore_check(true); + if (!parse_smt2_commands(*ctx.get(), s)) { + ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR); return; } - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); + ptr_vector::const_iterator it = ctx->begin_assertions(); + ptr_vector::const_iterator end = ctx->end_assertions(); for (; it != end; ++it) { to_optimize_ptr(opt)->add_hard_constraint(*it); } @@ -320,9 +321,6 @@ extern "C" { std::ostringstream strm; strm << "Could not open file " << s; throw default_exception(strm.str()); - - SET_ERROR_CODE(Z3_PARSER_ERROR); - return; } Z3_optimize_from_stream(c, d, is); Z3_CATCH; diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index 71fa945d3..b3252281b 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -56,7 +56,7 @@ extern "C" { Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); - std::ostringstream* outs = alloc(std::ostringstream); + scoped_ptr outs = alloc(std::ostringstream); bool ok = false; RESET_ERROR_CODE(); @@ -69,7 +69,7 @@ extern "C" { ok = false; } mk_c(c)->m_smtlib_error_buffer = outs->str(); - dealloc(outs); + outs = nullptr; if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); @@ -89,7 +89,7 @@ extern "C" { LOG_Z3_parse_smtlib_file(c, file_name, num_sorts, sort_names, types, num_decls, decl_names, decls); bool ok = false; RESET_ERROR_CODE(); - std::ostringstream* outs = alloc(std::ostringstream); + scoped_ptr outs = alloc(std::ostringstream); init_smtlib_parser(c, num_sorts, sort_names, types, num_decls, decl_names, decls); mk_c(c)->m_smtlib_parser->set_error_stream(*outs); try { @@ -99,7 +99,7 @@ extern "C" { ok = false; } mk_c(c)->m_smtlib_error_buffer = outs->str(); - dealloc(outs); + outs = nullptr; if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); @@ -262,21 +262,22 @@ extern "C" { Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; - cmd_context ctx(false, &(mk_c(c)->m())); - ctx.set_ignore_check(true); + scoped_ptr ctx = alloc(cmd_context, false, &(mk_c(c)->m())); + ctx->set_ignore_check(true); for (unsigned i = 0; i < num_decls; ++i) { - ctx.insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); + ctx->insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); } for (unsigned i = 0; i < num_sorts; ++i) { - psort* ps = ctx.pm().mk_psort_cnst(to_sort(sorts[i])); - ctx.insert(ctx.pm().mk_psort_user_decl(0, to_symbol(sort_names[i]), ps)); + psort* ps = ctx->pm().mk_psort_cnst(to_sort(sorts[i])); + ctx->insert(ctx->pm().mk_psort_user_decl(0, to_symbol(sort_names[i]), ps)); } - if (!parse_smt2_commands(ctx, is)) { + if (!parse_smt2_commands(*ctx.get(), is)) { + ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR); return of_ast(mk_c(c)->m().mk_true()); } - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); + ptr_vector::const_iterator it = ctx->begin_assertions(); + ptr_vector::const_iterator end = ctx->end_assertions(); unsigned size = static_cast(end - it); return of_ast(mk_c(c)->mk_and(size, it)); Z3_CATCH_RETURN(0); diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index d64768b3b..e56505e6d 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -63,9 +63,11 @@ extern "C" { RESET_ERROR_CODE(); if (!mk_c(c)->m().is_bool(to_expr(body))) { SET_ERROR_CODE(Z3_SORT_ERROR); + return nullptr; } if (num_patterns > 0 && num_no_patterns > 0) { SET_ERROR_CODE(Z3_INVALID_USAGE); + return nullptr; } expr * const* ps = reinterpret_cast(patterns); expr * const* no_ps = reinterpret_cast(no_patterns); @@ -76,7 +78,7 @@ extern "C" { for (unsigned i = 0; i < num_patterns; i++) { if (!v(num_decls, ps[i], 0, 0)) { SET_ERROR_CODE(Z3_INVALID_PATTERN); - return 0; + return nullptr; } } } diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 3a81080ed..2030c5210 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -442,6 +442,7 @@ extern "C" { unsigned sz = __assumptions.size(); for (unsigned i = 0; i < sz; ++i) { if (!is_expr(__assumptions[i])) { + _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE); return Z3_L_UNDEF; } @@ -451,6 +452,7 @@ extern "C" { sz = __variables.size(); for (unsigned i = 0; i < sz; ++i) { if (!is_expr(__variables[i])) { + _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE); return Z3_L_UNDEF; } @@ -471,6 +473,7 @@ extern "C" { } catch (z3_exception & ex) { to_solver_ref(s)->set_reason_unknown(eh); + _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); mk_c(c)->handle_exception(ex); return Z3_L_UNDEF; } From a3b109cc143356f9095640b65d1795cfe9874e1e Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 11 Oct 2017 19:37:18 +0100 Subject: [PATCH 071/148] [ASan] Fix some leaks reported in the small object allocator test. --- src/test/small_object_allocator.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/small_object_allocator.cpp b/src/test/small_object_allocator.cpp index cdac0370d..2f08d553e 100644 --- a/src/test/small_object_allocator.cpp +++ b/src/test/small_object_allocator.cpp @@ -18,8 +18,11 @@ void tst_small_object_allocator() { TRACE("small_object_allocator", tout << "p1: " << (void*)p1 << " q1: " << (void*)q1 << " p2: " << (void*)p2 << "\n";); soa.deallocate(13,p1); + soa.deallocate(14,q1); + soa.deallocate(13,p2); char * p3 = new (soa) char[13]; TRACE("small_object_allocator", tout << "p3: " << (void*)p3 << "\n";); + soa.deallocate(13,p3); char * r1 = new (soa) char[1]; char * r2 = new (soa) char[1]; @@ -36,6 +39,10 @@ void tst_small_object_allocator() { r3 = new (soa) char[1]; TRACE("small_object_allocator", tout << "r1: " << (void*)r1 << " r2: " << (void*)r2 << " r3: " << (void*)r3 << " r4: " << (void*)r4 << "\n";); + soa.deallocate(1,r1); + soa.deallocate(1,r2); + soa.deallocate(1,r3); + soa.deallocate(1,r4); (void)r1; (void)r2; (void)r3; From da2b876acb0330f0afaa15911d56fbb2d7027c7c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Oct 2017 07:39:27 -0700 Subject: [PATCH 072/148] fix #1303 Signed-off-by: Nikolaj Bjorner --- src/math/polynomial/polynomial.cpp | 17 +++++++++-------- src/test/nlsat.cpp | 2 -- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 80669648e..7adaf9adb 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -3536,10 +3536,11 @@ namespace polynomial { iccp(p, max_var(p), i, c, pp); } - void pp(polynomial const * p, var x, polynomial_ref & pp) { + polynomial_ref pp(polynomial const * p, var x) { scoped_numeral i(m_manager); - polynomial_ref c(pm()); - iccp(p, x, i, c, pp); + polynomial_ref c(pm()), result(pm()); + iccp(p, x, i, c, result); + return result; } bool is_primitive(polynomial const * p, var x) { @@ -3598,7 +3599,7 @@ namespace polynomial { if (is_zero(rem)) { TRACE("polynomial", tout << "rem is zero...\npp_v: " << pp_v << "\n";); flip_sign_if_lm_neg(pp_v); - pp(pp_v, x, r); + r = pp(pp_v, x); r = mul(d_a, d_r, r); return; } @@ -3849,7 +3850,7 @@ namespace polynomial { TRACE("mgcd", tout << "new combined:\n" << C_star << "\n";); } } - pp(C_star, x, candidate); + candidate = pp(C_star, x); TRACE("mgcd", tout << "candidate:\n" << candidate << "\n";); scoped_numeral lc_candidate(m()); lc_candidate = univ_coeff(candidate, degree(candidate, x)); @@ -6619,8 +6620,8 @@ namespace polynomial { polynomial_ref cf1(pm()); m_wrapper.content(f1, x, cf1); polynomial_ref cf2(pm()); m_wrapper.content(f2, x, cf2); tout << "content(f1): " << cf1 << "\ncontent(f2): " << cf2 << "\n";); - pp(f1, x, f1); - pp(f2, x, f2); + f1 = pp(f1, x); + f2 = pp(f2, x); TRACE("factor", tout << "f1: " << f1 << "\nf2: " << f2 << "\n";); DEBUG_CODE({ polynomial_ref f1f2(pm()); @@ -7150,7 +7151,7 @@ namespace polynomial { } void manager::primitive(polynomial const * p, var x, polynomial_ref & pp) { - m_imp->pp(p, x, pp); + pp = m_imp->pp(p, x); } void manager::icpp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp) { diff --git a/src/test/nlsat.cpp b/src/test/nlsat.cpp index 4d9d4579e..ecf73843f 100644 --- a/src/test/nlsat.cpp +++ b/src/test/nlsat.cpp @@ -698,10 +698,8 @@ static void tst10() { void tst_nlsat() { tst10(); std::cout << "------------------\n"; - exit(0); tst9(); std::cout << "------------------\n"; - exit(0); tst8(); std::cout << "------------------\n"; tst7(); From 11f1a81d7b4911d969ccd354c6cff2d65fd75b1e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Oct 2017 10:12:37 -0700 Subject: [PATCH 073/148] disable failing unit tests Signed-off-by: Nikolaj Bjorner --- src/test/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/main.cpp b/src/test/main.cpp index 03fbba1df..cddf97700 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -222,14 +222,14 @@ int main(int argc, char ** argv) { TST(heap_trie); TST(karr); TST(no_overflow); - TST(memory); + // TST(memory); TST(datalog_parser); TST_ARGV(datalog_parser_file); TST(dl_query); TST(quant_solve); TST(rcf); TST(polynorm); - TST(qe_arith); + // TST(qe_arith); TST(expr_substitution); TST(sorting_network); TST(theory_pb); From d338fab4f632307eff3e85786e2d44cc96dce7aa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Oct 2017 13:58:14 -0700 Subject: [PATCH 074/148] fix #1305 Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 59 +++++++++++++++++++++------------------------ src/test/main.cpp | 2 +- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 9b3033397..6b3b3a11f 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -105,10 +105,10 @@ namespace qe { rational r; app* alit = to_app(lit); vector > nums; - for (unsigned i = 0; i < alit->get_num_args(); ++i) { - val = eval(alit->get_arg(i)); + for (expr* arg : *alit) { + val = eval(arg); if (!a.is_numeral(val, r)) return false; - nums.push_back(std::make_pair(alit->get_arg(i), r)); + nums.push_back(std::make_pair(arg, r)); } std::sort(nums.begin(), nums.end(), compare_second()); for (unsigned i = 0; i + 1 < nums.size(); ++i) { @@ -168,8 +168,8 @@ namespace qe { } else if (a.is_add(t)) { app* ap = to_app(t); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - linearize(mbo, eval, mul, ap->get_arg(i), c, fmls, ts, tids); + for (expr* arg : *ap) { + linearize(mbo, eval, mul, arg, c, fmls, ts, tids); } } else if (a.is_sub(t, t1, t2)) { @@ -226,16 +226,16 @@ namespace qe { else if (a.is_mul(t)) { app* ap = to_app(t); r = rational(1); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - if (!is_numeral(ap->get_arg(i), r1)) return false; + for (expr * arg : *ap) { + if (!is_numeral(arg, r1)) return false; r *= r1; } } else if (a.is_add(t)) { app* ap = to_app(t); r = rational(0); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - if (!is_numeral(ap->get_arg(i), r1)) return false; + for (expr * arg : *ap) { + if (!is_numeral(arg, r1)) return false; r += r1; } } @@ -297,6 +297,7 @@ namespace qe { opt::model_based_opt mbo; obj_map tids; + expr_ref_vector pinned(m); unsigned j = 0; for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(); @@ -308,6 +309,7 @@ namespace qe { } else { TRACE("qe", tout << mk_pp(fml, m) << "\n";); + pinned.push_back(fml); } } fmls.resize(j); @@ -321,8 +323,7 @@ namespace qe { // return those to fmls. expr_mark var_mark, fmls_mark; - for (unsigned i = 0; i < vars.size(); ++i) { - app* v = vars[i].get(); + for (app * v : vars) { var_mark.mark(v); if (is_arith(v) && !tids.contains(v)) { rational r; @@ -332,17 +333,16 @@ namespace qe { tids.insert(v, mbo.add_var(r, a.is_int(v))); } } - for (unsigned i = 0; i < fmls.size(); ++i) { - fmls_mark.mark(fmls[i].get()); + for (expr* fml : fmls) { + fmls_mark.mark(fml); } - obj_map::iterator it = tids.begin(), end = tids.end(); ptr_vector index2expr; - for (; it != end; ++it) { - expr* e = it->m_key; + for (auto& kv : tids) { + expr* e = kv.m_key; if (!var_mark.is_marked(e)) { mark_rec(fmls_mark, e); } - index2expr.setx(it->m_value, e, 0); + index2expr.setx(kv.m_value, e, 0); } j = 0; unsigned_vector real_vars; @@ -360,8 +360,7 @@ namespace qe { } vars.resize(j); TRACE("qe", tout << "remaining vars: " << vars << "\n"; - for (unsigned i = 0; i < real_vars.size(); ++i) { - unsigned v = real_vars[i]; + for (unsigned v : real_vars) { tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n"; } mbo.display(tout);); @@ -449,8 +448,8 @@ namespace qe { // extract linear constraints - for (unsigned i = 0; i < fmls.size(); ++i) { - linearize(mbo, eval, fmls[i].get(), fmls, tids); + for (expr * fml : fmls) { + linearize(mbo, eval, fml, fmls, tids); } // find optimal value @@ -459,11 +458,10 @@ namespace qe { // update model to use new values that satisfy optimality ptr_vector vars; - obj_map::iterator it = tids.begin(), end = tids.end(); - for (; it != end; ++it) { - expr* e = it->m_key; + for (auto& kv : tids) { + expr* e = kv.m_key; if (is_uninterp_const(e)) { - unsigned id = it->m_value; + unsigned id = kv.m_value; func_decl* f = to_app(e)->get_decl(); expr_ref val(a.mk_numeral(mbo.get_value(id), false), m); mdl.register_decl(f, val); @@ -509,10 +507,9 @@ namespace qe { void extract_coefficients(opt::model_based_opt& mbo, model_evaluator& eval, obj_map const& ts, obj_map& tids, vars& coeffs) { coeffs.reset(); eval.set_model_completion(true); - obj_map::iterator it = ts.begin(), end = ts.end(); - for (; it != end; ++it) { + for (auto& kv : ts) { unsigned id; - expr* v = it->m_key; + expr* v = kv.m_key; if (!tids.find(v, id)) { rational r; expr_ref val = eval(v); @@ -520,9 +517,9 @@ namespace qe { id = mbo.add_var(r, a.is_int(v)); tids.insert(v, id); } - CTRACE("qe", it->m_value.is_zero(), tout << mk_pp(v, m) << " has coefficeint 0\n";); - if (!it->m_value.is_zero()) { - coeffs.push_back(var(id, it->m_value)); + CTRACE("qe", kv.m_value.is_zero(), tout << mk_pp(v, m) << " has coefficeint 0\n";); + if (!kv.m_value.is_zero()) { + coeffs.push_back(var(id, kv.m_value)); } } } diff --git a/src/test/main.cpp b/src/test/main.cpp index cddf97700..7d61f9cad 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -229,7 +229,7 @@ int main(int argc, char ** argv) { TST(quant_solve); TST(rcf); TST(polynorm); - // TST(qe_arith); + TST(qe_arith); TST(expr_substitution); TST(sorting_network); TST(theory_pb); From 355455453325fe2b677f24663414bc0900383f59 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Oct 2017 14:17:52 -0700 Subject: [PATCH 075/148] command to exit tests early Signed-off-by: Nikolaj Bjorner --- src/test/main.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/test/main.cpp b/src/test/main.cpp index cddf97700..77df6c2fb 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -10,27 +10,30 @@ #include "util/memory_manager.h" #include "util/gparams.h" +static void tst_exit_all_tests() { + exit(0); +} // // Unit tests fail by asserting. // If they return, we assume the unit test succeeds // and print "PASS" to indicate success. // -#define TST(MODULE) { \ - std::string s("test "); \ - s += #MODULE; \ - void tst_##MODULE(); \ - if (do_display_usage) \ - std::cout << #MODULE << "\n"; \ - for (int i = 0; i < argc; i++) \ - if (test_all || strcmp(argv[i], #MODULE) == 0) { \ - enable_trace(#MODULE); \ - enable_debug(#MODULE); \ - timeit timeit(true, s.c_str()); \ - tst_##MODULE(); \ - std::cout << "PASS" << std::endl; \ - } \ -} +#define TST(MODULE) { \ + std::string s("test "); \ + s += #MODULE; \ + void tst_##MODULE(); \ + if (do_display_usage) \ + std::cout << #MODULE << "\n"; \ + for (int i = 0; i < argc; i++) \ + if (test_all || strcmp(argv[i], #MODULE) == 0) { \ + enable_trace(#MODULE); \ + enable_debug(#MODULE); \ + timeit timeit(true, s.c_str()); \ + tst_##MODULE(); \ + std::cout << "PASS" << std::endl; \ + } \ + } #define TST_ARGV(MODULE) { \ std::string s("test "); \ @@ -207,6 +210,7 @@ int main(int argc, char ** argv) { TST(prime_generator); TST(permutation); TST(nlsat); + TST(exit_all_tests); TST(ext_numeral); TST(interval); TST(f2n); From 8cf0c94e5ff3e2f57c250e1265a9d8c02c254092 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 12 Oct 2017 14:34:04 -0700 Subject: [PATCH 076/148] address some ASan leaks Signed-off-by: Nikolaj Bjorner --- src/math/automata/symbolic_automata_def.h | 3 ++- src/util/scoped_ptr_vector.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 3be6a1da0..be38a60bc 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -24,6 +24,7 @@ Revision History: #include "math/automata/symbolic_automata.h" #include "util/hashtable.h" +#include "util/vector.h" @@ -311,7 +312,7 @@ symbolic_automata::mk_determinstic_param(automaton_t& a, bool flip_accepta s2id.insert(set, p_state_id++); // the index to the initial state is 0 id2s.push_back(set); - svector todo; //States to visit + ::vector todo; //States to visit todo.push_back(set); uint_set state; diff --git a/src/util/scoped_ptr_vector.h b/src/util/scoped_ptr_vector.h index a9ef92766..0bd0fd47e 100644 --- a/src/util/scoped_ptr_vector.h +++ b/src/util/scoped_ptr_vector.h @@ -42,7 +42,7 @@ public: bool empty() const { return m_vector.empty(); } void resize(unsigned sz) { if (sz < m_vector.size()) { - for (unsigned i = m_vector.size(); i < sz; i++) + for (unsigned i = m_vector.size(); i-- > sz; ) dealloc(m_vector[i]); m_vector.shrink(sz); } From c12439fe1e3fb93a77e42e97780544a92206e011 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Oct 2017 07:29:16 -0700 Subject: [PATCH 077/148] fix #1306 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 8782cb462..03c17aaf0 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -726,7 +726,7 @@ namespace sat { SASSERT(scope_lvl() == 0); if (m_config.m_dimacs_display) { display_dimacs(std::cout); - for (unsigned i = 0; i < num_lits; ++lits) { + for (unsigned i = 0; i < num_lits; ++i) { std::cout << dimacs_lit(lits[i]) << " 0\n"; } return l_undef; From 40dfdb6606045240f519a42042bd29d6143fa6bd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Oct 2017 07:37:18 -0700 Subject: [PATCH 078/148] bypass UBSan error warnings by using nullptr as error handler. Has same no-op effect. Issue #1287 --- src/api/c++/z3++.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 2200874d3..84ddfacc6 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -140,18 +140,17 @@ namespace z3 { class context { bool m_enable_exceptions; Z3_context m_ctx; - static void Z3_API error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; - Z3_set_error_handler(m_ctx, error_handler); + Z3_set_error_handler(m_ctx, nullptr); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } void init_interp(config & c) { m_ctx = Z3_mk_interpolation_context(c); m_enable_exceptions = true; - Z3_set_error_handler(m_ctx, error_handler); + Z3_set_error_handler(m_ctx, nullptr); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } From 5b6472f022594dc64336da33d013d9b43e76ab0a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Oct 2017 10:54:29 -0700 Subject: [PATCH 079/148] change nullptr to 0 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 84ddfacc6..f55ece034 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -143,14 +143,14 @@ namespace z3 { void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; - Z3_set_error_handler(m_ctx, nullptr); + Z3_set_error_handler(m_ctx, 0); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } void init_interp(config & c) { m_ctx = Z3_mk_interpolation_context(c); m_enable_exceptions = true; - Z3_set_error_handler(m_ctx, nullptr); + Z3_set_error_handler(m_ctx, 0); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } From f79cd8f0bc58e118b1af1f7a6048d063dac97d85 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 13 Oct 2017 10:58:42 -0700 Subject: [PATCH 080/148] unused variables Signed-off-by: Nikolaj Bjorner --- src/muz/spacer/spacer_qe_project.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/muz/spacer/spacer_qe_project.cpp b/src/muz/spacer/spacer_qe_project.cpp index dd6472224..9f4f4e4fe 100644 --- a/src/muz/spacer/spacer_qe_project.cpp +++ b/src/muz/spacer/spacer_qe_project.cpp @@ -66,7 +66,6 @@ class peq { app_ref m_peq; // partial equality application app_ref m_eq; // equivalent std equality using def. of partial eq array_util m_arr_u; - ast_eq_proc m_eq_proc; // for checking if two asts are equal public: static const char* PARTIAL_EQ; @@ -102,7 +101,7 @@ peq::peq (app* p, ast_manager& m): VERIFY (is_partial_eq (p)); SASSERT (m_arr_u.is_array (m_lhs) && m_arr_u.is_array (m_rhs) && - m_eq_proc (m.get_sort (m_lhs), m.get_sort (m_rhs))); + ast_eq_proc() (m.get_sort (m_lhs), m.get_sort (m_rhs))); for (unsigned i = 2; i < p->get_num_args (); i++) { m_diff_indices.push_back (p->get_arg (i)); } @@ -121,7 +120,7 @@ peq::peq (expr* lhs, expr* rhs, unsigned num_indices, expr * const * diff_indice { SASSERT (m_arr_u.is_array (lhs) && m_arr_u.is_array (rhs) && - m_eq_proc (m.get_sort (lhs), m.get_sort (rhs))); + ast_eq_proc() (m.get_sort (lhs), m.get_sort (rhs))); ptr_vector sorts; sorts.push_back (m.get_sort (m_lhs)); sorts.push_back (m.get_sort (m_rhs)); From 7f8a7c3d83011dd1d0571b2c4d954b3434f60901 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 14 Oct 2017 11:59:09 -0700 Subject: [PATCH 081/148] fix the fixme of #1307 Signed-off-by: Nikolaj Bjorner --- src/test/main.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/main.cpp b/src/test/main.cpp index cdc06404c..98be722e3 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -10,9 +10,7 @@ #include "util/memory_manager.h" #include "util/gparams.h" -static void tst_exit_all_tests() { - exit(0); -} + // // Unit tests fail by asserting. // If they return, we assume the unit test succeeds @@ -210,7 +208,7 @@ int main(int argc, char ** argv) { TST(prime_generator); TST(permutation); TST(nlsat); - TST(exit_all_tests); + if (test_all) return 0; TST(ext_numeral); TST(interval); TST(f2n); From 4d1acadabbe9f218681bb8c6228dc97dd337cf9b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2017 09:56:21 -0700 Subject: [PATCH 082/148] fix leaks reported in #1309 Signed-off-by: Nikolaj Bjorner --- src/sat/sat_simplifier.cpp | 5 +---- src/smt/theory_str.cpp | 5 +++++ src/smt/theory_str.h | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 67101b4d8..84534bf3f 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -1311,7 +1311,6 @@ namespace sat { clause_use_list & neg_occs = m_use_list.get(neg_l); unsigned num_pos = pos_occs.size() + num_bin_pos; unsigned num_neg = neg_occs.size() + num_bin_neg; - m_elim_counter -= num_pos + num_neg; TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << "\n";); @@ -1352,8 +1351,6 @@ namespace sat { collect_clauses(pos_l, m_pos_cls); collect_clauses(neg_l, m_neg_cls); - m_elim_counter -= num_pos * num_neg + before_lits; - TRACE("resolution_detail", tout << "collecting number of after_clauses\n";); unsigned before_clauses = num_pos + num_neg; unsigned after_clauses = 0; @@ -1376,7 +1373,7 @@ namespace sat { } } TRACE("resolution", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); - + m_elim_counter -= num_pos * num_neg + before_lits; // eliminate variable model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 8a141665c..97c8db125 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -315,6 +315,7 @@ namespace smt { m_trail.push_back(node); if (!cut_var_map.contains(baseNode)) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; varInfo->vars[node] = 1; cut_var_map.insert(baseNode, std::stack()); @@ -323,6 +324,7 @@ namespace smt { } else { if (cut_var_map[baseNode].empty()) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; varInfo->vars[node] = 1; cut_var_map[baseNode].push(varInfo); @@ -330,6 +332,7 @@ namespace smt { } else { if (cut_var_map[baseNode].top()->level < slevel) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[baseNode].top()->vars); varInfo->vars[node] = 1; @@ -359,6 +362,7 @@ namespace smt { if (!cut_var_map.contains(destNode)) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[srcNode].top()->vars); cut_var_map.insert(destNode, std::stack()); @@ -367,6 +371,7 @@ namespace smt { } else { if (cut_var_map[destNode].empty() || cut_var_map[destNode].top()->level < slevel) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[destNode].top()->vars); cut_vars_map_copy(varInfo->vars, cut_var_map[srcNode].top()->vars); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 686fcdd57..acac8cad1 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -18,9 +18,12 @@ #define _THEORY_STR_H_ #include "util/trail.h" +#include "util/union_find.h" +#include "util/scoped_ptr_vector.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" +#include "ast/seq_decl_plugin.h" #include "smt/smt_theory.h" #include "smt/params/theory_str_params.h" #include "smt/proto_model/value_factory.h" @@ -29,8 +32,6 @@ #include #include #include -#include "ast/seq_decl_plugin.h" -#include "util/union_find.h" namespace smt { @@ -292,6 +293,7 @@ protected: bool avoidLoopCut; bool loopDetected; obj_map > cut_var_map; + scoped_ptr_vector m_cut_allocs; expr_ref m_theoryStrOverlapAssumption_term; obj_hashtable variable_set; From 9b54b4e7848ff8f1df54378d5091867c37c16023 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 10 Oct 2017 17:24:22 +0100 Subject: [PATCH 083/148] fix vector<> to support non-POD types adjust code to std::move and avoid unnecessary/illegal --- src/ast/ast.cpp | 2 +- src/ast/ast.h | 14 +++ src/ast/pattern/expr_pattern_match.cpp | 6 +- .../bit_blaster/bit_blaster_tpl_def.h | 2 +- src/ast/substitution/substitution_tree.cpp | 4 +- src/ast/used_vars.cpp | 2 +- src/math/polynomial/algebraic_numbers.cpp | 12 +- src/math/polynomial/polynomial.cpp | 6 +- .../upolynomial_factorization_int.h | 2 +- src/math/simplex/sparse_matrix.h | 4 +- src/math/subpaving/subpaving_t_def.h | 2 +- src/model/func_interp.cpp | 2 +- src/muz/rel/dl_instruction.h | 2 +- src/muz/rel/dl_mk_explanations.cpp | 2 +- src/qe/qe.cpp | 2 +- src/smt/mam.cpp | 2 +- src/smt/smt_context.cpp | 2 +- src/smt/theory_array_base.cpp | 4 +- src/smt/theory_datatype.cpp | 2 +- src/smt/theory_dense_diff_logic_def.h | 9 +- src/smt/theory_diff_logic_def.h | 9 +- src/smt/theory_pb.cpp | 3 +- src/smt/theory_pb.h | 1 - src/tactic/sls/bvsls_opt_engine.cpp | 2 +- src/tactic/sls/bvsls_opt_engine.h | 2 +- src/tactic/sls/sls_tracker.h | 37 ++++--- src/util/buffer.h | 7 ++ src/util/hashtable.h | 58 ++++++++-- src/util/mpq.h | 9 +- src/util/mpz.h | 15 +++ src/util/obj_hashtable.h | 10 +- src/util/ref_vector.h | 4 +- src/util/scoped_numeral_vector.h | 3 +- src/util/util.h | 2 +- src/util/vector.h | 103 ++++++++++++++---- 35 files changed, 253 insertions(+), 95 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index cac12413e..9116c5d95 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -768,7 +768,7 @@ func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, ptr_vector & cache) { if (num_parents >= cache.size()) { - cache.resize(num_parents+1, 0); + cache.resize(num_parents+1); } if (cache[num_parents] == 0) { cache[num_parents] = mk_proof_decl(name, k, num_parents); diff --git a/src/ast/ast.h b/src/ast/ast.h index 4eb43d30b..54f8f5e62 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -121,6 +121,20 @@ public: explicit parameter(unsigned ext_id, bool):m_kind(PARAM_EXTERNAL), m_ext_id(ext_id) {} parameter(parameter const&); + parameter(parameter && other) noexcept : m_kind(other.m_kind) { + switch (other.m_kind) { + case PARAM_INT: m_int = other.get_int(); break; + case PARAM_AST: m_ast = other.get_ast(); break; + case PARAM_SYMBOL: m_symbol = other.m_symbol; break; + case PARAM_RATIONAL: m_rational = 0; std::swap(m_rational, other.m_rational); break; + case PARAM_DOUBLE: m_dval = other.m_dval; break; + case PARAM_EXTERNAL: m_ext_id = other.m_ext_id; break; + default: + UNREACHABLE(); + break; + } + } + ~parameter(); parameter& operator=(parameter const& other); diff --git a/src/ast/pattern/expr_pattern_match.cpp b/src/ast/pattern/expr_pattern_match.cpp index 770832d1f..628c777d3 100644 --- a/src/ast/pattern/expr_pattern_match.cpp +++ b/src/ast/pattern/expr_pattern_match.cpp @@ -179,11 +179,11 @@ expr_pattern_match::compile(expr* q) } if (m_regs.size() <= max_reg) { - m_regs.resize(max_reg+1, 0); + m_regs.resize(max_reg+1); } if (m_bound_dom.size() <= num_bound) { - m_bound_dom.resize(num_bound+1, 0); - m_bound_rng.resize(num_bound+1, 0); + m_bound_dom.resize(num_bound+1); + m_bound_rng.resize(num_bound+1); } instr.m_kind = YIELD; diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index cd66a5124..a80994f6c 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -272,7 +272,7 @@ void bit_blaster_tpl::mk_multiplier(unsigned sz, expr * const * a_bits, exp zero = m().mk_false(); vector< expr_ref_vector > pps; - pps.resize(sz, m()); + pps.resize(sz, expr_ref_vector(m())); for (unsigned i = 0; i < sz; i++) { checkpoint(); diff --git a/src/ast/substitution/substitution_tree.cpp b/src/ast/substitution/substitution_tree.cpp index 9befb582f..20d5f1590 100644 --- a/src/ast/substitution/substitution_tree.cpp +++ b/src/ast/substitution/substitution_tree.cpp @@ -256,7 +256,7 @@ void substitution_tree::insert(expr * new_expr) { sort * s = to_var(new_expr)->get_sort(); unsigned id = s->get_decl_id(); if (id >= m_vars.size()) - m_vars.resize(id+1, 0); + m_vars.resize(id+1); if (m_vars[id] == 0) m_vars[id] = alloc(var_ref_vector, m_manager); var_ref_vector * v = m_vars[id]; @@ -277,7 +277,7 @@ void substitution_tree::insert(app * new_expr) { unsigned id = d->get_decl_id(); if (id >= m_roots.size()) - m_roots.resize(id+1, 0); + m_roots.resize(id+1); if (!m_roots[id]) { // there is no tree for the function symbol heading new_expr diff --git a/src/ast/used_vars.cpp b/src/ast/used_vars.cpp index a1cd65feb..a3030f087 100644 --- a/src/ast/used_vars.cpp +++ b/src/ast/used_vars.cpp @@ -58,7 +58,7 @@ void used_vars::process(expr * n, unsigned delta) { if (idx >= delta) { idx = idx - delta; if (idx >= m_found_vars.size()) - m_found_vars.resize(idx + 1, 0); + m_found_vars.resize(idx + 1); m_found_vars[idx] = to_var(n)->get_sort(); } break; diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index 22b50326b..9484c3c83 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -2632,10 +2632,14 @@ namespace algebraic_numbers { scoped_mpz neg_n(qm()); qm().set(neg_n, v.numerator()); qm().neg(neg_n); - mpz const coeffs[2] = { neg_n.get(), v.denominator() }; + unsynch_mpz_manager zmgr; + // FIXME: remove these copies + mpz coeffs[2] = { zmgr.dup(neg_n.get()), zmgr.dup(v.denominator()) }; out << "("; upm().display(out, 2, coeffs, "#"); out << ", 1)"; // first root of the polynomial d*# - n + zmgr.del(coeffs[0]); + zmgr.del(coeffs[1]); } else { algebraic_cell * c = a.to_algebraic(); @@ -2678,10 +2682,14 @@ namespace algebraic_numbers { scoped_mpz neg_n(qm()); qm().set(neg_n, v.numerator()); qm().neg(neg_n); - mpz const coeffs[2] = { neg_n.get(), v.denominator() }; + unsynch_mpz_manager zmgr; + // FIXME: remove these copies + mpz coeffs[2] = { zmgr.dup(neg_n.get()), zmgr.dup(v.denominator()) }; out << "(root-obj "; upm().display_smt2(out, 2, coeffs, "x"); out << " 1)"; // first root of the polynomial d*# - n + zmgr.del(coeffs[0]); + zmgr.del(coeffs[1]); } else { algebraic_cell * c = a.to_algebraic(); diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 7adaf9adb..d6d392148 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -4822,10 +4822,9 @@ namespace polynomial { polynomial * mk_x_minus_y(var x, var y) { numeral zero(0); - numeral one(1); numeral minus_one; // It is not safe to initialize with -1 when numeral_manager is GF_2 m_manager.set(minus_one, -1); - numeral as[2] = { one, minus_one }; + numeral as[2] = { numeral(1), std::move(minus_one) }; var xs[2] = { x, y }; return mk_linear(2, as, xs, zero); } @@ -4845,8 +4844,7 @@ namespace polynomial { polynomial * mk_x_plus_y(var x, var y) { numeral zero(0); - numeral one(1); - numeral as[2] = { one, one }; + numeral as[2] = { numeral(1), numeral(1) }; var xs[2] = { x, y }; return mk_linear(2, as, xs, zero); } diff --git a/src/math/polynomial/upolynomial_factorization_int.h b/src/math/polynomial/upolynomial_factorization_int.h index 640c5ad5c..ea2b51d8f 100644 --- a/src/math/polynomial/upolynomial_factorization_int.h +++ b/src/math/polynomial/upolynomial_factorization_int.h @@ -45,7 +45,7 @@ namespace upolynomial { for (unsigned i = 0; i < p.size(); ++ i) { numeral p_i; // no need to delete, we keep it pushed in zp_p zp_nm.set(p_i, p[i]); - zp_p.push_back(p_i); + zp_p.push_back(std::move(p_i)); } zp_upm.trim(zp_p); } diff --git a/src/math/simplex/sparse_matrix.h b/src/math/simplex/sparse_matrix.h index dc4ce8695..63bbe9e78 100644 --- a/src/math/simplex/sparse_matrix.h +++ b/src/math/simplex/sparse_matrix.h @@ -35,7 +35,7 @@ namespace simplex { struct row_entry { numeral m_coeff; var_t m_var; - row_entry(numeral const& c, var_t v): m_coeff(c), m_var(v) {} + row_entry(numeral && c, var_t v) noexcept : m_coeff(std::move(c)), m_var(v) {} }; private: @@ -61,7 +61,7 @@ namespace simplex { int m_col_idx; int m_next_free_row_entry_idx; }; - _row_entry(numeral const & c, var_t v): row_entry(c, v), m_col_idx(0) {} + _row_entry(numeral && c, var_t v) : row_entry(std::move(c), v), m_col_idx(0) {} _row_entry() : row_entry(numeral(), dead_id), m_col_idx(0) {} bool is_dead() const { return row_entry::m_var == dead_id; } }; diff --git a/src/math/subpaving/subpaving_t_def.h b/src/math/subpaving/subpaving_t_def.h index 108b6aac3..3574787d8 100644 --- a/src/math/subpaving/subpaving_t_def.h +++ b/src/math/subpaving/subpaving_t_def.h @@ -739,7 +739,7 @@ void context_t::del_sum(polynomial * p) { template var context_t::mk_sum(numeral const & c, unsigned sz, numeral const * as, var const * xs) { - m_num_buffer.reserve(num_vars(), numeral()); + m_num_buffer.reserve(num_vars()); for (unsigned i = 0; i < sz; i++) { SASSERT(xs[i] < num_vars()); nm().set(m_num_buffer[xs[i]], as[i]); diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 6699ef0e2..e458cc4b0 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -117,7 +117,7 @@ bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity))) return false; - args.resize(m_arity, 0); + args.resize(m_arity); for (unsigned i = 0; i < m_arity; i++) { expr * ci = (m_arity == 1 && i == 0) ? c : to_app(c)->get_arg(i); diff --git a/src/muz/rel/dl_instruction.h b/src/muz/rel/dl_instruction.h index c29681f37..56dd249a5 100644 --- a/src/muz/rel/dl_instruction.h +++ b/src/muz/rel/dl_instruction.h @@ -128,7 +128,7 @@ namespace datalog { void set_reg(reg_idx i, reg_type val) { if (i >= m_registers.size()) { check_overflow(i); - m_registers.resize(i+1,0); + m_registers.resize(i+1); } if (m_registers[i]) { m_registers[i]->deallocate(); diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp index fd13978d2..c4fb57eeb 100644 --- a/src/muz/rel/dl_mk_explanations.cpp +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -465,7 +465,7 @@ namespace datalog { unsigned sz = r.get_signature().size(); ptr_vector subst_arg; - subst_arg.resize(sz, 0); + subst_arg.resize(sz); unsigned ofs = sz-1; for (unsigned i=0; iget_family_id(); SASSERT(fid != null_family_id); if (static_cast(m_plugins.size()) <= fid) { - m_plugins.resize(fid+1,0); + m_plugins.resize(fid+1); } SASSERT(!m_plugins[fid]); m_plugins[fid] = p; diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 497aab8db..96334e3ce 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -1999,7 +1999,7 @@ namespace smt { m_ast_manager(ctx.get_manager()), m_mam(m), m_use_filters(use_filters) { - m_args.resize(INIT_ARGS_SIZE, 0); + m_args.resize(INIT_ARGS_SIZE); } ~interpreter() { diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index ac7b1f44b..48d02da26 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1286,7 +1286,7 @@ namespace smt { else { if (depth >= m_almost_cg_tables.size()) { unsigned old_sz = m_almost_cg_tables.size(); - m_almost_cg_tables.resize(depth+1, 0); + m_almost_cg_tables.resize(depth+1); for (unsigned i = old_sz; i < depth + 1; i++) m_almost_cg_tables[i] = alloc(almost_cg_table); } diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 1f519dfda..21df02c76 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -617,8 +617,8 @@ namespace smt { m_else_values.reset(); m_parents.reset(); m_parents.resize(num_vars, -1); - m_defaults.resize(num_vars, 0); - m_else_values.resize(num_vars, 0); + m_defaults.resize(num_vars); + m_else_values.resize(num_vars); if (m_use_unspecified_default) return; diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index 616314117..bbed6840d 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -620,7 +620,7 @@ namespace smt { sort * s = recognizer->get_decl()->get_domain(0); if (d->m_recognizers.empty()) { SASSERT(m_util.is_datatype(s)); - d->m_recognizers.resize(m_util.get_datatype_num_constructors(s), 0); + d->m_recognizers.resize(m_util.get_datatype_num_constructors(s)); } SASSERT(d->m_recognizers.size() == m_util.get_datatype_num_constructors(s)); unsigned c_idx = m_util.get_recognizer_constructor_idx(recognizer->get_decl()); diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 064bdd433..78fb4d03d 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -914,6 +914,8 @@ namespace smt { } verbose_stream() << " + " << m_objective_consts[v] << "\n";); + unsynch_mpq_manager mgr; + unsynch_mpq_inf_manager inf_mgr; unsigned num_nodes = get_num_vars(); unsigned num_edges = m_edges.size(); S.ensure_var(num_nodes + num_edges + m_objectives.size()); @@ -921,8 +923,9 @@ namespace smt { numeral const& a = m_assignment[i]; rational fin = a.get_rational().to_rational(); rational inf = a.get_infinitesimal().to_rational(); - mpq_inf q(fin.to_mpq(), inf.to_mpq()); + mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_value(i, q); + inf_mgr.del(q); } for (unsigned i = 0; i < num_nodes; ++i) { enode * n = get_enode(i); @@ -933,7 +936,6 @@ namespace smt { } } svector vars; - unsynch_mpq_manager mgr; scoped_mpq_vector coeffs(mgr); coeffs.push_back(mpq(1)); coeffs.push_back(mpq(-1)); @@ -954,8 +956,9 @@ namespace smt { numeral const& w = e.m_offset; rational fin = w.get_rational().to_rational(); rational inf = w.get_infinitesimal().to_rational(); - mpq_inf q(fin.to_mpq(),inf.to_mpq()); + mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_upper(base_var, q); + inf_mgr.del(q); } unsigned w = num_nodes + num_edges + v; diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index a7153234c..203dd24d2 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -1107,6 +1107,8 @@ unsigned theory_diff_logic::simplex2edge(unsigned e) { template void theory_diff_logic::update_simplex(Simplex& S) { + unsynch_mpq_manager mgr; + unsynch_mpq_inf_manager inf_mgr; unsigned num_nodes = m_graph.get_num_nodes(); vector > const& es = m_graph.get_all_edges(); S.ensure_var(num_simplex_vars()); @@ -1114,13 +1116,13 @@ void theory_diff_logic::update_simplex(Simplex& S) { numeral const& a = m_graph.get_assignment(i); rational fin = a.get_rational().to_rational(); rational inf = a.get_infinitesimal().to_rational(); - mpq_inf q(fin.to_mpq(), inf.to_mpq()); + mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_value(node2simplex(i), q); + inf_mgr.del(q); } S.set_lower(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); S.set_upper(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); svector vars; - unsynch_mpq_manager mgr; scoped_mpq_vector coeffs(mgr); coeffs.push_back(mpq(1)); coeffs.push_back(mpq(-1)); @@ -1145,8 +1147,9 @@ void theory_diff_logic::update_simplex(Simplex& S) { numeral const& w = e.get_weight(); rational fin = w.get_rational().to_rational(); rational inf = w.get_infinitesimal().to_rational(); - mpq_inf q(fin.to_mpq(),inf.to_mpq()); + mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_upper(base_var, q); + inf_mgr.del(q); } else { S.unset_upper(base_var); diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 4b52d7950..9d2059f55 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -806,8 +806,9 @@ namespace smt { if (c != 0) { if (m_enable_simplex) { row_info const& info = m_ineq_row_info.find(v); + unsynch_mpq_manager mgr; scoped_eps_numeral coeff(m_mpq_inf_mgr); - coeff = std::make_pair(info.m_bound.to_mpq(), mpq(0)); + coeff = std::make_pair(mgr.dup(info.m_bound.to_mpq()), mpq(0)); unsigned slack = info.m_slack; if (is_true) { update_bound(slack, literal(v), true, coeff); diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index 7f530e5b0..662378bdf 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -279,7 +279,6 @@ namespace smt { // void compile_ineq(ineq& c); void inc_propagations(ineq& c); - unsigned get_compilation_threshold(ineq& c); // // Conflict resolution, cutting plane derivation. diff --git a/src/tactic/sls/bvsls_opt_engine.cpp b/src/tactic/sls/bvsls_opt_engine.cpp index e8547fdfd..502bcbde6 100644 --- a/src/tactic/sls/bvsls_opt_engine.cpp +++ b/src/tactic/sls/bvsls_opt_engine.cpp @@ -238,7 +238,7 @@ bool bvsls_opt_engine::what_if( mpz bvsls_opt_engine::find_best_move( ptr_vector & to_evaluate, - mpz score, + mpz & score, unsigned & best_const, mpz & best_value, unsigned & new_bit, diff --git a/src/tactic/sls/bvsls_opt_engine.h b/src/tactic/sls/bvsls_opt_engine.h index 67d9a5d02..9487130d3 100644 --- a/src/tactic/sls/bvsls_opt_engine.h +++ b/src/tactic/sls/bvsls_opt_engine.h @@ -61,7 +61,7 @@ protected: bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, mpz & best_score, unsigned & best_const, mpz & best_value); - mpz find_best_move(ptr_vector & to_evaluate, mpz score, + mpz find_best_move(ptr_vector & to_evaluate, mpz & score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move, mpz const & max_score, expr * objective); diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index 4ad5c65f4..b4969f433 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -41,7 +41,20 @@ class sls_tracker { struct value_score { value_score() : m(0), value(unsynch_mpz_manager::mk_z(0)), score(0.0), score_prune(0.0), has_pos_occ(0), has_neg_occ(0), distance(0), touched(1) {}; + value_score(value_score && other) : + m(other.m), + value(std::move(other.value)), + score(other.score), + score_prune(other.score_prune), + has_pos_occ(other.has_pos_occ), + has_neg_occ(other.has_neg_occ), + distance(other.distance), + touched(other.touched) {} ~value_score() { if (m) m->del(value); } + void operator=(value_score && other) { + this->~value_score(); + new (this) value_score(std::move(other)); + } unsynch_mpz_manager * m; mpz value; double score; @@ -50,15 +63,6 @@ class sls_tracker { unsigned has_neg_occ; unsigned distance; // max distance from any root unsigned touched; - value_score & operator=(const value_score & other) { - SASSERT(m == 0 || m == other.m); - if (m) m->set(value, 0); else m = other.m; - m->set(value, other.value); - score = other.score; - distance = other.distance; - touched = other.touched; - return *this; - } }; public: @@ -294,7 +298,7 @@ public: if (!m_scores.contains(n)) { value_score vs; vs.m = & m_mpz_manager; - m_scores.insert(n, vs); + m_scores.insert(n, std::move(vs)); } // Update uplinks @@ -539,7 +543,7 @@ public: rational r_val; unsigned bv_sz; m_bv_util.is_numeral(val, r_val, bv_sz); - mpq q = r_val.to_mpq(); + const mpq& q = r_val.to_mpq(); SASSERT(m_mpz_manager.is_one(q.denominator())); set_value(fd, q.numerator()); } @@ -630,7 +634,7 @@ public: if (m_bv_util.is_bv_sort(s)) return get_random_bv(s); else if (m_manager.is_bool(s)) - return get_random_bool(); + return m_mpz_manager.dup(get_random_bool()); else NOT_IMPLEMENTED_YET(); // This only works for bit-vectors for now. } @@ -653,9 +657,7 @@ public: TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { - mpz temp = m_zero; - set_value(it->m_value, temp); - m_mpz_manager.del(temp); + set_value(it->m_value, m_zero); } } @@ -931,7 +933,7 @@ public: rational q; if (!m_bv_util.is_numeral(n, q, bv_sz)) NOT_IMPLEMENTED_YET(); - mpq temp = q.to_mpq(); + const mpq& temp = q.to_mpq(); SASSERT(m_mpz_manager.is_one(temp.denominator())); m_mpz_manager.set(result, temp.numerator()); } @@ -1039,7 +1041,6 @@ public: unsigned pos = -1; if (m_ucb) { - value_score vscore; double max = -1.0; // Andreas: Commented things here might be used for track_unsat data structures as done in SLS for SAT. But seems to have no benefit. /* for (unsigned i = 0; i < m_where_false.size(); i++) { @@ -1048,7 +1049,7 @@ public: expr * e = as[i]; if (m_mpz_manager.neq(get_value(e), m_one)) { - vscore = m_scores.find(e); + value_score & vscore = m_scores.find(e); // Andreas: Select the assertion with the greatest ucb score. Potentially add some noise. // double q = vscore.score + m_ucb_constant * sqrt(log((double)m_touched) / vscore.touched); double q = vscore.score + m_ucb_constant * sqrt(log((double)m_touched) / vscore.touched) + m_ucb_noise * get_random_uint(8); diff --git a/src/util/buffer.h b/src/util/buffer.h index 503788fa0..c64e23d8b 100644 --- a/src/util/buffer.h +++ b/src/util/buffer.h @@ -149,6 +149,13 @@ public: new (m_buffer + m_pos) T(elem); m_pos++; } + + void push_back(T && elem) { + if (m_pos >= m_capacity) + expand(); + new (m_buffer + m_pos) T(std::move(elem)); + m_pos++; + } void pop_back() { if (CallDestructors) { diff --git a/src/util/hashtable.h b/src/util/hashtable.h index 020c47b2b..fa9fef180 100644 --- a/src/util/hashtable.h +++ b/src/util/hashtable.h @@ -54,7 +54,7 @@ public: bool is_used() const { return m_state == HT_USED; } T & get_data() { return m_data; } const T & get_data() const { return m_data; } - void set_data(const T & d) { m_data = d; m_state = HT_USED; } + void set_data(T && d) { m_data = std::move(d); m_state = HT_USED; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_state = HT_DELETED; } void mark_as_free() { m_state = HT_FREE; } @@ -187,10 +187,42 @@ protected: } } + static void move_table(entry * source, unsigned source_capacity, entry * target, unsigned target_capacity) { + SASSERT(target_capacity >= source_capacity); + unsigned target_mask = target_capacity - 1; + entry * source_end = source + source_capacity; + entry * target_end = target + target_capacity; + for (entry * source_curr = source; source_curr != source_end; ++source_curr) { + if (source_curr->is_used()) { + unsigned hash = source_curr->get_hash(); + unsigned idx = hash & target_mask; + entry * target_begin = target + idx; + entry * target_curr = target_begin; + for (; target_curr != target_end; ++target_curr) { + SASSERT(!target_curr->is_deleted()); + if (target_curr->is_free()) { + *target_curr = std::move(*source_curr); + goto end; + } + } + for (target_curr = target; target_curr != target_begin; ++target_curr) { + SASSERT(!target_curr->is_deleted()); + if (target_curr->is_free()) { + *target_curr = std::move(*source_curr); + goto end; + } + } + UNREACHABLE(); + end: + ; + } + } + } + void expand_table() { unsigned new_capacity = m_capacity << 1; entry * new_table = alloc_table(new_capacity); - copy_table(m_table, m_capacity, new_table, new_capacity); + move_table(m_table, m_capacity, new_table, new_capacity); delete_table(); m_table = new_table; m_capacity = new_capacity; @@ -202,7 +234,7 @@ protected: if (memory::is_out_of_memory()) return; entry * new_table = alloc_table(m_capacity); - copy_table(m_table, m_capacity, new_table, m_capacity); + move_table(m_table, m_capacity, new_table, m_capacity); delete_table(); m_table = new_table; m_num_deleted = 0; @@ -321,7 +353,7 @@ public: #define INSERT_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ - curr->set_data(e); \ + curr->set_data(std::move(e)); \ return; \ } \ HS_CODE(m_st_collision++;); \ @@ -330,7 +362,7 @@ public: entry * new_entry; \ if (del_entry) { new_entry = del_entry; m_num_deleted--; } \ else { new_entry = curr; } \ - new_entry->set_data(e); \ + new_entry->set_data(std::move(e)); \ new_entry->set_hash(hash); \ m_size++; \ return; \ @@ -342,7 +374,7 @@ public: } \ } ((void) 0) - void insert(data const & e) { + void insert(data && e) { if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { expand_table(); @@ -363,6 +395,11 @@ public: UNREACHABLE(); } + void insert(const data & e) { + data tmp(e); + insert(std::move(tmp)); + } + #define INSERT_LOOP_CORE_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ @@ -375,7 +412,7 @@ public: entry * new_entry; \ if (del_entry) { new_entry = del_entry; m_num_deleted--; } \ else { new_entry = curr; } \ - new_entry->set_data(e); \ + new_entry->set_data(std::move(e)); \ new_entry->set_hash(hash); \ m_size++; \ et = new_entry; \ @@ -393,7 +430,7 @@ public: Return true if it is a new element, and false otherwise. Store the entry/slot of the table in et. */ - bool insert_if_not_there_core(data const & e, entry * & et) { + bool insert_if_not_there_core(data && e, entry * & et) { if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { expand_table(); @@ -415,6 +452,11 @@ public: return 0; } + bool insert_if_not_there_core(const data & e, entry * & et) { + data temp(e); + return insert_if_not_there_core(std::move(temp), et); + } + /** \brief Insert the element e if it is not in the table. Return a reference to e or to an object identical to e diff --git a/src/util/mpq.h b/src/util/mpq.h index 5aa3ca083..b34e9afae 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -31,11 +31,10 @@ class mpq { public: mpq(int v):m_num(v), m_den(1) {} mpq():m_den(1) {} + mpq(mpq && other) noexcept : m_num(std::move(other.m_num)), m_den(std::move(other.m_den)) {} void swap(mpq & other) { m_num.swap(other.m_num); m_den.swap(other.m_den); } mpz const & numerator() const { return m_num; } mpz const & denominator() const { return m_den; } - - double get_double() const; }; inline void swap(mpq & m1, mpq & m2) { m1.swap(m2); } @@ -745,6 +744,12 @@ public: reset_denominator(a); } + mpq dup(const mpq & source) { + mpq temp; + set(temp, source); + return temp; + } + void swap(mpz & a, mpz & b) { mpz_manager::swap(a, b); } void swap(mpq & a, mpq & b) { diff --git a/src/util/mpz.h b/src/util/mpz.h index 7001b9a42..bdfbc8061 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -94,6 +94,9 @@ class mpz { public: mpz(int v):m_val(v), m_ptr(0) {} mpz():m_val(0), m_ptr(0) {} + mpz(mpz && other) noexcept : m_val(other.m_val), m_ptr(0) { + std::swap(m_val, other.m_val); + } void swap(mpz & other) { std::swap(m_val, other.m_val); std::swap(m_ptr, other.m_ptr); @@ -668,6 +671,12 @@ public: } } + void set(mpz & target, mpz && source) { + del(target); + target.m_val = source.m_val; + std::swap(target.m_ptr, source.m_ptr); + } + void set(mpz & a, int val) { del(a); a.m_val = val; @@ -700,6 +709,12 @@ public: void set(mpz & target, unsigned sz, digit_t const * digits); + mpz dup(const mpz & source) { + mpz temp; + set(temp, source); + return temp; + } + void reset(mpz & a) { del(a); a.m_val = 0; diff --git a/src/util/obj_hashtable.h b/src/util/obj_hashtable.h index 189d1e1a0..df279383b 100644 --- a/src/util/obj_hashtable.h +++ b/src/util/obj_hashtable.h @@ -69,6 +69,10 @@ public: m_key(k), m_value(v) { } + key_data(Key * k, Value && v) : + m_key(k), + m_value(std::move(v)) { + } Value const & get_value() const { return m_value; } Key & get_key () const { return *m_key; } unsigned hash() const { return m_key->hash(); } @@ -86,7 +90,7 @@ public: bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } - void set_data(key_data const & d) { m_data = d; } + void set_data(key_data && d) { m_data = std::move(d); } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } void mark_as_free() { m_data.m_key = 0; } @@ -137,6 +141,10 @@ public: void insert(Key * const k, Value const & v) { m_table.insert(key_data(k, v)); } + + void insert(Key * const k, Value && v) { + m_table.insert(key_data(k, std::move(v))); + } key_data const & insert_if_not_there(Key * k, Value const & v) { return m_table.insert_if_not_there(key_data(k, v)); diff --git a/src/util/ref_vector.h b/src/util/ref_vector.h index 469183b76..9b7657fb2 100644 --- a/src/util/ref_vector.h +++ b/src/util/ref_vector.h @@ -63,7 +63,7 @@ public: void resize(unsigned sz) { if (sz < m_nodes.size()) dec_range_ref(m_nodes.begin() + sz, m_nodes.end()); - m_nodes.resize(sz, 0); + m_nodes.resize(sz); } void resize(unsigned sz, T * d) { @@ -80,7 +80,7 @@ public: void reserve(unsigned sz) { if (sz <= m_nodes.size()) return; - m_nodes.resize(sz, 0); + m_nodes.resize(sz); } void shrink(unsigned sz) { diff --git a/src/util/scoped_numeral_vector.h b/src/util/scoped_numeral_vector.h index fdf63bf35..cb9a6b4fd 100644 --- a/src/util/scoped_numeral_vector.h +++ b/src/util/scoped_numeral_vector.h @@ -63,8 +63,7 @@ public: unsigned old_sz = this->size(); if (sz <= old_sz) shrink(sz); - typename Manager::numeral zero(0); - svector::resize(sz, zero); + svector::resize(sz, 0); } }; diff --git a/src/util/util.h b/src/util/util.h index 1f753099c..f8c464197 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -322,7 +322,7 @@ bool compare_arrays(const T * array1, const T * array2, unsigned size) { template void force_ptr_array_size(T & v, unsigned sz) { if (sz > v.size()) { - v.resize(sz, 0); + v.resize(sz); } } diff --git a/src/util/vector.h b/src/util/vector.h index 2d499a900..c21d33bab 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -26,6 +26,7 @@ Revision History: #include "util/debug.h" #include +#include #include #include "util/memory_manager.h" #include "util/hash.h" @@ -74,9 +75,24 @@ class vector { if (new_capacity <= old_capacity || new_capacity_T <= old_capacity_T) { throw default_exception("Overflow encountered when expanding vector"); } - SZ *mem = (SZ*)memory::reallocate(reinterpret_cast(m_data)-2, new_capacity_T); + SZ *mem, *old_mem = reinterpret_cast(m_data) - 2; + if (std::is_trivially_copyable::value) { + mem = (SZ*)memory::reallocate(old_mem, new_capacity_T); + } else { + mem = (SZ*)memory::allocate(new_capacity_T); + } + auto old_data = m_data; + auto old_size = size(); *mem = new_capacity; m_data = reinterpret_cast(mem + 2); + if (!std::is_trivially_copyable::value) { + static_assert(std::is_move_constructible::value, ""); + int i = 0; + for (auto I = old_data; I != old_data + old_size; ++I) { + new (&m_data[i++]) T(std::move(*I)); + } + memory::deallocate(old_mem); + } } } @@ -148,6 +164,10 @@ public: SASSERT(size() == source.size()); } + vector(vector&& other) noexcept : m_data(0) { + std::swap(m_data, other.m_data); + } + vector(SZ s, T const * data): m_data(0) { for (SZ i = 0; i < s; i++) { @@ -179,6 +199,16 @@ public: return *this; } + vector & operator=(vector && source) { + if (this == &source) { + return *this; + } + destroy(); + m_data = 0; + std::swap(m_data, source.m_data); + return *this; + } + void reset() { if (m_data) { if (CallDestructors) { @@ -292,6 +322,11 @@ public: m_data[idx] = val; } + void set(SZ idx, T && val) { + SASSERT(idx < size()); + m_data[idx] = std::move(val); + } + T & back() { SASSERT(!empty()); return operator[](size() - 1); @@ -318,6 +353,14 @@ public: reinterpret_cast(m_data)[SIZE_IDX]++; } + void push_back(T && elem) { + if (m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { + expand_vector(); + } + new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(std::move(elem)); + reinterpret_cast(m_data)[SIZE_IDX]++; + } + void insert(T const & elem) { push_back(elem); } @@ -357,7 +400,8 @@ public: } } - void resize(SZ s, T const & elem=T()) { + template + void resize(SZ s, Args args...) { SZ sz = size(); if (s <= sz) { shrink(s); return; } while (s > capacity()) { @@ -367,8 +411,23 @@ public: reinterpret_cast(m_data)[SIZE_IDX] = s; iterator it = m_data + sz; iterator end = m_data + s; - for(; it != end; ++it) { - new (it) T(elem); + for (; it != end; ++it) { + new (it) T(std::forward(args)); + } + } + + void resize(SZ s) { + SZ sz = size(); + if (s <= sz) { shrink(s); return; } + while (s > capacity()) { + expand_vector(); + } + SASSERT(m_data != 0); + reinterpret_cast(m_data)[SIZE_IDX] = s; + iterator it = m_data + sz; + iterator end = m_data + s; + for (; it != end; ++it) { + new (it) T(); } } @@ -439,10 +498,15 @@ public: return m_data[idx]; } - void reserve(SZ s, T const & d = T()) { + void reserve(SZ s, T const & d) { if (s > size()) resize(s, d); } + + void reserve(SZ s) { + if (s > size()) + resize(s); + } }; template @@ -452,7 +516,12 @@ public: ptr_vector(unsigned s):vector(s) {} ptr_vector(unsigned s, T * elem):vector(s, elem) {} ptr_vector(ptr_vector const & source):vector(source) {} + ptr_vector(ptr_vector && other) noexcept : vector(std::move(other)) {} ptr_vector(unsigned s, T * const * data):vector(s, const_cast(data)) {} + ptr_vector & operator=(ptr_vector const & source) { + vector::operator=(source); + return *this; + } }; template @@ -462,7 +531,12 @@ public: svector(SZ s):vector(s) {} svector(SZ s, T const & elem):vector(s, elem) {} svector(svector const & source):vector(source) {} + svector(svector && other) noexcept : vector(std::move(other)) {} svector(SZ s, T const * data):vector(s, data) {} + svector & operator=(svector const & source) { + vector::operator=(source); + return *this; + } }; typedef svector int_vector; @@ -494,23 +568,4 @@ struct vector_hash : public vector_hash_tpl > template struct svector_hash : public vector_hash_tpl > {}; -#include -// Specialize vector to be an instance of std::vector instead. -// This will catch any regression of issue #564 and #420. - -template <> -class vector : public std::vector { -public: - vector(vector const& other): std::vector(other) {} - vector(size_t sz, char const* s): std::vector(sz, s) {} - vector() {} - - void reset() { clear(); } - - -}; - - - #endif /* VECTOR_H_ */ - From 27e84c5ffcb770374de14cb09fa08a32c4d58744 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 11 Oct 2017 00:15:04 +0100 Subject: [PATCH 084/148] mpz.h: fix typo in previous commit (found by Nikolaj) --- src/util/mpz.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index bdfbc8061..9933e4485 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -95,7 +95,7 @@ public: mpz(int v):m_val(v), m_ptr(0) {} mpz():m_val(0), m_ptr(0) {} mpz(mpz && other) noexcept : m_val(other.m_val), m_ptr(0) { - std::swap(m_val, other.m_val); + std::swap(m_ptr, other.m_ptr); } void swap(mpz & other) { std::swap(m_val, other.m_val); From d30a099cd0f9d57b9c3a5850b541b785e205bdd4 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 11 Oct 2017 02:43:13 +0100 Subject: [PATCH 085/148] fix crash in vector::expand() --- src/util/vector.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/vector.h b/src/util/vector.h index c21d33bab..03c450660 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -87,6 +87,7 @@ class vector { m_data = reinterpret_cast(mem + 2); if (!std::is_trivially_copyable::value) { static_assert(std::is_move_constructible::value, ""); + mem[1] = old_size; int i = 0; for (auto I = old_data; I != old_data + old_size; ++I) { new (&m_data[i++]) T(std::move(*I)); From 3cc6dd1cbdc194ebbc7025f5f819b30910f1da0c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 12 Oct 2017 21:34:21 +0100 Subject: [PATCH 086/148] bv_decl_plugin: remove mem allocation --- src/ast/bv_decl_plugin.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index fcf9a9f8f..093d0f548 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -863,8 +863,7 @@ app * bv_util::mk_numeral(rational const & val, sort* s) const { } app * bv_util::mk_numeral(rational const & val, unsigned bv_size) const { - parameter p1(val); - parameter p[2] = { p1, parameter(static_cast(bv_size)) }; + parameter p[2] = { parameter(val), parameter(static_cast(bv_size)) }; return m_manager.mk_app(get_fid(), OP_BV_NUM, 2, p, 0, 0); } From b53d69be18044ac7bf378dc1b1a61bd4266cfcd1 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 13 Oct 2017 01:00:10 +0100 Subject: [PATCH 087/148] fpa_rewriter: remove a mpq copy --- src/ast/rewriter/fpa_rewriter.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/fpa_rewriter.cpp b/src/ast/rewriter/fpa_rewriter.cpp index 818336c75..ce14349b2 100644 --- a/src/ast/rewriter/fpa_rewriter.cpp +++ b/src/ast/rewriter/fpa_rewriter.cpp @@ -125,11 +125,10 @@ br_status fpa_rewriter::mk_to_fp(func_decl * f, unsigned num_args, expr * const const mpz & sm1 = m_fm.m_powers2(sbits - 1); const mpz & em1 = m_fm.m_powers2(ebits); - scoped_mpq q(mpqm); - mpqm.set(q, r1.to_mpq()); - SASSERT(mpzm.is_one(q.get().denominator())); + const mpq & q = r1.to_mpq(); + SASSERT(mpzm.is_one(q.denominator())); scoped_mpz z(mpzm); - z = q.get().numerator(); + z = q.numerator(); mpzm.rem(z, sm1, sig); mpzm.div(z, sm1, z); From d1c13f17b0922fa53f5d42adc847877de0b48a28 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 13 Oct 2017 01:07:04 +0100 Subject: [PATCH 088/148] remove noexcept since MSVC 2012 doest support it --- src/ast/ast.h | 2 +- src/math/simplex/sparse_matrix.h | 2 +- src/util/mpq.h | 2 +- src/util/mpz.h | 2 +- src/util/vector.h | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 54f8f5e62..7e1645753 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -121,7 +121,7 @@ public: explicit parameter(unsigned ext_id, bool):m_kind(PARAM_EXTERNAL), m_ext_id(ext_id) {} parameter(parameter const&); - parameter(parameter && other) noexcept : m_kind(other.m_kind) { + parameter(parameter && other) : m_kind(other.m_kind) { switch (other.m_kind) { case PARAM_INT: m_int = other.get_int(); break; case PARAM_AST: m_ast = other.get_ast(); break; diff --git a/src/math/simplex/sparse_matrix.h b/src/math/simplex/sparse_matrix.h index 63bbe9e78..4edbb2b9d 100644 --- a/src/math/simplex/sparse_matrix.h +++ b/src/math/simplex/sparse_matrix.h @@ -35,7 +35,7 @@ namespace simplex { struct row_entry { numeral m_coeff; var_t m_var; - row_entry(numeral && c, var_t v) noexcept : m_coeff(std::move(c)), m_var(v) {} + row_entry(numeral && c, var_t v) : m_coeff(std::move(c)), m_var(v) {} }; private: diff --git a/src/util/mpq.h b/src/util/mpq.h index b34e9afae..fd0ae13d4 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -31,7 +31,7 @@ class mpq { public: mpq(int v):m_num(v), m_den(1) {} mpq():m_den(1) {} - mpq(mpq && other) noexcept : m_num(std::move(other.m_num)), m_den(std::move(other.m_den)) {} + mpq(mpq && other) : m_num(std::move(other.m_num)), m_den(std::move(other.m_den)) {} void swap(mpq & other) { m_num.swap(other.m_num); m_den.swap(other.m_den); } mpz const & numerator() const { return m_num; } mpz const & denominator() const { return m_den; } diff --git a/src/util/mpz.h b/src/util/mpz.h index 9933e4485..f04430e17 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -94,7 +94,7 @@ class mpz { public: mpz(int v):m_val(v), m_ptr(0) {} mpz():m_val(0), m_ptr(0) {} - mpz(mpz && other) noexcept : m_val(other.m_val), m_ptr(0) { + mpz(mpz && other) : m_val(other.m_val), m_ptr(0) { std::swap(m_ptr, other.m_ptr); } void swap(mpz & other) { diff --git a/src/util/vector.h b/src/util/vector.h index 03c450660..a925792eb 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -165,7 +165,7 @@ public: SASSERT(size() == source.size()); } - vector(vector&& other) noexcept : m_data(0) { + vector(vector&& other) : m_data(0) { std::swap(m_data, other.m_data); } @@ -517,7 +517,7 @@ public: ptr_vector(unsigned s):vector(s) {} ptr_vector(unsigned s, T * elem):vector(s, elem) {} ptr_vector(ptr_vector const & source):vector(source) {} - ptr_vector(ptr_vector && other) noexcept : vector(std::move(other)) {} + ptr_vector(ptr_vector && other) : vector(std::move(other)) {} ptr_vector(unsigned s, T * const * data):vector(s, const_cast(data)) {} ptr_vector & operator=(ptr_vector const & source) { vector::operator=(source); @@ -532,7 +532,7 @@ public: svector(SZ s):vector(s) {} svector(SZ s, T const & elem):vector(s, elem) {} svector(svector const & source):vector(source) {} - svector(svector && other) noexcept : vector(std::move(other)) {} + svector(svector && other) : vector(std::move(other)) {} svector(SZ s, T const * data):vector(s, data) {} svector & operator=(svector const & source) { vector::operator=(source); From 468e0207f711bf50df8909f443c64ed21ad169de Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 13 Oct 2017 18:23:30 +0100 Subject: [PATCH 089/148] add move constructor to mpf --- src/util/mpf.cpp | 6 ------ src/util/mpf.h | 7 ++++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 3218419a9..9e309a726 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -40,12 +40,6 @@ mpf::mpf(unsigned _ebits, unsigned _sbits): set(ebits, sbits); } -mpf::mpf(mpf const & other) { - // It is safe if the mpz numbers are small. - // I need it for resize method in vector. - // UNREACHABLE(); -} - mpf::~mpf() { } diff --git a/src/util/mpf.h b/src/util/mpf.h index 8070768b2..e679be558 100644 --- a/src/util/mpf.h +++ b/src/util/mpf.h @@ -50,7 +50,12 @@ class mpf { public: mpf(); mpf(unsigned ebits, unsigned sbits); - mpf(mpf const & other); + mpf(mpf && other) : + ebits(other.ebits), + sbits(other.sbits), + sign(other.sign), + significand(std::move(other.significand)), + exponent(other.exponent) {} ~mpf(); unsigned get_ebits() const { return ebits; } unsigned get_sbits() const { return sbits; } From 912a729097e778998a6d78f7cce28187dbe5dca1 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sat, 14 Oct 2017 20:41:08 +0100 Subject: [PATCH 090/148] fix build of unit tests --- src/math/interval/interval.h | 2 +- src/math/interval/interval_def.h | 2 +- src/test/im_float_config.h | 6 --- src/test/interval.cpp | 78 +++++++++++++------------------- src/test/trigo.cpp | 17 +++---- src/util/f2n.h | 3 ++ 6 files changed, 43 insertions(+), 65 deletions(-) diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index bae644fac..db3c5a850 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -162,7 +162,7 @@ private: void checkpoint(); public: - interval_manager(reslimit& lim, C const & c); + interval_manager(reslimit& lim, C && c); ~interval_manager(); numeral_manager & m() const { return m_c.m(); } diff --git a/src/math/interval/interval_def.h b/src/math/interval/interval_def.h index e18d9edda..de3a5fa19 100644 --- a/src/math/interval/interval_def.h +++ b/src/math/interval/interval_def.h @@ -31,7 +31,7 @@ Revision History: // #define TRACE_NTH_ROOT template -interval_manager::interval_manager(reslimit& lim, C const & c): m_limit(lim), m_c(c) { +interval_manager::interval_manager(reslimit& lim, C && c): m_limit(lim), m_c(std::move(c)) { m().set(m_minus_one, -1); m().set(m_one, 1); m_pi_n = 0; diff --git a/src/test/im_float_config.h b/src/test/im_float_config.h index 436d9ecaf..3905358ea 100644 --- a/src/test/im_float_config.h +++ b/src/test/im_float_config.h @@ -63,10 +63,4 @@ public: numeral_manager & m() const { return const_cast(m_manager); } }; -template -inline void del_f_interval(im_float_config & cfg, typename im_float_config::interval & a) { - cfg.m().del(a.m_lower); - cfg.m().del(a.m_upper); -} - #endif diff --git a/src/test/interval.cpp b/src/test/interval.cpp index ba218c010..21b25a9be 100644 --- a/src/test/interval.cpp +++ b/src/test/interval.cpp @@ -125,7 +125,8 @@ static bool mk_interval(im_default_config & cfg, interval & a, bool l_inf, bool } #endif -static void mk_random_interval(im_default_config & cfg, interval & a, unsigned magnitude) { +template +static void mk_random_interval(T & cfg, interval & a, unsigned magnitude) { switch (rand()%3) { case 0: // Neg, Neg @@ -195,11 +196,6 @@ static void mk_random_interval(im_default_config & cfg, interval & a, unsigned m } } -static void del_interval(im_default_config & cfg, interval & a) { - cfg.m().del(a.m_lower); - cfg.m().del(a.m_upper); -} - #define BUFFER_SZ 256 static int g_problem_id = 0; static char g_buffer[BUFFER_SZ]; @@ -238,19 +234,18 @@ static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, static void tst_ ## NAME(unsigned N, unsigned magnitude) { \ reslimit rl; \ unsynch_mpq_manager nm; \ - im_default_config imc(nm); \ - interval_manager im(rl, imc); \ + interval_manager im(rl, nm); \ interval a, b, r; \ \ for (unsigned i = 0; i < N; i++) { \ - mk_random_interval(imc, a, magnitude); \ - mk_random_interval(imc, b, magnitude); \ + mk_random_interval(im, a, magnitude); \ + mk_random_interval(im, b, magnitude); \ interval_deps deps; \ im.NAME(a, b, r, deps); \ \ display_lemmas(nm, RES_TERM, a, b, r, deps); \ } \ - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); \ + im.del(a); im.del(b); im.del(r); \ } MK_BINARY(mul, "(* a b)"); @@ -260,56 +255,52 @@ MK_BINARY(sub, "(- a b)"); static void tst_neg(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); interval_deps deps; im.neg(a, r, deps); display_lemmas(nm, "(- a)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_pw_2(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); interval_deps deps; im.power(a, 2, r, deps); display_lemmas(nm, "(* a a)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_pw_3(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); interval_deps deps; im.power(a, 3, r, deps); display_lemmas(nm, "(* a a a)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_root_2(unsigned N, unsigned magnitude, unsigned precision) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; scoped_mpq p(nm); p = precision; @@ -317,7 +308,7 @@ static void tst_root_2(unsigned N, unsigned magnitude, unsigned precision) { unsigned i = 0; while (i < N) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); if (!im.lower_is_neg(a)) { i++; interval_deps deps; @@ -325,14 +316,13 @@ static void tst_root_2(unsigned N, unsigned magnitude, unsigned precision) { display_lemmas(nm, "(^ a (/ 1.0 2.0))", a, b, r, deps); } } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_root_3(unsigned N, unsigned magnitude, unsigned precision) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; scoped_mpq p(nm); p = precision; @@ -340,25 +330,24 @@ static void tst_root_3(unsigned N, unsigned magnitude, unsigned precision) { unsigned i = 0; while (i < N) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); i++; interval_deps deps; im.nth_root(a, 3, p, r, deps); display_lemmas(nm, "(^ a (/ 1.0 3.0))", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_inv(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { while (true) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); if (!im.contains_zero(a)) break; } @@ -366,20 +355,19 @@ static void tst_inv(unsigned N, unsigned magnitude) { im.inv(a, r, deps); display_lemmas(nm, "(/ 1 a)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_div(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); while (true) { - mk_random_interval(imc, b, magnitude); + mk_random_interval(im, b, magnitude); if (!im.contains_zero(b)) break; } @@ -387,7 +375,7 @@ static void tst_div(unsigned N, unsigned magnitude) { im.div(a, b, r, deps); display_lemmas(nm, "(/ a b)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } #include "test/im_float_config.h" @@ -396,8 +384,7 @@ static void tst_div(unsigned N, unsigned magnitude) { static void tst_float() { unsynch_mpq_manager qm; mpf_manager fm; - im_float_config ifc(fm); - interval_manager > im(ifc); + interval_manager > im(fm); im_float_config::interval a, b, c; scoped_mpq minus_one_third(qm), one_third(qm), two_third(qm), minus_two_third(qm); qm.set(minus_one_third, -1, 3); @@ -424,15 +411,14 @@ static void tst_float() { im.display(std::cout, c); std::cout << "\n"; - del_f_interval(ifc, a); del_f_interval(ifc, b); del_f_interval(ifc, c); + im.del(a); im.del(b); im.del(r); } #endif void tst_pi() { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval r; for (unsigned i = 0; i < 8; i++) { im.pi(i, r); @@ -440,7 +426,7 @@ void tst_pi() { nm.display_decimal(std::cout, im.upper(r), 32); std::cout << "\n"; ENSURE(nm.lt(im.lower(r), im.upper(r))); } - del_interval(imc, r); + im.del(r); } #if 0 diff --git a/src/test/trigo.cpp b/src/test/trigo.cpp index c1b73c423..d1b75c7fb 100644 --- a/src/test/trigo.cpp +++ b/src/test/trigo.cpp @@ -39,9 +39,8 @@ static void tst_sine_core(std::ostream & out, unsynch_mpq_manager & nm, interval static void tst_sine(std::ostream & out, unsigned N, unsigned k) { unsynch_mpq_manager nm; - im_default_config imc(nm); reslimit rl; - interval_manager im(rl, imc); + interval_manager im(rl, nm); scoped_mpq a(nm); nm.set(a, 0); tst_sine_core(out, nm, im, a, 1); @@ -67,8 +66,7 @@ static void tst_cosine_core(std::ostream & out, unsynch_mpq_manager & nm, interv static void tst_cosine(std::ostream & out, unsigned N, unsigned k) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); scoped_mpq a(nm); nm.set(a, 0); tst_cosine_core(out, nm, im, a, 1); @@ -100,8 +98,7 @@ template static void tst_float_sine(std::ostream & out, unsigned N, unsigned k) { reslimit rl; fmanager fm; - im_float_config ifc(fm, EBITS, SBITS); - interval_manager > im(rl, ifc); + interval_manager > im(rl, { fm, EBITS, SBITS }); _scoped_numeral a(fm); fm.set(a, EBITS, SBITS, static_cast(0)); tst_float_sine_core(out, fm, im, a, 1); @@ -136,8 +133,7 @@ static void tst_mpf_bug() { static void tst_e(std::ostream & out) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); im_default_config::interval r; for (unsigned i = 0; i < 64; i++) { im.e(i, r); @@ -152,8 +148,7 @@ static void tst_e_float(std::ostream & out) { reslimit rl; unsynch_mpq_manager qm; mpf_manager fm; - im_float_config ifc(fm); - interval_manager > im(rl, ifc); + interval_manager > im(rl, fm); scoped_mpq q(qm); im_float_config::interval r; for (unsigned i = 0; i < 64; i++) { @@ -161,7 +156,7 @@ static void tst_e_float(std::ostream & out) { out << fm.to_rational_string(im.lower(r)) << " <= E\n"; out << "E <= " << fm.to_rational_string(im.upper(r)) << "\n"; } - del_f_interval(ifc, r); + im.del(r); } void tst_trigo() { diff --git a/src/util/f2n.h b/src/util/f2n.h index e5e84f6f0..d55b21d3d 100644 --- a/src/util/f2n.h +++ b/src/util/f2n.h @@ -46,6 +46,9 @@ public: m_manager.set(m_one, ebits, sbits, 1); } + f2n(f2n && other) : m_manager(other.m_manager), m_mode(other.m_mode), m_ebits(other.m_ebits), m_sbits(other.m_sbits), + m_tmp1(std::move(other.m_tmp1)), m_one(std::move(other.m_one)) {} + ~f2n() { m().del(m_tmp1); m().del(m_one); From 6c2d0394acc8a20fbe4f2aeb2b30b134d86a94d9 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Oct 2017 00:08:50 +0100 Subject: [PATCH 091/148] add move constructor to rational --- src/util/rational.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/rational.h b/src/util/rational.h index 803c562ad..392a1982b 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -41,6 +41,7 @@ public: rational() {} rational(rational const & r) { m().set(m_val, r.m_val); } + rational(rational && r) : m_val(std::move(r.m_val)) {} explicit rational(int n) { m().set(m_val, n); } From 29acec672ffdf5251bd6799406c6b12fdce255a0 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Oct 2017 01:41:07 +0100 Subject: [PATCH 092/148] nnf: remove ast incref --- src/ast/normal_forms/nnf.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 247e9dea1..014568812 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -207,8 +207,8 @@ struct nnf::imp { unsigned m_new_child:1; unsigned m_cache_result:1; unsigned m_spos; // top of the result stack, when the frame was created. - frame(expr_ref& n, bool pol, bool in_q, bool cache_res, unsigned spos): - m_curr(n), + frame(expr_ref&& n, bool pol, bool in_q, bool cache_res, unsigned spos): + m_curr(std::move(n)), m_i(0), m_pol(pol), m_in_q(in_q), @@ -324,8 +324,7 @@ struct nnf::imp { } void push_frame(expr * t, bool pol, bool in_q, bool cache_res) { - expr_ref tr(t, m()); - m_frame_stack.push_back(frame(tr, pol, in_q, cache_res, m_result_stack.size())); + m_frame_stack.push_back(frame({ t, m() }, pol, in_q, cache_res, m_result_stack.size())); } static unsigned get_cache_idx(bool pol, bool in_q) { From e7f0f3b834f2b71d0fd9afa3296113199193f166 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Oct 2017 03:41:34 +0100 Subject: [PATCH 093/148] add move constructor to obj_ref --- src/util/obj_ref.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/obj_ref.h b/src/util/obj_ref.h index 1aa562a8f..72762ea5b 100644 --- a/src/util/obj_ref.h +++ b/src/util/obj_ref.h @@ -53,6 +53,10 @@ public: inc_ref(); } + obj_ref(obj_ref && other) : m_obj(0), m_manager(other.m_manager) { + std::swap(m_obj, other.m_obj); + } + ~obj_ref() { dec_ref(); } TManager & get_manager() const { return m_manager; } From d18e975a492cdd45ad7aad59831148b02b0c9862 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Oct 2017 03:52:11 +0100 Subject: [PATCH 094/148] vector: make expand_vector() less prone to mem leaks by calling the destructors after move --- src/util/vector.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/vector.h b/src/util/vector.h index a925792eb..27de49c44 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -91,6 +91,7 @@ class vector { int i = 0; for (auto I = old_data; I != old_data + old_size; ++I) { new (&m_data[i++]) T(std::move(*I)); + I->~T(); } memory::deallocate(old_mem); } From 82b25a0608d55616c2df365b7af19f4e21a0bd9c Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Oct 2017 05:31:28 +0100 Subject: [PATCH 095/148] add move constructor to watch_list --- src/smt/watch_list.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/smt/watch_list.h b/src/smt/watch_list.h index 19d3f20a8..1cc29da5a 100644 --- a/src/smt/watch_list.h +++ b/src/smt/watch_list.h @@ -85,6 +85,10 @@ namespace smt { watch_list(): m_data(0) { } + + watch_list(watch_list && other) : m_data(0) { + std::swap(m_data, other.m_data); + } ~watch_list() { destroy(); From 6cefb700ac559af28477e49f3b5a17dae0e3a602 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Oct 2017 19:21:02 +0100 Subject: [PATCH 096/148] add move constructor to ref_vector --- src/util/ref_vector.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/ref_vector.h b/src/util/ref_vector.h index 9b7657fb2..f340d8886 100644 --- a/src/util/ref_vector.h +++ b/src/util/ref_vector.h @@ -45,6 +45,10 @@ public: typedef T * data; ref_vector_core(Ref const & r = Ref()):Ref(r) {} + + ref_vector_core(ref_vector_core && other) : + Ref(std::move(other)), + m_nodes(std::move(other.m_nodes)) {} ~ref_vector_core() { dec_range_ref(m_nodes.begin(), m_nodes.end()); @@ -207,6 +211,8 @@ public: this->append(other); } + ref_vector(ref_vector && other) : super(std::move(other)) {} + ref_vector(TManager & m, unsigned sz, T * const * data): super(ref_manager_wrapper(m)) { this->append(sz, data); From 2905bdebefe30112556e2f8f9cfd68d080094c48 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 15 Oct 2017 23:36:00 +0100 Subject: [PATCH 097/148] make vector friendly to gcc < 5 --- src/util/vector.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/util/vector.h b/src/util/vector.h index 27de49c44..f5792a5d3 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -76,25 +76,26 @@ class vector { throw default_exception("Overflow encountered when expanding vector"); } SZ *mem, *old_mem = reinterpret_cast(m_data) - 2; +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 + if (__has_trivial_copy(T)) { +#else if (std::is_trivially_copyable::value) { +#endif mem = (SZ*)memory::reallocate(old_mem, new_capacity_T); + m_data = reinterpret_cast(mem + 2); } else { mem = (SZ*)memory::allocate(new_capacity_T); - } - auto old_data = m_data; - auto old_size = size(); - *mem = new_capacity; - m_data = reinterpret_cast(mem + 2); - if (!std::is_trivially_copyable::value) { - static_assert(std::is_move_constructible::value, ""); + auto old_data = m_data; + auto old_size = size(); mem[1] = old_size; - int i = 0; - for (auto I = old_data; I != old_data + old_size; ++I) { - new (&m_data[i++]) T(std::move(*I)); - I->~T(); + m_data = reinterpret_cast(mem + 2); + for (unsigned i = 0; i < old_size; ++i) { + new (&m_data[i]) T(std::move(old_data[i])); + old_data[i].~T(); } memory::deallocate(old_mem); } + *mem = new_capacity; } } From b63754e3624c0978e3d8be2d500a6a1ab978e859 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 15 Oct 2017 21:16:54 -0700 Subject: [PATCH 098/148] adding explicit assignment for auto-generated function. Signed-off-by: Nikolaj Bjorner --- src/tactic/sls/sls_tracker.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index b4969f433..15f06f096 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -55,6 +55,10 @@ class sls_tracker { this->~value_score(); new (this) value_score(std::move(other)); } + value_score& operator=(value_score& other) { + UNREACHABLE(); + return *this; + } unsynch_mpz_manager * m; mpz value; double score; From 64ee9f168df650537c6264c1c4e6c16dd315966d Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 12:26:35 +0100 Subject: [PATCH 099/148] [TravisCI] Add ASan/LSan/UBSan suppression files and use them in CI. --- contrib/ci/Dockerfiles/z3_build.Dockerfile | 9 ++++++++- contrib/ci/scripts/sanitizer_env.sh | 19 +++++++++++++++++++ contrib/ci/scripts/test_z3_examples_cmake.sh | 3 +++ contrib/ci/scripts/test_z3_system_tests.sh | 3 +++ .../ci/scripts/test_z3_unit_tests_cmake.sh | 3 +++ contrib/suppressions/README.md | 7 +++++++ contrib/suppressions/maintainers.txt | 3 +++ contrib/suppressions/sanitizers/README.md | 4 ++++ contrib/suppressions/sanitizers/asan.txt | 1 + contrib/suppressions/sanitizers/lsan.txt | 1 + contrib/suppressions/sanitizers/ubsan.txt | 1 + 11 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 contrib/ci/scripts/sanitizer_env.sh create mode 100644 contrib/suppressions/README.md create mode 100644 contrib/suppressions/maintainers.txt create mode 100644 contrib/suppressions/sanitizers/README.md create mode 100644 contrib/suppressions/sanitizers/asan.txt create mode 100644 contrib/suppressions/sanitizers/lsan.txt create mode 100644 contrib/suppressions/sanitizers/ubsan.txt diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 07504e6b9..65cf08087 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -62,7 +62,8 @@ ENV \ # Build Z3 RUN mkdir -p "${Z3_SRC_DIR}" && \ - mkdir -p "${Z3_SRC_DIR}/contrib/ci/scripts" + mkdir -p "${Z3_SRC_DIR}/contrib/ci/scripts" && \ + mkdir -p "${Z3_SRC_DIR}/contrib/ci/suppressions/sanitizers" # Deliberately leave out `contrib` ADD /cmake ${Z3_SRC_DIR}/cmake/ ADD /doc ${Z3_SRC_DIR}/doc/ @@ -89,7 +90,13 @@ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_docs.sh # Test examples ADD \ /contrib/ci/scripts/test_z3_examples_cmake.sh \ + /contrib/ci/scripts/sanitizer_env.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ +ADD \ + /contrib/suppressions/sanitizers/asan.txt \ + /contrib/suppressions/sanitizers/lsan.txt \ + /contrib/suppressions/sanitizers/ubsan.txt \ + ${Z3_SRC_DIR}/contrib/suppressions/sanitizers/ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_examples_cmake.sh # Run unit tests diff --git a/contrib/ci/scripts/sanitizer_env.sh b/contrib/ci/scripts/sanitizer_env.sh new file mode 100644 index 000000000..e9af255d3 --- /dev/null +++ b/contrib/ci/scripts/sanitizer_env.sh @@ -0,0 +1,19 @@ +# This script is intended to be included by other +# scripts and should not be executed directly + +: ${Z3_SRC_DIR?"Z3_SRC_DIR must be specified"} +: ${ASAN_BUILD?"ASAN_BUILD must be specified"} +: ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} + +if [ "X${ASAN_BUILD}" = "X1" ]; then + # Use suppression files + export LSAN_OPTIONS="print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/lsan.txt" + export ASAN_OPTIONS="print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" +fi + +if [ "X${UBSAN_BUILD}" = "X1" ]; then + # `halt_on_error=1,abort_on_error=1` means that on the first UBSan error + # the program will terminate by calling `abort(). Without this UBSan will + # allow execution to continue. We also use a suppression file. + export UBSAN_OPTIONS="halt_on_error=1,abort_on_error=1,print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/ubsan.txt" +fi diff --git a/contrib/ci/scripts/test_z3_examples_cmake.sh b/contrib/ci/scripts/test_z3_examples_cmake.sh index 2eda3de7b..1b8f988a2 100755 --- a/contrib/ci/scripts/test_z3_examples_cmake.sh +++ b/contrib/ci/scripts/test_z3_examples_cmake.sh @@ -21,6 +21,9 @@ source ${SCRIPT_DIR}/set_compiler_flags.sh # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh +# Sanitizer environment variables +source ${SCRIPT_DIR}/sanitizer_env.sh + cd "${Z3_BUILD_DIR}" # Build and run C example diff --git a/contrib/ci/scripts/test_z3_system_tests.sh b/contrib/ci/scripts/test_z3_system_tests.sh index dfb1084a4..d61a0ef02 100755 --- a/contrib/ci/scripts/test_z3_system_tests.sh +++ b/contrib/ci/scripts/test_z3_system_tests.sh @@ -16,6 +16,9 @@ if [ "X${RUN_SYSTEM_TESTS}" != "X1" ]; then exit 0 fi +# Sanitizer environment variables +source ${SCRIPT_DIR}/sanitizer_env.sh + Z3_EXE="${Z3_BUILD_DIR}/z3" Z3_LIB_DIR="${Z3_BUILD_DIR}" diff --git a/contrib/ci/scripts/test_z3_unit_tests_cmake.sh b/contrib/ci/scripts/test_z3_unit_tests_cmake.sh index e1c927f58..60c29556b 100755 --- a/contrib/ci/scripts/test_z3_unit_tests_cmake.sh +++ b/contrib/ci/scripts/test_z3_unit_tests_cmake.sh @@ -13,6 +13,9 @@ set -o pipefail # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh +# Sanitizer environment variables +source ${SCRIPT_DIR}/sanitizer_env.sh + cd "${Z3_BUILD_DIR}" function build_unit_tests() { diff --git a/contrib/suppressions/README.md b/contrib/suppressions/README.md new file mode 100644 index 000000000..90df39084 --- /dev/null +++ b/contrib/suppressions/README.md @@ -0,0 +1,7 @@ +# Suppression files + +This directory contains suppression files used by various +program analysis tools. + +Suppression files tell a program analysis tool to suppress +various warnings/errors. diff --git a/contrib/suppressions/maintainers.txt b/contrib/suppressions/maintainers.txt new file mode 100644 index 000000000..caa6798c6 --- /dev/null +++ b/contrib/suppressions/maintainers.txt @@ -0,0 +1,3 @@ +# Maintainers + +- Dan Liew (@delcypher) diff --git a/contrib/suppressions/sanitizers/README.md b/contrib/suppressions/sanitizers/README.md new file mode 100644 index 000000000..f76f920b2 --- /dev/null +++ b/contrib/suppressions/sanitizers/README.md @@ -0,0 +1,4 @@ +# Sanitizer supression files + +This directory contains files used to suppress +ASan/LSan/UBSan warnings/errors. diff --git a/contrib/suppressions/sanitizers/asan.txt b/contrib/suppressions/sanitizers/asan.txt new file mode 100644 index 000000000..f058adfe2 --- /dev/null +++ b/contrib/suppressions/sanitizers/asan.txt @@ -0,0 +1 @@ +# AddressSanitizer suppression file diff --git a/contrib/suppressions/sanitizers/lsan.txt b/contrib/suppressions/sanitizers/lsan.txt new file mode 100644 index 000000000..a4ce0881a --- /dev/null +++ b/contrib/suppressions/sanitizers/lsan.txt @@ -0,0 +1 @@ +# LeakSanitizer suppression file diff --git a/contrib/suppressions/sanitizers/ubsan.txt b/contrib/suppressions/sanitizers/ubsan.txt new file mode 100644 index 000000000..8feef305f --- /dev/null +++ b/contrib/suppressions/sanitizers/ubsan.txt @@ -0,0 +1 @@ +# UndefinedBehavior sanitizer suppression file From a9fcfc531bda243b7b7cf30f2d6591d6430ac8b6 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 15:25:41 +0100 Subject: [PATCH 100/148] [TravisCI][CMake] Add `Z3_C_EXAMPLES_FORCE_CXX_LINKER` CMake option and propagate its value into the C API examples. This flag forces the C API examples to use the C++ compiler as the linker rather than the C compiler. This a workaround to avoid linking errors when building with UBSan. --- README-CMake.md | 1 + contrib/ci/scripts/build_z3_cmake.sh | 11 +++++++++++ examples/CMakeLists.txt | 21 +++++++++++++++++++-- examples/c/CMakeLists.txt | 11 +++++++++++ examples/maxsat/CMakeLists.txt | 10 ++++++++++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/README-CMake.md b/README-CMake.md index 605c14818..0d323e08f 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -270,6 +270,7 @@ The following useful options can be passed to CMake whilst configuring. * ``API_LOG_SYNC`` - BOOL. If set to ``TRUE`` will enable experimental API log sync feature. * ``WARNINGS_AS_ERRORS`` - STRING. If set to ``TRUE`` compiler warnings will be treated as errors. If set to ``False`` compiler warnings will not be treated as errors. If set to ``SERIOUS_ONLY`` a subset of compiler warnings will be treated as errors. +* ``Z3_C_EXAMPLES_FORCE_CXX_LINKER`` - BOOL. If set to ``TRUE`` the C API examples will request that the C++ linker is used rather than the C linker. On the command line these can be passed to ``cmake`` using the ``-D`` option. In ``ccmake`` and ``cmake-gui`` these can be set in the user interface. diff --git a/contrib/ci/scripts/build_z3_cmake.sh b/contrib/ci/scripts/build_z3_cmake.sh index 76fd0fb84..c1014d5d5 100755 --- a/contrib/ci/scripts/build_z3_cmake.sh +++ b/contrib/ci/scripts/build_z3_cmake.sh @@ -22,6 +22,7 @@ set -o pipefail : ${USE_LTO?"USE_LTO must be specified"} : ${Z3_INSTALL_PREFIX?"Z3_INSTALL_PREFIX must be specified"} : ${Z3_WARNINGS_AS_ERRORS?"Z3_WARNINGS_AS_ERRORS must be specified"} +: ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} ADDITIONAL_Z3_OPTS=() @@ -105,6 +106,16 @@ fi # Set compiler flags source ${SCRIPT_DIR}/set_compiler_flags.sh +if [ "X${UBSAN_BUILD}" = "X1" ]; then + # HACK: When building with UBSan the C++ linker + # must be used to avoid the following linker errors. + # undefined reference to `__ubsan_vptr_type_cache' + # undefined reference to `__ubsan_handle_dynamic_type_cache_miss' + ADDITIONAL_Z3_OPTS+=( \ + '-DZ3_C_EXAMPLES_FORCE_CXX_LINKER=ON' \ + ) +fi + # Sanity check if [ ! -e "${Z3_SRC_DIR}/CMakeLists.txt" ]; then echo "Z3_SRC_DIR is invalid" diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ba7f6ee59..338d2e4bb 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,6 +7,19 @@ else() set(EXTERNAL_PROJECT_BUILD_ALWAYS_ARG "") endif() +option(Z3_C_EXAMPLES_FORCE_CXX_LINKER + "Force C++ linker when building C example projects" OFF) + +if (Z3_C_EXAMPLES_FORCE_CXX_LINKER) + # HACK: This is a workaround for UBSan. + message(STATUS "Forcing C++ linker to be used when building example C projects") + set(EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG + "-DFORCE_CXX_LINKER=ON" + ) +else() + set(EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG "") +endif() + ################################################################################ # Build example project using libz3's C API as an external project ################################################################################ @@ -14,7 +27,9 @@ ExternalProject_Add(c_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c" - CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + CMAKE_ARGS + "-DZ3_DIR=${CMAKE_BINARY_DIR}" + "${EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_example_build_dir" @@ -30,7 +45,9 @@ ExternalProject_Add(c_maxsat_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/maxsat" - CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + CMAKE_ARGS + "-DZ3_DIR=${CMAKE_BINARY_DIR}" + "${EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_maxsat_example_build_dir" diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index dd8fa6328..bac08e460 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -22,6 +22,17 @@ message(STATUS "Found Z3 ${Z3_VERSION_STRING}") message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(c_example test_capi.c) + +option(FORCE_CXX_LINKER "Force linker with C++ linker" OFF) +if (FORCE_CXX_LINKER) + # This is a hack for avoiding UBSan linking errors + message(STATUS "Forcing use of C++ linker") + set_target_properties(c_example + PROPERTIES + LINKER_LANGUAGE CXX + ) +endif() + target_include_directories(c_example PRIVATE ${Z3_C_INCLUDE_DIRS}) target_link_libraries(c_example PRIVATE ${Z3_LIBRARIES}) diff --git a/examples/maxsat/CMakeLists.txt b/examples/maxsat/CMakeLists.txt index b48e167ea..019243ecf 100644 --- a/examples/maxsat/CMakeLists.txt +++ b/examples/maxsat/CMakeLists.txt @@ -25,6 +25,16 @@ add_executable(c_maxsat_example maxsat.c) target_include_directories(c_maxsat_example PRIVATE ${Z3_C_INCLUDE_DIRS}) target_link_libraries(c_maxsat_example PRIVATE ${Z3_LIBRARIES}) +option(FORCE_CXX_LINKER "Force linker with C++ linker" OFF) +if (FORCE_CXX_LINKER) + # This is a hack for avoiding UBSan linking errors + message(STATUS "Forcing use of C++ linker") + set_target_properties(c_maxsat_example + PROPERTIES + LINKER_LANGUAGE CXX + ) +endif() + if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") # On Windows we need to copy the Z3 libraries # into the same directory as the executable From f15766baee334b2d045e72b392712ef133c28c3a Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 15:56:43 +0100 Subject: [PATCH 101/148] [TravisCI] Don't run the non-native example when building with UBSan. This a workaround. Right now `libz3` gets linked against a static UBSan runtime which means none of the non-native language bindings (e.g. python) can load `libz3` due to undefined symbols. We need to link `libz3` against a shared UBSan runtime to fix this. --- contrib/ci/scripts/test_z3_examples_cmake.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contrib/ci/scripts/test_z3_examples_cmake.sh b/contrib/ci/scripts/test_z3_examples_cmake.sh index 1b8f988a2..c188dbcf4 100755 --- a/contrib/ci/scripts/test_z3_examples_cmake.sh +++ b/contrib/ci/scripts/test_z3_examples_cmake.sh @@ -14,6 +14,7 @@ set -o pipefail : ${PYTHON_EXECUTABLE?"PYTHON_EXECUTABLE must be specified"} : ${DOTNET_BINDINGS?"DOTNET_BINDINGS must be specified"} : ${JAVA_BINDINGS?"JAVA_BINDINGS must be specified"} +: ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} # Set compiler flags source ${SCRIPT_DIR}/set_compiler_flags.sh @@ -45,6 +46,14 @@ run_quiet \ examples/c_maxsat_example_build_dir/c_maxsat_example \ ${Z3_SRC_DIR}/examples/maxsat/ex.smt +if [ "X${UBSAN_BUILD}" = "X1" ]; then + # FIXME: We really need libz3 to link against a shared UBSan runtime. + # Right now we link against the static runtime which breaks all the + # non-native language bindings. + echo "FIXME: Can't run other examples when building with UBSan" + exit 0 +fi + if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # Run python examples From fb3d4cfed9970e44712a3ee580e6152f7f17f966 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 16:13:57 +0100 Subject: [PATCH 102/148] [TravisCI] Add a UBSan configuration --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 81ddefb8b..9584d3869 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,9 @@ env: ############################################################################### # Ubuntu 16.04 LTS ############################################################################### + # 64-bit UBSan build + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo UBSAN_BUILD=1 + # 64-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo # 64-bit Clang 3.9 RelWithDebInfo From f756bf6c86e6ea91aacbcc05b9cb0fa75bdcdef1 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 16:27:31 +0100 Subject: [PATCH 103/148] [TravisCI] Fix undefined SCRIPT_DIR variable --- contrib/ci/scripts/test_z3_system_tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/ci/scripts/test_z3_system_tests.sh b/contrib/ci/scripts/test_z3_system_tests.sh index d61a0ef02..1b6433806 100755 --- a/contrib/ci/scripts/test_z3_system_tests.sh +++ b/contrib/ci/scripts/test_z3_system_tests.sh @@ -17,6 +17,7 @@ if [ "X${RUN_SYSTEM_TESTS}" != "X1" ]; then fi # Sanitizer environment variables +SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" source ${SCRIPT_DIR}/sanitizer_env.sh Z3_EXE="${Z3_BUILD_DIR}/z3" From 9455391f1f60d0f4ff958dd95fb0f74cbe399ad8 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 16:46:26 +0100 Subject: [PATCH 104/148] [TravisCI] Don't run the python binding system tests when building with UBSan. This is a workaround. We can't fix this unless we build libz3 with a shared UBSan runtime. --- contrib/ci/scripts/test_z3_system_tests.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/contrib/ci/scripts/test_z3_system_tests.sh b/contrib/ci/scripts/test_z3_system_tests.sh index 1b6433806..71ecf2a1e 100755 --- a/contrib/ci/scripts/test_z3_system_tests.sh +++ b/contrib/ci/scripts/test_z3_system_tests.sh @@ -10,6 +10,7 @@ set -o pipefail : ${PYTHON_BINDINGS?"PYTHON_BINDINGS must be specified"} : ${PYTHON_EXECUTABLE?"PYTHON_EXECUTABLE must be specified"} : ${Z3_SYSTEM_TEST_DIR?"Z3_SYSTEM_TEST_DIR must be specified"} +: ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} if [ "X${RUN_SYSTEM_TESTS}" != "X1" ]; then echo "Skipping system tests" @@ -52,7 +53,13 @@ fi if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # Run python binding tests - ${PYTHON_EXECUTABLE} scripts/test_pyscripts.py "${Z3_LIB_DIR}" regressions/python/ + if [ "X${UBSAN_BUILD}" = "X1" ]; then + # FIXME: We need to build libz3 with a shared UBSan runtime for the bindings + # to work. + echo "FIXME: Skipping python binding tests when building with UBSan" + else + ${PYTHON_EXECUTABLE} scripts/test_pyscripts.py "${Z3_LIB_DIR}" regressions/python/ + fi fi # FIXME: Run `scripts/test_cs.py` once it has been modified to support mono From 71dcec311349e37ef5192c331174ab4f7bcb853d Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 16:48:12 +0100 Subject: [PATCH 105/148] [UBSan] Update UBSan suppression file to suppress all undefined behaviour I have observed running in CI. --- contrib/suppressions/sanitizers/ubsan.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/contrib/suppressions/sanitizers/ubsan.txt b/contrib/suppressions/sanitizers/ubsan.txt index 8feef305f..6ceacd400 100644 --- a/contrib/suppressions/sanitizers/ubsan.txt +++ b/contrib/suppressions/sanitizers/ubsan.txt @@ -1 +1,20 @@ # UndefinedBehavior sanitizer suppression file +# FIXME: UBSan doesn't usually have false positives so we need to fix all of these! + +# Occurs when running C API example (`interpolation_example`) +# See https://github.com/Z3Prover/z3/issues/1286 +null:iz3mgr.h + +# Occurs when running C++ API example +# See https://github.com/Z3Prover/z3/issues/1287 +function:api_context.cpp + +# Occurs when running tptp example +# See https://github.com/Z3Prover/z3/issues/964 +null:rational.h +null:mpq.h + +# Occurs when running `test-z3 /a` +# See https://github.com/Z3Prover/z3/issues/1288 +shift-exponent:tbv.cpp +signed-integer-overflow:mpz.cpp From 4db5980a234802db1c379f3358230631636edf08 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 19:01:20 +0100 Subject: [PATCH 106/148] [TravisCI] Fix getting proper stack traces for ASan/LSan. The `llvm-symbolizer` tool needs to be installed and ASan/LSan needs to be told where to find it. --- contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile | 3 ++- contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile | 3 ++- contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile | 3 ++- contrib/ci/scripts/sanitizer_env.sh | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile index d8a32edea..87e3c8d67 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile @@ -30,6 +30,7 @@ RUN apt-get update && \ libgomp1 \ libomp5 \ libomp-dev \ + llvm-3.9 \ make \ mono-devel \ ninja-build \ @@ -47,4 +48,4 @@ RUN useradd -m user && \ echo 'user ALL=(root) NOPASSWD: ALL' >> /etc/sudoers USER user WORKDIR /home/user - +ENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.9/bin/llvm-symbolizer diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile index c28e59e97..c963ce255 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile @@ -16,6 +16,7 @@ RUN apt-get update && \ libgmp-dev \ libgomp1 \ lib32gomp1 \ + llvm-3.9 \ make \ mono-devel \ ninja-build \ @@ -32,4 +33,4 @@ RUN useradd -m user && \ echo 'user ALL=(root) NOPASSWD: ALL' >> /etc/sudoers USER user WORKDIR /home/user - +ENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.9/bin/llvm-symbolizer diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile index 98a5a3e09..08686e275 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile @@ -18,6 +18,7 @@ RUN apt-get update && \ libgomp1 \ libomp5 \ libomp-dev \ + llvm-3.9 \ make \ mono-devel \ ninja-build \ @@ -35,4 +36,4 @@ RUN useradd -m user && \ echo 'user ALL=(root) NOPASSWD: ALL' >> /etc/sudoers USER user WORKDIR /home/user - +ENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.9/bin/llvm-symbolizer diff --git a/contrib/ci/scripts/sanitizer_env.sh b/contrib/ci/scripts/sanitizer_env.sh index e9af255d3..3b1ff7297 100644 --- a/contrib/ci/scripts/sanitizer_env.sh +++ b/contrib/ci/scripts/sanitizer_env.sh @@ -9,6 +9,7 @@ if [ "X${ASAN_BUILD}" = "X1" ]; then # Use suppression files export LSAN_OPTIONS="print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/lsan.txt" export ASAN_OPTIONS="print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" + : ${ASAN_SYMBOLIZER_PATH?"ASAN_SYMBOLIZER_PATH must be specified"} fi if [ "X${UBSAN_BUILD}" = "X1" ]; then From 157c8064e8f867c134c3979fd574264cf0c990aa Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 19:06:57 +0100 Subject: [PATCH 107/148] [CMake] When building C/C++ API examples use the same build type as Z3 if doing a single configuration build. --- examples/CMakeLists.txt | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 338d2e4bb..6c50320ed 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -20,6 +20,17 @@ else() set(EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG "") endif() +if (DEFINED CMAKE_CONFIGURATION_TYPES) + message(WARNING + "Cannot set built type of external project when building with a " + "multi-configuration generator") + set(EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG "") +else() + set(EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG + "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}" + ) +endif() + ################################################################################ # Build example project using libz3's C API as an external project ################################################################################ @@ -30,6 +41,7 @@ ExternalProject_Add(c_example CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" "${EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_example_build_dir" @@ -48,6 +60,7 @@ ExternalProject_Add(c_maxsat_example CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" "${EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_maxsat_example_build_dir" @@ -64,7 +77,9 @@ ExternalProject_Add(cpp_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c++" - CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + CMAKE_ARGS + "-DZ3_DIR=${CMAKE_BINARY_DIR}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/cpp_example_build_dir" @@ -80,7 +95,9 @@ ExternalProject_Add(z3_tptp5 DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tptp" - CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + CMAKE_ARGS + "-DZ3_DIR=${CMAKE_BINARY_DIR}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/tptp_build_dir" From 11f7298c52f2b41eb64775d419f17b4c6333bcf4 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 19:52:25 +0100 Subject: [PATCH 108/148] [TravisCI] Add ASan configuration --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9584d3869..bf69573aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,13 @@ env: ############################################################################### # Ubuntu 16.04 LTS ############################################################################### + # FIXME: We should probably do unoptimized build to avoid the compiler's + # optimizer removing bad behaviour. However we can't run the unit tests + # if we do a Debug build because they are too slow. # 64-bit UBSan build - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo UBSAN_BUILD=1 + # 64-bit ASan build + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo ASAN_BUILD=1 # 64-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo From 38d9e2df8489a16470239369cfc41f313446b755 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 20:02:25 +0100 Subject: [PATCH 109/148] [TravisCI] Make ASan/UBSan configuration a debug build. --- .travis.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index bf69573aa..534aeacf0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,13 +17,11 @@ env: ############################################################################### # Ubuntu 16.04 LTS ############################################################################### - # FIXME: We should probably do unoptimized build to avoid the compiler's - # optimizer removing bad behaviour. However we can't run the unit tests - # if we do a Debug build because they are too slow. - # 64-bit UBSan build - - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo UBSAN_BUILD=1 - # 64-bit ASan build - - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo ASAN_BUILD=1 + # FIXME: We should really run the unit tests too under ASan/UBSan but a debug build is too slow. + # 64-bit UBSan Debug build + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug UBSAN_BUILD=1 RUN_UNIT_TESTS=SKIP + # 64-bit ASan Debug build + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug ASAN_BUILD=1 RUN_UNIT_TESTS=SKIP # 64-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo From 5bcdea1ae501d9f7c6ff3759d4a9347ea7075d70 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 6 Oct 2017 20:16:09 +0100 Subject: [PATCH 110/148] [TravisCI] For ASan/LSan use larger context so we get larger stack traces if needed. --- contrib/ci/scripts/sanitizer_env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ci/scripts/sanitizer_env.sh b/contrib/ci/scripts/sanitizer_env.sh index 3b1ff7297..30a8ce5a4 100644 --- a/contrib/ci/scripts/sanitizer_env.sh +++ b/contrib/ci/scripts/sanitizer_env.sh @@ -8,7 +8,7 @@ if [ "X${ASAN_BUILD}" = "X1" ]; then # Use suppression files export LSAN_OPTIONS="print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/lsan.txt" - export ASAN_OPTIONS="print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" + export ASAN_OPTIONS="malloc_context_size=100,print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" : ${ASAN_SYMBOLIZER_PATH?"ASAN_SYMBOLIZER_PATH must be specified"} fi From a991e44a2528bdee40e43c90b7902ec136968d16 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 9 Oct 2017 23:25:53 +0100 Subject: [PATCH 111/148] [TravisCI] Fix typo in created directory for suppression files --- contrib/ci/Dockerfiles/z3_build.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 65cf08087..0b6d0ac0d 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -63,7 +63,7 @@ ENV \ # Build Z3 RUN mkdir -p "${Z3_SRC_DIR}" && \ mkdir -p "${Z3_SRC_DIR}/contrib/ci/scripts" && \ - mkdir -p "${Z3_SRC_DIR}/contrib/ci/suppressions/sanitizers" + mkdir -p "${Z3_SRC_DIR}/contrib/suppressions/sanitizers" # Deliberately leave out `contrib` ADD /cmake ${Z3_SRC_DIR}/cmake/ ADD /doc ${Z3_SRC_DIR}/doc/ From bcff86a31606e0410a9295ed8ba35636d4b078a2 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 9 Oct 2017 23:29:08 +0100 Subject: [PATCH 112/148] [LSan] Add suppression for part of #1297. --- contrib/suppressions/sanitizers/lsan.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/suppressions/sanitizers/lsan.txt b/contrib/suppressions/sanitizers/lsan.txt index a4ce0881a..437358132 100644 --- a/contrib/suppressions/sanitizers/lsan.txt +++ b/contrib/suppressions/sanitizers/lsan.txt @@ -1 +1,5 @@ # LeakSanitizer suppression file + +# FIXME: This looks a bug in Clang/LSan the error reported +# doesn't make sense. See https://github.com/Z3Prover/z3/issues/1297 +leak:_fini From f90fe928af7029cb4c2179da5eca6268b1d2efe5 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 9 Oct 2017 23:42:10 +0100 Subject: [PATCH 113/148] [LSan] Suppress another leak until I can figure out what is going on. --- contrib/suppressions/sanitizers/lsan.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/suppressions/sanitizers/lsan.txt b/contrib/suppressions/sanitizers/lsan.txt index 437358132..81a3c8b85 100644 --- a/contrib/suppressions/sanitizers/lsan.txt +++ b/contrib/suppressions/sanitizers/lsan.txt @@ -3,3 +3,6 @@ # FIXME: This looks a bug in Clang/LSan the error reported # doesn't make sense. See https://github.com/Z3Prover/z3/issues/1297 leak:_fini +# FIXME: I don't understand this leak. +# See https://github.com/Z3Prover/z3/issues/1297 +leak:error_code_example2 From ff5df20deb0e49a73514906db6491a80932ca5a0 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 11 Oct 2017 08:10:13 +0100 Subject: [PATCH 114/148] [LSan] Don't run `c_maxsat_example` with LeakSanitizer because it contains leaks that the Z3 developers don't intend to fix. --- contrib/ci/scripts/sanitizer_env.sh | 13 +++++++++++++ contrib/ci/scripts/test_z3_examples_cmake.sh | 10 +++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/contrib/ci/scripts/sanitizer_env.sh b/contrib/ci/scripts/sanitizer_env.sh index 30a8ce5a4..8cd646ce6 100644 --- a/contrib/ci/scripts/sanitizer_env.sh +++ b/contrib/ci/scripts/sanitizer_env.sh @@ -8,8 +8,21 @@ if [ "X${ASAN_BUILD}" = "X1" ]; then # Use suppression files export LSAN_OPTIONS="print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/lsan.txt" + # NOTE: If you get bad stacktraces try using `fast_unwind_on_malloc=0` + # NOTE: `malloc_context_size` controls size of recorded stacktrace for allocations. + # If the reported stacktraces appear incomplete try increasing the value. export ASAN_OPTIONS="malloc_context_size=100,print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" : ${ASAN_SYMBOLIZER_PATH?"ASAN_SYMBOLIZER_PATH must be specified"} + + # Run command without checking for leaks + function run_no_lsan() { + ASAN_OPTIONS="${ASAN_OPTIONS},detect_leaks=0" "${@}" + } +else + # In non-ASan build just run directly + function run_no_lsan() { + "${@}" + } fi if [ "X${UBSAN_BUILD}" = "X1" ]; then diff --git a/contrib/ci/scripts/test_z3_examples_cmake.sh b/contrib/ci/scripts/test_z3_examples_cmake.sh index c188dbcf4..477f7f747 100755 --- a/contrib/ci/scripts/test_z3_examples_cmake.sh +++ b/contrib/ci/scripts/test_z3_examples_cmake.sh @@ -42,9 +42,13 @@ run_quiet examples/tptp_build_dir/z3_tptp5 -help # Build an run c_maxsat_example cmake --build $(pwd) --target c_maxsat_example "${GENERATOR_ARGS[@]}" -run_quiet \ - examples/c_maxsat_example_build_dir/c_maxsat_example \ - ${Z3_SRC_DIR}/examples/maxsat/ex.smt +# FIXME: It is known that the maxsat example leaks memory and the +# the Z3 developers have stated this is "wontfix". +# See https://github.com/Z3Prover/z3/issues/1299 +run_no_lsan \ + run_quiet \ + examples/c_maxsat_example_build_dir/c_maxsat_example \ + ${Z3_SRC_DIR}/examples/maxsat/ex.smt if [ "X${UBSAN_BUILD}" = "X1" ]; then # FIXME: We really need libz3 to link against a shared UBSan runtime. From 8d600050dbd9c73c58152259d91f999f33c19a11 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 11 Oct 2017 08:17:57 +0100 Subject: [PATCH 115/148] [LSan] Remove suppression files. The were fixed on rebase --- contrib/suppressions/sanitizers/lsan.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/contrib/suppressions/sanitizers/lsan.txt b/contrib/suppressions/sanitizers/lsan.txt index 81a3c8b85..a4ce0881a 100644 --- a/contrib/suppressions/sanitizers/lsan.txt +++ b/contrib/suppressions/sanitizers/lsan.txt @@ -1,8 +1 @@ # LeakSanitizer suppression file - -# FIXME: This looks a bug in Clang/LSan the error reported -# doesn't make sense. See https://github.com/Z3Prover/z3/issues/1297 -leak:_fini -# FIXME: I don't understand this leak. -# See https://github.com/Z3Prover/z3/issues/1297 -leak:error_code_example2 From db7b2e989db5a597812f0153827155a8b909e7c1 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 11 Oct 2017 17:27:39 +0100 Subject: [PATCH 116/148] [TravisCI] Try to run the Python and .NET examples under ASan. --- .travis.yml | 2 +- contrib/ci/Dockerfiles/z3_build.Dockerfile | 2 ++ contrib/ci/scripts/ci_defaults.sh | 1 + contrib/ci/scripts/sanitizer_env.sh | 32 ++++++++++++++++++ contrib/ci/scripts/test_z3_examples_cmake.sh | 33 +++++++++++++------ .../ci/scripts/travis_ci_linux_entry_point.sh | 4 +++ 6 files changed, 63 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 534aeacf0..940f15f98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ env: # 64-bit UBSan Debug build - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug UBSAN_BUILD=1 RUN_UNIT_TESTS=SKIP # 64-bit ASan Debug build - - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug ASAN_BUILD=1 RUN_UNIT_TESTS=SKIP + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug ASAN_BUILD=1 RUN_UNIT_TESTS=SKIP ASAN_DSO=/usr/lib/clang/3.9/lib/linux/libclang_rt.asan-x86_64.so # 64-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 0b6d0ac0d..5b9ac9442 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -5,6 +5,7 @@ FROM ${DOCKER_IMAGE_BASE} # Build arguments. This can be changed when invoking # `docker build`. ARG ASAN_BUILD +ARG ASAN_DSO ARG BUILD_DOCS ARG CC ARG CXX @@ -32,6 +33,7 @@ ARG Z3_VERBOSE_BUILD_OUTPUT ENV \ ASAN_BUILD=${ASAN_BUILD} \ + ASAN_DSO=${ASAN_DSO} \ BUILD_DOCS=${BUILD_DOCS} \ CC=${CC} \ CXX=${CXX} \ diff --git a/contrib/ci/scripts/ci_defaults.sh b/contrib/ci/scripts/ci_defaults.sh index d8e9376d8..076d70180 100644 --- a/contrib/ci/scripts/ci_defaults.sh +++ b/contrib/ci/scripts/ci_defaults.sh @@ -49,6 +49,7 @@ unset PLATFORM # NOTE: The following variables are not set here because # they are specific to the CI implementation # PYTHON_EXECUTABLE +# ASAN_DSO # Z3_SRC_DIR # Z3_BUILD_DIR # Z3_SYSTEM_TEST_DIR diff --git a/contrib/ci/scripts/sanitizer_env.sh b/contrib/ci/scripts/sanitizer_env.sh index 8cd646ce6..0499eb268 100644 --- a/contrib/ci/scripts/sanitizer_env.sh +++ b/contrib/ci/scripts/sanitizer_env.sh @@ -18,11 +18,43 @@ if [ "X${ASAN_BUILD}" = "X1" ]; then function run_no_lsan() { ASAN_OPTIONS="${ASAN_OPTIONS},detect_leaks=0" "${@}" } + + # Check path to ASan DSO + : ${ASAN_DSO?"ASAN_DSO must be specified"} + if [ ! -e "${ASAN_DSO}" ]; then + echo "ASAN_DSO (${ASAN_DSO}) does not exist" + exit 1 + fi + # FIXME: We'll need to refactor this when we can do UBSan builds + # against a UBSan DSO. + function run_non_native_binding() { + # We need to preload the ASan DSO that libz3 + # will have undefined references to. + # Don't run leak checking because we get lots reported leaks + # in the language runtime (e.g. python). + PLATFORM="$(uname -s)" + case "${PLATFORM}" in + Linux*) + LD_PRELOAD="${ASAN_DSO}" run_no_lsan "${@}" + ;; + Darwin*) + DYLD_INSERT_LIBRARIES="${ASAN_DSO}" run_no_lsan "${@}" + ;; + *) + echo "Unknown platform \"${PLATFORM}\"" + exit 1 + ;; + esac + unset PLATFORM + } else # In non-ASan build just run directly function run_no_lsan() { "${@}" } + function run_non_native_binding() { + "${@}" + } fi if [ "X${UBSAN_BUILD}" = "X1" ]; then diff --git a/contrib/ci/scripts/test_z3_examples_cmake.sh b/contrib/ci/scripts/test_z3_examples_cmake.sh index 477f7f747..515027509 100755 --- a/contrib/ci/scripts/test_z3_examples_cmake.sh +++ b/contrib/ci/scripts/test_z3_examples_cmake.sh @@ -64,16 +64,21 @@ if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # `all_interval_series.py` produces a lot of output so just throw # away output. # TODO: This example is slow should we remove it from testing? - run_quiet ${PYTHON_EXECUTABLE} python/all_interval_series.py - run_quiet ${PYTHON_EXECUTABLE} python/complex.py - run_quiet ${PYTHON_EXECUTABLE} python/example.py + if [ "X${ASAN_BUILD}" = "X1" -a "X${Z3_BUILD_TYPE}" = "XDebug" ]; then + # Too slow when doing ASan Debug build + echo "Skipping all_interval_series.py under ASan Debug build" + else + run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/all_interval_series.py + fi + run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/complex.py + run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/example.py # FIXME: `hamiltonian.py` example is disabled because its too slow. #${PYTHON_EXECUTABLE} python/hamiltonian.py - run_quiet ${PYTHON_EXECUTABLE} python/marco.py - run_quiet ${PYTHON_EXECUTABLE} python/mss.py - run_quiet ${PYTHON_EXECUTABLE} python/socrates.py - run_quiet ${PYTHON_EXECUTABLE} python/visitor.py - run_quiet ${PYTHON_EXECUTABLE} python/z3test.py + run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/marco.py + run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/mss.py + run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/socrates.py + run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/visitor.py + run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/z3test.py fi if [ "X${DOTNET_BINDINGS}" = "X1" ]; then @@ -81,7 +86,7 @@ if [ "X${DOTNET_BINDINGS}" = "X1" ]; then # FIXME: Move compliation step into CMake target mcs ${Z3_SRC_DIR}/examples/dotnet/Program.cs /target:exe /out:dotnet_test.exe /reference:Microsoft.Z3.dll /r:System.Numerics.dll # Run .NET example - run_quiet mono ./dotnet_test.exe + run_non_native_binding run_quiet mono ./dotnet_test.exe fi if [ "X${JAVA_BINDINGS}" = "X1" ]; then @@ -98,6 +103,14 @@ if [ "X${JAVA_BINDINGS}" = "X1" ]; then # Assume Linux for now export LD_LIBRARY_PATH=$(pwd):${LD_LIBRARY_PATH} fi - run_quiet java -cp .:examples/java:com.microsoft.z3.jar JavaExample + if [ "X${ASAN_BUILD}" = "X1" ]; then + # The JVM seems to crash (SEGV) if we pre-load ASan + # so don't run it for now. + echo "Skipping JavaExample under ASan build" + else + run_non_native_binding \ + run_quiet \ + java -cp .:examples/java:com.microsoft.z3.jar JavaExample + fi fi diff --git a/contrib/ci/scripts/travis_ci_linux_entry_point.sh b/contrib/ci/scripts/travis_ci_linux_entry_point.sh index bd2c9d2d1..d60b06f8f 100755 --- a/contrib/ci/scripts/travis_ci_linux_entry_point.sh +++ b/contrib/ci/scripts/travis_ci_linux_entry_point.sh @@ -84,6 +84,10 @@ if [ -n "${ASAN_BUILD}" ]; then BUILD_OPTS+=("--build-arg" "ASAN_BUILD=${ASAN_BUILD}") fi +if [ -n "${ASAN_DSO}" ]; then + BUILD_OPTS+=("--build-arg" "ASAN_DSO=${ASAN_DSO}") +fi + if [ -n "${UBSAN_BUILD}" ]; then BUILD_OPTS+=("--build-arg" "UBSAN_BUILD=${UBSAN_BUILD}") fi From 675a3ae9dd14f8dbda6d9d017566279e716b0c68 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 14 Oct 2017 22:40:42 +0100 Subject: [PATCH 117/148] [UBSan] Remove a bunch of suppressions. --- contrib/suppressions/sanitizers/ubsan.txt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/contrib/suppressions/sanitizers/ubsan.txt b/contrib/suppressions/sanitizers/ubsan.txt index 6ceacd400..4d19e40be 100644 --- a/contrib/suppressions/sanitizers/ubsan.txt +++ b/contrib/suppressions/sanitizers/ubsan.txt @@ -1,20 +1,7 @@ # UndefinedBehavior sanitizer suppression file # FIXME: UBSan doesn't usually have false positives so we need to fix all of these! -# Occurs when running C API example (`interpolation_example`) -# See https://github.com/Z3Prover/z3/issues/1286 -null:iz3mgr.h - -# Occurs when running C++ API example -# See https://github.com/Z3Prover/z3/issues/1287 -function:api_context.cpp - # Occurs when running tptp example # See https://github.com/Z3Prover/z3/issues/964 null:rational.h null:mpq.h - -# Occurs when running `test-z3 /a` -# See https://github.com/Z3Prover/z3/issues/1288 -shift-exponent:tbv.cpp -signed-integer-overflow:mpz.cpp From fd391e75a6afdc997322d87371b2228b419b2296 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 11:54:05 +0100 Subject: [PATCH 118/148] [TravisCI] Fix `Z3_BUILD_TYPE` variable that was not propagated into the Docker image as an environment variable. --- contrib/ci/Dockerfiles/z3_build.Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 5b9ac9442..562c2dfe5 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -52,6 +52,7 @@ ENV \ USE_OPENMP=${USE_OPENMP} \ Z3_SRC_DIR=${Z3_SRC_DIR} \ Z3_BUILD_DIR=/home/user/z3_build \ + Z3_BUILD_TYPE=${Z3_BUILD_TYPE} \ Z3_CMAKE_GENERATOR=${Z3_CMAKE_GENERATOR} \ Z3_VERBOSE_BUILD_OUTPUT=${Z3_VERBOSE_BUILD_OUTPUT} \ Z3_STATIC_BUILD=${Z3_STATIC_BUILD} \ From 2dd1a4046d1579a3f953b53cb1b613c289cd51f4 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 12:02:13 +0100 Subject: [PATCH 119/148] [TravisCI] Fix typo --- contrib/ci/scripts/test_z3_system_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ci/scripts/test_z3_system_tests.sh b/contrib/ci/scripts/test_z3_system_tests.sh index 71ecf2a1e..bcffaf56e 100755 --- a/contrib/ci/scripts/test_z3_system_tests.sh +++ b/contrib/ci/scripts/test_z3_system_tests.sh @@ -28,7 +28,7 @@ Z3_LIB_DIR="${Z3_BUILD_DIR}" Z3_SYSTEM_TEST_GIT_URL="${Z3_GIT_URL:-https://github.com/Z3Prover/z3test.git}" # Clone repo to destination -mkdir -p "${Z3_SYSTEM_TEST_GIT_URL}" +mkdir -p "${Z3_SYSTEM_TEST_DIR}" git clone "${Z3_SYSTEM_TEST_GIT_URL}" "${Z3_SYSTEM_TEST_DIR}" cd "${Z3_SYSTEM_TEST_DIR}" From fd98593a5882075df3afa4c63cb47ebd7f73abee Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 12:19:16 +0100 Subject: [PATCH 120/148] [ASan] Ignore Clang OpenMP leaks for now. --- contrib/suppressions/sanitizers/lsan.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/suppressions/sanitizers/lsan.txt b/contrib/suppressions/sanitizers/lsan.txt index a4ce0881a..1480b7c09 100644 --- a/contrib/suppressions/sanitizers/lsan.txt +++ b/contrib/suppressions/sanitizers/lsan.txt @@ -1 +1,5 @@ # LeakSanitizer suppression file + +# Ignore Clang OpenMP leaks. +# See https://github.com/Z3Prover/z3/issues/1308 +leak:___kmp_allocate From 35f6746c60a40eeb3c3356e6a8652f72792a1c30 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 13:21:40 +0100 Subject: [PATCH 121/148] Workaround `regressions/smt2/error.smt2` test timing out. When ASan's LeakSanitizer is enabled leak checking is triggered when `exit()` is called and it returns so many false positives that it takes a long time to write them to the console. To workaround this we simply call `_Exit()` instead. --- src/parsers/smt2/smt2parser.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index b8ad7e610..b43ee9f6e 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -444,7 +444,10 @@ namespace smt2 { m_ctx.regular_stream()<< "line " << line << " column " << pos << ": " << escaped(msg, true) << "\")" << std::endl; } if (m_ctx.exit_on_error()) { - exit(1); + // WORKAROUND: ASan's LeakSanitizer reports many false positives when + // calling `exit()` so call `_Exit()` instead which avoids invoking leak + // checking. + _Exit(1); } } From ad2a0a0085773e1279526dce7b7bfaf167caeb40 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 15:27:10 +0100 Subject: [PATCH 122/148] [TravisCI] Don't print sanitizer suppressions by default because that breaks Z3's regression tests. --- contrib/ci/Dockerfiles/z3_build.Dockerfile | 2 ++ contrib/ci/README.md | 1 + contrib/ci/scripts/ci_defaults.sh | 4 ++++ contrib/ci/scripts/sanitizer_env.sh | 18 +++++++++++++++--- .../ci/scripts/travis_ci_linux_entry_point.sh | 4 ++++ 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 562c2dfe5..152b109d7 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -16,6 +16,7 @@ ARG PYTHON_BINDINGS ARG PYTHON_EXECUTABLE=/usr/bin/python2.7 ARG RUN_SYSTEM_TESTS ARG RUN_UNIT_TESTS +ARG SANITIZER_PRINT_SUPPRESSIONS ARG TARGET_ARCH ARG TEST_INSTALL ARG UBSAN_BUILD @@ -42,6 +43,7 @@ ENV \ NO_SUPPRESS_OUTPUT=${NO_SUPPRESS_OUTPUT} \ PYTHON_BINDINGS=${PYTHON_BINDINGS} \ PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} \ + SANITIZER_PRINT_SUPPRESSIONS=${SANITIZER_PRINT_SUPPRESSIONS} \ RUN_SYSTEM_TESTS=${RUN_SYSTEM_TESTS} \ RUN_UNIT_TESTS=${RUN_UNIT_TESTS} \ TARGET_ARCH=${TARGET_ARCH} \ diff --git a/contrib/ci/README.md b/contrib/ci/README.md index 99bbcd7a6..95ed7f398 100644 --- a/contrib/ci/README.md +++ b/contrib/ci/README.md @@ -32,6 +32,7 @@ the future. * `PYTHON_BINDINGS` - Build and test Python API bindings (`0` or `1`) * `RUN_SYSTEM_TESTS` - Run system tests (`0` or `1`) * `RUN_UNIT_TESTS` - Run unit tests (`BUILD_ONLY` or `BUILD_AND_RUN` or `SKIP`) +* `SANITIZER_PRINT_SUPPRESSIONS` - Show ASan/UBSan suppressions (`0` or `1`) * `TARGET_ARCH` - Target architecture (`x86_64` or `i686`) * `TEST_INSTALL` - Test running `install` target (`0` or `1`) * `UBSAN_BUILD` - Do [UndefinedBehaviourSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) build (`0` or `1`) diff --git a/contrib/ci/scripts/ci_defaults.sh b/contrib/ci/scripts/ci_defaults.sh index 076d70180..2029981c0 100644 --- a/contrib/ci/scripts/ci_defaults.sh +++ b/contrib/ci/scripts/ci_defaults.sh @@ -11,6 +11,10 @@ export NO_SUPPRESS_OUTPUT="${NO_SUPPRESS_OUTPUT:-0}" export PYTHON_BINDINGS="${PYTHON_BINDINGS:-1}" export RUN_SYSTEM_TESTS="${RUN_SYSTEM_TESTS:-1}" export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-BUILD_AND_RUN}" +# Don't print suppressions by default because that breaks the Z3 +# regression tests because they don't expect them to appear in Z3's +# output. +export SANITIZER_PRINT_SUPPRESSIONS="${SANITIZER_PRINT_SUPPRESSIONS:-0}" export TARGET_ARCH="${TARGET_ARCH:-x86_64}" export TEST_INSTALL="${TEST_INSTALL:-1}" export UBSAN_BUILD="${UBSAN_BUILD:-0}" diff --git a/contrib/ci/scripts/sanitizer_env.sh b/contrib/ci/scripts/sanitizer_env.sh index 0499eb268..959f136e5 100644 --- a/contrib/ci/scripts/sanitizer_env.sh +++ b/contrib/ci/scripts/sanitizer_env.sh @@ -7,11 +7,18 @@ if [ "X${ASAN_BUILD}" = "X1" ]; then # Use suppression files - export LSAN_OPTIONS="print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/lsan.txt" + export LSAN_OPTIONS="suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/lsan.txt" # NOTE: If you get bad stacktraces try using `fast_unwind_on_malloc=0` # NOTE: `malloc_context_size` controls size of recorded stacktrace for allocations. # If the reported stacktraces appear incomplete try increasing the value. - export ASAN_OPTIONS="malloc_context_size=100,print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" + export ASAN_OPTIONS="malloc_context_size=100,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" + + : ${SANITIZER_PRINT_SUPPRESSIONS?"SANITIZER_PRINT_SUPPRESSIONS must be specified"} + if [ "X${SANITIZER_PRINT_SUPPRESSIONS}" = "X1" ]; then + export LSAN_OPTIONS="${LSAN_OPTIONS},print_suppressions=1" + export ASAN_OPTIONS="${ASAN_OPTIONS},print_suppressions=1" + fi + : ${ASAN_SYMBOLIZER_PATH?"ASAN_SYMBOLIZER_PATH must be specified"} # Run command without checking for leaks @@ -61,5 +68,10 @@ if [ "X${UBSAN_BUILD}" = "X1" ]; then # `halt_on_error=1,abort_on_error=1` means that on the first UBSan error # the program will terminate by calling `abort(). Without this UBSan will # allow execution to continue. We also use a suppression file. - export UBSAN_OPTIONS="halt_on_error=1,abort_on_error=1,print_suppressions=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/ubsan.txt" + export UBSAN_OPTIONS="halt_on_error=1,abort_on_error=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/ubsan.txt" + + : ${SANITIZER_PRINT_SUPPRESSIONS?"SANITIZER_PRINT_SUPPRESSIONS must be specified"} + if [ "X${SANITIZER_PRINT_SUPPRESSIONS}" = "X1" ]; then + export UBSAN_OPTIONS="${UBSAN_OPTIONS},print_suppressions=1" + fi fi diff --git a/contrib/ci/scripts/travis_ci_linux_entry_point.sh b/contrib/ci/scripts/travis_ci_linux_entry_point.sh index d60b06f8f..352e13de0 100755 --- a/contrib/ci/scripts/travis_ci_linux_entry_point.sh +++ b/contrib/ci/scripts/travis_ci_linux_entry_point.sh @@ -88,6 +88,10 @@ if [ -n "${ASAN_DSO}" ]; then BUILD_OPTS+=("--build-arg" "ASAN_DSO=${ASAN_DSO}") fi +if [ -n "${SANITIZER_PRINT_SUPPRESSIONS}" ]; then + BUILD_OPTS+=("--build-arg" "SANITIZER_PRINT_SUPPRESSIONS=${SANITIZER_PRINT_SUPPRESSIONS}") +fi + if [ -n "${UBSAN_BUILD}" ]; then BUILD_OPTS+=("--build-arg" "UBSAN_BUILD=${UBSAN_BUILD}") fi From ecadef6e48460cdbb271c13817534b357bd92341 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 17:25:12 +0100 Subject: [PATCH 123/148] [TravisCI] Try to fix case in `run_quiet` where the script would fail with. ``` -ne: unary operator expected ``` --- contrib/ci/scripts/run_quiet.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/ci/scripts/run_quiet.sh b/contrib/ci/scripts/run_quiet.sh index 5abc910e8..0f49da3be 100644 --- a/contrib/ci/scripts/run_quiet.sh +++ b/contrib/ci/scripts/run_quiet.sh @@ -34,8 +34,8 @@ function run_quiet() { fi # Clean up rm "${STDOUT}" "${STDERR}" - [ $( echo "${OLD_SETTINGS}" | grep -c 'e') -ne 0 ] && set -e - [ $( echo "${OLD_SETTINGS}" | grep -c 'x') -ne 0 ] && set -x + [ "$( echo "${OLD_SETTINGS}" | grep -c 'e')" != "0" ] && set -e + [ "$( echo "${OLD_SETTINGS}" | grep -c 'x')" != "0" ] && set -x return ${EXIT_STATUS} fi } From ead6e56d15432384ece80361dd1969c2367a0fe2 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 17:41:40 +0100 Subject: [PATCH 124/148] [TravisCI] Swap `run_quiet` and `run_non_native_binding`. In the previous order `grep` inside `run_quiet` would get ASan LD_PRELOAD'ed which would sometimes fail. --- contrib/ci/scripts/test_z3_examples_cmake.sh | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/contrib/ci/scripts/test_z3_examples_cmake.sh b/contrib/ci/scripts/test_z3_examples_cmake.sh index 515027509..9979b8773 100755 --- a/contrib/ci/scripts/test_z3_examples_cmake.sh +++ b/contrib/ci/scripts/test_z3_examples_cmake.sh @@ -68,17 +68,17 @@ if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # Too slow when doing ASan Debug build echo "Skipping all_interval_series.py under ASan Debug build" else - run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/all_interval_series.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/all_interval_series.py fi - run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/complex.py - run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/example.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/complex.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/example.py # FIXME: `hamiltonian.py` example is disabled because its too slow. #${PYTHON_EXECUTABLE} python/hamiltonian.py - run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/marco.py - run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/mss.py - run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/socrates.py - run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/visitor.py - run_non_native_binding run_quiet ${PYTHON_EXECUTABLE} python/z3test.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/marco.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/mss.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/socrates.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/visitor.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/z3test.py fi if [ "X${DOTNET_BINDINGS}" = "X1" ]; then @@ -86,7 +86,7 @@ if [ "X${DOTNET_BINDINGS}" = "X1" ]; then # FIXME: Move compliation step into CMake target mcs ${Z3_SRC_DIR}/examples/dotnet/Program.cs /target:exe /out:dotnet_test.exe /reference:Microsoft.Z3.dll /r:System.Numerics.dll # Run .NET example - run_non_native_binding run_quiet mono ./dotnet_test.exe + run_quiet run_non_native_binding mono ./dotnet_test.exe fi if [ "X${JAVA_BINDINGS}" = "X1" ]; then @@ -108,8 +108,8 @@ if [ "X${JAVA_BINDINGS}" = "X1" ]; then # so don't run it for now. echo "Skipping JavaExample under ASan build" else - run_non_native_binding \ - run_quiet \ + run_quiet \ + run_non_native_binding \ java -cp .:examples/java:com.microsoft.z3.jar JavaExample fi fi From e51ce8bcafc585ee23bea555f2c91a22646445ef Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 19:54:50 +0100 Subject: [PATCH 125/148] [TravisCI] Try again to not show suppressions by default --- contrib/ci/scripts/sanitizer_env.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/ci/scripts/sanitizer_env.sh b/contrib/ci/scripts/sanitizer_env.sh index 959f136e5..f1fa87442 100644 --- a/contrib/ci/scripts/sanitizer_env.sh +++ b/contrib/ci/scripts/sanitizer_env.sh @@ -17,6 +17,9 @@ if [ "X${ASAN_BUILD}" = "X1" ]; then if [ "X${SANITIZER_PRINT_SUPPRESSIONS}" = "X1" ]; then export LSAN_OPTIONS="${LSAN_OPTIONS},print_suppressions=1" export ASAN_OPTIONS="${ASAN_OPTIONS},print_suppressions=1" + else + export LSAN_OPTIONS="${LSAN_OPTIONS},print_suppressions=0" + export ASAN_OPTIONS="${ASAN_OPTIONS},print_suppressions=0" fi : ${ASAN_SYMBOLIZER_PATH?"ASAN_SYMBOLIZER_PATH must be specified"} @@ -73,5 +76,7 @@ if [ "X${UBSAN_BUILD}" = "X1" ]; then : ${SANITIZER_PRINT_SUPPRESSIONS?"SANITIZER_PRINT_SUPPRESSIONS must be specified"} if [ "X${SANITIZER_PRINT_SUPPRESSIONS}" = "X1" ]; then export UBSAN_OPTIONS="${UBSAN_OPTIONS},print_suppressions=1" + else + export UBSAN_OPTIONS="${UBSAN_OPTIONS},print_suppressions=0" fi fi From dbb7f616c157359eaaeee9b9a340217a8b522521 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sun, 15 Oct 2017 21:36:02 +0100 Subject: [PATCH 126/148] More LSan workarounds. --- src/cmd_context/cmd_context.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 95c030687..9615e86ce 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1608,7 +1608,9 @@ void cmd_context::validate_check_sat_result(lbool r) { throw cmd_exception("check annotation that says unsat"); #else diagnostic_stream() << "BUG: incompleteness" << std::endl; - exit(ERR_INCOMPLETENESS); + // WORKAROUND: `exit()` causes LSan to be invoked and produce + // many false positives. + _Exit(ERR_INCOMPLETENESS); #endif } break; @@ -1618,7 +1620,9 @@ void cmd_context::validate_check_sat_result(lbool r) { throw cmd_exception("check annotation that says sat"); #else diagnostic_stream() << "BUG: unsoundness" << std::endl; - exit(ERR_UNSOUNDNESS); + // WORKAROUND: `exit()` causes LSan to be invoked and produce + // many false positives. + _Exit(ERR_UNSOUNDNESS); #endif } break; From 88fb31ac089a4220fc25f3d3917d88a2af746eca Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 16 Oct 2017 08:50:50 +0100 Subject: [PATCH 127/148] [TravisCI] Add `RUN_API_EXAMPLES` option so that we can disable building/running examples in some configurations. --- contrib/ci/Dockerfiles/z3_build.Dockerfile | 2 ++ contrib/ci/README.md | 1 + contrib/ci/scripts/ci_defaults.sh | 1 + contrib/ci/scripts/test_z3_examples_cmake.sh | 6 ++++++ contrib/ci/scripts/travis_ci_linux_entry_point.sh | 4 ++++ 5 files changed, 14 insertions(+) diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 152b109d7..eb2e03a43 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -14,6 +14,7 @@ ARG JAVA_BINDINGS ARG NO_SUPPRESS_OUTPUT ARG PYTHON_BINDINGS ARG PYTHON_EXECUTABLE=/usr/bin/python2.7 +ARG RUN_API_EXAMPLES ARG RUN_SYSTEM_TESTS ARG RUN_UNIT_TESTS ARG SANITIZER_PRINT_SUPPRESSIONS @@ -44,6 +45,7 @@ ENV \ PYTHON_BINDINGS=${PYTHON_BINDINGS} \ PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} \ SANITIZER_PRINT_SUPPRESSIONS=${SANITIZER_PRINT_SUPPRESSIONS} \ + RUN_API_EXAMPLES=${RUN_API_EXAMPLES} \ RUN_SYSTEM_TESTS=${RUN_SYSTEM_TESTS} \ RUN_UNIT_TESTS=${RUN_UNIT_TESTS} \ TARGET_ARCH=${TARGET_ARCH} \ diff --git a/contrib/ci/README.md b/contrib/ci/README.md index 95ed7f398..bd1c52792 100644 --- a/contrib/ci/README.md +++ b/contrib/ci/README.md @@ -30,6 +30,7 @@ the future. * `JAVA_BINDINGS` - Build and test Java API bindings (`0` or `1`) * `NO_SUPPRESS_OUTPUT` - Don't suppress output of some commands (`0` or `1`) * `PYTHON_BINDINGS` - Build and test Python API bindings (`0` or `1`) +* `RUN_API_EXAMPLES` - Build and run API examples (`0` or `1`) * `RUN_SYSTEM_TESTS` - Run system tests (`0` or `1`) * `RUN_UNIT_TESTS` - Run unit tests (`BUILD_ONLY` or `BUILD_AND_RUN` or `SKIP`) * `SANITIZER_PRINT_SUPPRESSIONS` - Show ASan/UBSan suppressions (`0` or `1`) diff --git a/contrib/ci/scripts/ci_defaults.sh b/contrib/ci/scripts/ci_defaults.sh index 2029981c0..7a4434bd5 100644 --- a/contrib/ci/scripts/ci_defaults.sh +++ b/contrib/ci/scripts/ci_defaults.sh @@ -9,6 +9,7 @@ export DOTNET_BINDINGS="${DOTNET_BINDINGS:-1}" export JAVA_BINDINGS="${JAVA_BINDINGS:-1}" export NO_SUPPRESS_OUTPUT="${NO_SUPPRESS_OUTPUT:-0}" export PYTHON_BINDINGS="${PYTHON_BINDINGS:-1}" +export RUN_API_EXAMPLES="${RUN_API_EXAMPLES:-1}" export RUN_SYSTEM_TESTS="${RUN_SYSTEM_TESTS:-1}" export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-BUILD_AND_RUN}" # Don't print suppressions by default because that breaks the Z3 diff --git a/contrib/ci/scripts/test_z3_examples_cmake.sh b/contrib/ci/scripts/test_z3_examples_cmake.sh index 9979b8773..687efebb4 100755 --- a/contrib/ci/scripts/test_z3_examples_cmake.sh +++ b/contrib/ci/scripts/test_z3_examples_cmake.sh @@ -15,6 +15,12 @@ set -o pipefail : ${DOTNET_BINDINGS?"DOTNET_BINDINGS must be specified"} : ${JAVA_BINDINGS?"JAVA_BINDINGS must be specified"} : ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} +: ${RUN_API_EXAMPLES?"RUN_API_EXAMPLES must be specified"} + +if [ "X${RUN_API_EXAMPLES}" = "X0" ]; then + echo "Skipping run of API examples" + exit 0 +fi # Set compiler flags source ${SCRIPT_DIR}/set_compiler_flags.sh diff --git a/contrib/ci/scripts/travis_ci_linux_entry_point.sh b/contrib/ci/scripts/travis_ci_linux_entry_point.sh index 352e13de0..731ea9ff0 100755 --- a/contrib/ci/scripts/travis_ci_linux_entry_point.sh +++ b/contrib/ci/scripts/travis_ci_linux_entry_point.sh @@ -100,6 +100,10 @@ if [ -n "${TEST_INSTALL}" ]; then BUILD_OPTS+=("--build-arg" "TEST_INSTALL=${TEST_INSTALL}") fi +if [ -n "${RUN_API_EXAMPLES}" ]; then + BUILD_OPTS+=("--build-arg" "RUN_API_EXAMPLES=${RUN_API_EXAMPLES}") +fi + if [ -n "${RUN_SYSTEM_TESTS}" ]; then BUILD_OPTS+=("--build-arg" "RUN_SYSTEM_TESTS=${RUN_SYSTEM_TESTS}") fi From 8835b54d1669d54d9595287a015acdf39e84a55c Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 16 Oct 2017 08:55:57 +0100 Subject: [PATCH 128/148] [TravisCI] Add ASan/UBSan configuration that runs unit tests. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 940f15f98..a3ffb221e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,11 +17,14 @@ env: ############################################################################### # Ubuntu 16.04 LTS ############################################################################### - # FIXME: We should really run the unit tests too under ASan/UBSan but a debug build is too slow. # 64-bit UBSan Debug build - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug UBSAN_BUILD=1 RUN_UNIT_TESTS=SKIP # 64-bit ASan Debug build - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug ASAN_BUILD=1 RUN_UNIT_TESTS=SKIP ASAN_DSO=/usr/lib/clang/3.9/lib/linux/libclang_rt.asan-x86_64.so + # Build for running unit tests under ASan/UBSan + # FIXME: We should really be doing a debug build but the unit tests run too + # slowly when we do that. + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo ASAN_BUILD=1 RUN_UNIT_TESTS=BUILD_AND_RUN ASAN_DSO=/usr/lib/clang/3.9/lib/linux/libclang_rt.asan-x86_64.so UBSAN_BUILD=1 RUN_API_EXAMPLES=0 RUN_SYSTEM_TESTS=0 DOTNET_BINDINGS=0 JAVA_BINDINGS=0 PYTHON_BINDINGS=0 # 64-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo From a1b6316e2e3ae19e9e8f67af6e17c13771d73c81 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 16 Oct 2017 09:49:54 +0100 Subject: [PATCH 129/148] [TravisCI] Try to unbreak running Python regression tests under ASan. --- contrib/ci/scripts/test_z3_system_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ci/scripts/test_z3_system_tests.sh b/contrib/ci/scripts/test_z3_system_tests.sh index bcffaf56e..6f9901687 100755 --- a/contrib/ci/scripts/test_z3_system_tests.sh +++ b/contrib/ci/scripts/test_z3_system_tests.sh @@ -58,7 +58,7 @@ if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # to work. echo "FIXME: Skipping python binding tests when building with UBSan" else - ${PYTHON_EXECUTABLE} scripts/test_pyscripts.py "${Z3_LIB_DIR}" regressions/python/ + run_non_native_binding ${PYTHON_EXECUTABLE} scripts/test_pyscripts.py "${Z3_LIB_DIR}" regressions/python/ fi fi From 7c99721b60e9f5a8e6a2fe502ff56380e808916b Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 16 Oct 2017 13:21:03 +0100 Subject: [PATCH 130/148] [TravisCI] Don't run Python regression tests under ASan for now. The script that runs them doesn't propagate LD_PRELOAD and so the tests fail. --- contrib/ci/scripts/test_z3_system_tests.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/ci/scripts/test_z3_system_tests.sh b/contrib/ci/scripts/test_z3_system_tests.sh index 6f9901687..19c179268 100755 --- a/contrib/ci/scripts/test_z3_system_tests.sh +++ b/contrib/ci/scripts/test_z3_system_tests.sh @@ -57,6 +57,11 @@ if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # FIXME: We need to build libz3 with a shared UBSan runtime for the bindings # to work. echo "FIXME: Skipping python binding tests when building with UBSan" + elif [ "X${ASAN_BUILD}" = "X1" ]; then + # FIXME: The `test_pyscripts.py` doesn't propagate LD_PRELOAD + # so under ASan the tests fail to run + # to work. + echo "FIXME: Skipping python binding tests when building with ASan" else run_non_native_binding ${PYTHON_EXECUTABLE} scripts/test_pyscripts.py "${Z3_LIB_DIR}" regressions/python/ fi From cda03b4238852544b27a750603c4b3d4178bba8f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 16 Oct 2017 17:01:09 +0100 Subject: [PATCH 131/148] Whitespace --- src/ast/normal_forms/nnf.cpp | 94 ++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 014568812..42b4e9350 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -40,7 +40,7 @@ enum nnf_mode { transformation will be in skolem normal form. If a formula is too expensive to be put into NNF, then nested quantifiers and labels are renamed. - + This mode is sufficient when using E-matching. */ NNF_QUANT, /* A subformula is put into NNF if it contains @@ -48,7 +48,7 @@ enum nnf_mode { quantifier. The result of the transformation will be in skolem normal form, and the body of quantifiers will be in NNF. If a ground formula is too expensive to - be put into NNF, then nested quantifiers and labels + be put into NNF, then nested quantifiers and labels are renamed. This mode is sufficient when using Superposition @@ -89,7 +89,7 @@ class skolemizer { } TRACE("skolemizer", tout << "skid: " << q->get_skid() << "\n";); - + expr_ref_vector substitution(m()); unsigned num_decls = q->get_num_decls(); for (unsigned i = num_decls; i > 0; ) { @@ -111,7 +111,7 @@ class skolemizer { substitution.push_back(0); } // - // (VAR num_decls) ... (VAR num_decls+sz-1) + // (VAR num_decls) ... (VAR num_decls+sz-1) // are in positions num_decls .. num_decls+sz-1 // std::reverse(substitution.c_ptr(), substitution.c_ptr() + substitution.size()); @@ -139,7 +139,7 @@ class skolemizer { s(body, substitution.size(), substitution.c_ptr(), r); p = 0; if (m().proofs_enabled()) { - if (q->is_forall()) + if (q->is_forall()) p = m().mk_skolemization(m().mk_not(q), m().mk_not(r)); else p = m().mk_skolemization(q, r); @@ -175,7 +175,7 @@ public: m_cache_pr.insert(q, p); } } - + bool is_sk_hack(expr * p) const { SASSERT(m().is_pattern(p)); if (to_app(p)->get_num_args() != 1) @@ -204,7 +204,7 @@ struct nnf::imp { unsigned m_i:28; unsigned m_pol:1; // pos/neg polarity unsigned m_in_q:1; // true if m_curr is nested in a quantifier - unsigned m_new_child:1; + unsigned m_new_child:1; unsigned m_cache_result:1; unsigned m_spos; // top of the result stack, when the frame was created. frame(expr_ref&& n, bool pol, bool in_q, bool cache_res, unsigned spos): @@ -223,22 +223,22 @@ struct nnf::imp { #define POS_NQ_CIDX 1 // positive polarity and not nested in a quantifier #define NEG_Q_CIDX 2 // negative polarity and nested in a quantifier #define POS_Q_CIDX 3 // positive polarity and nested in a quantifier - + ast_manager & m_manager; vector m_frame_stack; expr_ref_vector m_result_stack; - + typedef act_cache cache; cache * m_cache[4]; expr_ref_vector m_todo_defs; proof_ref_vector m_todo_proofs; - + // proof generation goodness ---- proof_ref_vector m_result_pr_stack; cache * m_cache_pr[4]; // ------------------------------ - + skolemizer m_skolemizer; // configuration ---------------- @@ -249,7 +249,7 @@ struct nnf::imp { name_exprs * m_name_nested_formulas; name_exprs * m_name_quant; - + unsigned long long m_max_memory; // in bytes imp(ast_manager & m, defined_names & n, params_ref const & p): @@ -292,9 +292,9 @@ struct nnf::imp { m_mode = NNF_FULL; else if (mode_sym == "quantifiers") m_mode = NNF_QUANT; - else + else throw nnf_params_exception("invalid NNF mode"); - + TRACE("nnf", tout << "nnf-mode: " << m_mode << " " << mode_sym << "\n" << _p << "\n";); m_ignore_labels = p.ignore_labels(); @@ -327,8 +327,8 @@ struct nnf::imp { m_frame_stack.push_back(frame({ t, m() }, pol, in_q, cache_res, m_result_stack.size())); } - static unsigned get_cache_idx(bool pol, bool in_q) { - return static_cast(in_q) * 2 + static_cast(pol); + static unsigned get_cache_idx(bool pol, bool in_q) { + return static_cast(in_q) * 2 + static_cast(pol); } void cache_result(expr * t, bool pol, bool in_q, expr * v, proof * pr) { @@ -338,8 +338,8 @@ struct nnf::imp { m_cache_pr[idx]->insert(t, pr); } - expr * get_cached(expr * t, bool pol, bool in_q) const { - return m_cache[get_cache_idx(pol, in_q)]->find(t); + expr * get_cached(expr * t, bool pol, bool in_q) const { + return m_cache[get_cache_idx(pol, in_q)]->find(t); } proof * get_cached_pr(expr * t, bool pol, bool in_q) const { @@ -367,12 +367,12 @@ struct nnf::imp { return false; } - + void checkpoint() { cooperate("nnf"); if (memory::get_allocation_size() > m_max_memory) throw nnf_exception(Z3_MAX_MEMORY_MSG); - if (m().canceled()) + if (m().canceled()) throw nnf_exception(m().limit().get_cancel_msg()); } @@ -381,11 +381,11 @@ struct nnf::imp { m_frame_stack.back().m_new_child = true; } - void set_new_child_flag(expr * old_t, expr * new_t) { - if (old_t != new_t) - set_new_child_flag(); + void set_new_child_flag(expr * old_t, expr * new_t) { + if (old_t != new_t) + set_new_child_flag(); } - + void skip(expr * t, bool pol) { expr * r = pol ? t : m().mk_not(t); m_result_stack.push_back(r); @@ -447,10 +447,10 @@ struct nnf::imp { if (pol) { if (old_e->get_decl() == new_e->get_decl()) return m().mk_oeq_congruence(old_e, new_e, num_parents, parents); - else + else return m().mk_nnf_pos(old_e, new_e, num_parents, parents); } - else + else return m().mk_nnf_neg(old_e, new_e, num_parents, parents); } @@ -467,7 +467,7 @@ struct nnf::imp { r = m().mk_and(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); else r = m().mk_or(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); - + m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -519,7 +519,7 @@ struct nnf::imp { r = m().mk_or(2, m_result_stack.c_ptr() + fr.m_spos); else r = m().mk_and(2, m_result_stack.c_ptr() + fr.m_spos); - + m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -553,7 +553,7 @@ struct nnf::imp { default: break; } - + expr * const * rs = m_result_stack.c_ptr() + fr.m_spos; expr * _cond = rs[0]; expr * _not_cond = rs[1]; @@ -573,7 +573,7 @@ struct nnf::imp { } bool is_eq(app * t) const { return m().is_eq(t) || m().is_iff(t); } - + bool process_iff_xor(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); switch (fr.m_i) { @@ -604,7 +604,7 @@ struct nnf::imp { expr * not_rhs = rs[3]; app * r; - if (is_eq(t) == fr.m_pol) + if (is_eq(t) == fr.m_pol) r = m().mk_and(m().mk_or(not_lhs, rhs), m().mk_or(lhs, not_rhs)); else r = m().mk_and(m().mk_or(lhs, rhs), m().mk_or(not_lhs, not_rhs)); @@ -625,7 +625,7 @@ struct nnf::imp { else return process_default(t, fr); } - + bool process_default(app * t, frame & fr) { SASSERT(fr.m_i == 0); if (m_mode == NNF_FULL || t->has_quantifiers() || t->has_labels()) { @@ -635,10 +635,10 @@ struct nnf::imp { m_name_nested_formulas->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); else m_name_quant->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); - + if (!fr.m_pol) n2 = m().mk_not(n2); - + m_result_stack.push_back(n2); if (proofs_enabled()) { if (!fr.m_pol) { @@ -665,10 +665,10 @@ struct nnf::imp { expr * arg = m_result_stack.back(); proof * arg_pr = proofs_enabled() ? m_result_pr_stack.back() : 0; - if (m_ignore_labels && !proofs_enabled()) + if (m_ignore_labels && !proofs_enabled()) return true; // the result is already on the stack - + buffer names; bool pos; m().is_label(t, pos, names); @@ -683,7 +683,7 @@ struct nnf::imp { pr = m().mk_transitivity(mk_proof(fr.m_pol, 1, &arg_pr, t, to_app(aux)), m().mk_iff_oeq(m().mk_rewrite(aux, r))); } - } + } else { r = arg; if (proofs_enabled()) { @@ -691,7 +691,7 @@ struct nnf::imp { pr = m().mk_transitivity(p1, arg_pr); } } - + m_result_stack.pop_back(); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -728,7 +728,7 @@ struct nnf::imp { if (m().is_label(t)) { return process_label(t, fr); } - + return process_default(t, fr); } @@ -736,7 +736,7 @@ struct nnf::imp { skip(v, fr.m_pol); return true; } - + bool process_quantifier(quantifier * q, frame & fr) { expr_ref r(m()); proof_ref pr(m()); @@ -756,7 +756,7 @@ struct nnf::imp { if (q->is_forall() == fr.m_pol || !m_skolemize) { expr * new_expr = m_result_stack.back(); proof * new_expr_pr = proofs_enabled() ? m_result_pr_stack.back() : 0; - + ptr_buffer new_patterns; if (q->is_forall() == fr.m_pol) { @@ -772,7 +772,7 @@ struct nnf::imp { // New quantifier has existential force. // So, ignore patterns } - + quantifier * new_q = 0; proof * new_q_pr = 0; if (fr.m_pol) { @@ -785,7 +785,7 @@ struct nnf::imp { if (proofs_enabled()) new_q_pr = m().mk_nnf_neg(q, new_q, 1, &new_expr_pr); } - + m_result_stack.pop_back(); m_result_stack.push_back(new_q); if (proofs_enabled()) { @@ -808,7 +808,7 @@ struct nnf::imp { } return true; } - + void recover_result(expr * t, expr_ref & result, proof_ref & result_pr) { // recover result from the top of the stack. result = m_result_stack.back(); @@ -872,7 +872,7 @@ struct nnf::imp { process(n, r, pr); unsigned old_sz1 = new_defs.size(); unsigned old_sz2 = new_def_proofs.size(); - + for (unsigned i = 0; i < m_todo_defs.size(); i++) { expr_ref dr(m()); proof_ref dpr(m()); @@ -880,7 +880,7 @@ struct nnf::imp { new_defs.push_back(dr); if (proofs_enabled()) { proof * new_pr = m().mk_modus_ponens(m_todo_proofs.get(i), dpr); - new_def_proofs.push_back(new_pr); + new_def_proofs.push_back(new_pr); } } std::reverse(new_defs.c_ptr() + old_sz1, new_defs.c_ptr() + new_defs.size()); @@ -897,7 +897,7 @@ nnf::nnf(ast_manager & m, defined_names & n, params_ref const & p) { nnf::~nnf() { dealloc(m_imp); } - + void nnf::operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { m_imp->operator()(n, new_defs, new_def_proofs, r, p); TRACE("nnf_result", tout << mk_ismt2_pp(n, m_imp->m()) << "\nNNF result:\n" << mk_ismt2_pp(r, m_imp->m()) << "\n";); From f9adf8e62a55a4f423682ebd31d254894fe36f95 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 16 Oct 2017 17:07:03 +0100 Subject: [PATCH 132/148] Backwards compatibility --- src/ast/normal_forms/nnf.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 42b4e9350..7368d7935 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -207,8 +207,8 @@ struct nnf::imp { unsigned m_new_child:1; unsigned m_cache_result:1; unsigned m_spos; // top of the result stack, when the frame was created. - frame(expr_ref&& n, bool pol, bool in_q, bool cache_res, unsigned spos): - m_curr(std::move(n)), + frame(expr_ref & n, bool pol, bool in_q, bool cache_res, unsigned spos): + m_curr(n), m_i(0), m_pol(pol), m_in_q(in_q), @@ -324,7 +324,7 @@ struct nnf::imp { } void push_frame(expr * t, bool pol, bool in_q, bool cache_res) { - m_frame_stack.push_back(frame({ t, m() }, pol, in_q, cache_res, m_result_stack.size())); + m_frame_stack.push_back(frame(expr_ref(t, m()), pol, in_q, cache_res, m_result_stack.size())); } static unsigned get_cache_idx(bool pol, bool in_q) { From 256c9d76d3fe85ac41486efd14506ff5436fc7c8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Oct 2017 09:14:10 -0700 Subject: [PATCH 133/148] add macro for _Exit under WINDOWS Signed-off-by: Nikolaj Bjorner --- src/ast/normal_forms/nnf.cpp | 2 ++ src/util/util.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 7368d7935..f625095d0 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -216,6 +216,8 @@ struct nnf::imp { m_cache_result(cache_res), m_spos(spos) { } + //frame():m_curr(*(ast_manager*)(nullptr)) { + //} }; // There are four caches: diff --git a/src/util/util.h b/src/util/util.h index f8c464197..6d38231ba 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -54,6 +54,7 @@ static_assert(sizeof(int64) == 8, "64 bits"); #ifdef _WINDOWS #define SSCANF sscanf_s #define SPRINTF sprintf_s +#define _Exit exit #else #define SSCANF sscanf #define SPRINTF sprintf From a93f1f88ccdb55f73326f087f7f34fdc70ded764 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Oct 2017 09:23:50 -0700 Subject: [PATCH 134/148] trying to fix mac build Signed-off-by: Nikolaj Bjorner --- src/ast/normal_forms/nnf.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index f625095d0..6363d374a 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -216,6 +216,15 @@ struct nnf::imp { m_cache_result(cache_res), m_spos(spos) { } + frame(frame & other): + m_curr(other.m_curr), + m_i(other.m_i), + m_pol(other.m_pol), + m_in_q(other.m_in_q), + m_new_child(other.m_new_child), + m_cache_result(other.m_cache_result), + m_spos(other.m_spos) { + } //frame():m_curr(*(ast_manager*)(nullptr)) { //} }; From 5f9891c2356857998eec7cfbf8b9585571aea892 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Oct 2017 09:29:26 -0700 Subject: [PATCH 135/148] moving out construction of expr_ref Signed-off-by: Nikolaj Bjorner --- src/ast/normal_forms/nnf.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 6363d374a..09fccf135 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -225,8 +225,6 @@ struct nnf::imp { m_cache_result(other.m_cache_result), m_spos(other.m_spos) { } - //frame():m_curr(*(ast_manager*)(nullptr)) { - //} }; // There are four caches: @@ -335,7 +333,8 @@ struct nnf::imp { } void push_frame(expr * t, bool pol, bool in_q, bool cache_res) { - m_frame_stack.push_back(frame(expr_ref(t, m()), pol, in_q, cache_res, m_result_stack.size())); + expr_ref tt(t, m()); + m_frame_stack.push_back(frame(tt, pol, in_q, cache_res, m_result_stack.size())); } static unsigned get_cache_idx(bool pol, bool in_q) { From 019edcb822955ec361ed4b1141eaf6c08b0068b4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 16 Oct 2017 09:35:00 -0700 Subject: [PATCH 136/148] frame, again Signed-off-by: Nikolaj Bjorner --- src/ast/normal_forms/nnf.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 09fccf135..3baf4e4ea 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -225,6 +225,16 @@ struct nnf::imp { m_cache_result(other.m_cache_result), m_spos(other.m_spos) { } + frame(frame && other): + m_curr(other.m_curr), + m_i(other.m_i), + m_pol(other.m_pol), + m_in_q(other.m_in_q), + m_new_child(other.m_new_child), + m_cache_result(other.m_cache_result), + m_spos(other.m_spos) { + } + }; // There are four caches: From 01f642a6f37e2a25cbfe45f0c48aa9341989028d Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 16 Oct 2017 18:19:55 +0100 Subject: [PATCH 137/148] Backward compatibility --- src/test/trigo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/trigo.cpp b/src/test/trigo.cpp index d1b75c7fb..686800162 100644 --- a/src/test/trigo.cpp +++ b/src/test/trigo.cpp @@ -98,7 +98,7 @@ template static void tst_float_sine(std::ostream & out, unsigned N, unsigned k) { reslimit rl; fmanager fm; - interval_manager > im(rl, { fm, EBITS, SBITS }); + interval_manager > im(rl, im_float_config(fm, EBITS, SBITS)); _scoped_numeral a(fm); fm.set(a, EBITS, SBITS, static_cast(0)); tst_float_sine_core(out, fm, im, a, 1); From 4e92caa5537a139b5b8a945a0195726ded94788b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 16 Oct 2017 22:33:23 +0100 Subject: [PATCH 138/148] nnf: let's try a different version of compatible frames wo/ copying --- src/ast/normal_forms/nnf.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 3baf4e4ea..6fc65543d 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -207,8 +207,8 @@ struct nnf::imp { unsigned m_new_child:1; unsigned m_cache_result:1; unsigned m_spos; // top of the result stack, when the frame was created. - frame(expr_ref & n, bool pol, bool in_q, bool cache_res, unsigned spos): - m_curr(n), + frame(expr_ref && n, bool pol, bool in_q, bool cache_res, unsigned spos): + m_curr(std::move(n)), m_i(0), m_pol(pol), m_in_q(in_q), @@ -216,17 +216,8 @@ struct nnf::imp { m_cache_result(cache_res), m_spos(spos) { } - frame(frame & other): - m_curr(other.m_curr), - m_i(other.m_i), - m_pol(other.m_pol), - m_in_q(other.m_in_q), - m_new_child(other.m_new_child), - m_cache_result(other.m_cache_result), - m_spos(other.m_spos) { - } frame(frame && other): - m_curr(other.m_curr), + m_curr(std::move(other.m_curr)), m_i(other.m_i), m_pol(other.m_pol), m_in_q(other.m_in_q), @@ -343,8 +334,7 @@ struct nnf::imp { } void push_frame(expr * t, bool pol, bool in_q, bool cache_res) { - expr_ref tt(t, m()); - m_frame_stack.push_back(frame(tt, pol, in_q, cache_res, m_result_stack.size())); + m_frame_stack.push_back(frame(expr_ref(t, m()), pol, in_q, cache_res, m_result_stack.size())); } static unsigned get_cache_idx(bool pol, bool in_q) { From 448cf8c31de75718b78dc7a02c313f29c1775df9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Oct 2017 10:14:26 -0700 Subject: [PATCH 139/148] fix scope accounting for dom simplifier Signed-off-by: Nikolaj Bjorner --- src/ast/expr_substitution.h | 1 + src/tactic/bv/bv_bounds_tactic.cpp | 12 +++++++++--- src/tactic/bv/bv_bounds_tactic.h | 10 +++++++++- src/tactic/core/dom_simplify_tactic.h | 10 +++++++--- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h index 90154d163..1590f888c 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -80,6 +80,7 @@ public: m_trail_lim.resize(new_sz); } } + unsigned scope_level() const { return m_trail_lim.size(); } bool empty() const { return m_subst.empty(); } expr* find(expr * e) { proof* pr; expr* d = 0; if (find(e, d, pr)) return d; else return e; } bool find(expr * s, expr * & def, proof * & def_pr) { return m_subst.find(s, def, def_pr); } diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 44f920840..22b74db77 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -11,7 +11,9 @@ Abstract: Author: - Nikolaj Bjorner (nbjorner) 2016-2-12 + Nuno Lopes (nlopes) 2016-2-12 + + Nikolaj Bjorner (nbjorner) --*/ @@ -650,11 +652,11 @@ namespace { return false; if (old == intr) return true; - m_scopes.insert(undo_bound(t1, old, false)); + m_scopes.push_back(undo_bound(t1, old, false)); old = intr; } else { m_bound.insert(t1, b); - m_scopes.insert(undo_bound(t1, interval(), true)); + m_scopes.push_back(undo_bound(t1, interval(), true)); } } return true; @@ -801,6 +803,10 @@ namespace { return alloc(dom_bv_bounds_simplifier, m, m_params); } + virtual unsigned scope_level() const { + return m_scopes.size(); + } + }; } diff --git a/src/tactic/bv/bv_bounds_tactic.h b/src/tactic/bv/bv_bounds_tactic.h index 6c2ce60ed..1d6748b27 100644 --- a/src/tactic/bv/bv_bounds_tactic.h +++ b/src/tactic/bv/bv_bounds_tactic.h @@ -11,7 +11,8 @@ Abstract: Author: - Nikolaj Bjorner (nbjorner) 2016-2-12 + Nuno Lopes (nlopes) 2016-2-12 + Nikolaj Bjorner (nbjorner) --*/ @@ -21,8 +22,15 @@ Author: tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); +tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); + /* ADD_TACTIC("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_tactic(m, p)") + + + ADD_TACTIC("propagate-bv-bounds-new", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)") + + */ #endif diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 79bc9728c..6cd94a3c1 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -83,6 +83,8 @@ class dom_simplifier { virtual void pop(unsigned num_scopes) = 0; virtual dom_simplifier * translate(ast_manager & m) = 0; + + virtual unsigned scope_level() const = 0; }; @@ -116,9 +118,9 @@ class dom_simplify_tactic : public tactic { ptr_vector const & tree(expr * e); expr* idom(expr *e) const { return m_dominators.idom(e); } - unsigned scope_level() { return m_scope_level; } - void pop(unsigned n) { SASSERT(n <= m_scope_level); m_scope_level -= n; m_simplifier->pop(n); } - bool assert_expr(expr* f, bool sign) { m_scope_level++; return m_simplifier->assert_expr(f, sign); } + unsigned scope_level() { return m_simplifier->scope_level(); } + void pop(unsigned n) { SASSERT(n <= m_simplifier->scope_level()); m_simplifier->pop(n); } + bool assert_expr(expr* f, bool sign) { return m_simplifier->assert_expr(f, sign); } bool init(goal& g); @@ -169,6 +171,8 @@ public: virtual void pop(unsigned num_scopes) { m_scoped_substitution.pop(num_scopes); } + virtual unsigned scope_level() const { return m_scoped_substitution.scope_level(); } + virtual dom_simplifier * translate(ast_manager & m) { SASSERT(m_subst.empty()); return alloc(expr_substitution_simplifier, m); From 7f590b5419f5dfc92a62e29db28947cb1dc73a07 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 17 Oct 2017 10:27:58 -0700 Subject: [PATCH 140/148] gift for Nuno Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_bounds_tactic.cpp | 8 ++++++-- src/tactic/core/dom_simplify_tactic.cpp | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 22b74db77..a279e441b 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -694,7 +694,8 @@ namespace { bool was_updated = true; if (b.is_full() && b.tight) { r = m.mk_true(); - } else if (m_bound.find(t1, ctx)) { + } + else if (m_bound.find(t1, ctx)) { if (ctx.implies(b)) { r = m.mk_true(); } @@ -705,12 +706,15 @@ namespace { r = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), m.get_sort(t1))); } + else { + was_updated = false; + } } else { was_updated = false; } - CTRACE("bv", was_updated, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); + TRACE("bv", tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); if (sign && was_updated) r = m.mk_not(r); } diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 3a6d55569..ee94c5e57 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -242,7 +242,7 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { r = new_t; } else { - TRACE("tactic", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";); + TRACE("simplify", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";); r = m.mk_ite(new_c, new_t, new_e); } } From 45c60ed55cdcf2ed18dd4ae0ace1043bc04e40b5 Mon Sep 17 00:00:00 2001 From: Adrian Stanciu Date: Wed, 18 Oct 2017 14:50:44 +0300 Subject: [PATCH 141/148] Update README.md Corrected path to Z3 Python interface --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 701556cc8..70956d439 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ utility is used to install ``Microsoft.Z3.dll`` into the [pkg-config](http://www.freedesktop.org/wiki/Software/pkg-config/) file (``Microsoft.Z3.Sharp.pc``) is also installed which allows the [MonoDevelop](http://www.monodevelop.com/) IDE to find the bindings. Running -``make uninstall`` will remove the dll from the GAC and the pkg-config file. +``make uninstall`` will remove the dll from the GAC and the ``pkg-config`` file. See [``examples/dotnet``](examples/dotnet) for examples. @@ -170,8 +170,8 @@ If you do need to install to a non standard prefix a better approach is to use a [Python virtual environment](https://virtualenv.readthedocs.org/en/latest/) and install Z3 there. Python packages also work for Python3. Under Windows, recall to build inside the Visual C++ native command build environment. -Note that the buit/python/z3 directory should be accessible from where python is used with Z3 -and it depends on libz3.dll to be in the path. +Note that the ``build/python/z3`` directory should be accessible from where python is used with Z3 +and it depends on ``libz3.dll`` to be in the path. ```bash virtualenv venv From abdb41c5df1dd7ba2209838696759d257bd0723a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 18 Oct 2017 16:08:39 -0400 Subject: [PATCH 142/148] add special case handling for string constant backpropagation in theory_str avoid a crash when asserting that a constant string is equal to itself by not generating this assert in the first place --- src/smt/theory_str.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 97c8db125..41d42c867 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -8888,15 +8888,34 @@ namespace smt { if (concat_lhs_haseqc && concat_rhs_haseqc && !var_haseqc) { TRACE("str", tout << "backpropagate into " << mk_pp(var, m) << " = " << mk_pp(concat, m) << std::endl << "LHS ~= " << mk_pp(concat_lhs_str, m) << " RHS ~= " << mk_pp(concat_rhs_str, m) << std::endl;); + + TRACE("str_enode_bug", tout << "backpropagate into " << mk_pp(var, m) << " = " << mk_pp(concat, m) << std::endl + << "LHS ~= " << mk_pp(concat_lhs_str, m) << " RHS ~= " << mk_pp(concat_rhs_str, m) << std::endl;); zstring lhsString, rhsString; u.str.is_string(concat_lhs_str, lhsString); u.str.is_string(concat_rhs_str, rhsString); zstring concatString = lhsString + rhsString; - expr_ref lhs1(ctx.mk_eq_atom(concat_lhs, concat_lhs_str), m); - expr_ref lhs2(ctx.mk_eq_atom(concat_rhs, concat_rhs_str), m); - expr_ref lhs(m.mk_and(lhs1, lhs2), m); - expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); - assert_implication(lhs, rhs); + + // special handling: don't assert that string constants are equal to themselves + expr_ref_vector lhs_terms(m); + if (!u.str.is_string(concat_lhs)) { + lhs_terms.push_back(ctx.mk_eq_atom(concat_lhs, concat_lhs_str)); + } + if (!u.str.is_string(concat_rhs)) { + lhs_terms.push_back(ctx.mk_eq_atom(concat_rhs, concat_rhs_str)); + } + + if (lhs_terms.empty()) { + // no assumptions on LHS + expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); + assert_axiom(rhs); + } else { + expr_ref lhs(mk_and(lhs_terms), m); + expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); + TRACE("str_enode_bug", tout << "axiom LHS: " << mk_pp(lhs, m) << std::endl;); + TRACE("str_enode_bug", tout << "axiom RHS: " << mk_pp(rhs, m) << std::endl;); + assert_implication(lhs, rhs); + } backpropagation_occurred = true; } } From c9f540b066a33196f47b379d95327d84f55ffacf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Oct 2017 11:08:48 -0700 Subject: [PATCH 143/148] additional array functions exposed over API, ping #1223 Signed-off-by: Nikolaj Bjorner --- src/api/api_array.cpp | 88 +++++++++++++++++++++++++- src/api/c++/z3++.h | 28 +++++++++ src/api/dotnet/ArraySort.cs | 7 +++ src/api/dotnet/Context.cs | 89 ++++++++++++++++++++++++--- src/api/java/ArraySort.java | 6 ++ src/api/java/Context.java | 64 ++++++++++++++++++- src/api/z3_api.h | 38 ++++++++++++ src/ast/array_decl_plugin.cpp | 12 +++- src/ast/array_decl_plugin.h | 6 +- src/ast/fpa/bv2fpa_converter.cpp | 2 +- src/smt/theory_array.cpp | 67 +++++++------------- src/smt/theory_array_base.cpp | 5 +- src/tactic/bv/bvarray2uf_rewriter.cpp | 14 ++--- 13 files changed, 354 insertions(+), 72 deletions(-) diff --git a/src/api/api_array.cpp b/src/api/api_array.cpp index a5916e987..5e6764dff 100644 --- a/src/api/api_array.cpp +++ b/src/api/api_array.cpp @@ -34,6 +34,19 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_sort Z3_API Z3_mk_array_sort_n(Z3_context c, unsigned n, Z3_sort const* domain, Z3_sort range) { + Z3_TRY; + LOG_Z3_mk_array_sort_n(c, n, domain, range); + RESET_ERROR_CODE(); + vector params; + for (unsigned i = 0; i < n; ++i) params.push_back(parameter(to_sort(domain[i]))); + params.push_back(parameter(to_sort(range))); + sort * ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, params.size(), params.c_ptr()); + mk_c(c)->save_ast_trail(ty); + RETURN_Z3(of_sort(ty)); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i) { Z3_TRY; LOG_Z3_mk_select(c, a, i); @@ -57,6 +70,35 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_ast Z3_API Z3_mk_select_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs) { + Z3_TRY; + LOG_Z3_mk_select_n(c, a, n, idxs); + RESET_ERROR_CODE(); + ast_manager & m = mk_c(c)->m(); + expr * _a = to_expr(a); + // expr * _i = to_expr(i); + sort * a_ty = m.get_sort(_a); + // sort * i_ty = m.get_sort(_i); + if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { + SET_ERROR_CODE(Z3_SORT_ERROR); + RETURN_Z3(0); + } + ptr_vector domain; + ptr_vector args; + args.push_back(_a); + domain.push_back(a_ty); + for (unsigned i = 0; i < n; ++i) { + args.push_back(to_expr(idxs[i])); + domain.push_back(m.get_sort(to_expr(idxs[i]))); + } + func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_SELECT, 2, a_ty->get_parameters(), domain.size(), domain.c_ptr()); + app * r = m.mk_app(d, args.size(), args.c_ptr()); + mk_c(c)->save_ast_trail(r); + check_sorts(c, r); + RETURN_Z3(of_ast(r)); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v) { Z3_TRY; LOG_Z3_mk_store(c, a, i, v); @@ -82,6 +124,37 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_ast Z3_API Z3_mk_store_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs, Z3_ast v) { + Z3_TRY; + LOG_Z3_mk_store_n(c, a, n, idxs, v); + RESET_ERROR_CODE(); + ast_manager & m = mk_c(c)->m(); + expr * _a = to_expr(a); + expr * _v = to_expr(v); + sort * a_ty = m.get_sort(_a); + sort * v_ty = m.get_sort(_v); + if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { + SET_ERROR_CODE(Z3_SORT_ERROR); + RETURN_Z3(0); + } + ptr_vector domain; + ptr_vector args; + args.push_back(_a); + domain.push_back(a_ty); + for (unsigned i = 0; i < n; ++i) { + args.push_back(to_expr(idxs[i])); + domain.push_back(m.get_sort(to_expr(idxs[i]))); + } + args.push_back(_v); + domain.push_back(v_ty); + func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_STORE, 2, a_ty->get_parameters(), domain.size(), domain.c_ptr()); + app * r = m.mk_app(d, args.size(), args.c_ptr()); + mk_c(c)->save_ast_trail(r); + check_sorts(c, r); + RETURN_Z3(of_ast(r)); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args) { Z3_TRY; LOG_Z3_mk_map(c, f, n, args); @@ -188,6 +261,18 @@ extern "C" { MK_BINARY(Z3_mk_set_subset, mk_c(c)->get_array_fid(), OP_SET_SUBSET, SKIP); MK_BINARY(Z3_mk_array_ext, mk_c(c)->get_array_fid(), OP_ARRAY_EXT, SKIP); + Z3_ast Z3_API Z3_mk_as_array(Z3_context c, Z3_func_decl f) { + Z3_TRY; + LOG_Z3_mk_as_array(c, f); + RESET_ERROR_CODE(); + ast_manager & m = mk_c(c)->m(); + array_util a(m); + app * r = a.mk_as_array(to_func_decl(f)); + mk_c(c)->save_ast_trail(r); + return of_ast(r); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_mk_set_member(Z3_context c, Z3_ast elem, Z3_ast set) { return Z3_mk_select(c, set, elem); } @@ -222,7 +307,8 @@ extern "C" { CHECK_VALID_AST(t, 0); if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { - Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(1).get_ast()); + unsigned n = to_sort(t)->get_num_parameters(); + Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(n-1).get_ast()); RETURN_Z3(r); } SET_ERROR_CODE(Z3_INVALID_ARG); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index f55ece034..23a7bd22e 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -250,6 +250,8 @@ namespace z3 { Example: Given a context \c c, c.array_sort(c.int_sort(), c.bool_sort()) is an array sort from integer to Boolean. */ sort array_sort(sort d, sort r); + sort array_sort(sort_vector const& d, sort r); + /** \brief Return an enumeration sort: enum_names[0], ..., enum_names[n-1]. \c cs and \c ts are output parameters. The method stores in \c cs the constants corresponding to the enumerated elements, @@ -2327,6 +2329,11 @@ namespace z3 { inline sort context::re_sort(sort& s) { Z3_sort r = Z3_mk_re_sort(m_ctx, s); check_error(); return sort(*this, r); } inline sort context::array_sort(sort d, sort r) { Z3_sort s = Z3_mk_array_sort(m_ctx, d, r); check_error(); return sort(*this, s); } + inline sort context::array_sort(sort_vector const& d, sort r) { + array dom(d); + Z3_sort s = Z3_mk_array_sort_n(m_ctx, dom.size(), dom.ptr(), r); check_error(); return sort(*this, s); + } + inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) { array _enum_names(n); for (unsigned i = 0; i < n; i++) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); } @@ -2573,11 +2580,32 @@ namespace z3 { a.check_error(); return expr(a.ctx(), r); } + inline expr select(expr const & a, expr_vector const & i) { + check_context(a, i); + array idxs(i); + Z3_ast r = Z3_mk_select_n(a.ctx(), a, idxs.size(), idxs.ptr()); + a.check_error(); + return expr(a.ctx(), r); + } + inline expr store(expr const & a, int i, expr const & v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), v); } inline expr store(expr const & a, expr i, int v) { return store(a, i, a.ctx().num_val(v, a.get_sort().array_range())); } inline expr store(expr const & a, int i, int v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), a.ctx().num_val(v, a.get_sort().array_range())); } + inline expr store(expr const & a, expr_vector const & i, expr const & v) { + check_context(a, i); check_context(a, v); + array idxs(i); + Z3_ast r = Z3_mk_store_n(a.ctx(), a, idxs.size(), idxs.ptr(), v); + a.check_error(); + return expr(a.ctx(), r); + } + + inline expr as_array(func_decl & f) { + Z3_ast r = Z3_mk_as_array(f.ctx(), f); + f.check_error(); + return expr(f.ctx(), r); + } #define MK_EXPR1(_fn, _arg) \ Z3_ast r = _fn(_arg.ctx(), _arg); \ diff --git a/src/api/dotnet/ArraySort.cs b/src/api/dotnet/ArraySort.cs index ddd27785c..47a73ae1f 100644 --- a/src/api/dotnet/ArraySort.cs +++ b/src/api/dotnet/ArraySort.cs @@ -63,6 +63,13 @@ namespace Microsoft.Z3 Contract.Requires(domain != null); Contract.Requires(range != null); } + internal ArraySort(Context ctx, Sort[] domain, Sort range) + : base(ctx, Native.Z3_mk_array_sort_n(ctx.nCtx, (uint)domain.Length, AST.ArrayToNative(domain), range.NativeObject)) + { + Contract.Requires(ctx != null); + Contract.Requires(domain != null); + Contract.Requires(range != null); + } #endregion }; diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index d7699c961..27711be81 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -274,6 +274,20 @@ namespace Microsoft.Z3 return new ArraySort(this, domain, range); } + /// + /// Create a new n-ary array sort. + /// + public ArraySort MkArraySort(Sort[] domain, Sort range) + { + Contract.Requires(domain != null); + Contract.Requires(range != null); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(domain); + CheckContextMatch(range); + return new ArraySort(this, domain, range); + } + /// /// Create a new tuple sort. /// @@ -2113,6 +2127,7 @@ namespace Microsoft.Z3 return (ArrayExpr)MkConst(MkSymbol(name), MkArraySort(domain, range)); } + /// /// Array read. /// @@ -2123,8 +2138,8 @@ namespace Microsoft.Z3 /// The node a must have an array sort [domain -> range], /// and i must have the sort domain. /// The sort of the result is range. - /// - /// + /// + /// /// public Expr MkSelect(ArrayExpr a, Expr i) { @@ -2137,6 +2152,30 @@ namespace Microsoft.Z3 return Expr.Create(this, Native.Z3_mk_select(nCtx, a.NativeObject, i.NativeObject)); } + /// + /// Array read. + /// + /// + /// The argument a is the array and args are the indices + /// of the array that gets read. + /// + /// The node a must have an array sort [domain1,..,domaink -> range], + /// and args must have the sort domain1,..,domaink. + /// The sort of the result is range. + /// + /// + /// + public Expr MkSelect(ArrayExpr a, params Expr[] args) + { + Contract.Requires(a != null); + Contract.Requires(args != null && Contract.ForAll(args, n => n != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(a); + CheckContextMatch(args); + return Expr.Create(this, Native.Z3_mk_select_n(nCtx, a.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args))); + } + /// /// Array update. /// @@ -2151,8 +2190,9 @@ namespace Microsoft.Z3 /// on all indices except for i, where it maps to v /// (and the select of a with /// respect to i may be a different value). - /// - /// + /// + /// + /// /// public ArrayExpr MkStore(ArrayExpr a, Expr i, Expr v) { @@ -2167,14 +2207,45 @@ namespace Microsoft.Z3 return new ArrayExpr(this, Native.Z3_mk_store(nCtx, a.NativeObject, i.NativeObject, v.NativeObject)); } + /// + /// Array update. + /// + /// + /// The node a must have an array sort [domain1,..,domaink -> range], + /// args must have sort domain1,..,domaink, + /// v must have sort range. The sort of the result is [domain -> range]. + /// The semantics of this function is given by the theory of arrays described in the SMT-LIB + /// standard. See http://smtlib.org for more details. + /// The result of this function is an array that is equal to a + /// (with respect to select) + /// on all indices except for args, where it maps to v + /// (and the select of a with + /// respect to args may be a different value). + /// + /// + /// + /// + public ArrayExpr MkStore(ArrayExpr a, Expr[] args, Expr v) + { + Contract.Requires(a != null); + Contract.Requires(args != null); + Contract.Requires(v != null); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(args); + CheckContextMatch(a); + CheckContextMatch(v); + return new ArrayExpr(this, Native.Z3_mk_store_n(nCtx, a.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args), v.NativeObject)); + } + /// /// Create a constant array. /// /// /// The resulting term is an array, such that a selecton an arbitrary index /// produces the value v. - /// - /// + /// + /// /// public ArrayExpr MkConstArray(Sort domain, Expr v) { @@ -2194,9 +2265,9 @@ namespace Microsoft.Z3 /// Eeach element of args must be of an array sort [domain_i -> range_i]. /// The function declaration f must have type range_1 .. range_n -> range. /// v must have sort range. The sort of the result is [domain_i -> range]. - /// - /// - /// + /// + /// + /// /// public ArrayExpr MkMap(FuncDecl f, params ArrayExpr[] args) { diff --git a/src/api/java/ArraySort.java b/src/api/java/ArraySort.java index 1574823d1..db4d992ed 100644 --- a/src/api/java/ArraySort.java +++ b/src/api/java/ArraySort.java @@ -56,4 +56,10 @@ public class ArraySort extends Sort super(ctx, Native.mkArraySort(ctx.nCtx(), domain.getNativeObject(), range.getNativeObject())); } + + ArraySort(Context ctx, Sort[] domains, Sort range) + { + super(ctx, Native.mkArraySortN(ctx.nCtx(), domains.length, AST.arrayToNative(domains), + range.getNativeObject())); + } }; diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 72866a0ba..76303d670 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -224,6 +224,17 @@ public class Context implements AutoCloseable { return new ArraySort(this, domain, range); } + + /** + * Create a new array sort. + **/ + public ArraySort mkArraySort(Sort[] domains, Sort range) + { + checkContextMatch(domains); + checkContextMatch(range); + return new ArraySort(this, domains, range); + } + /** * Create a new string sort **/ @@ -414,7 +425,7 @@ public class Context implements AutoCloseable { * that is passed in as argument is updated with value v, * the remaining fields of t are unchanged. **/ - public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) + public Expr mkUpdateField(FuncDecl field, Expr t, Expr v) throws Z3Exception { return Expr.create (this, @@ -706,7 +717,7 @@ public class Context implements AutoCloseable { } /** - * Mk an expression representing {@code not(a)}. + * Create an expression representing {@code not(a)}. **/ public BoolExpr mkNot(BoolExpr a) { @@ -1679,6 +1690,28 @@ public class Context implements AutoCloseable { i.getNativeObject())); } + /** + * Array read. + * Remarks: The argument {@code a} is the array and + * {@code args} are the indices of the array that gets read. + * + * The node {@code a} must have an array sort + * {@code [domains -> range]}, and {@code args} must have the sorts + * {@code domains}. The sort of the result is {@code range}. + * + * @see #mkArraySort + * @see #mkStore + + **/ + public Expr mkSelect(ArrayExpr a, Expr[] args) + { + checkContextMatch(a); + checkContextMatch(args); + return Expr.create( + this, + Native.mkSelectN(nCtx(), a.getNativeObject(), args.length, AST.arrayToNative(args))); + } + /** * Array update. * Remarks: The node {@code a} must have an array sort @@ -1704,6 +1737,31 @@ public class Context implements AutoCloseable { i.getNativeObject(), v.getNativeObject())); } + /** + * Array update. + * Remarks: The node {@code a} must have an array sort + * {@code [domains -> range]}, {@code i} must have sort + * {@code domain}, {@code v} must have sort range. The sort of the + * result is {@code [domains -> range]}. The semantics of this function + * is given by the theory of arrays described in the SMT-LIB standard. See + * http://smtlib.org for more details. The result of this function is an + * array that is equal to {@code a} (with respect to + * {@code select}) on all indices except for {@code args}, where it + * maps to {@code v} (and the {@code select} of {@code a} + * with respect to {@code args} may be a different value). + * @see #mkArraySort + * @see #mkSelect + + **/ + public ArrayExpr mkStore(ArrayExpr a, Expr[] args, Expr v) + { + checkContextMatch(a); + checkContextMatch(args); + checkContextMatch(v); + return new ArrayExpr(this, Native.mkStoreN(nCtx(), a.getNativeObject(), + args.length, AST.arrayToNative(args), v.getNativeObject())); + } + /** * Create a constant array. * Remarks: The resulting term is an array, such @@ -2104,7 +2162,7 @@ public class Context implements AutoCloseable { /** * Create a range expression. */ - public ReExpr MkRange(SeqExpr lo, SeqExpr hi) + public ReExpr mkRange(SeqExpr lo, SeqExpr hi) { checkContextMatch(lo, hi); return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 154b8eb3c..9f155b45e 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1881,6 +1881,17 @@ extern "C" { */ Z3_sort Z3_API Z3_mk_array_sort(Z3_context c, Z3_sort domain, Z3_sort range); + /** + \brief Create an array type with N arguments + + \sa Z3_mk_select_n + \sa Z3_mk_store_n + + def_API('Z3_mk_array_sort_n', SORT, (_in(CONTEXT), _in(UINT), _in_array(1, SORT), _in(SORT))) + */ + Z3_sort Z3_API Z3_mk_array_sort_n(Z3_context c, unsigned n, Z3_sort const * domain, Z3_sort range); + + /** \brief Create a tuple type. @@ -2973,6 +2984,15 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i); + /** + \brief n-ary Array read. + The argument \c a is the array and \c idxs are the indices of the array that gets read. + + def_API('Z3_mk_select_n', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) + + */ + Z3_ast Z3_API Z3_mk_select_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs); + /** \brief Array update. @@ -2991,6 +3011,14 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v); + /** + \brief n-ary Array update. + + def_API('Z3_mk_store_n', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST), _in(AST))) + + */ + Z3_ast Z3_API Z3_mk_store_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs, Z3_ast v); + /** \brief Create the constant array. @@ -3031,6 +3059,15 @@ extern "C" { def_API('Z3_mk_array_default', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_array_default(Z3_context c, Z3_ast array); + + /** + \brief Create array with the same interpretation as a function. + The array satisfies the property (f x) = (select (_ as-array f) x) + for every argument x. + + def_API('Z3_mk_as_array', AST, (_in(CONTEXT), _in(FUNC_DECL))) + */ + Z3_ast Z3_API Z3_mk_as_array(Z3_context c, Z3_func_decl f); /*@}*/ /** @name Sets */ @@ -3854,6 +3891,7 @@ extern "C" { /** \brief Return the domain of the given array sort. + In the case of a multi-dimensional array, this function returns the sort of the first dimension. \pre Z3_get_sort_kind(c, t) == Z3_ARRAY_SORT diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index cb016e263..0cc4f6031 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -242,7 +242,9 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) { parameter const* parameters = s->get_parameters(); if (num_parameters != arity) { - m_manager->raise_exception("select requires as many arguments as the size of the domain"); + std::stringstream strm; + strm << "select requires " << num_parameters << " arguments, but was provided with " << arity << " arguments"; + m_manager->raise_exception(strm.str().c_str()); return 0; } ptr_buffer new_domain; // we need this because of coercions. @@ -314,7 +316,7 @@ func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domai return 0; } sort * r = to_sort(s->get_parameter(i).get_ast()); - parameter param(s); + parameter param(i); return m_manager->mk_func_decl(m_array_ext_sym, arity, domain, r, func_decl_info(m_family_id, OP_ARRAY_EXT, 1, ¶m)); } @@ -592,3 +594,9 @@ sort * array_util::mk_array_sort(unsigned arity, sort* const* domain, sort* rang params.push_back(parameter(range)); return m_manager.mk_sort(m_fid, ARRAY_SORT, params.size(), params.c_ptr()); } + +func_decl* array_util::mk_array_ext(sort *domain, unsigned i) { + sort * domains[2] = { domain, domain }; + parameter p(i); + return m_manager.mk_func_decl(m_fid, OP_ARRAY_EXT, 1, &p, 2, domains); +} diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 0704fe56a..1257ab2c6 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -143,6 +143,7 @@ public: bool is_const(expr* n) const { return is_app_of(n, m_fid, OP_CONST_ARRAY); } bool is_map(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAP); } bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); } + bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); } bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); } bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); } bool is_const(func_decl* f) const { return is_decl_of(f, m_fid, OP_CONST_ARRAY); } @@ -182,12 +183,15 @@ public: return mk_const_array(s, m_manager.mk_true()); } + func_decl * mk_array_ext(sort* domain, unsigned i); + sort * mk_array_sort(sort* dom, sort* range) { return mk_array_sort(1, &dom, range); } sort * mk_array_sort(unsigned arity, sort* const* domain, sort* range); - app * mk_as_array(sort * s, func_decl * f) { + app * mk_as_array(func_decl * f) { parameter param(f); + sort * s = f->get_range(); return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, 0, s); } }; diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index 93bd79571..2f5a7c3c1 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -250,7 +250,7 @@ bv2fpa_converter::array_model bv2fpa_converter::convert_array_func_interp(model_ am.new_float_fd = m.mk_fresh_func_decl(arity, array_domain.c_ptr(), rng); am.new_float_fi = convert_func_interp(mc, am.new_float_fd, bv_f); am.bv_fd = bv_f; - am.result = arr_util.mk_as_array(f->get_range(), am.new_float_fd); + am.result = arr_util.mk_as_array(am.new_float_fd); return am; } diff --git a/src/smt/theory_array.cpp b/src/smt/theory_array.cpp index 18fc9f50b..b8f76f9ad 100644 --- a/src/smt/theory_array.cpp +++ b/src/smt/theory_array.cpp @@ -53,18 +53,12 @@ namespace smt { var_data * d2 = m_var_data[v2]; if (!d1->m_prop_upward && d2->m_prop_upward) set_prop_upward(v1); - ptr_vector::iterator it = d2->m_stores.begin(); - ptr_vector::iterator end = d2->m_stores.end(); - for (; it != end; ++it) - add_store(v1, *it); - it = d2->m_parent_stores.begin(); - end = d2->m_parent_stores.end(); - for (; it != end; ++it) - add_parent_store(v1, *it); - it = d2->m_parent_selects.begin(); - end = d2->m_parent_selects.end(); - for (; it != end; ++it) - add_parent_select(v1, *it); + for (enode* n : d2->m_stores) + add_store(v1, n); + for (enode* n : d2->m_parent_stores) + add_parent_store(v1, n); + for (enode* n : d2->m_parent_selects) + add_parent_select(v1, n); TRACE("array", tout << "after merge\n"; display_var(tout, v1);); } @@ -103,16 +97,11 @@ namespace smt { d->m_parent_selects.push_back(s); TRACE("array", tout << mk_pp(s->get_owner(), get_manager()) << " " << mk_pp(get_enode(v)->get_owner(), get_manager()) << "\n";); m_trail_stack.push(push_back_trail(d->m_parent_selects)); - ptr_vector::iterator it = d->m_stores.begin(); - ptr_vector::iterator end = d->m_stores.end(); - for (; it != end; ++it) { - instantiate_axiom2a(s, *it); + for (enode* n : d->m_stores) { + instantiate_axiom2a(s, n); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { - it = d->m_parent_stores.begin(); - end = d->m_parent_stores.end(); - for (; it != end; ++it) { - enode * store = *it; + for (enode* store : d->m_parent_stores) { SASSERT(is_store(store)); if (!m_params.m_array_cg || store->is_cgr()) { instantiate_axiom2b(s, store); @@ -129,27 +118,19 @@ namespace smt { var_data * d = m_var_data[v]; d->m_parent_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_parent_stores)); - if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { - ptr_vector::iterator it = d->m_parent_selects.begin(); - ptr_vector::iterator end = d->m_parent_selects.end(); - for (; it != end; ++it) - if (!m_params.m_array_cg || (*it)->is_cgr()) - instantiate_axiom2b(*it, s); - } + if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) + for (enode* n : d->m_parent_selects) + if (!m_params.m_array_cg || n->is_cgr()) + instantiate_axiom2b(n, s); } bool theory_array::instantiate_axiom2b_for(theory_var v) { bool result = false; var_data * d = m_var_data[v]; - ptr_vector::iterator it = d->m_parent_stores.begin(); - ptr_vector::iterator end = d->m_parent_stores.end(); - for (; it != end; ++it) { - ptr_vector::iterator it2 = d->m_parent_selects.begin(); - ptr_vector::iterator end2 = d->m_parent_selects.end(); - for (; it2 != end2; ++it2) - if (instantiate_axiom2b(*it2, *it)) + for (enode* n1 : d->m_parent_stores) + for (enode * n2 : d->m_parent_selects) + if (instantiate_axiom2b(n2, n1)) result = true; - } return result; } @@ -167,10 +148,8 @@ namespace smt { d->m_prop_upward = true; if (!m_params.m_array_delay_exp_axiom) instantiate_axiom2b_for(v); - ptr_vector::iterator it = d->m_stores.begin(); - ptr_vector::iterator end = d->m_stores.end(); - for (; it != end; ++it) - set_prop_upward(*it); + for (enode * n : d->m_stores) + set_prop_upward(n); } } @@ -209,11 +188,9 @@ namespace smt { } d->m_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_stores)); - ptr_vector::iterator it = d->m_parent_selects.begin(); - ptr_vector::iterator end = d->m_parent_selects.end(); - for (; it != end; ++it) { - SASSERT(is_select(*it)); - instantiate_axiom2a(*it, s); + for (enode * n : d->m_parent_selects) { + SASSERT(is_select(n)); + instantiate_axiom2a(n, s); } if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) set_prop_upward(s); @@ -374,7 +351,7 @@ namespace smt { final_check_status theory_array::final_check_eh() { m_final_check_idx++; - final_check_status r; + final_check_status r = FC_DONE; if (m_params.m_array_lazy_ieq) { // Delay the creation of interface equalities... The // motivation is too give other theories and quantifier diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 21df02c76..2aaf2833f 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -210,17 +210,16 @@ namespace smt { - func_decl_ref_vector * theory_array_base::register_sort(sort * s_array) { unsigned dimension = get_dimension(s_array); func_decl_ref_vector * ext_skolems = 0; if (!m_sort2skolem.find(s_array, ext_skolems)) { + array_util util(get_manager()); ast_manager & m = get_manager(); ext_skolems = alloc(func_decl_ref_vector, m); for (unsigned i = 0; i < dimension; ++i) { sort * ext_sk_domain[2] = { s_array, s_array }; - parameter p(i); - func_decl * ext_sk_decl = m.mk_func_decl(get_id(), OP_ARRAY_EXT, 1, &p, 2, ext_sk_domain); + func_decl * ext_sk_decl = util.mk_array_ext(s_array, i); ext_skolems->push_back(ext_sk_decl); } m_sort2skolem.insert(s_array, ext_skolems); diff --git a/src/tactic/bv/bvarray2uf_rewriter.cpp b/src/tactic/bv/bvarray2uf_rewriter.cpp index 9b67e7011..b92092739 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.cpp +++ b/src/tactic/bv/bvarray2uf_rewriter.cpp @@ -117,7 +117,7 @@ func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { if (is_uninterp_const(e)) { if (m_emc) m_emc->insert(to_app(e)->get_decl(), - m_array_util.mk_as_array(m_manager.get_sort(e), bv_f)); + m_array_util.mk_as_array(bv_f)); } else if (m_fmc) m_fmc->insert(bv_f); @@ -193,7 +193,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr if (is_uninterp_const(e)) { if (m_emc) m_emc->insert(e->get_decl(), - m_array_util.mk_as_array(m_manager.get_sort(e), bv_f)); + m_array_util.mk_as_array(bv_f)); } else if (m_fmc) m_fmc->insert(bv_f); @@ -207,7 +207,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr q = m_manager.mk_forall(1, sorts, names, body); extra_assertions.push_back(q); - result = m_array_util.mk_as_array(f->get_range(), bv_f); + result = m_array_util.mk_as_array(bv_f); TRACE("bvarray2uf_rw", tout << "result: " << mk_ismt2_pp(result, m_manager) << ")" << std::endl;); res = BR_DONE; @@ -234,7 +234,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr if (is_bv_array(t)) { // From [1]: For every array term t we create a fresh uninterpreted function f_t. f_t = mk_uf_for_array(t); - result = m_array_util.mk_as_array(m_manager.get_sort(t), f_t); + result = m_array_util.mk_as_array(f_t); res = BR_DONE; } else if (has_bv_arrays) { @@ -274,7 +274,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr expr * v = args[0]; func_decl_ref f_t(mk_uf_for_array(t), m_manager); - result = m_array_util.mk_as_array(f->get_range(), f_t); + result = m_array_util.mk_as_array(f_t); res = BR_DONE; // Add \forall x . f_t(x) = v @@ -321,7 +321,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr expr_ref frllx(m_manager.mk_forall(1, sorts, names, body), m_manager); extra_assertions.push_back(frllx); - result = m_array_util.mk_as_array(f->get_range(), f_t); + result = m_array_util.mk_as_array(f_t); res = BR_DONE; } else if (m_array_util.is_store(f)) { @@ -342,7 +342,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr func_decl_ref f_s(mk_uf_for_array(s), m_manager); func_decl_ref f_t(mk_uf_for_array(t), m_manager); - result = m_array_util.mk_as_array(f->get_range(), f_t); + result = m_array_util.mk_as_array(f_t); res = BR_DONE; sort * sorts[1] = { get_index_sort(f->get_range()) }; From d2e27f6f1f33cf6e0ae05cb78976a35fd6423f5f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Oct 2017 11:25:44 -0700 Subject: [PATCH 144/148] remove redundant and wrong range type, in extension to changes made for #1223 Signed-off-by: Nikolaj Bjorner --- src/ast/array_decl_plugin.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 1257ab2c6..911bb3f27 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -191,8 +191,7 @@ public: app * mk_as_array(func_decl * f) { parameter param(f); - sort * s = f->get_range(); - return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, 0, s); + return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, 0, 0); } }; From ce1c8f7be29f5c9bf4f22e1c697d4032e0828ee7 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 19 Oct 2017 17:01:10 -0400 Subject: [PATCH 145/148] remove debug code --- src/smt/theory_str.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 41d42c867..7e5685f9c 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -8889,8 +8889,6 @@ namespace smt { TRACE("str", tout << "backpropagate into " << mk_pp(var, m) << " = " << mk_pp(concat, m) << std::endl << "LHS ~= " << mk_pp(concat_lhs_str, m) << " RHS ~= " << mk_pp(concat_rhs_str, m) << std::endl;); - TRACE("str_enode_bug", tout << "backpropagate into " << mk_pp(var, m) << " = " << mk_pp(concat, m) << std::endl - << "LHS ~= " << mk_pp(concat_lhs_str, m) << " RHS ~= " << mk_pp(concat_rhs_str, m) << std::endl;); zstring lhsString, rhsString; u.str.is_string(concat_lhs_str, lhsString); u.str.is_string(concat_rhs_str, rhsString); @@ -8912,8 +8910,6 @@ namespace smt { } else { expr_ref lhs(mk_and(lhs_terms), m); expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); - TRACE("str_enode_bug", tout << "axiom LHS: " << mk_pp(lhs, m) << std::endl;); - TRACE("str_enode_bug", tout << "axiom RHS: " << mk_pp(rhs, m) << std::endl;); assert_implication(lhs, rhs); } backpropagation_occurred = true; From b2191cab020718362d544bad6e7624cf30948c97 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 21 Oct 2017 18:46:35 -0400 Subject: [PATCH 146/148] disable eager clear of check-sat-result to fix #1318 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 9615e86ce..77dce80c8 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -774,7 +774,6 @@ bool cmd_context::is_func_decl(symbol const & s) const { } void cmd_context::insert(symbol const & s, func_decl * f) { - m_check_sat_result = 0; if (!m_check_logic(f)) { throw cmd_exception(m_check_logic.get_last_error()); } @@ -805,7 +804,6 @@ void cmd_context::insert(symbol const & s, func_decl * f) { } void cmd_context::insert(symbol const & s, psort_decl * p) { - m_check_sat_result = 0; if (m_psort_decls.contains(s)) { throw cmd_exception("sort already defined ", s); } @@ -819,7 +817,6 @@ void cmd_context::insert(symbol const & s, psort_decl * p) { void cmd_context::insert(symbol const & s, unsigned arity, sort *const* domain, expr * t) { expr_ref _t(t, m()); - m_check_sat_result = 0; if (m_builtin_decls.contains(s)) { throw cmd_exception("invalid macro/named expression, builtin symbol ", s); } From 42fbe19814cdb25d450b4e222252a6ded6361fa0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 21 Oct 2017 18:56:36 -0400 Subject: [PATCH 147/148] fix #1316, segmentation fault when numeric value is not internalized Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 44ce804e7..d55484384 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -3466,6 +3466,8 @@ static bool get_arith_value(context& ctx, theory_id afid, expr* e, expr_ref& v) bool theory_seq::get_num_value(expr* e, rational& val) const { context& ctx = get_context(); expr_ref _val(m); + if (!ctx.e_internalized(e)) + return false; enode* next = ctx.get_enode(e), *n = next; do { if (get_arith_value(ctx, m_autil.get_family_id(), next->get_owner(), _val) && m_autil.is_numeral(_val, val) && val.is_int()) { From 77bbae65f5d602e1cf3cec81fb27720cd89a4370 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Oct 2017 08:17:38 -0700 Subject: [PATCH 148/148] fix #1319, fix #1320 Signed-off-by: Nikolaj Bjorner --- src/muz/base/dl_context.cpp | 3 ++- src/muz/fp/dl_cmds.cpp | 3 ++- src/parsers/smt2/smt2parser.cpp | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 97a2c841a..6c0537936 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -453,7 +453,8 @@ namespace datalog { return new_pred; } - void context::add_rule(expr* rl, symbol const& name, unsigned bound) { + void context::add_rule(expr* rl, symbol const& name, unsigned bound) { + SASSERT(rl); m_rule_fmls.push_back(rl); m_rule_names.push_back(name); m_rule_bounds.push_back(bound); diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 42c614912..2610f821c 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -189,11 +189,12 @@ public: m_bound = bound; m_arg_idx++; } - virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); } + virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); m_t = nullptr; } virtual void prepare(cmd_context& ctx) { m_arg_idx = 0; m_name = symbol::null; m_bound = UINT_MAX; } virtual void finalize(cmd_context & ctx) { } virtual void execute(cmd_context & ctx) { + if (!m_t) throw cmd_exception("invalid rule, expected formula"); m_dl_ctx->add_rule(m_t, m_name, m_bound); } }; diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index b43ee9f6e..fd592f7c7 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -1881,6 +1881,8 @@ namespace smt2 { // the resultant expression is on the top of the stack TRACE("let_frame", tout << "let result expr: " << mk_pp(expr_stack().back(), m()) << "\n";); expr_ref r(m()); + if (expr_stack().empty()) + throw parser_exception("invalid let expression"); r = expr_stack().back(); expr_stack().pop_back(); // remove local declarations from the stack