/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context.cpp Abstract: Command context. Author: Leonardo (leonardo) 2011-03-01 Notes: --*/ #include #include"tptr.h" #include"cmd_context.h" #include"func_decl_dependencies.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" #include"array_decl_plugin.h" #include"datatype_decl_plugin.h" #include"seq_decl_plugin.h" #include"float_decl_plugin.h" #include"ast_pp.h" #include"var_subst.h" #include"pp.h" #include"ast_smt2_pp.h" #include"basic_cmds.h" #include"cancel_eh.h" #include"scoped_ctrl_c.h" #include"dec_ref_util.h" #include"decl_collector.h" #include"well_sorted.h" #include"model_evaluator.h" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { m.inc_ref(f); } void func_decls::finalize(ast_manager & m) { TRACE("cmd_context_detail", tout << "finalizing func_decls...\n";); if (GET_TAG(m_decls) == 0) { m.dec_ref(UNTAG(func_decl *, m_decls)); } else { TRACE("func_decls", tout << "finalize...\n";); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); func_decl_set::iterator it = fs->begin(); func_decl_set::iterator end = fs->end(); for (; it != end; ++it) { TRACE("func_decls", tout << "dec_ref of " << (*it)->get_name() << " ref_count: " << (*it)->get_ref_count() << "\n";); m.dec_ref(*it); } dealloc(fs); } m_decls = 0; } bool func_decls::contains(func_decl * f) const { if (GET_TAG(m_decls) == 0) { return UNTAG(func_decl*, m_decls) == f; } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); return fs->contains(f); } } bool func_decls::insert(ast_manager & m, func_decl * f) { if (contains(f)) return false; m.inc_ref(f); if (m_decls == 0) { m_decls = TAG(func_decl*, f, 0); } else if (GET_TAG(m_decls) == 0) { func_decl_set * new_fs = alloc(func_decl_set); new_fs->insert(UNTAG(func_decl*, m_decls)); new_fs->insert(f); m_decls = TAG(func_decl*, new_fs, 1); } else { func_decl_set * fs = UNTAG(func_decl_set*, m_decls); fs->insert(f); } return true; } void func_decls::erase(ast_manager & m, func_decl * f) { if (!contains(f)) return; if (GET_TAG(m_decls) == 0) { m.dec_ref(f); m_decls = 0; } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); fs->erase(f); m.dec_ref(f); if (fs->empty()) { dealloc(fs); m_decls = 0; } } } /** \brief Return true if func_decls contains a declaration different from f, but with the same domain. */ bool func_decls::clash(func_decl * f) const { if (m_decls == 0) return false; if (GET_TAG(m_decls) == 0) return false; func_decl_set * fs = UNTAG(func_decl_set *, m_decls); func_decl_set::iterator it = fs->begin(); func_decl_set::iterator end = fs->end(); for (; it != end; ++it) { func_decl * g = *it; if (g == f) continue; if (g->get_arity() != f->get_arity()) continue; unsigned num = g->get_arity(); unsigned i; for (i = 0; i < num; i++) if (g->get_domain(i) != f->get_domain(i)) break; if (i == num) return true; } return false; } bool func_decls::more_than_one() const { if (m_decls == 0 || GET_TAG(m_decls) == 0) return false; func_decl_set * fs = UNTAG(func_decl_set *, m_decls); return fs->size() > 1; } func_decl * func_decls::first() const { if (m_decls == 0) return 0; if (GET_TAG(m_decls) == 0) return UNTAG(func_decl*, m_decls); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); SASSERT(!fs->empty()); return *(fs->begin()); } func_decl * func_decls::find(unsigned arity, sort * const * domain, sort * range) const { if (!more_than_one()) return first(); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); func_decl_set::iterator it = fs->begin(); func_decl_set::iterator end = fs->end(); for (; it != end; it++) { func_decl * f = *it; if (range != 0 && f->get_range() != range) continue; if (f->get_arity() != arity) continue; unsigned i = 0; for (i = 0; i < arity; i++) { if (f->get_domain(i) != domain[i]) break; } if (i == arity) return f; } return 0; } func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const { if (!more_than_one()) first(); ptr_buffer sorts; for (unsigned i = 0; i < num_args; i++) sorts.push_back(m.get_sort(args[i])); return find(num_args, sorts.c_ptr(), range); } ast_object_ref::ast_object_ref(cmd_context & ctx, ast * a):m_ast(a) { ctx.m().inc_ref(a); } void ast_object_ref::finalize(cmd_context & ctx) { ctx.m().dec_ref(m_ast); } void stream_ref::set(char const * name) { if (!name) { throw cmd_exception("invalid stream name"); } reset(); SASSERT(!m_owner); if (strcmp(name, "stdout") == 0) { m_name = "stdout"; m_stream = &std::cout; } else if (strcmp(name, "stderr") == 0) { m_name = "stderr"; m_stream = &std::cerr; } else { m_stream = alloc(std::ofstream, name, std::ios_base::app); m_name = name; m_owner = true; if (m_stream->bad() || m_stream->fail()) { reset(); std::string msg = "failed to set output stream '"; msg += name; msg += "'"; throw cmd_exception(msg); } SASSERT(m_stream); } } void stream_ref::reset() { if (m_owner) dealloc(m_stream); m_name = m_default_name; m_stream = &m_default; m_owner = false; } class cmd_context::pp_env : public smt2_pp_environment { protected: cmd_context & m_owner; arith_util m_autil; bv_util m_bvutil; array_util m_arutil; float_util m_futil; datalog::dl_decl_util m_dlutil; format_ns::format * pp_fdecl_name(symbol const & s, func_decls const & fs, func_decl * f, unsigned & len) { format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); if (!fs.more_than_one()) return f_name; if (!fs.clash(f)) return f_name; return pp_as(f_name, f->get_range()); } format_ns::format * pp_fdecl_ref_core(symbol const & s, func_decls const & fs, func_decl * f) { unsigned len; format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); if (!fs.more_than_one()) return f_name; return pp_signature(f_name, f); } public: pp_env(cmd_context & o):m_owner(o), m_autil(o.m()), m_bvutil(o.m()), m_arutil(o.m()), m_futil(o.m()), m_dlutil(o.m()) {} virtual ~pp_env() {} virtual ast_manager & get_manager() const { return m_owner.m(); } virtual arith_util & get_autil() { return m_autil; } virtual bv_util & get_bvutil() { return m_bvutil; } virtual array_util & get_arutil() { return m_arutil; } virtual float_util & get_futil() { return m_futil; } virtual datalog::dl_decl_util& get_dlutil() { return m_dlutil; } virtual bool uses(symbol const & s) const { return m_owner.m_builtin_decls.contains(s) || m_owner.m_func_decls.contains(s); } virtual format_ns::format * pp_sort(sort * s) { return m_owner.pp(s); } virtual format_ns::format * pp_fdecl(func_decl * f, unsigned & len) { symbol s = f->get_name(); func_decls fs; if (m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_name(s, fs, f, len); } if (m_owner.m_func_decl2alias.find(f, s) && m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_name(s, fs, f, len); } return smt2_pp_environment::pp_fdecl(f, len); } virtual format_ns::format * pp_fdecl_ref(func_decl * f) { symbol s = f->get_name(); func_decls fs; if (m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_ref_core(s, fs, f); } if (m_owner.m_func_decl2alias.find(f, s) && m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_ref_core(s, fs, f); } return smt2_pp_environment::pp_fdecl_ref(f); } }; cmd_context::cmd_context(front_end_params & params, bool main_ctx, ast_manager * m, symbol const & l): m_main_ctx(main_ctx), m_params(params), m_logic(l), m_interactive_mode(false), m_global_decls(!params.m_smtlib2_compliant), // SMTLIB 2.0 uses scoped decls. m_print_success(false), // params.m_smtlib2_compliant), m_random_seed(0), m_produce_unsat_cores(false), m_produce_assignments(false), m_status(UNKNOWN), m_numeral_as_real(false), m_ignore_check(false), m_exit_on_error(false), m_manager(m), m_own_manager(m == 0), m_pmanager(0), m_sexpr_manager(0), m_regular("stdout", std::cout), m_diagnostic("stderr", std::cerr) { SASSERT(m != 0 || !has_manager()); install_basic_cmds(*this); install_ext_basic_cmds(*this); install_tactic_cmds(*this); SASSERT(m != 0 || !has_manager()); if (m) init_external_manager(); if (m_main_ctx) { set_verbose_stream(diagnostic_stream()); } } cmd_context::~cmd_context() { if (m_main_ctx) { set_verbose_stream(std::cerr); } reset(true); finalize_cmds(); finalize_tactic_cmds(); finalize_probes(); } cmd_context::check_sat_state cmd_context::cs_state() const { if (m_check_sat_result.get() == 0) return css_clear; switch (m_check_sat_result->status()) { case l_true: return css_sat; case l_false: return css_unsat; default: return css_unknown; } } void cmd_context::register_builtin_sorts(decl_plugin * p) { svector names; p->get_sort_names(names, m_logic); family_id fid = p->get_family_id(); svector::const_iterator it = names.begin(); svector::const_iterator end = names.end(); for (; it != end; ++it) { psort_decl * d = pm().mk_psort_builtin_decl((*it).m_name, fid, (*it).m_kind); insert(d); } } void cmd_context::register_builtin_ops(decl_plugin * p) { svector names; p->get_op_names(names, m_logic); family_id fid = p->get_family_id(); svector::const_iterator it = names.begin(); svector::const_iterator end = names.end(); for (; it != end; ++it) { if (m_builtin_decls.contains((*it).m_name)) { builtin_decl & d = m_builtin_decls.find((*it).m_name); builtin_decl * new_d = alloc(builtin_decl, fid, (*it).m_kind, d.m_next); d.m_next = new_d; m_extra_builtin_decls.push_back(new_d); } else { m_builtin_decls.insert((*it).m_name, builtin_decl(fid, (*it).m_kind)); } } } void cmd_context::register_plugin(symbol const & name, decl_plugin * p, bool install_names) { m_manager->register_plugin(name, p); if (install_names) { register_builtin_sorts(p); register_builtin_ops(p); } } bool cmd_context::logic_has_arith_core(symbol const & s) const { return s == "QF_LRA" || s == "QF_LIA" || s == "QF_RDL" || s == "QF_IDL" || s == "QF_AUFLIA" || s == "QF_AUFLIRA" || s == "QF_AUFNIA" || s == "QF_AUFNIRA" || s == "QF_UFLIA" || s == "QF_UFLRA" || s == "QF_UFIDL" || s == "QF_UFRDL" || s == "QF_NIA" || s == "QF_NRA" || s == "QF_NIRA" || s == "QF_UFNRA" || s == "QF_UFNIA" || s == "QF_UFNIRA" || s == "QF_BVRE" || s == "AUFLIA" || s == "AUFLIRA" || s == "AUFNIA" || s == "AUFNIRA" || s == "UFLIA" || s == "UFLRA" || s == "UFNRA" || s == "UFNIRA" || s == "UFNIA" || s == "LIA" || s == "LRA" || s == "QF_FPA" ; } bool cmd_context::logic_has_arith() const { return !has_logic() || logic_has_arith_core(m_logic); } bool cmd_context::logic_has_bv_core(symbol const & s) const { return s == "UFBV" || s == "AUFBV" || s == "ABV" || s == "BV" || s == "QF_BV" || s == "QF_UFBV" || s == "QF_ABV" || s == "QF_AUFBV" || s == "QF_BVRE"; } bool cmd_context::logic_has_bv() const { return !has_logic() || logic_has_bv_core(m_logic); } bool cmd_context::logic_has_seq_core(symbol const& s) const { return s == "QF_BVRE"; } bool cmd_context::logic_has_seq() const { return !has_logic() || logic_has_seq_core(m_logic); } bool cmd_context::logic_has_floats() const { return !has_logic() || m_logic == "QF_FPA"; } bool cmd_context::logic_has_array_core(symbol const & s) const { return s == "QF_AX" || s == "QF_AUFLIA" || s == "QF_AUFLIRA" || s == "QF_AUFNIA" || s == "QF_AUFNIRA" || s == "AUFLIA" || s == "AUFLIRA" || s == "AUFNIA" || s == "AUFNIRA" || s == "AUFBV" || s == "ABV" || s == "QF_ABV" || s == "QF_AUFBV"; } bool cmd_context::logic_has_array() const { return !has_logic() || logic_has_array_core(m_logic); } bool cmd_context::logic_has_datatype() const { return !has_logic(); } void cmd_context::init_manager_core(bool new_manager) { SASSERT(m_manager != 0); SASSERT(m_pmanager != 0); m_dt_eh = alloc(dt_eh, *this); m_pmanager->set_new_datatype_eh(m_dt_eh.get()); if (new_manager) { decl_plugin * basic = m_manager->get_plugin(m_manager->get_basic_family_id()); register_builtin_sorts(basic); register_builtin_ops(basic); // the manager was created by the command context. register_plugin(symbol("arith"), alloc(arith_decl_plugin), logic_has_arith()); register_plugin(symbol("bv"), alloc(bv_decl_plugin), logic_has_bv()); register_plugin(symbol("array"), alloc(array_decl_plugin), logic_has_array()); register_plugin(symbol("datatype"), alloc(datatype_decl_plugin), logic_has_datatype()); register_plugin(symbol("seq"), alloc(seq_decl_plugin), logic_has_seq()); register_plugin(symbol("float"), alloc(float_decl_plugin), logic_has_floats()); } else { // the manager was created by an external module, we must register all plugins available in the manager. svector fids; m_manager->get_range(fids); svector::iterator it = fids.begin(); svector::iterator end = fids.end(); for (; it != end; ++it) { decl_plugin * p = m_manager->get_plugin(*it); if (p) { register_builtin_sorts(p); register_builtin_ops(p); } } } if (!has_logic()) { // add list type only if the logic is not specified. // it prevents clashes with builtin types. insert(pm().mk_plist_decl()); } if (m_solver) m_solver->init(m(), m_logic); m_check_logic.set_logic(m(), m_logic); } void cmd_context::init_manager() { SASSERT(m_manager == 0); SASSERT(m_pmanager == 0); m_check_sat_result = 0; m_manager = alloc(ast_manager, m_params.m_proof_mode, m_params.m_trace_stream); m_pmanager = alloc(pdecl_manager, *m_manager); init_manager_core(true); } void cmd_context::init_external_manager() { SASSERT(m_manager != 0); SASSERT(m_pmanager == 0); m_pmanager = alloc(pdecl_manager, *m_manager); init_manager_core(false); } bool cmd_context::supported_logic(symbol const & s) const { return s == "QF_UF" || s == "UF" || logic_has_arith_core(s) || logic_has_bv_core(s) || logic_has_array_core(s) || logic_has_seq_core(s) || s == "QF_FPA"; } void cmd_context::set_logic(symbol const & s) { if (has_logic()) throw cmd_exception("the logic has already been set"); if (has_manager() && m_main_ctx) throw cmd_exception("logic must be set before initialization"); if (!supported_logic(s)) { warning_msg("unknown logic, ignoring set-logic command"); return; } m_logic = s; if (is_logic("QF_RDL") || is_logic("QF_LRA") || is_logic("UFLRA") || is_logic("LRA") || is_logic("RDL") || is_logic("QF_NRA") || is_logic("QF_UFNRA") || is_logic("QF_UFLRA")) m_numeral_as_real = true; } std::string cmd_context::reason_unknown() const { if (m_check_sat_result.get() == 0) throw cmd_exception("state of the most recent check-sat command is not unknown"); return m_check_sat_result->reason_unknown(); } bool cmd_context::is_func_decl(symbol const & s) const { return m_builtin_decls.contains(s) || m_func_decls.contains(s); } 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()); } if (m_macros.contains(s)) { throw cmd_exception("invalid declaration, named expression already defined with this name ", s); } if (m_builtin_decls.contains(s)) { throw cmd_exception("invalid declaration, builtin symbol ", s); } dictionary::entry * e = m_func_decls.insert_if_not_there2(s, func_decls()); func_decls & fs = e->get_data().m_value; if (!fs.insert(m(), f)) { std::string msg = "invalid declaration, "; msg += f->get_arity() == 0 ? "constant" : "function"; msg += " '"; msg += s.str(); msg += "' (whith the given signature) already declared"; throw cmd_exception(msg.c_str()); } if (s != f->get_name()) { TRACE("func_decl_alias", tout << "adding alias for: " << f->get_name() << ", alias: " << s << "\n";); m_func_decl2alias.insert(f, s); } if (!m_global_decls) { m_func_decls_stack.push_back(sf_pair(s, f)); } TRACE("cmd_context", tout << "new sort decl\n" << mk_pp(f, m()) << "\n";); } 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); } pm().inc_ref(p); m_psort_decls.insert(s, p); if (!m_global_decls) { m_psort_decls_stack.push_back(s); } TRACE("cmd_context", tout << "new sort decl\n"; p->display(tout); tout << "\n";); } void cmd_context::insert(symbol const & s, unsigned arity, expr * t) { m_check_sat_result = 0; if (m_builtin_decls.contains(s)) { throw cmd_exception("invalid macro/named expression, builtin symbol ", s); } if (m_macros.contains(s)) { throw cmd_exception("named expression already defined"); } if (m_func_decls.contains(s)) { throw cmd_exception("invalid named expression, declaration already defined with this name ", s); } m().inc_ref(t); TRACE("insert_macro", tout << "new macro " << arity << "\n" << mk_pp(t, m()) << "\n";); m_macros.insert(s, macro(arity, t)); if (!m_global_decls) { m_macros_stack.push_back(s); } } void cmd_context::insert(cmd * c) { symbol const & s = c->get_name(); cmd * old_c; if (m_cmds.find(s, old_c) && c != old_c) { old_c->finalize(*this); dealloc(old_c); } m_cmds.insert(s, c); } void cmd_context::insert_user_tactic(symbol const & s, sexpr * d) { sm().inc_ref(d); sexpr * old_d; if (m_user_tactic_decls.find(s, old_d)) { sm().dec_ref(old_d); } m_user_tactic_decls.insert(s, d); } void cmd_context::insert(symbol const & s, object_ref * r) { r->inc_ref(*this); object_ref * old_r = 0; if (m_object_refs.find(s, old_r)) { old_r->dec_ref(*this); } m_object_refs.insert(s, r); } func_decl * cmd_context::find_func_decl(symbol const & s) const { builtin_decl d; if (m_builtin_decls.find(s, d)) { try { // Remark: ignoring m_next of d. We do not allow two different theories to define the same constant name. func_decl * f; f = m().mk_func_decl(d.m_fid, d.m_decl, 0, 0, 0, static_cast(0), 0); if (f != 0) return f; } catch (ast_exception &) { } throw cmd_exception("invalid function declaration reference, must provide signature for builtin symbol ", s); } if (m_macros.contains(s)) throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); func_decls fs; if (m_func_decls.find(s, fs)) { if (fs.more_than_one()) throw cmd_exception("ambiguous function declaration reference, provide full signature to disumbiguate ( (*) ) ", s); return fs.first(); } throw cmd_exception("invalid function declaration reference, unknown function ", s); return 0; } /** \brief Select a builtin_decl from the list starting at first. We select the decl d s.t. d->m_fid == target_id If there is none that satisfies this condition, we return first. This is a HACK for supporting arithmetic and floating-point arithmetic. These are two different theories in Z3, but they share builtin symbol names: +, -, *, /, <, <=, >, >= */ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family_id target_id) { builtin_decl const * curr = &first; while (curr != 0) { if (curr->m_fid == target_id) return *curr; curr = curr->m_next; } return first; } 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)) { 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. if (d.m_decl != 0 && arity > 0) { builtin_decl const & d2 = peek_builtin_decl(d, domain[0]->get_family_id()); fid = d2.m_fid; k = d2.m_decl; } func_decl * f; if (num_indices == 0) { f = m().mk_func_decl(fid, k, 0, 0, arity, domain, range); } else { buffer ps; for (unsigned i = 0; i < num_indices; i++) ps.push_back(parameter(indices[i])); f = m().mk_func_decl(fid, k, num_indices, ps.c_ptr(), arity, domain, range); } if (f == 0) throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s); return f; } if (m_macros.contains(s)) throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); if (num_indices > 0) throw cmd_exception("invalid indexed function declaration reference, unknown builtin function ", s); func_decl * f = 0; func_decls fs; if (m_func_decls.find(s, fs)) { f = fs.find(arity, domain, range); } if (f == 0) throw cmd_exception("invalid function declaration reference, unknown function ", s); return f; } psort_decl * cmd_context::find_psort_decl(symbol const & s) const { psort_decl * p = 0; m_psort_decls.find(s, p); return p; } cmd_context::macro cmd_context::find_macro(symbol const & s) const { macro m; m_macros.find(s, m); return m; } cmd * cmd_context::find_cmd(symbol const & s) const { cmd * c = 0; m_cmds.find(s, c); return c; } sexpr * cmd_context::find_user_tactic(symbol const & s) const { sexpr * n = 0; m_user_tactic_decls.find(s, n); return n; } object_ref * cmd_context::find_object_ref(symbol const & s) const { object_ref * r = 0; m_object_refs.find(s, r); if (r == 0) throw cmd_exception("unknown global variable ", s); return r; } #define CHECK_SORT(T) if (params().m_well_sorted_check) m().check_sorts_core(T) void cmd_context::mk_const(symbol const & s, expr_ref & result) const { mk_app(s, 0, 0, 0, 0, 0, result); } void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, expr_ref & result) const { builtin_decl d; if (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 the sort of args[0] (if available) to decide which plugin we use. if (d.m_decl != 0 && num_args > 0) { builtin_decl const & d2 = peek_builtin_decl(d, m().get_sort(args[0])->get_family_id()); fid = d2.m_fid; k = d2.m_decl; } if (num_indices == 0) { result = m().mk_app(fid, k, 0, 0, num_args, args, range); } else { result = m().mk_app(fid, k, num_indices, indices, num_args, args, range); } if (result.get() == 0) throw cmd_exception("invalid builtin application ", s); CHECK_SORT(result.get()); return; } if (num_indices > 0) throw cmd_exception("invalid use of indexed indentifier, unknown builtin function ", s); macro _m; if (m_macros.find(s, _m)) { if (num_args != _m.first) throw cmd_exception("invalid defined function application, incorrect number of arguments ", s); if (num_args == 0) { result = _m.second; return; } SASSERT(num_args > 0); TRACE("macro_bug", tout << "m_well_sorted_check: " << params().m_well_sorted_check << "\n"; tout << "s: " << s << "\n"; tout << "body:\n" << mk_ismt2_pp(_m.second, m()) << "\n"; tout << "args:\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n" << mk_pp(m().get_sort(args[i]), m()) << "\n";); var_subst subst(m()); subst(_m.second, num_args, args, result); if (params().m_well_sorted_check && !is_well_sorted(m(), result)) throw cmd_exception("invalid macro application, sort mismatch ", s); return; } func_decls fs; if (!m_func_decls.find(s, fs)) { if (num_args == 0) { throw cmd_exception("unknown constant ", s); } else throw cmd_exception("unknown function/constant ", s); } if (num_args == 0 && range == 0) { if (fs.more_than_one()) throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as ) to disumbiguate ", s); func_decl * f = fs.first(); if (f == 0) throw cmd_exception("unknown constant ", s); if (f->get_arity() != 0) throw cmd_exception("invalid function application, missing arguments ", s); result = m().mk_const(f); return; } else { func_decl * f = fs.find(m(), num_args, args, range); if (f == 0) throw cmd_exception("unknown constant ", s); if (params().m_well_sorted_check) m().check_sort(f, num_args, args); result = m().mk_app(f, num_args, args); return; } } void cmd_context::erase_func_decl(symbol const & s) { if (!global_decls()) { throw cmd_exception("function declarations can only be erased when global declarations (instead of scoped) are used"); } func_decls fs; m_func_decls.find(s, fs); while (!fs.empty()) { func_decl * f = fs.first(); if (s != f->get_name()) { SASSERT(m_func_decl2alias.contains(f)); m_func_decl2alias.erase(f); } fs.erase(m(), f); } fs.finalize(m()); m_func_decls.erase(s); } void cmd_context::erase_func_decl_core(symbol const & s, func_decl * f) { func_decls fs; m_func_decls.find(s, fs); if (fs.contains(f)) { if (s != f->get_name()) { SASSERT(m_func_decl2alias.contains(f)); m_func_decl2alias.erase(f); } fs.erase(m(), f); if (fs.empty()) m_func_decls.erase(s); } } void cmd_context::erase_func_decl(symbol const & s, func_decl * f) { if (!global_decls()) { throw cmd_exception("function declarations can only be erased when global (instead of scoped) declarations are used"); } erase_func_decl_core(s, f); } void cmd_context::erase_psort_decl_core(symbol const & s) { psort_decl * p; if (m_psort_decls.find(s, p)) { pm().dec_ref(p); m_psort_decls.erase(s); } } void cmd_context::erase_psort_decl(symbol const & s) { if (!global_decls()) { throw cmd_exception("sort declarations can only be erased when global (instead of scoped) declarations are used"); } erase_psort_decl_core(s); } void cmd_context::erase_macro_core(symbol const & s) { macro _m; if (m_macros.find(s, _m)) { m().dec_ref(_m.second); m_macros.erase(s); } } void cmd_context::erase_macro(symbol const & s) { if (!global_decls()) { throw cmd_exception("macros (aka named expressions) can only be erased when global (instead of scoped) declarations are used"); } erase_macro_core(s); } void cmd_context::erase_cmd(symbol const & s) { cmd * c; if (m_cmds.find(s, c)) { c->finalize(*this); m_cmds.erase(s); dealloc(c); } } void cmd_context::erase_user_tactic(symbol const & s) { sexpr * d; if (m_user_tactic_decls.find(s, d)) { m_user_tactic_decls.erase(s); sm().dec_ref(d); } } void cmd_context::erase_object_ref(symbol const & s) { object_ref * r = 0; if (m_object_refs.find(s, r)) { r->dec_ref(*this); m_object_refs.erase(s); } } void cmd_context::reset_func_decls() { dictionary::iterator it = m_func_decls.begin(); dictionary::iterator end = m_func_decls.end(); for (; it != end; ++it) { func_decls fs = (*it).m_value; fs.finalize(m()); } m_func_decls.reset(); m_func_decls_stack.reset(); m_func_decl2alias.reset(); } void cmd_context::reset_psort_decls() { dictionary::iterator it = m_psort_decls.begin(); dictionary::iterator end = m_psort_decls.end(); for (; it != end; ++it) { psort_decl * p = (*it).m_value; pm().dec_ref(p); } m_psort_decls.reset(); m_psort_decls_stack.reset(); } void cmd_context::reset_macros() { dictionary::iterator it = m_macros.begin(); dictionary::iterator end = m_macros.end(); for (; it != end; ++it) { expr * t = (*it).m_value.second; m().dec_ref(t); } m_macros.reset(); m_macros_stack.reset(); } void cmd_context::reset_cmds() { dictionary::iterator it = m_cmds.begin(); dictionary::iterator end = m_cmds.end(); for (; it != end; ++it) { cmd * c = (*it).m_value; c->reset(*this); } } void cmd_context::finalize_cmds() { dictionary::iterator it = m_cmds.begin(); dictionary::iterator end = m_cmds.end(); for (; it != end; ++it) { cmd * c = (*it).m_value; c->finalize(*this); dealloc(c); } m_cmds.reset(); } void cmd_context::reset_user_tactics() { dec_ref_values(sm(), m_user_tactic_decls); m_user_tactic_decls.reset(); } void cmd_context::reset_object_refs() { dictionary::iterator it = m_object_refs.begin(); dictionary::iterator end = m_object_refs.end(); for (; it != end; ++it) { object_ref * r = (*it).m_value; r->dec_ref(*this); } m_object_refs.reset(); } void cmd_context::insert_aux_pdecl(pdecl * p) { pm().inc_ref(p); m_aux_pdecls.push_back(p); } void cmd_context::reset(bool finalize) { m_check_sat_result = 0; m_logic = symbol::null; m_check_sat_result = 0; m_numeral_as_real = false; m_builtin_decls.reset(); m_extra_builtin_decls.reset(); m_check_logic.reset(); reset_object_refs(); reset_cmds(); reset_psort_decls(); restore_aux_pdecls(0); reset_macros(); reset_func_decls(); restore_assertions(0); restore_assumptions(0); if (m_solver) m_solver->reset(); m_pp_env = 0; m_dt_eh = 0; if (m_manager) { dealloc(m_pmanager); m_pmanager = 0; if (m_own_manager) { dealloc(m_manager); m_manager = 0; } else { // doesn't own manager... so it cannot be deleted // reinit cmd_context if this is not a finalization step if (!finalize) init_external_manager(); } } if (m_sexpr_manager) { dealloc(m_sexpr_manager); m_sexpr_manager = 0; } SASSERT(!m_own_manager || !has_manager()); } void cmd_context::assert_expr(expr * t) { if (!m_check_logic(t)) throw cmd_exception(m_check_logic.get_last_error()); m_check_sat_result = 0; m().inc_ref(t); m_assertions.push_back(t); if (m_solver) m_solver->assert_expr(t); } void cmd_context::assert_expr(symbol const & name, expr * t) { if (!m_check_logic(t)) throw cmd_exception(m_check_logic.get_last_error()); if (!m_produce_unsat_cores || name == symbol::null) { assert_expr(t); return; } app * proxy = m().mk_const(name, m().mk_bool_sort()); expr * new_t = m().mk_implies(proxy, t); m().inc_ref(proxy); m_assumptions.push_back(proxy); assert_expr(new_t); } void cmd_context::push() { m_check_sat_result = 0; if (!has_manager()) init_manager(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_func_decls_stack_lim = m_func_decls_stack.size(); s.m_psort_decls_stack_lim = m_psort_decls_stack.size(); s.m_macros_stack_lim = m_macros_stack.size(); s.m_aux_pdecls_lim = m_aux_pdecls.size(); s.m_assertions_lim = m_assertions.size(); s.m_assumptions_lim = m_assumptions.size(); if (m_solver) m_solver->push(); } void cmd_context::push(unsigned n) { for (unsigned i = 0; i < n; i++) push(); } void cmd_context::restore_func_decls(unsigned old_sz) { SASSERT(old_sz <= m_func_decls_stack.size()); svector::iterator it = m_func_decls_stack.begin() + old_sz; svector::iterator end = m_func_decls_stack.end(); for (; it != end; ++it) { sf_pair const & p = *it; erase_func_decl_core(p.first, p.second); } m_func_decls_stack.shrink(old_sz); } void cmd_context::restore_psort_decls(unsigned old_sz) { SASSERT(old_sz <= m_psort_decls_stack.size()); svector::iterator it = m_psort_decls_stack.begin() + old_sz; svector::iterator end = m_psort_decls_stack.end(); for (; it != end; ++it) { symbol const & s = *it; psort_decl * d = 0; if (!m_psort_decls.find(s, d)) { UNREACHABLE(); } pm().dec_ref(d); m_psort_decls.erase(s); } m_psort_decls_stack.shrink(old_sz); } void cmd_context::restore_macros(unsigned old_sz) { SASSERT(old_sz <= m_macros_stack.size()); svector::iterator it = m_macros_stack.begin() + old_sz; svector::iterator end = m_macros_stack.end(); for (; it != end; ++it) { symbol const & s = *it; macro _m; if (!m_macros.find(s, _m)) { UNREACHABLE(); } m().dec_ref(_m.second); m_macros.erase(s); } m_macros_stack.shrink(old_sz); } void cmd_context::restore_aux_pdecls(unsigned old_sz) { SASSERT(old_sz <= m_aux_pdecls.size()); ptr_vector::iterator it = m_aux_pdecls.begin() + old_sz; ptr_vector::iterator end = m_aux_pdecls.end(); for (; it != end; ++it) { pm().dec_ref(*it); } m_aux_pdecls.shrink(old_sz); } void cmd_context::restore_assertions(unsigned old_sz) { SASSERT(old_sz <= m_assertions.size()); SASSERT(!m_interactive_mode || m_assertions.size() == m_assertion_strings.size()); ptr_vector::iterator it = m_assertions.begin() + old_sz; ptr_vector::iterator end = m_assertions.end(); for (; it != end; ++it) { m().dec_ref(*it); } m_assertions.shrink(old_sz); if (m_interactive_mode) m_assertion_strings.shrink(old_sz); } void cmd_context::restore_assumptions(unsigned old_sz) { SASSERT(old_sz <= m_assumptions.size()); ptr_vector::iterator it = m_assumptions.begin() + old_sz; ptr_vector::iterator end = m_assumptions.end(); for (; it != end; ++it) { m().dec_ref(*it); } m_assumptions.shrink(old_sz); } void cmd_context::pop(unsigned n) { m_check_sat_result = 0; if (n == 0) return; unsigned lvl = m_scopes.size(); if (n > lvl) throw cmd_exception("invalid pop command, argument is greater than the current stack depth"); if (m_solver) { m_solver->pop(n); } unsigned new_lvl = lvl - n; scope & s = m_scopes[new_lvl]; restore_func_decls(s.m_func_decls_stack_lim); restore_psort_decls(s.m_psort_decls_stack_lim); restore_macros(s.m_macros_stack_lim); restore_aux_pdecls(s.m_aux_pdecls_lim); restore_assertions(s.m_assertions_lim); restore_assumptions(s.m_assumptions_lim); m_scopes.shrink(new_lvl); } void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions) { if (m_ignore_check) return; IF_VERBOSE(100, verbose_stream() << "check-sat..." << std::endl;); TRACE("before_check_sat", dump_assertions(tout);); if (params().m_ignore_checksat) { m_check_sat_result = 0; regular_stream() << "unknown" << std::endl; return; } if (!has_manager()) init_manager(); if (m_solver) { m_check_sat_result = m_solver.get(); // solver itself stores the result. m_solver->set_front_end_params(m_params); m_solver->set_progress_callback(this); m_solver->set_produce_proofs(produce_proofs()); m_solver->set_produce_models(produce_models()); m_solver->set_produce_unsat_cores(produce_unsat_cores()); scoped_watch sw(*this); cancel_eh eh(*m_solver); scoped_ctrl_c ctrlc(eh); unsigned old_sz = m_assumptions.size(); m_assumptions.append(num_assumptions, assumptions); lbool r; try { r = m_solver->check_sat(m_assumptions.size(), m_assumptions.c_ptr()); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { throw cmd_exception(ex.msg()); } m_assumptions.shrink(old_sz); m_solver->set_status(r); display_sat_result(r); validate_check_sat_result(r); if (r == l_true) validate_model(); } else { // There is no solver installed in the command context. regular_stream() << "unknown" << std::endl; } } void cmd_context::display_sat_result(lbool r) { switch (r) { case l_true: regular_stream() << "sat" << std::endl; break; case l_false: regular_stream() << "unsat" << std::endl; break; case l_undef: regular_stream() << "unknown" << std::endl; break; } } void cmd_context::validate_check_sat_result(lbool r) { switch (r) { case l_true: if (m_status == UNSAT) { #ifdef _EXTERNAL_RELEASE throw cmd_exception("check annotation that says unsat"); #else diagnostic_stream() << "BUG: incompleteness" << std::endl; exit(ERR_INCOMPLETENESS); #endif } break; case l_false: if (m_status == SAT) { #ifdef _EXTERNAL_RELEASE throw cmd_exception("check annotation that says sat"); #else diagnostic_stream() << "BUG: unsoundness" << std::endl; exit(ERR_UNSOUNDNESS); #endif } break; default: break; } } void cmd_context::set_diagnostic_stream(char const * name) { m_diagnostic.set(name); if (m_main_ctx) { set_warning_stream(&(*m_diagnostic)); set_verbose_stream(diagnostic_stream()); } } struct contains_array_op_proc { struct found {}; family_id m_array_fid; contains_array_op_proc(ast_manager & m):m_array_fid(m.get_family_id("array")) {} void operator()(var * n) {} void operator()(app * n) { if (n->get_family_id() != m_array_fid) return; decl_kind k = n->get_decl_kind(); if (k == OP_AS_ARRAY || k == OP_STORE || k == OP_ARRAY_MAP || k == OP_CONST_ARRAY) throw found(); } void operator()(quantifier * n) {} }; /** \brief Check if the current model satisfies the quantifier free formulas. */ void cmd_context::validate_model() { if (!params().m_model_validate) return; if (!is_model_available()) return; model_ref md; get_check_sat_result()->get_model(md); SASSERT(md.get() != 0); params_ref p; p.set_uint(":max-degree", UINT_MAX); // evaluate algebraic numbers of any degree. p.set_uint(":sort-store", true); p.set_bool(":model-completion", true); model_evaluator evaluator(*(md.get()), p); contains_array_op_proc contains_array(m()); { cancel_eh eh(evaluator); expr_ref r(m()); scoped_ctrl_c ctrlc(eh); ptr_vector::const_iterator it = begin_assertions(); ptr_vector::const_iterator end = end_assertions(); for (; it != end; ++it) { expr * a = *it; if (is_ground(a)) { r = 0; evaluator(a, r); TRACE("model_validate", tout << "checking\n" << mk_ismt2_pp(a, m()) << "\nresult:\n" << mk_ismt2_pp(r, m()) << "\n";); if (m().is_true(r)) continue; // The evaluator for array expressions is not complete // If r contains as_array/store/map/const expressions, then we do not generate the error. // TODO: improve evaluator for model expressions. // Note that, if "a" evaluates to false, then the error will be generated. try { for_each_expr(contains_array, r); } catch (contains_array_op_proc::found) { continue; } throw cmd_exception("an invalid model was generated"); } } } } void cmd_context::set_solver(solver * s) { m_check_sat_result = 0; m_solver = s; m_solver->set_front_end_params(m_params); if (has_manager() && s != 0) { m_solver->init(m(), m_logic); // assert formulas and create scopes in the new solver. unsigned lim = 0; svector::iterator it = m_scopes.begin(); svector::iterator end = m_scopes.end(); for (; it != end; ++it) { scope & s = *it; for (unsigned i = lim; i < s.m_assertions_lim; i++) { m_solver->assert_expr(m_assertions[i]); } lim = s.m_assertions_lim; m_solver->push(); } unsigned sz = m_assertions.size(); for (unsigned i = lim; i < sz; i++) { m_solver->assert_expr(m_assertions[i]); } } } void cmd_context::display_statistics(bool show_total_time, double total_time) { statistics st; unsigned long long mem = memory::get_max_used_memory(); if (show_total_time) st.update("total time", total_time); st.update("time", get_seconds()); st.update("memory", static_cast(mem)/static_cast(1024*1024)); if (m_check_sat_result) { m_check_sat_result->collect_statistics(st); } else if (m_solver) { m_solver->collect_statistics(st); } st.display_smt2(regular_stream()); } void cmd_context::display_assertions() { if (!m_interactive_mode) throw cmd_exception("command is only available in interactive mode, use command (set-option :interactive-mode true)"); vector::const_iterator it = m_assertion_strings.begin(); vector::const_iterator end = m_assertion_strings.end(); regular_stream() << "("; for (bool first = true; it != end; ++it) { std::string const & s = *it; if (first) first = false; else regular_stream() << "\n "; regular_stream() << s; } regular_stream() << ")" << std::endl; } bool cmd_context::is_model_available() const { if (params().m_model && has_manager() && (cs_state() == css_sat || cs_state() == css_unknown)) { model_ref md; get_check_sat_result()->get_model(md); return md.get() != 0; } return false; } format_ns::format * cmd_context::pp(sort * s) const { TRACE("cmd_context", tout << "pp(sort * s), s: " << mk_pp(s, m()) << "\n";); return pm().pp(s); } cmd_context::pp_env & cmd_context::get_pp_env() const { if (m_pp_env.get() == 0) { const_cast(this)->m_pp_env = alloc(pp_env, *const_cast(this)); } return *(m_pp_env.get()); } void cmd_context::pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const { mk_smt2_format(n, get_pp_env(), get_pp_default_params(), num_vars, var_prefix, r, var_names); } void cmd_context::pp(expr * n, format_ns::format_ref & r) const { sbuffer buf; pp(n, 0, 0, r, buf); } void cmd_context::pp(func_decl * f, format_ns::format_ref & r) const { mk_smt2_format(f, get_pp_env(), get_pp_default_params(), r); } void cmd_context::display(std::ostream & out, sort * s, unsigned indent) const { format_ns::format_ref f(format_ns::fm(m())); f = pp(s); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m(), get_pp_default_params()); } void cmd_context::display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const { format_ns::format_ref f(format_ns::fm(m())); pp(n, num_vars, var_prefix, f, var_names); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m(), get_pp_default_params()); } void cmd_context::display(std::ostream & out, expr * n, unsigned indent) const { sbuffer buf; display(out, n, indent, 0, 0, buf); } void cmd_context::display(std::ostream & out, func_decl * d, unsigned indent) const { format_ns::format_ref f(format_ns::fm(m())); pp(d, f); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m(), get_pp_default_params()); } void cmd_context::dump_assertions(std::ostream & out) const { ptr_vector::const_iterator it = m_assertions.begin(); ptr_vector::const_iterator end = m_assertions.end(); for (; it != end; ++it) { display(out, *it); out << std::endl; } } void cmd_context::display_smt2_benchmark(std::ostream & out, unsigned num, expr * const * assertions, symbol const & logic) const { if (logic != symbol::null) out << "(set-logic " << logic << ")" << std::endl; // collect uninterpreted function declarations decl_collector decls(m(), false); for (unsigned i = 0; i < num; i++) { decls.visit(assertions[i]); } // TODO: display uninterpreted sort decls, and datatype decls. unsigned num_decls = decls.get_num_decls(); func_decl * const * fs = decls.get_func_decls(); for (unsigned i = 0; i < num_decls; i++) { display(out, fs[i]); out << std::endl; } for (unsigned i = 0; i < num; i++) { out << "(assert "; display(out, assertions[i], 8); out << ")" << std::endl; } out << "(check-sat)" << std::endl; } void cmd_context::slow_progress_sample() { SASSERT(m_solver); statistics st; regular_stream() << "(progress\n"; m_solver->collect_statistics(st); st.display_smt2(regular_stream()); svector labels; m_solver->get_labels(labels); regular_stream() << "(labels"; for (unsigned i = 0; i < labels.size(); i++) { regular_stream() << " " << labels[i]; } regular_stream() << "))" << std::endl; } void cmd_context::fast_progress_sample() { } cmd_context::dt_eh::dt_eh(cmd_context & owner): m_owner(owner), m_dt_util(owner.m()) { } cmd_context::dt_eh::~dt_eh() { } void cmd_context::dt_eh::operator()(sort * dt) { TRACE("new_dt_eh", tout << "new datatype: "; m_owner.pm().display(tout, dt); tout << "\n";); ptr_vector const * constructors = m_dt_util.get_datatype_constructors(dt); unsigned num_constructors = constructors->size(); for (unsigned j = 0; j < num_constructors; j++) { func_decl * c = constructors->get(j); m_owner.insert(c); TRACE("new_dt_eh", tout << "new constructor: " << c->get_name() << "\n";); func_decl * r = m_dt_util.get_constructor_recognizer(c); m_owner.insert(r); TRACE("new_dt_eh", tout << "new recognizer: " << r->get_name() << "\n";); ptr_vector const * accessors = m_dt_util.get_constructor_accessors(c); unsigned num_accessors = accessors->size(); for (unsigned k = 0; k < num_accessors; k++) { func_decl * a = accessors->get(k); m_owner.insert(a); TRACE("new_dt_eh", tout << "new accessor: " << a->get_name() << "\n";); } } } std::ostream & operator<<(std::ostream & out, cmd_context::status st) { switch (st) { case cmd_context::UNSAT: out << "unsat"; break; case cmd_context::SAT: out << "sat"; break; default: out << "unknown"; break; } return out; }