/*++ Copyright (c) 2006 Microsoft Corporation Module Name: defined_names.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #include"defined_names.h" #include"used_vars.h" #include"var_subst.h" #include"ast_smt2_pp.h" #include"ast_pp.h" defined_names::impl::impl(ast_manager & m, char const * prefix): m_manager(m), m_exprs(m), m_names(m), m_apply_proofs(m) { if (prefix) m_z3name = prefix; } defined_names::impl::~impl() { } /** \brief Given an expression \c e that may contain free variables, return an application (sk x_1 ... x_n), where sk is a fresh variable name, and x_i's are the free variables of \c e. Store in var_sorts and var_names information about the free variables of \c e. This data is used to create an universal quantifier over the definition of the new name. */ app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffer & var_names) { used_vars uv; uv(e); unsigned num_vars = uv.get_max_found_var_idx_plus_1(); ptr_buffer new_args; ptr_buffer domain; for (unsigned i = 0; i < num_vars; i++) { sort * s = uv.get(i); if (s) { domain.push_back(s); new_args.push_back(m_manager.mk_var(i, s)); var_sorts.push_back(s); } else { var_sorts.push_back(m_manager.mk_bool_sort()); // could be any sort. } var_names.push_back(symbol(i)); } sort * range = m_manager.get_sort(e); func_decl * new_skolem_decl = m_manager.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.c_ptr(), range); app * n = m_manager.mk_app(new_skolem_decl, new_args.size(), new_args.c_ptr()); TRACE("mk_definition_bug", tout << "gen_name: " << mk_ismt2_pp(n, m_manager) << "\n"; for (unsigned i = 0; i < var_sorts.size(); i++) tout << mk_pp(var_sorts[i], m_manager) << " "; tout << "\n";); return n; } /** \brief Cache \c n as a name for expression \c e. */ void defined_names::impl::cache_new_name(expr * e, app * n) { m_expr2name.insert(e, n); m_exprs.push_back(e); m_names.push_back(n); } /** \brief Cache \c pr as a proof that m_expr2name[e] is a name for expression \c e. */ void defined_names::impl::cache_new_name_intro_proof(expr * e, proof * pr) { SASSERT(m_expr2name.contains(e)); m_expr2proof.insert(e, pr); m_apply_proofs.push_back(pr); } /** \brief Given a definition conjunct \c def of the name \c name, store in \c result this definition. A quantifier is added around \c def_conjunct, if sorts and names are not empty. In this case, The application \c name is used as a pattern for the new quantifier. */ void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result) { SASSERT(sorts.size() == names.size()); if (sorts.empty()) result = def_conjunct; else { expr * patterns[1] = { m_manager.mk_pattern(name) }; quantifier_ref q(m_manager); q = m_manager.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), def_conjunct, 1, symbol::null, symbol::null, 1, patterns); TRACE("mk_definition_bug", tout << "before elim_unused_vars:\n" << mk_ismt2_pp(q, m_manager) << "\n";); elim_unused_vars(m_manager, q, result); TRACE("mk_definition_bug", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m_manager) << "\n";); } } /** \brief Given a definition conjunct \c def of the name \c name, store in \c result this definition. A quantifier is added around \c def_conjunct, if sorts and names are not empty. In this case, The application \c name is used as a pattern for the new quantifier. */ void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result) { expr_ref tmp(m_manager); bound_vars(sorts, names, def_conjunct, name, tmp); result.push_back(tmp); } #define MK_OR m_manager.mk_or #define MK_NOT m_manager.mk_not #define MK_EQ m_manager.mk_eq void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def) { expr_ref_buffer defs(m_manager); if (m_manager.is_bool(e)) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, defs); bound_vars(var_sorts, var_names, MK_OR(n, MK_NOT(e)), n, defs); } else if (m_manager.is_term_ite(e)) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(to_app(e)->get_arg(0)), MK_EQ(n, to_app(e)->get_arg(1))), n, defs); bound_vars(var_sorts, var_names, MK_OR(to_app(e)->get_arg(0), MK_EQ(n, to_app(e)->get_arg(2))), n, defs); } else { bound_vars(var_sorts, var_names, MK_EQ(e, n), n, defs); } new_def = defs.size() == 1 ? defs[0] : m_manager.mk_and(defs.size(), defs.c_ptr()); } void defined_names::pos_impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, new_def); } bool defined_names::impl::mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) { TRACE("mk_definition_bug", tout << "making name for:\n" << mk_ismt2_pp(e, m_manager) << "\n";); app * n_ptr; if (m_expr2name.find(e, n_ptr)) { TRACE("mk_definition_bug", tout << "name for expression is already cached..., returning false...\n";); n = n_ptr; if (m_manager.proofs_enabled()) { proof * pr_ptr; m_expr2proof.find(e, pr_ptr); SASSERT(pr_ptr); pr = pr_ptr; } return false; } else { sort_ref_buffer var_sorts(m_manager); buffer var_names; n = gen_name(e, var_sorts, var_names); cache_new_name(e, n); TRACE("mk_definition_bug", tout << "name: " << mk_ismt2_pp(n, m_manager) << "\n";); // variables are in reverse order in quantifiers std::reverse(var_sorts.c_ptr(), var_sorts.c_ptr() + var_sorts.size()); std::reverse(var_names.c_ptr(), var_names.c_ptr() + var_names.size()); mk_definition(e, n, var_sorts, var_names, new_def); TRACE("mk_definition_bug", tout << "new_def:\n" << mk_ismt2_pp(new_def, m_manager) << "\n";); if (m_manager.proofs_enabled()) { new_def_pr = m_manager.mk_def_intro(new_def); pr = m_manager.mk_apply_def(e, n, new_def_pr); cache_new_name_intro_proof(e, pr); } return true; } } void defined_names::impl::push_scope() { SASSERT(m_exprs.size() == m_names.size()); m_lims.push_back(m_exprs.size()); } void defined_names::impl::pop_scope(unsigned num_scopes) { unsigned lvl = m_lims.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_sz = m_lims[new_lvl]; unsigned sz = m_exprs.size(); SASSERT(old_sz <= sz); SASSERT(sz == m_names.size()); while (old_sz != sz) { --sz; if (m_manager.proofs_enabled()) { m_expr2proof.erase(m_exprs.back()); m_apply_proofs.pop_back(); } m_expr2name.erase(m_exprs.back()); m_exprs.pop_back(); m_names.pop_back(); } SASSERT(m_exprs.size() == old_sz); m_lims.shrink(new_lvl); } void defined_names::impl::reset() { m_expr2name.reset(); m_expr2proof.reset(); m_exprs.reset(); m_names.reset(); m_apply_proofs.reset(); m_lims.reset(); }