mirror of
https://github.com/Z3Prover/z3
synced 2025-04-05 17:14:07 +00:00
3380 lines
118 KiB
C++
3380 lines
118 KiB
C++
/*++
|
|
Copyright (c) 2006 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ast.cpp
|
|
|
|
Abstract:
|
|
|
|
Expression DAG
|
|
|
|
Author:
|
|
|
|
Leonardo de Moura (leonardo) 2006-09-28.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include<sstream>
|
|
#include<cstring>
|
|
#include "ast/ast.h"
|
|
#include "ast/ast_pp.h"
|
|
#include "ast/ast_ll_pp.h"
|
|
#include "util/buffer.h"
|
|
#include "util/warning.h"
|
|
#include "util/string_buffer.h"
|
|
#include "ast/ast_util.h"
|
|
#include "ast/ast_smt2_pp.h"
|
|
|
|
// -----------------------------------
|
|
//
|
|
// parameter
|
|
//
|
|
// -----------------------------------
|
|
|
|
parameter::~parameter() {
|
|
if (m_kind == PARAM_RATIONAL) {
|
|
dealloc(m_rational);
|
|
}
|
|
}
|
|
|
|
parameter::parameter(parameter const& other) {
|
|
m_kind = PARAM_INT;
|
|
m_int = 0;
|
|
*this = other;
|
|
}
|
|
|
|
parameter& parameter::operator=(parameter const& other) {
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
if (m_kind == PARAM_RATIONAL) {
|
|
dealloc(m_rational);
|
|
}
|
|
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 = alloc(rational, other.get_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;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void parameter::init_eh(ast_manager & m) {
|
|
if (is_ast()) {
|
|
m.inc_ref(get_ast());
|
|
}
|
|
}
|
|
|
|
void parameter::del_eh(ast_manager & m, family_id fid) {
|
|
if (is_ast()) {
|
|
m.dec_ref(get_ast());
|
|
}
|
|
else if (is_external()) {
|
|
SASSERT(fid != null_family_id);
|
|
decl_plugin * plugin = m.get_plugin(fid);
|
|
if (plugin) {
|
|
plugin->del(*this);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool parameter::operator==(parameter const & p) const {
|
|
if (m_kind != p.m_kind) return false;
|
|
switch(m_kind) {
|
|
case PARAM_INT: return m_int == p.m_int;
|
|
case PARAM_AST: return m_ast == p.m_ast;
|
|
case PARAM_SYMBOL: return get_symbol() == p.get_symbol();
|
|
case PARAM_RATIONAL: return get_rational() == p.get_rational();
|
|
case PARAM_DOUBLE: return m_dval == p.m_dval;
|
|
case PARAM_EXTERNAL: return m_ext_id == p.m_ext_id;
|
|
default: UNREACHABLE(); return false;
|
|
}
|
|
}
|
|
|
|
unsigned parameter::hash() const {
|
|
unsigned b = 0;
|
|
switch(m_kind) {
|
|
case PARAM_INT: b = m_int; break;
|
|
case PARAM_AST: b = m_ast->hash(); break;
|
|
case PARAM_SYMBOL: b = get_symbol().hash(); break;
|
|
case PARAM_RATIONAL: b = get_rational().hash(); break;
|
|
case PARAM_DOUBLE: b = static_cast<unsigned>(m_dval); break;
|
|
case PARAM_EXTERNAL: b = m_ext_id; break;
|
|
}
|
|
return (b << 2) | m_kind;
|
|
}
|
|
|
|
std::ostream& parameter::display(std::ostream& out) const {
|
|
switch(m_kind) {
|
|
case PARAM_INT: return out << get_int();
|
|
case PARAM_SYMBOL: return out << get_symbol();
|
|
case PARAM_RATIONAL: return out << get_rational();
|
|
case PARAM_AST: return out << "#" << get_ast()->get_id();
|
|
case PARAM_DOUBLE: return out << m_dval;
|
|
case PARAM_EXTERNAL: return out << "@" << m_ext_id;
|
|
default:
|
|
UNREACHABLE();
|
|
return out << "[invalid parameter]";
|
|
}
|
|
}
|
|
|
|
void display_parameters(std::ostream & out, unsigned n, parameter const * p) {
|
|
if (n > 0) {
|
|
out << "[";
|
|
for (unsigned i = 0; i < n; i ++)
|
|
out << p[i] << (i < n-1 ? ":" : "");
|
|
out << "]";
|
|
}
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// family_manager
|
|
//
|
|
// -----------------------------------
|
|
|
|
family_id family_manager::mk_family_id(symbol const & s) {
|
|
family_id r;
|
|
if (m_families.find(s, r)) {
|
|
return r;
|
|
}
|
|
r = m_next_id;
|
|
m_next_id++;
|
|
m_families.insert(s, r);
|
|
m_names.push_back(s);
|
|
return r;
|
|
}
|
|
|
|
family_id family_manager::get_family_id(symbol const & s) const {
|
|
family_id r;
|
|
if (m_families.find(s, r))
|
|
return r;
|
|
else
|
|
return null_family_id;
|
|
}
|
|
|
|
bool family_manager::has_family(symbol const & s) const {
|
|
return m_families.contains(s);
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// decl_info
|
|
//
|
|
// -----------------------------------
|
|
|
|
decl_info::decl_info(family_id family_id, decl_kind k, unsigned num_parameters,
|
|
parameter const * parameters, bool private_params):
|
|
m_family_id(family_id),
|
|
m_kind(k),
|
|
m_parameters(num_parameters, const_cast<parameter *>(parameters)),
|
|
m_private_parameters(private_params) {
|
|
}
|
|
|
|
decl_info::decl_info(decl_info const& other) :
|
|
m_family_id(other.m_family_id),
|
|
m_kind(other.m_kind),
|
|
m_parameters(other.m_parameters.size(), other.m_parameters.c_ptr()),
|
|
m_private_parameters(other.m_private_parameters) {
|
|
}
|
|
|
|
|
|
void decl_info::init_eh(ast_manager & m) {
|
|
for (parameter & p : m_parameters) {
|
|
p.init_eh(m);
|
|
}
|
|
}
|
|
|
|
void decl_info::del_eh(ast_manager & m) {
|
|
for (parameter & p : m_parameters) {
|
|
p.del_eh(m, m_family_id);
|
|
}
|
|
}
|
|
|
|
struct decl_info_child_hash_proc {
|
|
unsigned operator()(decl_info const * info, unsigned idx) const { return info->get_parameter(idx).hash(); }
|
|
};
|
|
|
|
unsigned decl_info::hash() const {
|
|
unsigned a = m_family_id;
|
|
unsigned b = m_kind;
|
|
unsigned c = get_num_parameters() == 0 ? 0 : get_composite_hash<decl_info const *, default_kind_hash_proc<decl_info const *>, decl_info_child_hash_proc>(this, get_num_parameters());
|
|
mix(a, b, c);
|
|
return c;
|
|
}
|
|
|
|
bool decl_info::operator==(decl_info const & info) const {
|
|
return m_family_id == info.m_family_id && m_kind == info.m_kind &&
|
|
compare_arrays<parameter>(m_parameters.begin(), info.m_parameters.begin(), m_parameters.size());
|
|
}
|
|
|
|
std::ostream & operator<<(std::ostream & out, decl_info const & info) {
|
|
out << ":fid " << info.get_family_id() << " :decl-kind " << info.get_decl_kind() << " :parameters (";
|
|
for (unsigned i = 0; i < info.get_num_parameters(); i++) {
|
|
if (i > 0) out << " ";
|
|
out << info.get_parameter(i);
|
|
}
|
|
out << ")";
|
|
return out;
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// sort_size
|
|
//
|
|
// -----------------------------------
|
|
|
|
std::ostream& operator<<(std::ostream& out, sort_size const & ss) {
|
|
if (ss.is_infinite()) { return out << "infinite"; }
|
|
else if (ss.is_very_big()) { return out << "very-big"; }
|
|
else { SASSERT(ss.is_finite()); return out << ss.size(); }
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// sort_info
|
|
//
|
|
// -----------------------------------
|
|
std::ostream & operator<<(std::ostream & out, sort_info const & info) {
|
|
operator<<(out, static_cast<decl_info const&>(info));
|
|
out << " :size " << info.get_num_elements();
|
|
return out;
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// func_decl_info
|
|
//
|
|
// -----------------------------------
|
|
|
|
func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_parameters, parameter const * parameters):
|
|
decl_info(family_id, k, num_parameters, parameters),
|
|
m_left_assoc(false),
|
|
m_right_assoc(false),
|
|
m_flat_associative(false),
|
|
m_commutative(false),
|
|
m_chainable(false),
|
|
m_pairwise(false),
|
|
m_injective(false),
|
|
m_idempotent(false),
|
|
m_skolem(false) {
|
|
}
|
|
|
|
bool func_decl_info::operator==(func_decl_info const & info) const {
|
|
return decl_info::operator==(info) &&
|
|
m_left_assoc == info.m_left_assoc &&
|
|
m_right_assoc == info.m_right_assoc &&
|
|
m_flat_associative == info.m_flat_associative &&
|
|
m_commutative == info.m_commutative &&
|
|
m_chainable == info.m_chainable &&
|
|
m_pairwise == info.m_pairwise &&
|
|
m_injective == info.m_injective &&
|
|
m_skolem == info.m_skolem;
|
|
}
|
|
|
|
std::ostream & operator<<(std::ostream & out, func_decl_info const & info) {
|
|
operator<<(out, static_cast<decl_info const&>(info));
|
|
out << " :left-assoc " << info.is_left_associative();
|
|
out << " :right-assoc " << info.is_right_associative();
|
|
out << " :flat-associative " << info.is_flat_associative();
|
|
out << " :commutative " << info.is_commutative();
|
|
out << " :chainable " << info.is_chainable();
|
|
out << " :pairwise " << info.is_pairwise();
|
|
out << " :injective " << info.is_injective();
|
|
out << " :idempotent " << info.is_idempotent();
|
|
out << " :skolem " << info.is_skolem();
|
|
return out;
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// ast
|
|
//
|
|
// -----------------------------------
|
|
|
|
static char const * g_ast_kind_names[] = {"application", "variable", "quantifier", "sort", "function declaration" };
|
|
|
|
char const * get_ast_kind_name(ast_kind k) {
|
|
return g_ast_kind_names[k];
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// func_decl
|
|
//
|
|
// -----------------------------------
|
|
|
|
func_decl::func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info):
|
|
decl(AST_FUNC_DECL, name, info),
|
|
m_arity(arity),
|
|
m_range(range) {
|
|
if (arity != 0)
|
|
memcpy(const_cast<sort **>(get_domain()), domain, sizeof(sort *) * arity);
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// application
|
|
//
|
|
// -----------------------------------
|
|
|
|
static app_flags mk_const_flags() {
|
|
app_flags r;
|
|
r.m_depth = 1;
|
|
r.m_ground = true;
|
|
r.m_has_quantifiers = false;
|
|
r.m_has_labels = false;
|
|
return r;
|
|
}
|
|
|
|
static app_flags mk_default_app_flags() {
|
|
app_flags r;
|
|
r.m_depth = 1;
|
|
r.m_ground = true;
|
|
r.m_has_quantifiers = false;
|
|
r.m_has_labels = false;
|
|
return r;
|
|
}
|
|
|
|
app_flags app::g_constant_flags = mk_const_flags();
|
|
|
|
app::app(func_decl * decl, unsigned num_args, expr * const * args):
|
|
expr(AST_APP),
|
|
m_decl(decl),
|
|
m_num_args(num_args) {
|
|
for (unsigned i = 0; i < num_args; i++)
|
|
m_args[i] = args[i];
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// quantifier
|
|
//
|
|
// -----------------------------------
|
|
|
|
quantifier::quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body,
|
|
int weight, symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns,
|
|
unsigned num_no_patterns, expr * const * no_patterns):
|
|
expr(AST_QUANTIFIER),
|
|
m_forall(forall),
|
|
m_num_decls(num_decls),
|
|
m_expr(body),
|
|
m_depth(::get_depth(body) + 1),
|
|
m_weight(weight),
|
|
m_has_unused_vars(true),
|
|
m_has_labels(::has_labels(body)),
|
|
m_qid(qid),
|
|
m_skid(skid),
|
|
m_num_patterns(num_patterns),
|
|
m_num_no_patterns(num_no_patterns) {
|
|
SASSERT(m_num_patterns == 0 || m_num_no_patterns == 0);
|
|
|
|
memcpy(const_cast<sort **>(get_decl_sorts()), decl_sorts, sizeof(sort *) * num_decls);
|
|
memcpy(const_cast<symbol*>(get_decl_names()), decl_names, sizeof(symbol) * num_decls);
|
|
if (num_patterns != 0)
|
|
memcpy(const_cast<expr **>(get_patterns()), patterns, sizeof(expr *) * num_patterns);
|
|
if (num_no_patterns != 0)
|
|
memcpy(const_cast<expr **>(get_no_patterns()), no_patterns, sizeof(expr *) * num_no_patterns);
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// Auxiliary functions
|
|
//
|
|
// -----------------------------------
|
|
|
|
sort * get_sort(expr const * n) {
|
|
while (true) {
|
|
switch(n->get_kind()) {
|
|
case AST_APP:
|
|
return to_app(n)->get_decl()->get_range();
|
|
case AST_VAR:
|
|
return to_var(n)->get_sort();
|
|
case AST_QUANTIFIER:
|
|
// The sort of the quantifier is the sort of the nested expression.
|
|
// This code assumes the given expression is well-sorted.
|
|
n = to_quantifier(n)->get_expr();
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// AST hash-consing
|
|
//
|
|
// -----------------------------------
|
|
|
|
unsigned get_node_size(ast const * n) {
|
|
switch(n->get_kind()) {
|
|
case AST_SORT: return to_sort(n)->get_size();
|
|
case AST_FUNC_DECL: return to_func_decl(n)->get_size();
|
|
case AST_APP: return to_app(n)->get_size();
|
|
case AST_VAR: return to_var(n)->get_size();
|
|
case AST_QUANTIFIER: return to_quantifier(n)->get_size();
|
|
default: UNREACHABLE();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool compare_nodes(ast const * n1, ast const * n2) {
|
|
if (n1->get_kind() != n2->get_kind()) {
|
|
return false;
|
|
}
|
|
switch (n1->get_kind()) {
|
|
case AST_SORT:
|
|
if ((to_sort(n1)->get_info() == 0) != (to_sort(n2)->get_info() == 0)) {
|
|
return false;
|
|
}
|
|
if (to_sort(n1)->get_info() != 0 && !(*to_sort(n1)->get_info() == *to_sort(n2)->get_info())) {
|
|
return false;
|
|
}
|
|
return to_sort(n1)->get_name() == to_sort(n2)->get_name();
|
|
case AST_FUNC_DECL:
|
|
if ((to_func_decl(n1)->get_info() == 0) != (to_func_decl(n2)->get_info() == 0)) {
|
|
return false;
|
|
}
|
|
if (to_func_decl(n1)->get_info() != 0 && !(*to_func_decl(n1)->get_info() == *to_func_decl(n2)->get_info())) {
|
|
return false;
|
|
}
|
|
return
|
|
to_func_decl(n1)->get_name() == to_func_decl(n2)->get_name() &&
|
|
to_func_decl(n1)->get_arity() == to_func_decl(n2)->get_arity() &&
|
|
to_func_decl(n1)->get_range() == to_func_decl(n2)->get_range() &&
|
|
compare_arrays(to_func_decl(n1)->get_domain(),
|
|
to_func_decl(n2)->get_domain(),
|
|
to_func_decl(n1)->get_arity());
|
|
case AST_APP:
|
|
return
|
|
to_app(n1)->get_decl() == to_app(n2)->get_decl() &&
|
|
to_app(n1)->get_num_args() == to_app(n2)->get_num_args() &&
|
|
compare_arrays(to_app(n1)->get_args(), to_app(n2)->get_args(), to_app(n1)->get_num_args());
|
|
case AST_VAR:
|
|
return
|
|
to_var(n1)->get_idx() == to_var(n2)->get_idx() &&
|
|
to_var(n1)->get_sort() == to_var(n2)->get_sort();
|
|
case AST_QUANTIFIER:
|
|
return
|
|
to_quantifier(n1)->is_forall() == to_quantifier(n2)->is_forall() &&
|
|
to_quantifier(n1)->get_num_decls() == to_quantifier(n2)->get_num_decls() &&
|
|
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() &&
|
|
compare_arrays(to_quantifier(n1)->get_patterns(),
|
|
to_quantifier(n2)->get_patterns(),
|
|
to_quantifier(n1)->get_num_patterns()) &&
|
|
to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() &&
|
|
compare_arrays(to_quantifier(n1)->get_no_patterns(),
|
|
to_quantifier(n2)->get_no_patterns(),
|
|
to_quantifier(n1)->get_num_no_patterns());
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<typename T>
|
|
inline unsigned ast_array_hash(T * const * array, unsigned size, unsigned init_value) {
|
|
if (size == 0)
|
|
return init_value;
|
|
switch (size) {
|
|
case 1:
|
|
return combine_hash(array[0]->hash(), init_value);
|
|
case 2:
|
|
return combine_hash(combine_hash(array[0]->hash(), array[1]->hash()),
|
|
init_value);
|
|
case 3:
|
|
return combine_hash(combine_hash(array[0]->hash(), array[1]->hash()),
|
|
combine_hash(array[2]->hash(), init_value));
|
|
default: {
|
|
unsigned a, b, c;
|
|
a = b = 0x9e3779b9;
|
|
c = init_value;
|
|
while (size >= 3) {
|
|
size--;
|
|
a += array[size]->hash();
|
|
size--;
|
|
b += array[size]->hash();
|
|
size--;
|
|
c += array[size]->hash();
|
|
mix(a, b, c);
|
|
}
|
|
switch (size) {
|
|
case 2:
|
|
b += array[1]->hash();
|
|
Z3_fallthrough;
|
|
case 1:
|
|
c += array[0]->hash();
|
|
}
|
|
mix(a, b, c);
|
|
return c;
|
|
} }
|
|
}
|
|
|
|
unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init) {
|
|
return ast_array_hash<ast>(ns, sz, init);
|
|
}
|
|
unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init) {
|
|
return ast_array_hash<app>(ns, sz, init);
|
|
}
|
|
unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init) {
|
|
return ast_array_hash<expr>(ns, sz, init);
|
|
}
|
|
unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init) {
|
|
return ast_array_hash<sort>(ns, sz, init);
|
|
}
|
|
unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init) {
|
|
return ast_array_hash<func_decl>(ns, sz, init);
|
|
}
|
|
|
|
unsigned get_node_hash(ast const * n) {
|
|
unsigned a, b, c;
|
|
|
|
switch (n->get_kind()) {
|
|
case AST_SORT:
|
|
if (to_sort(n)->get_info() == 0)
|
|
return to_sort(n)->get_name().hash();
|
|
else
|
|
return combine_hash(to_sort(n)->get_name().hash(), to_sort(n)->get_info()->hash());
|
|
case AST_FUNC_DECL:
|
|
return ast_array_hash(to_func_decl(n)->get_domain(), to_func_decl(n)->get_arity(),
|
|
to_func_decl(n)->get_info() == 0 ?
|
|
to_func_decl(n)->get_name().hash() : combine_hash(to_func_decl(n)->get_name().hash(), to_func_decl(n)->get_info()->hash()));
|
|
case AST_APP:
|
|
return ast_array_hash(to_app(n)->get_args(),
|
|
to_app(n)->get_num_args(),
|
|
to_app(n)->get_decl()->hash());
|
|
case AST_VAR:
|
|
return combine_hash(to_var(n)->get_idx(), to_var(n)->get_sort()->hash());
|
|
case AST_QUANTIFIER:
|
|
a = ast_array_hash(to_quantifier(n)->get_decl_sorts(),
|
|
to_quantifier(n)->get_num_decls(),
|
|
to_quantifier(n)->is_forall() ? 31 : 19);
|
|
b = to_quantifier(n)->get_num_patterns();
|
|
c = to_quantifier(n)->get_expr()->hash();
|
|
mix(a,b,c);
|
|
return c;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ast_table::erase(ast * n) {
|
|
// Customized erase method
|
|
// It uses two important properties:
|
|
// 1. n is known to be in the table.
|
|
// 2. operator== can be used instead of compare_nodes (big savings)
|
|
unsigned mask = m_slots - 1;
|
|
unsigned h = n->hash();
|
|
unsigned idx = h & mask;
|
|
cell * c = m_table + idx;
|
|
SASSERT(!c->is_free());
|
|
cell * prev = 0;
|
|
while (true) {
|
|
if (c->m_data == n) {
|
|
m_size--;
|
|
if (prev == 0) {
|
|
cell * next = c->m_next;
|
|
if (next == 0) {
|
|
m_used_slots--;
|
|
c->mark_free();
|
|
SASSERT(c->is_free());
|
|
}
|
|
else {
|
|
*c = *next;
|
|
recycle_cell(next);
|
|
}
|
|
}
|
|
else {
|
|
prev->m_next = c->m_next;
|
|
recycle_cell(c);
|
|
}
|
|
return;
|
|
}
|
|
CHS_CODE(m_collisions++;);
|
|
prev = c;
|
|
c = c->m_next;
|
|
SASSERT(c);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// decl_plugin
|
|
//
|
|
// -----------------------------------
|
|
|
|
func_decl * decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned num_args, expr * const * args, sort * range) {
|
|
ptr_buffer<sort> sorts;
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
sorts.push_back(m_manager->get_sort(args[i]));
|
|
}
|
|
return mk_func_decl(k, num_parameters, parameters, num_args, sorts.c_ptr(), range);
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// basic_decl_plugin (i.e., builtin plugin)
|
|
//
|
|
// -----------------------------------
|
|
|
|
basic_decl_plugin::basic_decl_plugin():
|
|
m_bool_sort(0),
|
|
m_true_decl(0),
|
|
m_false_decl(0),
|
|
m_and_decl(0),
|
|
m_or_decl(0),
|
|
m_iff_decl(0),
|
|
m_xor_decl(0),
|
|
m_not_decl(0),
|
|
m_interp_decl(0),
|
|
m_implies_decl(0),
|
|
|
|
m_proof_sort(0),
|
|
m_undef_decl(0),
|
|
m_true_pr_decl(0),
|
|
m_asserted_decl(0),
|
|
m_goal_decl(0),
|
|
m_modus_ponens_decl(0),
|
|
m_reflexivity_decl(0),
|
|
m_symmetry_decl(0),
|
|
m_transitivity_decl(0),
|
|
m_quant_intro_decl(0),
|
|
m_and_elim_decl(0),
|
|
m_not_or_elim_decl(0),
|
|
m_rewrite_decl(0),
|
|
m_pull_quant_decl(0),
|
|
m_pull_quant_star_decl(0),
|
|
m_push_quant_decl(0),
|
|
m_elim_unused_vars_decl(0),
|
|
m_der_decl(0),
|
|
m_quant_inst_decl(0),
|
|
|
|
m_hypothesis_decl(0),
|
|
m_iff_true_decl(0),
|
|
m_iff_false_decl(0),
|
|
m_commutativity_decl(0),
|
|
m_def_axiom_decl(0),
|
|
m_lemma_decl(0),
|
|
|
|
m_def_intro_decl(0),
|
|
m_iff_oeq_decl(0),
|
|
m_skolemize_decl(0),
|
|
m_mp_oeq_decl(0),
|
|
m_hyper_res_decl0(0) {
|
|
}
|
|
|
|
bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort * const * domain) const {
|
|
if (k == PR_UNDEF)
|
|
return arity == 0;
|
|
if (arity == 0)
|
|
return false;
|
|
else {
|
|
for (unsigned i = 0; i < arity - 1; i++)
|
|
if (domain[i] != m_proof_sort)
|
|
return false;
|
|
return domain[arity-1] == m_bool_sort || domain[arity-1] == m_proof_sort;
|
|
}
|
|
}
|
|
|
|
bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, expr * const * args) const {
|
|
if (k == PR_UNDEF)
|
|
return num_args == 0;
|
|
if (num_args == 0)
|
|
return false;
|
|
else {
|
|
for (unsigned i = 0; i < num_args - 1; i++)
|
|
if (m_manager->get_sort(args[i]) != m_proof_sort)
|
|
return false;
|
|
return
|
|
m_manager->get_sort(args[num_args - 1]) == m_bool_sort ||
|
|
m_manager->get_sort(args[num_args - 1]) == m_proof_sort;
|
|
}
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args, bool assoc, bool comm, bool idempotent,
|
|
bool flat_associative, bool chainable) {
|
|
ptr_buffer<sort> domain;
|
|
for (unsigned i = 0; i < num_args; i++)
|
|
domain.push_back(m_bool_sort);
|
|
func_decl_info info(m_family_id, k);
|
|
info.set_associative(assoc);
|
|
info.set_flat_associative(flat_associative);
|
|
info.set_commutative(comm);
|
|
info.set_idempotent(idempotent);
|
|
info.set_chainable(chainable);
|
|
func_decl * d = m_manager->mk_func_decl(symbol(name), num_args, domain.c_ptr(), m_bool_sort, info);
|
|
m_manager->inc_ref(d);
|
|
return d;
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_implies_decl() {
|
|
sort * domain[2] = { m_bool_sort, m_bool_sort };
|
|
func_decl_info info(m_family_id, OP_IMPLIES);
|
|
info.set_right_associative();
|
|
func_decl * d = m_manager->mk_func_decl(symbol("=>"), 2, domain, m_bool_sort, info);
|
|
m_manager->inc_ref(d);
|
|
return d;
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_proof_decl(
|
|
char const * name, basic_op_kind k,
|
|
unsigned num_parameters, parameter const* params, unsigned num_parents) {
|
|
ptr_buffer<sort> domain;
|
|
for (unsigned i = 0; i < num_parents; i++)
|
|
domain.push_back(m_proof_sort);
|
|
domain.push_back(m_bool_sort);
|
|
func_decl_info info(m_family_id, k, num_parameters, params);
|
|
return m_manager->mk_func_decl(symbol(name), num_parents+1, domain.c_ptr(), m_proof_sort, info);
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) {
|
|
ptr_buffer<sort> domain;
|
|
for (unsigned i = 0; i < num_parents; i++)
|
|
domain.push_back(m_proof_sort);
|
|
domain.push_back(m_bool_sort);
|
|
func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents+1, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k));
|
|
m_manager->inc_ref(d);
|
|
return d;
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) {
|
|
ptr_buffer<sort> domain;
|
|
for (unsigned i = 0; i < num_parents; i++)
|
|
domain.push_back(m_proof_sort);
|
|
func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k));
|
|
m_manager->inc_ref(d);
|
|
return d;
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, ptr_vector<func_decl> & cache) {
|
|
if (num_parents >= cache.size()) {
|
|
cache.resize(num_parents+1);
|
|
}
|
|
if (cache[num_parents] == 0) {
|
|
cache[num_parents] = mk_proof_decl(name, k, num_parents);
|
|
}
|
|
return cache[num_parents];
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents) {
|
|
switch(k) {
|
|
case PR_TH_LEMMA: {
|
|
return mk_proof_decl("th-lemma", k, num_parameters, params, num_parents);
|
|
}
|
|
case PR_QUANT_INST: {
|
|
SASSERT(num_parents == 0);
|
|
return mk_proof_decl("quant-inst", k, num_parameters, params, num_parents);
|
|
}
|
|
case PR_HYPER_RESOLVE: {
|
|
return mk_proof_decl("hyper-res", k, num_parameters, params, num_parents);
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define MK_DECL(_decl_,_mk_decl_) if (!_decl_) _decl_ = _mk_decl_; return _decl_;
|
|
|
|
|
|
func_decl * basic_decl_plugin::mk_proof_decl(char const* name, basic_op_kind k, unsigned num_parents, func_decl*& fn) {
|
|
if (!fn) {
|
|
fn = mk_proof_decl(name, k, num_parents);
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_parents) {
|
|
SASSERT(k == PR_UNDEF || m_manager->proofs_enabled());
|
|
switch (static_cast<basic_op_kind>(k)) {
|
|
//
|
|
// A description of the semantics of the proof
|
|
// declarations is provided in z3_api.h
|
|
//
|
|
case PR_UNDEF: return m_undef_decl;
|
|
case PR_TRUE: return mk_proof_decl("true-axiom", k, 0, m_true_pr_decl);
|
|
case PR_ASSERTED: return mk_proof_decl("asserted", k, 0, m_asserted_decl);
|
|
case PR_GOAL: return mk_proof_decl("goal", k, 2, m_goal_decl);
|
|
case PR_MODUS_PONENS: return mk_proof_decl("mp", k, 2, m_modus_ponens_decl);
|
|
case PR_REFLEXIVITY: return mk_proof_decl("refl", k, 0, m_reflexivity_decl);
|
|
case PR_SYMMETRY: return mk_proof_decl("symm", k, 1, m_symmetry_decl);
|
|
case PR_TRANSITIVITY: return mk_proof_decl("trans", k, 2, m_transitivity_decl);
|
|
case PR_TRANSITIVITY_STAR: return mk_proof_decl("trans*", k, num_parents, m_transitivity_star_decls);
|
|
case PR_MONOTONICITY: return mk_proof_decl("monotonicity", k, num_parents, m_monotonicity_decls);
|
|
case PR_QUANT_INTRO: return mk_proof_decl("quant-intro", k, 1, m_quant_intro_decl);
|
|
case PR_DISTRIBUTIVITY: return mk_proof_decl("distributivity", k, num_parents, m_distributivity_decls);
|
|
case PR_AND_ELIM: return mk_proof_decl("and-elim", k, 1, m_and_elim_decl);
|
|
case PR_NOT_OR_ELIM: return mk_proof_decl("not-or-elim", k, 1, m_not_or_elim_decl);
|
|
case PR_REWRITE: return mk_proof_decl("rewrite", k, 0, m_rewrite_decl);
|
|
case PR_REWRITE_STAR: return mk_proof_decl("rewrite*", k, num_parents, m_rewrite_star_decls);
|
|
case PR_PULL_QUANT: return mk_proof_decl("pull-quant", k, 0, m_pull_quant_decl);
|
|
case PR_PULL_QUANT_STAR: return mk_proof_decl("pull-quant*", k, 0, m_pull_quant_star_decl);
|
|
case PR_PUSH_QUANT: return mk_proof_decl("push-quant", k, 0, m_push_quant_decl);
|
|
case PR_ELIM_UNUSED_VARS: return mk_proof_decl("elim-unused", k, 0, m_elim_unused_vars_decl);
|
|
case PR_DER: return mk_proof_decl("der", k, 0, m_der_decl);
|
|
case PR_QUANT_INST: return mk_proof_decl("quant-inst", k, 0, m_quant_inst_decl);
|
|
case PR_HYPOTHESIS: return mk_proof_decl("hypothesis", k, 0, m_hypothesis_decl);
|
|
case PR_LEMMA: return mk_proof_decl("lemma", k, 1, m_lemma_decl);
|
|
case PR_UNIT_RESOLUTION: return mk_proof_decl("unit-resolution", k, num_parents, m_unit_resolution_decls);
|
|
case PR_IFF_TRUE: return mk_proof_decl("iff-true", k, 1, m_iff_true_decl);
|
|
case PR_IFF_FALSE: return mk_proof_decl("iff-false", k, 1, m_iff_false_decl);
|
|
case PR_COMMUTATIVITY: return mk_proof_decl("commutativity", k, 0, m_commutativity_decl);
|
|
case PR_DEF_AXIOM: return mk_proof_decl("def-axiom", k, 0, m_def_axiom_decl);
|
|
case PR_DEF_INTRO: return mk_proof_decl("intro-def", k, 0, m_def_intro_decl);
|
|
case PR_APPLY_DEF: return mk_proof_decl("apply-def", k, num_parents, m_apply_def_decls);
|
|
case PR_IFF_OEQ: return mk_proof_decl("iff~", k, 1, m_iff_oeq_decl);
|
|
case PR_NNF_POS: return mk_proof_decl("nnf-pos", k, num_parents, m_nnf_pos_decls);
|
|
case PR_NNF_NEG: return mk_proof_decl("nnf-neg", k, num_parents, m_nnf_neg_decls);
|
|
case PR_NNF_STAR: return mk_proof_decl("nnf*", k, num_parents, m_nnf_star_decls);
|
|
case PR_CNF_STAR: return mk_proof_decl("cnf*", k, num_parents, m_cnf_star_decls);
|
|
case PR_SKOLEMIZE: return mk_proof_decl("sk", k, 0, m_skolemize_decl);
|
|
case PR_MODUS_PONENS_OEQ: return mk_proof_decl("mp~", k, 2, m_mp_oeq_decl);
|
|
case PR_TH_LEMMA: return mk_proof_decl("th-lemma", k, num_parents, m_th_lemma_decls);
|
|
case PR_HYPER_RESOLVE: return mk_proof_decl("hyper-res", k, num_parents, m_hyper_res_decl0);
|
|
default:
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void basic_decl_plugin::set_manager(ast_manager * m, family_id id) {
|
|
decl_plugin::set_manager(m, id);
|
|
|
|
m_bool_sort = m->mk_sort(symbol("Bool"), sort_info(id, BOOL_SORT, sort_size(2)));
|
|
m->inc_ref(m_bool_sort);
|
|
|
|
m_true_decl = mk_bool_op_decl("true", OP_TRUE);
|
|
m_false_decl = mk_bool_op_decl("false", OP_FALSE);
|
|
m_and_decl = mk_bool_op_decl("and", OP_AND, 2, true, true, true, true);
|
|
m_or_decl = mk_bool_op_decl("or", OP_OR, 2, true, true, true, true);
|
|
m_iff_decl = mk_bool_op_decl("iff", OP_IFF, 2, false, true, false, false, true);
|
|
m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true);
|
|
m_not_decl = mk_bool_op_decl("not", OP_NOT, 1);
|
|
m_interp_decl = mk_bool_op_decl("interp", OP_INTERP, 1);
|
|
m_implies_decl = mk_implies_decl();
|
|
|
|
m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT));
|
|
m->inc_ref(m_proof_sort);
|
|
|
|
m_undef_decl = mk_compressed_proof_decl("undef", PR_UNDEF, 0);
|
|
}
|
|
|
|
void basic_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) {
|
|
if (logic == symbol::null)
|
|
sort_names.push_back(builtin_name("bool", BOOL_SORT));
|
|
sort_names.push_back(builtin_name("Bool", BOOL_SORT));
|
|
}
|
|
|
|
void basic_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
|
|
op_names.push_back(builtin_name("true", OP_TRUE));
|
|
op_names.push_back(builtin_name("false", OP_FALSE));
|
|
op_names.push_back(builtin_name("=", OP_EQ));
|
|
op_names.push_back(builtin_name("distinct", OP_DISTINCT));
|
|
op_names.push_back(builtin_name("ite", OP_ITE));
|
|
op_names.push_back(builtin_name("and", OP_AND));
|
|
op_names.push_back(builtin_name("or", OP_OR));
|
|
op_names.push_back(builtin_name("xor", OP_XOR));
|
|
op_names.push_back(builtin_name("not", OP_NOT));
|
|
op_names.push_back(builtin_name("interp", OP_INTERP));
|
|
op_names.push_back(builtin_name("=>", OP_IMPLIES));
|
|
if (logic == symbol::null) {
|
|
// user friendly aliases
|
|
op_names.push_back(builtin_name("implies", OP_IMPLIES));
|
|
op_names.push_back(builtin_name("iff", OP_IFF));
|
|
op_names.push_back(builtin_name("if_then_else", OP_ITE));
|
|
op_names.push_back(builtin_name("if", OP_ITE));
|
|
op_names.push_back(builtin_name("&&", OP_AND));
|
|
op_names.push_back(builtin_name("||", OP_OR));
|
|
op_names.push_back(builtin_name("equals", OP_EQ));
|
|
op_names.push_back(builtin_name("equiv", OP_IFF));
|
|
op_names.push_back(builtin_name("@@", OP_INTERP));
|
|
}
|
|
}
|
|
|
|
bool basic_decl_plugin::is_value(app* a) const {
|
|
return a->get_decl() == m_true_decl || a->get_decl() == m_false_decl;
|
|
}
|
|
|
|
bool basic_decl_plugin::is_unique_value(app* a) const {
|
|
return is_value(a);
|
|
}
|
|
|
|
void basic_decl_plugin::finalize() {
|
|
#define DEC_REF(FIELD) if (FIELD) { m_manager->dec_ref(FIELD); }
|
|
#define DEC_ARRAY_REF(FIELD) m_manager->dec_array_ref(FIELD.size(), FIELD.begin())
|
|
DEC_REF(m_bool_sort);
|
|
DEC_REF(m_true_decl);
|
|
DEC_REF(m_false_decl);
|
|
DEC_REF(m_and_decl);
|
|
DEC_REF(m_or_decl);
|
|
DEC_REF(m_not_decl);
|
|
DEC_REF(m_interp_decl);
|
|
DEC_REF(m_iff_decl);
|
|
DEC_REF(m_xor_decl);
|
|
DEC_REF(m_implies_decl);
|
|
DEC_ARRAY_REF(m_eq_decls);
|
|
DEC_ARRAY_REF(m_ite_decls);
|
|
|
|
DEC_ARRAY_REF(m_oeq_decls);
|
|
DEC_REF(m_proof_sort);
|
|
DEC_REF(m_undef_decl);
|
|
DEC_REF(m_true_pr_decl);
|
|
DEC_REF(m_asserted_decl);
|
|
DEC_REF(m_goal_decl);
|
|
DEC_REF(m_modus_ponens_decl);
|
|
DEC_REF(m_reflexivity_decl);
|
|
DEC_REF(m_symmetry_decl);
|
|
DEC_REF(m_transitivity_decl);
|
|
DEC_REF(m_quant_intro_decl);
|
|
DEC_REF(m_and_elim_decl);
|
|
DEC_REF(m_not_or_elim_decl);
|
|
DEC_REF(m_rewrite_decl);
|
|
DEC_REF(m_pull_quant_decl);
|
|
DEC_REF(m_pull_quant_star_decl);
|
|
DEC_REF(m_push_quant_decl);
|
|
DEC_REF(m_elim_unused_vars_decl);
|
|
DEC_REF(m_der_decl);
|
|
DEC_REF(m_quant_inst_decl);
|
|
DEC_ARRAY_REF(m_monotonicity_decls);
|
|
DEC_ARRAY_REF(m_transitivity_star_decls);
|
|
DEC_ARRAY_REF(m_distributivity_decls);
|
|
DEC_ARRAY_REF(m_assoc_flat_decls);
|
|
DEC_ARRAY_REF(m_rewrite_star_decls);
|
|
|
|
DEC_REF(m_hypothesis_decl);
|
|
DEC_REF(m_iff_true_decl);
|
|
DEC_REF(m_iff_false_decl);
|
|
DEC_REF(m_commutativity_decl);
|
|
DEC_REF(m_def_axiom_decl);
|
|
DEC_REF(m_lemma_decl);
|
|
DEC_ARRAY_REF(m_unit_resolution_decls);
|
|
|
|
DEC_REF(m_def_intro_decl);
|
|
DEC_REF(m_iff_oeq_decl);
|
|
DEC_REF(m_skolemize_decl);
|
|
DEC_REF(m_mp_oeq_decl);
|
|
DEC_ARRAY_REF(m_apply_def_decls);
|
|
DEC_ARRAY_REF(m_nnf_pos_decls);
|
|
DEC_ARRAY_REF(m_nnf_neg_decls);
|
|
DEC_ARRAY_REF(m_nnf_star_decls);
|
|
DEC_ARRAY_REF(m_cnf_star_decls);
|
|
|
|
DEC_ARRAY_REF(m_th_lemma_decls);
|
|
DEC_REF(m_hyper_res_decl0);
|
|
|
|
}
|
|
|
|
sort * basic_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
|
|
if (k == BOOL_SORT)
|
|
return m_bool_sort;
|
|
SASSERT(k == PROOF_SORT);
|
|
return m_proof_sort;
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector<func_decl> & cache) {
|
|
unsigned id = s->get_decl_id();
|
|
force_ptr_array_size(cache, id + 1);
|
|
if (cache[id] == 0) {
|
|
sort * domain[2] = { s, s};
|
|
func_decl_info info(m_family_id, k);
|
|
info.set_commutative();
|
|
info.set_chainable();
|
|
func_decl * decl = m_manager->mk_func_decl(symbol(name), 2, domain, m_bool_sort, info);
|
|
SASSERT(decl->is_chainable());
|
|
cache[id] = decl;
|
|
m_manager->inc_ref(decl);
|
|
}
|
|
return cache[id];
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_ite_decl(sort * s) {
|
|
unsigned id = s->get_decl_id();
|
|
force_ptr_array_size(m_ite_decls, id + 1);
|
|
if (m_ite_decls[id] == 0) {
|
|
sort * domain[3] = { m_bool_sort, s, s};
|
|
func_decl * decl = m_manager->mk_func_decl(symbol("if"), 3, domain, s, func_decl_info(m_family_id, OP_ITE));
|
|
m_ite_decls[id] = decl;
|
|
m_manager->inc_ref(decl);
|
|
}
|
|
return m_ite_decls[id];
|
|
}
|
|
|
|
sort* basic_decl_plugin::join(unsigned n, sort* const* srts) {
|
|
SASSERT(n > 0);
|
|
sort* s = srts[0];
|
|
while (n > 1) {
|
|
++srts;
|
|
--n;
|
|
s = join(s, *srts);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
sort* basic_decl_plugin::join(unsigned n, expr* const* es) {
|
|
SASSERT(n > 0);
|
|
sort* s = m_manager->get_sort(*es);
|
|
while (n > 1) {
|
|
++es;
|
|
--n;
|
|
s = join(s, m_manager->get_sort(*es));
|
|
}
|
|
return s;
|
|
}
|
|
|
|
sort* basic_decl_plugin::join(sort* s1, sort* s2) {
|
|
if (s1 == s2) return s1;
|
|
if (s1->get_family_id() == m_manager->m_arith_family_id &&
|
|
s2->get_family_id() == m_manager->m_arith_family_id) {
|
|
if (s1->get_decl_kind() == REAL_SORT) {
|
|
return s1;
|
|
}
|
|
return s2;
|
|
}
|
|
std::ostringstream buffer;
|
|
buffer << "Sorts " << mk_pp(s1, *m_manager) << " and " << mk_pp(s2, *m_manager) << " are incompatible";
|
|
throw ast_exception(buffer.str().c_str());
|
|
}
|
|
|
|
|
|
func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned arity, sort * const * domain, sort * range) {
|
|
switch (static_cast<basic_op_kind>(k)) {
|
|
case OP_TRUE: return m_true_decl;
|
|
case OP_FALSE: return m_false_decl;
|
|
case OP_AND: return m_and_decl;
|
|
case OP_OR: return m_or_decl;
|
|
case OP_NOT: return m_not_decl;
|
|
case OP_INTERP: return m_interp_decl;
|
|
case OP_IFF: return m_iff_decl;
|
|
case OP_IMPLIES: return m_implies_decl;
|
|
case OP_XOR: return m_xor_decl;
|
|
case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : 0;
|
|
// eq and oeq must have at least two arguments, they can have more since they are chainable
|
|
case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(arity, domain), m_eq_decls) : 0;
|
|
case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(arity, domain), m_oeq_decls) : 0;
|
|
case OP_DISTINCT: {
|
|
func_decl_info info(m_family_id, OP_DISTINCT);
|
|
info.set_pairwise();
|
|
for (unsigned i = 1; i < arity; i++) {
|
|
if (domain[i] != domain[0]) {
|
|
std::ostringstream buffer;
|
|
buffer << "Sort mismatch between first argument and argument " << (i+1);
|
|
throw ast_exception(buffer.str().c_str());
|
|
}
|
|
}
|
|
return m_manager->mk_func_decl(symbol("distinct"), arity, domain, m_bool_sort, info);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
SASSERT(is_proof(k));
|
|
|
|
if (!check_proof_sorts(static_cast<basic_op_kind>(k), arity, domain))
|
|
m_manager->raise_exception("Invalid proof object.");
|
|
|
|
if (num_parameters == 0) {
|
|
return mk_proof_decl(static_cast<basic_op_kind>(k), arity - 1);
|
|
}
|
|
return mk_proof_decl(static_cast<basic_op_kind>(k), num_parameters, parameters, arity - 1);
|
|
}
|
|
|
|
func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned num_args, expr * const * args, sort * range) {
|
|
switch (static_cast<basic_op_kind>(k)) {
|
|
case OP_TRUE: return m_true_decl;
|
|
case OP_FALSE: return m_false_decl;
|
|
case OP_AND: return m_and_decl;
|
|
case OP_OR: return m_or_decl;
|
|
case OP_NOT: return m_not_decl;
|
|
case OP_INTERP: return m_interp_decl;
|
|
case OP_IFF: return m_iff_decl;
|
|
case OP_IMPLIES: return m_implies_decl;
|
|
case OP_XOR: return m_xor_decl;
|
|
case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): 0;
|
|
// eq and oeq must have at least two arguments, they can have more since they are chainable
|
|
case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, join(num_args, args), m_eq_decls) : 0;
|
|
case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(num_args, args), m_oeq_decls) : 0;
|
|
case OP_DISTINCT:
|
|
return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
SASSERT(is_proof(k));
|
|
|
|
if (!check_proof_args(static_cast<basic_op_kind>(k), num_args, args))
|
|
m_manager->raise_exception("Invalid proof object.");
|
|
|
|
if (num_parameters == 0) {
|
|
return mk_proof_decl(static_cast<basic_op_kind>(k), num_args - 1);
|
|
}
|
|
return mk_proof_decl(static_cast<basic_op_kind>(k), num_parameters, parameters, num_args - 1);
|
|
}
|
|
|
|
expr * basic_decl_plugin::get_some_value(sort * s) {
|
|
if (s == m_bool_sort)
|
|
return m_manager->mk_false();
|
|
return 0;
|
|
}
|
|
|
|
bool basic_recognizers::is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const {
|
|
if (is_ite(n)) {
|
|
t1 = to_app(n)->get_arg(0);
|
|
t2 = to_app(n)->get_arg(1);
|
|
t3 = to_app(n)->get_arg(2);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// label_decl_plugin
|
|
//
|
|
// -----------------------------------
|
|
|
|
label_decl_plugin::label_decl_plugin():
|
|
m_lblpos("lblpos"),
|
|
m_lblneg("lblneg"),
|
|
m_lbllit("lbl-lit") {
|
|
}
|
|
|
|
label_decl_plugin::~label_decl_plugin() {
|
|
}
|
|
|
|
void label_decl_plugin::set_manager(ast_manager * m, family_id id) {
|
|
decl_plugin::set_manager(m, id);
|
|
}
|
|
|
|
sort * label_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
|
|
func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned arity, sort * const * domain, sort * range) {
|
|
if (k == OP_LABEL) {
|
|
if (arity != 1 || num_parameters < 2 || !parameters[0].is_int() || !parameters[1].is_symbol() || !m_manager->is_bool(domain[0])) {
|
|
m_manager->raise_exception("invalid label declaration");
|
|
return 0;
|
|
}
|
|
for (unsigned i = 2; i < num_parameters; i++) {
|
|
if (!parameters[i].is_symbol()) {
|
|
m_manager->raise_exception("invalid label declaration");
|
|
return 0;
|
|
}
|
|
}
|
|
return m_manager->mk_func_decl(parameters[0].get_int() ? m_lblpos : m_lblneg, arity, domain, domain[0],
|
|
func_decl_info(m_family_id, OP_LABEL, num_parameters, parameters));
|
|
}
|
|
else {
|
|
SASSERT(k == OP_LABEL_LIT);
|
|
if (arity != 0) {
|
|
m_manager->raise_exception("invalid label literal declaration");
|
|
return 0;
|
|
}
|
|
for (unsigned i = 0; i < num_parameters; i++) {
|
|
if (!parameters[i].is_symbol()) {
|
|
m_manager->raise_exception("invalid label literal declaration");
|
|
return 0;
|
|
}
|
|
}
|
|
return m_manager->mk_func_decl(m_lbllit, 0, static_cast<sort * const *>(0), m_manager->mk_bool_sort(),
|
|
func_decl_info(m_family_id, OP_LABEL_LIT, num_parameters, parameters));
|
|
}
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// pattern_decl_plugin
|
|
//
|
|
// -----------------------------------
|
|
|
|
sort * pattern_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
|
|
func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned arity, sort * const * domain, sort * range) {
|
|
return m_manager->mk_func_decl(symbol("pattern"), arity, domain,
|
|
m_manager->mk_bool_sort(), // the range can be an arbitrary sort.
|
|
func_decl_info(m_family_id, OP_PATTERN));
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// model_value_decl_plugin
|
|
//
|
|
// -----------------------------------
|
|
|
|
sort * model_value_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
|
|
func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned arity, sort * const * domain, sort * range) {
|
|
SASSERT(k == OP_MODEL_VALUE);
|
|
if (arity != 0 || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_ast() || !is_sort(parameters[1].get_ast())) {
|
|
UNREACHABLE();
|
|
m_manager->raise_exception("invalid model value");
|
|
return 0;
|
|
}
|
|
int idx = parameters[0].get_int();
|
|
sort * s = to_sort(parameters[1].get_ast());
|
|
string_buffer<64> buffer;
|
|
buffer << s->get_name().bare_str() << "!val!" << idx;
|
|
func_decl_info info(m_family_id, k, num_parameters, parameters);
|
|
info.m_private_parameters = true;
|
|
return m_manager->mk_func_decl(symbol(buffer.c_str()), 0, static_cast<sort * const *>(0), s, info);
|
|
}
|
|
|
|
bool model_value_decl_plugin::is_value(app* n) const {
|
|
return is_app_of(n, m_family_id, OP_MODEL_VALUE);
|
|
}
|
|
|
|
bool model_value_decl_plugin::is_unique_value(app* n) const {
|
|
return is_value(n);
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// user_sort_plugin
|
|
//
|
|
// -----------------------------------
|
|
|
|
sort * user_sort_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
|
|
SASSERT(m_family_id != null_family_id);
|
|
SASSERT(k < static_cast<int>(m_sort_names.size()));
|
|
sort_info si(m_family_id, k, num_parameters, parameters);
|
|
return m_manager->mk_sort(m_sort_names[k], si);
|
|
}
|
|
|
|
func_decl * user_sort_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned arity, sort * const * domain, sort * range) {
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
|
|
decl_kind user_sort_plugin::register_name(symbol s) {
|
|
decl_kind k;
|
|
if (m_name2decl_kind.find(s, k))
|
|
return k;
|
|
k = m_sort_names.size();
|
|
m_sort_names.push_back(s);
|
|
m_name2decl_kind.insert(s, k);
|
|
return k;
|
|
}
|
|
|
|
decl_plugin * user_sort_plugin::mk_fresh() {
|
|
user_sort_plugin * p = alloc(user_sort_plugin);
|
|
for (symbol const& s : m_sort_names)
|
|
p->register_name(s);
|
|
return p;
|
|
}
|
|
|
|
|
|
// -----------------------------------
|
|
//
|
|
// ast_manager
|
|
//
|
|
// -----------------------------------
|
|
|
|
ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_format_manager):
|
|
m_alloc("ast_manager"),
|
|
m_expr_array_manager(*this, m_alloc),
|
|
m_expr_dependency_manager(*this, m_alloc),
|
|
m_expr_dependency_array_manager(*this, m_alloc),
|
|
m_proof_mode(m),
|
|
m_trace_stream(0),
|
|
m_trace_stream_owner(false),
|
|
m_rec_fun(":rec-fun") {
|
|
|
|
if (trace_file) {
|
|
m_trace_stream = alloc(std::fstream, trace_file, std::ios_base::out);
|
|
m_trace_stream_owner = true;
|
|
}
|
|
|
|
if (!is_format_manager)
|
|
m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true);
|
|
else
|
|
m_format_manager = 0;
|
|
init();
|
|
}
|
|
|
|
ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_format_manager):
|
|
m_alloc("ast_manager"),
|
|
m_expr_array_manager(*this, m_alloc),
|
|
m_expr_dependency_manager(*this, m_alloc),
|
|
m_expr_dependency_array_manager(*this, m_alloc),
|
|
m_proof_mode(m),
|
|
m_trace_stream(trace_stream),
|
|
m_trace_stream_owner(false),
|
|
m_rec_fun(":rec-fun") {
|
|
|
|
if (!is_format_manager)
|
|
m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true);
|
|
else
|
|
m_format_manager = 0;
|
|
init();
|
|
}
|
|
|
|
ast_manager::ast_manager(ast_manager const & src, bool disable_proofs):
|
|
m_alloc("ast_manager"),
|
|
m_expr_array_manager(*this, m_alloc),
|
|
m_expr_dependency_manager(*this, m_alloc),
|
|
m_expr_dependency_array_manager(*this, m_alloc),
|
|
m_proof_mode(disable_proofs ? PGM_DISABLED : src.m_proof_mode),
|
|
m_trace_stream(src.m_trace_stream),
|
|
m_trace_stream_owner(false),
|
|
m_rec_fun(":rec-fun") {
|
|
SASSERT(!src.is_format_manager());
|
|
m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true);
|
|
init();
|
|
copy_families_plugins(src);
|
|
}
|
|
|
|
void ast_manager::init() {
|
|
m_int_real_coercions = true;
|
|
m_debug_ref_count = false;
|
|
m_fresh_id = 0;
|
|
m_expr_id_gen.reset(0);
|
|
m_decl_id_gen.reset(c_first_decl_id);
|
|
m_some_value_proc = 0;
|
|
m_basic_family_id = mk_family_id("basic");
|
|
m_label_family_id = mk_family_id("label");
|
|
m_pattern_family_id = mk_family_id("pattern");
|
|
m_model_value_family_id = mk_family_id("model-value");
|
|
m_user_sort_family_id = mk_family_id("user-sort");
|
|
m_arith_family_id = mk_family_id("arith");
|
|
basic_decl_plugin * plugin = alloc(basic_decl_plugin);
|
|
register_plugin(m_basic_family_id, plugin);
|
|
m_bool_sort = plugin->mk_bool_sort();
|
|
inc_ref(m_bool_sort);
|
|
m_proof_sort = plugin->mk_proof_sort();
|
|
inc_ref(m_proof_sort);
|
|
m_undef_proof = mk_const(m_basic_family_id, PR_UNDEF);
|
|
inc_ref(m_undef_proof);
|
|
register_plugin(m_label_family_id, alloc(label_decl_plugin));
|
|
register_plugin(m_pattern_family_id, alloc(pattern_decl_plugin));
|
|
register_plugin(m_model_value_family_id, alloc(model_value_decl_plugin));
|
|
register_plugin(m_user_sort_family_id, alloc(user_sort_plugin));
|
|
m_true = mk_const(m_basic_family_id, OP_TRUE);
|
|
inc_ref(m_true);
|
|
m_false = mk_const(m_basic_family_id, OP_FALSE);
|
|
inc_ref(m_false);
|
|
}
|
|
|
|
template<typename T>
|
|
static void mark_array_ref(ast_mark& mark, unsigned sz, T * const * a) {
|
|
for(unsigned i = 0; i < sz; i++) {
|
|
mark.mark(a[i], true);
|
|
}
|
|
}
|
|
|
|
static void mark_array_ref(ast_mark& mark, unsigned sz, parameter const * a) {
|
|
for(unsigned i = 0; i < sz; i++) {
|
|
if (a[i].is_ast()) {
|
|
mark.mark(a[i].get_ast(), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ast_manager::~ast_manager() {
|
|
SASSERT(is_format_manager() || !m_family_manager.has_family(symbol("format")));
|
|
|
|
dec_ref(m_bool_sort);
|
|
dec_ref(m_proof_sort);
|
|
dec_ref(m_true);
|
|
dec_ref(m_false);
|
|
dec_ref(m_undef_proof);
|
|
for (decl_plugin* p : m_plugins) {
|
|
if (p)
|
|
p->finalize();
|
|
}
|
|
for (decl_plugin* p : m_plugins) {
|
|
if (p)
|
|
dealloc(p);
|
|
}
|
|
m_plugins.reset();
|
|
while (!m_ast_table.empty()) {
|
|
DEBUG_CODE(std::cout << "ast_manager LEAKED: " << m_ast_table.size() << std::endl;);
|
|
ptr_vector<ast> roots;
|
|
ast_mark mark;
|
|
for (ast * n : m_ast_table) {
|
|
switch (n->get_kind()) {
|
|
case AST_SORT: {
|
|
sort_info* info = to_sort(n)->get_info();
|
|
if (info != 0) {
|
|
mark_array_ref(mark, info->get_num_parameters(), info->get_parameters());
|
|
}
|
|
break;
|
|
}
|
|
case AST_FUNC_DECL: {
|
|
func_decl_info* info = to_func_decl(n)->get_info();
|
|
if (info != 0) {
|
|
mark_array_ref(mark, info->get_num_parameters(), info->get_parameters());
|
|
}
|
|
mark_array_ref(mark, to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain());
|
|
mark.mark(to_func_decl(n)->get_range(), true);
|
|
break;
|
|
}
|
|
case AST_APP:
|
|
mark.mark(to_app(n)->get_decl(), true);
|
|
mark_array_ref(mark, to_app(n)->get_num_args(), to_app(n)->get_args());
|
|
break;
|
|
case AST_VAR:
|
|
mark.mark(to_var(n)->get_sort(), true);
|
|
break;
|
|
case AST_QUANTIFIER:
|
|
mark_array_ref(mark, to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts());
|
|
mark.mark(to_quantifier(n)->get_expr(), true);
|
|
mark_array_ref(mark, to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns());
|
|
mark_array_ref(mark, to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns());
|
|
break;
|
|
}
|
|
}
|
|
for (ast * n : m_ast_table) {
|
|
if (!mark.is_marked(n)) {
|
|
roots.push_back(n);
|
|
}
|
|
}
|
|
SASSERT(!roots.empty());
|
|
for (unsigned i = 0; i < roots.size(); ++i) {
|
|
ast* a = roots[i];
|
|
DEBUG_CODE(
|
|
std::cout << "Leaked: ";
|
|
if (is_sort(a)) {
|
|
std::cout << to_sort(a)->get_name() << "\n";
|
|
}
|
|
else {
|
|
std::cout << mk_ll_pp(a, *this, false) << "id: " << a->get_id() << "\n";
|
|
});
|
|
a->m_ref_count = 0;
|
|
delete_node(a);
|
|
}
|
|
}
|
|
if (m_format_manager != 0)
|
|
dealloc(m_format_manager);
|
|
if (m_trace_stream_owner) {
|
|
std::fstream & tmp = * m_trace_stream;
|
|
tmp << "[eof]\n";
|
|
tmp.close();
|
|
dealloc(m_trace_stream);
|
|
m_trace_stream = 0;
|
|
}
|
|
}
|
|
|
|
void ast_manager::compact_memory() {
|
|
m_alloc.consolidate();
|
|
unsigned capacity = m_ast_table.capacity();
|
|
if (capacity > 4*m_ast_table.size()) {
|
|
ast_table new_ast_table;
|
|
ast_table::iterator it = m_ast_table.begin();
|
|
ast_table::iterator end = m_ast_table.end();
|
|
for (; it != end; ++it) {
|
|
new_ast_table.insert(*it);
|
|
}
|
|
m_ast_table.swap(new_ast_table);
|
|
IF_VERBOSE(10, verbose_stream() << "(ast-table :prev-capacity " << capacity
|
|
<< " :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";);
|
|
}
|
|
else {
|
|
IF_VERBOSE(10, verbose_stream() << "(ast-table :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";);
|
|
}
|
|
}
|
|
|
|
void ast_manager::compress_ids() {
|
|
ptr_vector<ast> asts;
|
|
m_expr_id_gen.cleanup();
|
|
m_decl_id_gen.cleanup(c_first_decl_id);
|
|
ast_table::iterator it = m_ast_table.begin();
|
|
ast_table::iterator end = m_ast_table.end();
|
|
for (; it != end; ++it) {
|
|
ast * n = *it;
|
|
if (is_decl(n))
|
|
n->m_id = m_decl_id_gen.mk();
|
|
else
|
|
n->m_id = m_expr_id_gen.mk();
|
|
asts.push_back(n);
|
|
}
|
|
m_ast_table.finalize();
|
|
ptr_vector<ast>::iterator it2 = asts.begin();
|
|
ptr_vector<ast>::iterator end2 = asts.end();
|
|
for (; it2 != end2; ++it2)
|
|
m_ast_table.insert(*it2);
|
|
}
|
|
|
|
void ast_manager::raise_exception(char const * msg) {
|
|
throw ast_exception(msg);
|
|
}
|
|
|
|
#include "ast/ast_translation.h"
|
|
|
|
void ast_manager::copy_families_plugins(ast_manager const & from) {
|
|
TRACE("copy_families_plugins",
|
|
tout << "target:\n";
|
|
for (family_id fid = 0; m_family_manager.has_family(fid); fid++) {
|
|
tout << "fid: " << fid << " fidname: " << get_family_name(fid) << "\n";
|
|
});
|
|
ast_translation trans(const_cast<ast_manager&>(from), *this, false);
|
|
for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) {
|
|
SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid));
|
|
SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid));
|
|
symbol fid_name = from.get_family_name(fid);
|
|
TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid
|
|
<< ", target has_family: " << m_family_manager.has_family(fid) << "\n";
|
|
if (m_family_manager.has_family(fid)) tout << get_family_id(fid_name) << "\n";);
|
|
if (!m_family_manager.has_family(fid)) {
|
|
family_id new_fid = mk_family_id(fid_name);
|
|
(void)new_fid;
|
|
TRACE("copy_families_plugins", tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";);
|
|
}
|
|
TRACE("copy_families_plugins", tout << "target fid: " << get_family_id(fid_name) << "\n";);
|
|
SASSERT(fid == get_family_id(fid_name));
|
|
if (from.has_plugin(fid) && !has_plugin(fid)) {
|
|
decl_plugin * new_p = from.get_plugin(fid)->mk_fresh();
|
|
register_plugin(fid, new_p);
|
|
SASSERT(new_p->get_family_id() == fid);
|
|
SASSERT(has_plugin(fid));
|
|
}
|
|
if (from.has_plugin(fid)) {
|
|
get_plugin(fid)->inherit(from.get_plugin(fid), trans);
|
|
}
|
|
SASSERT(from.m_family_manager.has_family(fid) == m_family_manager.has_family(fid));
|
|
SASSERT(from.get_family_id(fid_name) == get_family_id(fid_name));
|
|
SASSERT(!from.has_plugin(fid) || has_plugin(fid));
|
|
}
|
|
}
|
|
|
|
void ast_manager::set_next_expr_id(unsigned id) {
|
|
while (true) {
|
|
id = m_expr_id_gen.set_next_id(id);
|
|
ast_table::iterator it = m_ast_table.begin();
|
|
ast_table::iterator end = m_ast_table.end();
|
|
for (; it != end; ++it) {
|
|
ast * curr = *it;
|
|
if (curr->get_id() == id)
|
|
break;
|
|
}
|
|
if (it == end)
|
|
return;
|
|
// id is in use, move to the next one.
|
|
id++;
|
|
}
|
|
}
|
|
|
|
unsigned ast_manager::get_node_size(ast const * n) { return ::get_node_size(n); }
|
|
|
|
void ast_manager::register_plugin(symbol const & s, decl_plugin * plugin) {
|
|
family_id id = m_family_manager.mk_family_id(s);
|
|
SASSERT(is_format_manager() || s != symbol("format"));
|
|
register_plugin(id, plugin);
|
|
}
|
|
|
|
decl_plugin * ast_manager::get_plugin(family_id fid) const {
|
|
return m_plugins.get(fid, 0);
|
|
}
|
|
|
|
|
|
bool ast_manager::is_value(expr* e) const {
|
|
decl_plugin const * p = 0;
|
|
if (is_app(e)) {
|
|
p = get_plugin(to_app(e)->get_family_id());
|
|
return p && p->is_value(to_app(e));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ast_manager::is_unique_value(expr* e) const {
|
|
decl_plugin const * p = 0;
|
|
if (is_app(e)) {
|
|
p = get_plugin(to_app(e)->get_family_id());
|
|
return p && p->is_unique_value(to_app(e));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ast_manager::are_equal(expr * a, expr * b) const {
|
|
if (a == b) {
|
|
return true;
|
|
}
|
|
if (is_app(a) && is_app(b)) {
|
|
app* ap = to_app(a), *bp = to_app(b);
|
|
decl_plugin const * p = get_plugin(ap->get_family_id());
|
|
if (!p) {
|
|
p = get_plugin(bp->get_family_id());
|
|
}
|
|
return p && p->are_equal(ap, bp);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ast_manager::are_distinct(expr* a, expr* b) const {
|
|
if (is_app(a) && is_app(b)) {
|
|
app* ap = to_app(a), *bp = to_app(b);
|
|
decl_plugin const * p = get_plugin(ap->get_family_id());
|
|
if (!p) {
|
|
p = get_plugin(bp->get_family_id());
|
|
}
|
|
return p && p->are_distinct(ap, bp);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ast_manager::register_plugin(family_id id, decl_plugin * plugin) {
|
|
SASSERT(m_plugins.get(id, 0) == 0);
|
|
m_plugins.setx(id, plugin, 0);
|
|
plugin->set_manager(this, id);
|
|
}
|
|
|
|
bool ast_manager::is_bool(expr const * n) const {
|
|
return get_sort(n) == m_bool_sort;
|
|
}
|
|
|
|
#ifdef Z3DEBUG
|
|
bool ast_manager::slow_not_contains(ast const * n) {
|
|
unsigned num = 0;
|
|
for (ast * curr : m_ast_table) {
|
|
if (compare_nodes(curr, n)) {
|
|
TRACE("nondet_bug",
|
|
tout << "id1: " << curr->get_id() << ", id2: " << n->get_id() << "\n";
|
|
tout << "hash1: " << get_node_hash(curr) << ", hash2: " << get_node_hash(n) << "\n";);
|
|
return false;
|
|
}
|
|
SASSERT(!(is_app(n) && is_app(curr) &&
|
|
to_app(n)->get_decl() == to_app(curr)->get_decl() &&
|
|
to_app(n)->get_num_args() == 0 &&
|
|
to_app(curr)->get_num_args() == 0));
|
|
num++;
|
|
}
|
|
SASSERT(num == m_ast_table.size());
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
ast * ast_manager::register_node_core(ast * n) {
|
|
unsigned h = get_node_hash(n);
|
|
n->m_hash = h;
|
|
#ifdef Z3DEBUG
|
|
bool contains = m_ast_table.contains(n);
|
|
CASSERT("nondet_bug", contains || slow_not_contains(n));
|
|
#endif
|
|
|
|
#if 0
|
|
static unsigned counter = 0;
|
|
counter++;
|
|
if (counter % 100000 == 0)
|
|
verbose_stream() << "[ast-table] counter: " << counter << " collisions: " << m_ast_table.collisions() << " capacity: " << m_ast_table.capacity() << " size: " << m_ast_table.size() << "\n";
|
|
#endif
|
|
|
|
ast * r = m_ast_table.insert_if_not_there(n);
|
|
SASSERT(r->m_hash == h);
|
|
if (r != n) {
|
|
#if 0
|
|
static unsigned reused = 0;
|
|
reused++;
|
|
if (reused % 100000 == 0)
|
|
verbose_stream() << "[ast-table] reused: " << reused << "\n";
|
|
#endif
|
|
SASSERT(contains);
|
|
SASSERT(m_ast_table.contains(n));
|
|
if (is_func_decl(r) && to_func_decl(r)->get_range() != to_func_decl(n)->get_range()) {
|
|
std::ostringstream buffer;
|
|
buffer << "Recycling of declaration for the same name '" << to_func_decl(r)->get_name().str().c_str() << "'"
|
|
<< " and domain, but different range type is not permitted";
|
|
throw ast_exception(buffer.str().c_str());
|
|
}
|
|
deallocate_node(n, ::get_node_size(n));
|
|
return r;
|
|
}
|
|
else {
|
|
SASSERT(!contains);
|
|
SASSERT(m_ast_table.contains(n));
|
|
}
|
|
|
|
|
|
n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk();
|
|
|
|
static unsigned count = 0;
|
|
if (n->m_id == 404) {
|
|
++count;
|
|
//if (count == 2) SASSERT(false);
|
|
}
|
|
|
|
TRACE("ast", tout << "Object " << n->m_id << " was created.\n";);
|
|
TRACE("mk_var_bug", tout << "mk_ast: " << n->m_id << "\n";);
|
|
// increment reference counters
|
|
switch (n->get_kind()) {
|
|
case AST_SORT:
|
|
if (to_sort(n)->m_info != 0) {
|
|
to_sort(n)->m_info = alloc(sort_info, *(to_sort(n)->get_info()));
|
|
to_sort(n)->m_info->init_eh(*this);
|
|
}
|
|
break;
|
|
case AST_FUNC_DECL:
|
|
if (to_func_decl(n)->m_info != 0) {
|
|
to_func_decl(n)->m_info = alloc(func_decl_info, *(to_func_decl(n)->get_info()));
|
|
to_func_decl(n)->m_info->init_eh(*this);
|
|
}
|
|
inc_array_ref(to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain());
|
|
inc_ref(to_func_decl(n)->get_range());
|
|
break;
|
|
case AST_APP: {
|
|
app * t = to_app(n);
|
|
inc_ref(t->get_decl());
|
|
unsigned num_args = t->get_num_args();
|
|
if (num_args > 0) {
|
|
app_flags * f = t->flags();
|
|
*f = mk_default_app_flags();
|
|
SASSERT(t->is_ground());
|
|
SASSERT(!t->has_quantifiers());
|
|
SASSERT(!t->has_labels());
|
|
if (is_label(t))
|
|
f->m_has_labels = true;
|
|
unsigned depth = 0;
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
expr * arg = t->get_arg(i);
|
|
inc_ref(arg);
|
|
unsigned arg_depth = 0;
|
|
switch (arg->get_kind()) {
|
|
case AST_APP: {
|
|
app_flags * arg_flags = to_app(arg)->flags();
|
|
arg_depth = arg_flags->m_depth;
|
|
if (arg_flags->m_has_quantifiers)
|
|
f->m_has_quantifiers = true;
|
|
if (arg_flags->m_has_labels)
|
|
f->m_has_labels = true;
|
|
if (!arg_flags->m_ground)
|
|
f->m_ground = false;
|
|
break;
|
|
}
|
|
case AST_QUANTIFIER:
|
|
f->m_has_quantifiers = true;
|
|
f->m_ground = false;
|
|
arg_depth = to_quantifier(arg)->get_depth();
|
|
break;
|
|
case AST_VAR:
|
|
f->m_ground = false;
|
|
arg_depth = 1;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
if (arg_depth > depth)
|
|
depth = arg_depth;
|
|
}
|
|
depth++;
|
|
if (depth > c_max_depth)
|
|
depth = c_max_depth;
|
|
f->m_depth = depth;
|
|
SASSERT(t->get_depth() == depth);
|
|
}
|
|
break;
|
|
}
|
|
case AST_VAR:
|
|
inc_ref(to_var(n)->get_sort());
|
|
break;
|
|
case AST_QUANTIFIER:
|
|
inc_array_ref(to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts());
|
|
inc_ref(to_quantifier(n)->get_expr());
|
|
inc_array_ref(to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns());
|
|
inc_array_ref(to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void ast_manager::delete_node(ast * n) {
|
|
TRACE("delete_node_bug", tout << mk_ll_pp(n, *this) << "\n";);
|
|
ptr_buffer<ast> worklist;
|
|
worklist.push_back(n);
|
|
|
|
while (!worklist.empty()) {
|
|
n = worklist.back();
|
|
worklist.pop_back();
|
|
|
|
TRACE("ast", tout << "Deleting object " << n->m_id << " " << n << "\n";);
|
|
CTRACE("del_quantifier", is_quantifier(n), tout << "deleting quantifier " << n->m_id << " " << n << "\n";);
|
|
TRACE("mk_var_bug", tout << "del_ast: " << n->m_id << "\n";);
|
|
TRACE("ast_delete_node", tout << mk_bounded_pp(n, *this) << "\n";);
|
|
|
|
SASSERT(m_ast_table.contains(n));
|
|
m_ast_table.erase(n);
|
|
SASSERT(!m_ast_table.contains(n));
|
|
SASSERT(!m_debug_ref_count || !m_debug_free_indices.contains(n->m_id));
|
|
|
|
#ifdef RECYCLE_FREE_AST_INDICES
|
|
if (!m_debug_ref_count) {
|
|
if (is_decl(n))
|
|
m_decl_id_gen.recycle(n->m_id);
|
|
else
|
|
m_expr_id_gen.recycle(n->m_id);
|
|
}
|
|
#endif
|
|
switch (n->get_kind()) {
|
|
case AST_SORT:
|
|
if (to_sort(n)->m_info != 0 && !m_debug_ref_count) {
|
|
sort_info * info = to_sort(n)->get_info();
|
|
info->del_eh(*this);
|
|
dealloc(info);
|
|
}
|
|
break;
|
|
case AST_FUNC_DECL:
|
|
if (to_func_decl(n)->m_info != 0 && !m_debug_ref_count) {
|
|
func_decl_info * info = to_func_decl(n)->get_info();
|
|
info->del_eh(*this);
|
|
dealloc(info);
|
|
}
|
|
dec_array_ref(worklist, to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain());
|
|
dec_ref(worklist, to_func_decl(n)->get_range());
|
|
break;
|
|
case AST_APP:
|
|
dec_ref(worklist, to_app(n)->get_decl());
|
|
dec_array_ref(worklist, to_app(n)->get_num_args(), to_app(n)->get_args());
|
|
break;
|
|
case AST_VAR:
|
|
dec_ref(worklist, to_var(n)->get_sort());
|
|
break;
|
|
case AST_QUANTIFIER:
|
|
dec_array_ref(worklist, to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts());
|
|
dec_ref(worklist, to_quantifier(n)->get_expr());
|
|
dec_array_ref(worklist, to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns());
|
|
dec_array_ref(worklist, to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (m_debug_ref_count) {
|
|
m_debug_free_indices.insert(n->m_id,0);
|
|
}
|
|
deallocate_node(n, ::get_node_size(n));
|
|
}
|
|
}
|
|
|
|
sort * ast_manager::mk_sort(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters) {
|
|
decl_plugin * p = get_plugin(fid);
|
|
if (p)
|
|
return p->mk_sort(k, num_parameters, parameters);
|
|
return 0;
|
|
}
|
|
|
|
func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned arity, sort * const * domain, sort * range) {
|
|
decl_plugin * p = get_plugin(fid);
|
|
if (p)
|
|
return p->mk_func_decl(k, num_parameters, parameters, arity, domain, range);
|
|
return 0;
|
|
}
|
|
|
|
func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned num_args, expr * const * args, sort * range) {
|
|
decl_plugin * p = get_plugin(fid);
|
|
if (p)
|
|
return p->mk_func_decl(k, num_parameters, parameters, num_args, args, range);
|
|
return 0;
|
|
}
|
|
|
|
app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters,
|
|
unsigned num_args, expr * const * args, sort * range) {
|
|
func_decl * decl = mk_func_decl(fid, k, num_parameters, parameters, num_args, args, range);
|
|
if (decl != 0)
|
|
return mk_app(decl, num_args, args);
|
|
return 0;
|
|
}
|
|
|
|
app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args) {
|
|
return mk_app(fid, k, 0, 0, num_args, args);
|
|
}
|
|
|
|
app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg) {
|
|
return mk_app(fid, k, 0, 0, 1, &arg);
|
|
}
|
|
|
|
app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2) {
|
|
expr * args[2] = { arg1, arg2 };
|
|
return mk_app(fid, k, 0, 0, 2, args);
|
|
}
|
|
|
|
app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3) {
|
|
expr * args[3] = { arg1, arg2, arg3 };
|
|
return mk_app(fid, k, 0, 0, 3, args);
|
|
}
|
|
|
|
sort * ast_manager::mk_sort(symbol const & name, sort_info * info) {
|
|
unsigned sz = sort::get_obj_size();
|
|
void * mem = allocate_node(sz);
|
|
sort * new_node = new (mem) sort(name, info);
|
|
return register_node(new_node);
|
|
}
|
|
|
|
sort * ast_manager::substitute(sort* s, unsigned n, sort * const * src, sort * const * dst) {
|
|
for (unsigned i = 0; i < n; ++i) {
|
|
if (s == src[i]) return dst[i];
|
|
}
|
|
|
|
vector<parameter> ps;
|
|
bool change = false;
|
|
sort_ref_vector sorts(*this);
|
|
for (unsigned i = 0; i < s->get_num_parameters(); ++i) {
|
|
parameter const& p = s->get_parameter(i);
|
|
if (p.is_ast()) {
|
|
SASSERT(is_sort(p.get_ast()));
|
|
change = true;
|
|
sorts.push_back(substitute(to_sort(p.get_ast()), n, src, dst));
|
|
ps.push_back(parameter(sorts.back()));
|
|
}
|
|
else {
|
|
ps.push_back(p);
|
|
}
|
|
}
|
|
if (!change) {
|
|
return s;
|
|
}
|
|
decl_info dinfo(s->get_family_id(), s->get_decl_kind(), ps.size(), ps.c_ptr(), s->private_parameters());
|
|
sort_info sinfo(dinfo, s->get_num_elements());
|
|
return mk_sort(s->get_name(), &sinfo);
|
|
}
|
|
|
|
|
|
sort * ast_manager::mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters) {
|
|
user_sort_plugin * plugin = get_user_sort_plugin();
|
|
decl_kind kind = plugin->register_name(name);
|
|
return plugin->mk_sort(kind, num_parameters, parameters);
|
|
}
|
|
|
|
func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range,
|
|
bool assoc, bool comm, bool inj) {
|
|
func_decl_info info(null_family_id, null_decl_kind);
|
|
info.set_associative(assoc);
|
|
info.set_commutative(comm);
|
|
info.set_injective(inj);
|
|
return mk_func_decl(name, arity, domain, range, info);
|
|
}
|
|
|
|
func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info) {
|
|
SASSERT(arity == 1 || info == 0 || !info->is_injective());
|
|
SASSERT(arity == 2 || info == 0 || !info->is_associative());
|
|
SASSERT(arity == 2 || info == 0 || !info->is_commutative());
|
|
unsigned sz = func_decl::get_obj_size(arity);
|
|
void * mem = allocate_node(sz);
|
|
func_decl * new_node = new (mem) func_decl(name, arity, domain, range, info);
|
|
return register_node(new_node);
|
|
}
|
|
|
|
void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * const * args) const {
|
|
ast_manager& m = const_cast<ast_manager&>(*this);
|
|
|
|
if (decl->is_associative()) {
|
|
sort * expected = decl->get_domain(0);
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
sort * given = get_sort(args[i]);
|
|
if (!compatible_sorts(expected, given)) {
|
|
std::ostringstream buff;
|
|
buff << "invalid function application for " << decl->get_name() << ", ";
|
|
buff << "sort mismatch on argument at position " << (i+1) << ", ";
|
|
buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m);
|
|
throw ast_exception(buff.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (decl->get_arity() != num_args) {
|
|
throw ast_exception("invalid function application, wrong number of arguments");
|
|
}
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
sort * expected = decl->get_domain(i);
|
|
sort * given = get_sort(args[i]);
|
|
if (!compatible_sorts(expected, given)) {
|
|
std::ostringstream buff;
|
|
buff << "invalid function application for " << decl->get_name() << ", ";
|
|
buff << "sort mismatch on argument at position " << (i+1) << ", ";
|
|
buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m);
|
|
throw ast_exception(buff.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
\brief Shallow sort checker.
|
|
Return true if success.
|
|
If n == 0, then fail.
|
|
If n is an application, checks whether the arguments of n match the expected types.
|
|
*/
|
|
void ast_manager::check_sorts_core(ast const * n) const {
|
|
if (!n) {
|
|
throw ast_exception("expression is null");
|
|
}
|
|
if (n->get_kind() != AST_APP)
|
|
return; // nothing else to check...
|
|
app const * a = to_app(n);
|
|
func_decl* d = a->get_decl();
|
|
check_sort(d, a->get_num_args(), a->get_args());
|
|
if (a->get_num_args() == 2 &&
|
|
!d->is_flat_associative() &&
|
|
d->is_right_associative()) {
|
|
check_sorts_core(a->get_arg(1));
|
|
}
|
|
if (a->get_num_args() == 2 &&
|
|
!d->is_flat_associative() &&
|
|
d->is_left_associative()) {
|
|
check_sorts_core(a->get_arg(0));
|
|
}
|
|
}
|
|
|
|
bool ast_manager::check_sorts(ast const * n) const {
|
|
try {
|
|
check_sorts_core(n);
|
|
return true;
|
|
}
|
|
catch (ast_exception & ex) {
|
|
warning_msg("%s", ex.msg());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ast_manager::compatible_sorts(sort * s1, sort * s2) const {
|
|
if (s1 == s2)
|
|
return true;
|
|
if (m_int_real_coercions)
|
|
return s1->get_family_id() == m_arith_family_id && s2->get_family_id() == m_arith_family_id;
|
|
return false;
|
|
}
|
|
|
|
bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * const * args) {
|
|
SASSERT(m_int_real_coercions);
|
|
if (decl->is_associative()) {
|
|
sort * d = decl->get_domain(0);
|
|
if (d->get_family_id() == m_arith_family_id) {
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
if (d != get_sort(args[i]))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (decl->get_arity() != num_args) {
|
|
// Invalid input: unexpected number of arguments for non-associative operator.
|
|
// So, there is no point in coercing the input arguments.
|
|
return false;
|
|
}
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
sort * d = decl->get_domain(i);
|
|
if (d->get_family_id() == m_arith_family_id && d != get_sort(args[i]))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const * args) {
|
|
app * r = 0;
|
|
app * new_node = 0;
|
|
unsigned sz = app::get_obj_size(num_args);
|
|
void * mem = allocate_node(sz);
|
|
|
|
try {
|
|
if (m_int_real_coercions && coercion_needed(decl, num_args, args)) {
|
|
expr_ref_buffer new_args(*this);
|
|
if (decl->is_associative()) {
|
|
sort * d = decl->get_domain(0);
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
sort * s = get_sort(args[i]);
|
|
if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) {
|
|
if (d->get_decl_kind() == REAL_SORT)
|
|
new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i]));
|
|
else
|
|
new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i]));
|
|
}
|
|
else {
|
|
new_args.push_back(args[i]);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
sort * d = decl->get_domain(i);
|
|
sort * s = get_sort(args[i]);
|
|
if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) {
|
|
if (d->get_decl_kind() == REAL_SORT)
|
|
new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i]));
|
|
else
|
|
new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i]));
|
|
}
|
|
else {
|
|
new_args.push_back(args[i]);
|
|
}
|
|
}
|
|
}
|
|
check_args(decl, num_args, new_args.c_ptr());
|
|
SASSERT(new_args.size() == num_args);
|
|
new_node = new (mem)app(decl, num_args, new_args.c_ptr());
|
|
r = register_node(new_node);
|
|
}
|
|
else {
|
|
check_args(decl, num_args, args);
|
|
new_node = new (mem)app(decl, num_args, args);
|
|
r = register_node(new_node);
|
|
}
|
|
|
|
if (m_trace_stream && r == new_node) {
|
|
*m_trace_stream << "[mk-app] #" << r->get_id() << " ";
|
|
if (r->get_num_args() == 0 && r->get_decl()->get_name() == "int") {
|
|
ast_ll_pp(*m_trace_stream, *this, r);
|
|
}
|
|
else if (is_label_lit(r)) {
|
|
ast_ll_pp(*m_trace_stream, *this, r);
|
|
}
|
|
else {
|
|
*m_trace_stream << r->get_decl()->get_name();
|
|
for (unsigned i = 0; i < r->get_num_args(); i++)
|
|
*m_trace_stream << " #" << r->get_arg(i)->get_id();
|
|
*m_trace_stream << "\n";
|
|
}
|
|
}
|
|
}
|
|
catch (...) {
|
|
deallocate_node(static_cast<ast*>(mem), sz);
|
|
throw;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) {
|
|
for (unsigned i = 0; i < n; i++) {
|
|
sort * actual_sort = get_sort(es[i]);
|
|
sort * expected_sort = f->is_associative() ? f->get_domain(0) : f->get_domain(i);
|
|
if (expected_sort != actual_sort) {
|
|
std::ostringstream buffer;
|
|
buffer << "Sort mismatch at argument #" << (i+1)
|
|
<< " for function " << mk_pp(f,*this)
|
|
<< " supplied sort is "
|
|
<< mk_pp(actual_sort, *this);
|
|
throw ast_exception(buffer.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
inline app * ast_manager::mk_app_core(func_decl * decl, expr * arg1, expr * arg2) {
|
|
expr * args[2] = { arg1, arg2 };
|
|
return mk_app_core(decl, 2, args);
|
|
}
|
|
|
|
app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * args) {
|
|
|
|
bool type_error =
|
|
decl->get_arity() != num_args && !decl->is_right_associative() &&
|
|
!decl->is_left_associative() && !decl->is_chainable();
|
|
|
|
type_error |= (decl->get_arity() != num_args && num_args < 2 &&
|
|
decl->get_family_id() == m_basic_family_id && !decl->is_associative());
|
|
|
|
if (type_error) {
|
|
std::ostringstream buffer;
|
|
buffer << "Wrong number of arguments (" << num_args
|
|
<< ") passed to function " << mk_pp(decl, *this);
|
|
throw ast_exception(buffer.str().c_str());
|
|
}
|
|
app * r = 0;
|
|
if (num_args == 1 && decl->is_chainable() && decl->get_arity() == 2) {
|
|
r = mk_true();
|
|
}
|
|
else if (num_args > 2 && !decl->is_flat_associative()) {
|
|
if (decl->is_right_associative()) {
|
|
unsigned j = num_args - 1;
|
|
r = mk_app_core(decl, args[j-1], args[j]);
|
|
-- j;
|
|
while (j > 0) {
|
|
--j;
|
|
r = mk_app_core(decl, args[j], r);
|
|
}
|
|
}
|
|
else if (decl->is_left_associative()) {
|
|
r = mk_app_core(decl, args[0], args[1]);
|
|
for (unsigned i = 2; i < num_args; i++) {
|
|
r = mk_app_core(decl, r, args[i]);
|
|
}
|
|
}
|
|
else if (decl->is_chainable()) {
|
|
TRACE("chainable", tout << "chainable...\n";);
|
|
ptr_buffer<expr> new_args;
|
|
for (unsigned i = 1; i < num_args; i++) {
|
|
new_args.push_back(mk_app_core(decl, args[i-1], args[i]));
|
|
}
|
|
r = mk_and(new_args.size(), new_args.c_ptr());
|
|
}
|
|
}
|
|
if (r == 0) {
|
|
r = mk_app_core(decl, num_args, args);
|
|
}
|
|
SASSERT(r != 0);
|
|
TRACE("app_ground", tout << "ground: " << r->is_ground() << " id: " << r->get_id() << "\n" << mk_ll_pp(r, *this) << "\n";);
|
|
return r;
|
|
}
|
|
|
|
|
|
|
|
func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity,
|
|
sort * const * domain, sort * range) {
|
|
func_decl_info info(null_family_id, null_decl_kind);
|
|
info.m_skolem = true;
|
|
SASSERT(info.is_skolem());
|
|
func_decl * d;
|
|
if (prefix == symbol::null && suffix == symbol::null) {
|
|
d = mk_func_decl(symbol(m_fresh_id), arity, domain, range, &info);
|
|
}
|
|
else {
|
|
string_buffer<64> buffer;
|
|
buffer << prefix;
|
|
if (prefix == symbol::null)
|
|
buffer << "sk";
|
|
buffer << "!";
|
|
if (suffix != symbol::null)
|
|
buffer << suffix << "!";
|
|
buffer << m_fresh_id;
|
|
d = mk_func_decl(symbol(buffer.c_str()), arity, domain, range, &info);
|
|
}
|
|
m_fresh_id++;
|
|
SASSERT(d->get_info());
|
|
SASSERT(d->is_skolem());
|
|
return d;
|
|
}
|
|
|
|
sort * ast_manager::mk_fresh_sort(char const * prefix) {
|
|
string_buffer<32> buffer;
|
|
buffer << prefix << "!" << m_fresh_id;
|
|
m_fresh_id++;
|
|
return mk_uninterpreted_sort(symbol(buffer.c_str()));
|
|
}
|
|
|
|
symbol ast_manager::mk_fresh_var_name(char const * prefix) {
|
|
string_buffer<32> buffer;
|
|
buffer << (prefix ? prefix : "var") << "!" << m_fresh_id;
|
|
m_fresh_id++;
|
|
return symbol(buffer.c_str());
|
|
}
|
|
|
|
var * ast_manager::mk_var(unsigned idx, sort * s) {
|
|
unsigned sz = var::get_obj_size();
|
|
void * mem = allocate_node(sz);
|
|
var * new_node = new (mem) var(idx, s);
|
|
return register_node(new_node);
|
|
}
|
|
|
|
app * ast_manager::mk_label(bool pos, unsigned num_names, symbol const * names, expr * n) {
|
|
SASSERT(num_names > 0);
|
|
SASSERT(get_sort(n) == m_bool_sort);
|
|
buffer<parameter> p;
|
|
p.push_back(parameter(static_cast<int>(pos)));
|
|
for (unsigned i = 0; i < num_names; i++)
|
|
p.push_back(parameter(names[i]));
|
|
return mk_app(m_label_family_id, OP_LABEL, p.size(), p.c_ptr(), 1, &n);
|
|
}
|
|
|
|
app * ast_manager::mk_label(bool pos, symbol const & name, expr * n) {
|
|
return mk_label(pos, 1, &name, n);
|
|
}
|
|
|
|
bool ast_manager::is_label(expr const * n, bool & pos, buffer<symbol> & names) const {
|
|
if (!is_app_of(n, m_label_family_id, OP_LABEL)) {
|
|
return false;
|
|
}
|
|
func_decl const * decl = to_app(n)->get_decl();
|
|
pos = decl->get_parameter(0).get_int() != 0;
|
|
for (unsigned i = 1; i < decl->get_num_parameters(); i++)
|
|
names.push_back(decl->get_parameter(i).get_symbol());
|
|
return true;
|
|
}
|
|
|
|
app * ast_manager::mk_label_lit(unsigned num_names, symbol const * names) {
|
|
SASSERT(num_names > 0);
|
|
buffer<parameter> p;
|
|
for (unsigned i = 0; i < num_names; i++)
|
|
p.push_back(parameter(names[i]));
|
|
return mk_app(m_label_family_id, OP_LABEL_LIT, p.size(), p.c_ptr(), 0, 0);
|
|
}
|
|
|
|
app * ast_manager::mk_label_lit(symbol const & name) {
|
|
return mk_label_lit(1, &name);
|
|
}
|
|
|
|
bool ast_manager::is_label_lit(expr const * n, buffer<symbol> & names) const {
|
|
if (!is_app_of(n, m_label_family_id, OP_LABEL_LIT)) {
|
|
return false;
|
|
}
|
|
func_decl const * decl = to_app(n)->get_decl();
|
|
for (unsigned i = 0; i < decl->get_num_parameters(); i++)
|
|
names.push_back(decl->get_parameter(i).get_symbol());
|
|
return true;
|
|
}
|
|
|
|
app * ast_manager::mk_pattern(unsigned num_exprs, app * const * exprs) {
|
|
DEBUG_CODE({
|
|
for (unsigned i = 0; i < num_exprs; ++i) {
|
|
SASSERT(is_app(exprs[i]));
|
|
}});
|
|
return mk_app(m_pattern_family_id, OP_PATTERN, 0, 0, num_exprs, (expr*const*)exprs);
|
|
}
|
|
|
|
bool ast_manager::is_pattern(expr const * n) const {
|
|
if (!is_app_of(n, m_pattern_family_id, OP_PATTERN)) {
|
|
return false;
|
|
}
|
|
for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) {
|
|
if (!is_app(to_app(n)->get_arg(i))) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ast_manager::is_pattern(expr const * n, ptr_vector<expr> &args) {
|
|
if (!is_app_of(n, m_pattern_family_id, OP_PATTERN)) {
|
|
return false;
|
|
}
|
|
for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) {
|
|
expr *arg = to_app(n)->get_arg(i);
|
|
if (!is_app(arg)) {
|
|
return false;
|
|
}
|
|
args.push_back(arg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names,
|
|
expr * body, int weight , symbol const & qid, symbol const & skid,
|
|
unsigned num_patterns, expr * const * patterns,
|
|
unsigned num_no_patterns, expr * const * no_patterns) {
|
|
SASSERT(body);
|
|
SASSERT(num_patterns == 0 || num_no_patterns == 0);
|
|
SASSERT(num_decls > 0);
|
|
DEBUG_CODE({
|
|
for (unsigned i = 0; i < num_patterns; ++i) {
|
|
TRACE("ast", tout << i << " " << mk_pp(patterns[i], *this) << "\n";);
|
|
SASSERT(is_pattern(patterns[i]));
|
|
}});
|
|
unsigned sz = quantifier::get_obj_size(num_decls, num_patterns, num_no_patterns);
|
|
void * mem = allocate_node(sz);
|
|
quantifier * new_node = new (mem) quantifier(forall, num_decls, decl_sorts, decl_names, body,
|
|
weight, qid, skid, num_patterns, patterns,
|
|
num_no_patterns, no_patterns);
|
|
quantifier * r = register_node(new_node);
|
|
|
|
if (m_trace_stream && r == new_node) {
|
|
*m_trace_stream << "[mk-quant] #" << r->get_id() << " " << qid;
|
|
for (unsigned i = 0; i < num_patterns; ++i) {
|
|
*m_trace_stream << " #" << patterns[i]->get_id();
|
|
}
|
|
*m_trace_stream << " #" << body->get_id() << "\n";
|
|
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
// Return true if the patterns of q are the given ones.
|
|
static bool same_patterns(quantifier * q, unsigned num_patterns, expr * const * patterns) {
|
|
if (num_patterns != q->get_num_patterns())
|
|
return false;
|
|
for (unsigned i = 0; i < num_patterns; i++)
|
|
if (q->get_pattern(i) != patterns[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Return true if the no patterns of q are the given ones.
|
|
static bool same_no_patterns(quantifier * q, unsigned num_no_patterns, expr * const * no_patterns) {
|
|
if (num_no_patterns != q->get_num_no_patterns())
|
|
return false;
|
|
for (unsigned i = 0; i < num_no_patterns; i++)
|
|
if (q->get_no_pattern(i) != no_patterns[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, expr * body) {
|
|
if (q->get_expr() == body && same_patterns(q, num_patterns, patterns))
|
|
return q;
|
|
return mk_quantifier(q->is_forall(),
|
|
q->get_num_decls(),
|
|
q->get_decl_sorts(),
|
|
q->get_decl_names(),
|
|
body,
|
|
q->get_weight(),
|
|
q->get_qid(),
|
|
q->get_skid(),
|
|
num_patterns,
|
|
patterns,
|
|
num_patterns == 0 ? q->get_num_no_patterns() : 0,
|
|
num_patterns == 0 ? q->get_no_patterns() : 0);
|
|
}
|
|
|
|
quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns, expr * body) {
|
|
if (q->get_expr() == body && same_patterns(q, num_patterns, patterns) && same_no_patterns(q, num_no_patterns, no_patterns))
|
|
return q;
|
|
return mk_quantifier(q->is_forall(),
|
|
q->get_num_decls(),
|
|
q->get_decl_sorts(),
|
|
q->get_decl_names(),
|
|
body,
|
|
q->get_weight(),
|
|
q->get_qid(),
|
|
q->get_skid(),
|
|
num_patterns,
|
|
patterns,
|
|
num_no_patterns,
|
|
no_patterns);
|
|
}
|
|
|
|
quantifier * ast_manager::update_quantifier(quantifier * q, expr * body) {
|
|
if (q->get_expr() == body)
|
|
return q;
|
|
return mk_quantifier(q->is_forall(),
|
|
q->get_num_decls(),
|
|
q->get_decl_sorts(),
|
|
q->get_decl_names(),
|
|
body,
|
|
q->get_weight(),
|
|
q->get_qid(),
|
|
q->get_skid(),
|
|
q->get_num_patterns(),
|
|
q->get_patterns(),
|
|
q->get_num_no_patterns(),
|
|
q->get_no_patterns());
|
|
}
|
|
|
|
quantifier * ast_manager::update_quantifier_weight(quantifier * q, int w) {
|
|
if (q->get_weight() == w)
|
|
return q;
|
|
TRACE("update_quantifier_weight", tout << "#" << q->get_id() << " " << q->get_weight() << " -> " << w << "\n";);
|
|
return mk_quantifier(q->is_forall(),
|
|
q->get_num_decls(),
|
|
q->get_decl_sorts(),
|
|
q->get_decl_names(),
|
|
q->get_expr(),
|
|
w,
|
|
q->get_qid(),
|
|
q->get_skid(),
|
|
q->get_num_patterns(),
|
|
q->get_patterns(),
|
|
q->get_num_no_patterns(),
|
|
q->get_no_patterns());
|
|
}
|
|
|
|
quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, expr * body) {
|
|
if (q->get_expr() == body && q->is_forall() == is_forall)
|
|
return q;
|
|
return mk_quantifier(is_forall,
|
|
q->get_num_decls(),
|
|
q->get_decl_sorts(),
|
|
q->get_decl_names(),
|
|
body,
|
|
q->get_weight(),
|
|
q->get_qid(),
|
|
q->get_skid(),
|
|
q->get_num_patterns(),
|
|
q->get_patterns(),
|
|
q->get_num_no_patterns(),
|
|
q->get_no_patterns());
|
|
}
|
|
|
|
quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, unsigned num_patterns, expr * const * patterns, expr * body) {
|
|
if (q->get_expr() == body && q->is_forall() == is_forall && same_patterns(q, num_patterns, patterns))
|
|
return q;
|
|
return mk_quantifier(is_forall,
|
|
q->get_num_decls(),
|
|
q->get_decl_sorts(),
|
|
q->get_decl_names(),
|
|
body,
|
|
q->get_weight(),
|
|
q->get_qid(),
|
|
q->get_skid(),
|
|
num_patterns,
|
|
patterns,
|
|
num_patterns == 0 ? q->get_num_no_patterns() : 0,
|
|
num_patterns == 0 ? q->get_no_patterns() : 0);
|
|
}
|
|
|
|
app * ast_manager::mk_distinct(unsigned num_args, expr * const * args) {
|
|
return mk_app(m_basic_family_id, OP_DISTINCT, num_args, args);
|
|
}
|
|
|
|
app * ast_manager::mk_distinct_expanded(unsigned num_args, expr * const * args) {
|
|
if (num_args < 2)
|
|
return mk_true();
|
|
if (num_args == 2)
|
|
return mk_not(mk_eq(args[0], args[1]));
|
|
ptr_buffer<expr> new_args;
|
|
for (unsigned i = 0; i < num_args - 1; i++) {
|
|
expr * a1 = args[i];
|
|
for (unsigned j = i + 1; j < num_args; j++) {
|
|
expr * a2 = args[j];
|
|
new_args.push_back(mk_not(mk_eq(a1, a2)));
|
|
}
|
|
}
|
|
app * r = mk_and(new_args.size(), new_args.c_ptr());
|
|
TRACE("distinct", tout << "expanded distinct:\n" << mk_pp(r, *this) << "\n";);
|
|
return r;
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// expr_dependency
|
|
//
|
|
// -----------------------------------
|
|
|
|
expr_dependency * ast_manager::mk_leaf(expr * t) {
|
|
if (t == 0)
|
|
return 0;
|
|
else
|
|
return m_expr_dependency_manager.mk_leaf(t);
|
|
}
|
|
|
|
expr_dependency * ast_manager::mk_join(unsigned n, expr * const * ts) {
|
|
expr_dependency * d = 0;
|
|
for (unsigned i = 0; i < n; i++)
|
|
d = mk_join(d, mk_leaf(ts[i]));
|
|
return d;
|
|
}
|
|
|
|
void ast_manager::linearize(expr_dependency * d, ptr_vector<expr> & ts) {
|
|
m_expr_dependency_manager.linearize(d, ts);
|
|
remove_duplicates(ts);
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// Values
|
|
//
|
|
// -----------------------------------
|
|
|
|
app * ast_manager::mk_model_value(unsigned idx, sort * s) {
|
|
parameter p[2] = { parameter(idx), parameter(s) };
|
|
return mk_app(m_model_value_family_id, OP_MODEL_VALUE, 2, p, 0, static_cast<expr * const *>(0));
|
|
}
|
|
|
|
expr * ast_manager::get_some_value(sort * s, some_value_proc * p) {
|
|
flet<some_value_proc*> l(m_some_value_proc, p);
|
|
return get_some_value(s);
|
|
}
|
|
|
|
expr * ast_manager::get_some_value(sort * s) {
|
|
expr * v = 0;
|
|
if (m_some_value_proc)
|
|
v = (*m_some_value_proc)(s);
|
|
if (v != 0)
|
|
return v;
|
|
family_id fid = s->get_family_id();
|
|
if (fid != null_family_id) {
|
|
decl_plugin * p = get_plugin(fid);
|
|
if (p != 0) {
|
|
v = p->get_some_value(s);
|
|
if (v != 0)
|
|
return v;
|
|
}
|
|
}
|
|
return mk_model_value(0, s);
|
|
}
|
|
|
|
bool ast_manager::is_fully_interp(sort * s) const {
|
|
if (is_uninterp(s))
|
|
return false;
|
|
family_id fid = s->get_family_id();
|
|
SASSERT(fid != null_family_id);
|
|
decl_plugin * p = get_plugin(fid);
|
|
if (p != 0)
|
|
return p->is_fully_interp(s);
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// Proof generation
|
|
//
|
|
// -----------------------------------
|
|
|
|
proof * ast_manager::mk_proof(family_id fid, decl_kind k, unsigned num_args, expr * const * args) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(fid, k, num_args, args);
|
|
}
|
|
|
|
proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg) {
|
|
return mk_proof(fid, k, 1, &arg);
|
|
}
|
|
|
|
proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2) {
|
|
expr * args[2] = { arg1, arg2 };
|
|
return mk_proof(fid, k, 2, args);
|
|
}
|
|
|
|
proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3) {
|
|
expr * args[3] = { arg1, arg2, arg3 };
|
|
return mk_proof(fid, k, 2, args);
|
|
}
|
|
|
|
proof * ast_manager::mk_true_proof() {
|
|
expr * f = mk_true();
|
|
return mk_proof(m_basic_family_id, PR_TRUE, f);
|
|
}
|
|
|
|
proof * ast_manager::mk_asserted(expr * f) {
|
|
CTRACE("mk_asserted_bug", !is_bool(f), tout << mk_ismt2_pp(f, *this) << "\nsort: " << mk_ismt2_pp(get_sort(f), *this) << "\n";);
|
|
SASSERT(is_bool(f));
|
|
return mk_proof(m_basic_family_id, PR_ASSERTED, f);
|
|
}
|
|
|
|
proof * ast_manager::mk_goal(expr * f) {
|
|
SASSERT(is_bool(f));
|
|
return mk_proof(m_basic_family_id, PR_GOAL, f);
|
|
}
|
|
|
|
proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) {
|
|
if (!p1 || !p2) return nullptr;
|
|
SASSERT(has_fact(p1));
|
|
SASSERT(has_fact(p2));
|
|
CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))),
|
|
tout << mk_ll_pp(p1, *this) << "\n";
|
|
tout << mk_ll_pp(p2, *this) << "\n";);
|
|
SASSERT(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2)));
|
|
CTRACE("mk_modus_ponens", to_app(get_fact(p2))->get_arg(0) != get_fact(p1),
|
|
tout << mk_pp(get_fact(p1), *this) << "\n" << mk_pp(get_fact(p2), *this) << "\n";);
|
|
SASSERT(to_app(get_fact(p2))->get_arg(0) == get_fact(p1));
|
|
CTRACE("mk_modus_ponens", !is_ground(p2) && !has_quantifiers(p2), tout << "Non-ground: " << mk_pp(p2, *this) << "\n";);
|
|
CTRACE("mk_modus_ponens", !is_ground(p1) && !has_quantifiers(p1), tout << "Non-ground: " << mk_pp(p1, *this) << "\n";);
|
|
if (is_reflexivity(p2))
|
|
return p1;
|
|
expr * f = to_app(get_fact(p2))->get_arg(1);
|
|
if (is_oeq(get_fact(p2)))
|
|
return mk_app(m_basic_family_id, PR_MODUS_PONENS_OEQ, p1, p2, f);
|
|
return mk_app(m_basic_family_id, PR_MODUS_PONENS, p1, p2, f);
|
|
}
|
|
|
|
proof * ast_manager::mk_reflexivity(expr * e) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_eq(e, e));
|
|
}
|
|
|
|
proof * ast_manager::mk_oeq_reflexivity(expr * e) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_oeq(e, e));
|
|
}
|
|
|
|
proof * ast_manager::mk_commutativity(app * f) {
|
|
SASSERT(f->get_num_args() == 2);
|
|
app * f_prime = mk_app(f->get_decl(), f->get_arg(1), f->get_arg(0));
|
|
return mk_app(m_basic_family_id, PR_COMMUTATIVITY, mk_eq(f, f_prime));
|
|
}
|
|
|
|
/**
|
|
\brief Given a proof of p, return a proof of (p <=> true)
|
|
*/
|
|
proof * ast_manager::mk_iff_true(proof * pr) {
|
|
if (!pr) return pr;
|
|
SASSERT(has_fact(pr));
|
|
SASSERT(is_bool(get_fact(pr)));
|
|
return mk_app(m_basic_family_id, PR_IFF_TRUE, pr, mk_iff(get_fact(pr), mk_true()));
|
|
}
|
|
|
|
/**
|
|
\brief Given a proof of (not p), return a proof of (p <=> false)
|
|
*/
|
|
proof * ast_manager::mk_iff_false(proof * pr) {
|
|
if (!pr) return pr;
|
|
SASSERT(has_fact(pr));
|
|
SASSERT(is_not(get_fact(pr)));
|
|
expr * p = to_app(get_fact(pr))->get_arg(0);
|
|
return mk_app(m_basic_family_id, PR_IFF_FALSE, pr, mk_iff(p, mk_false()));
|
|
}
|
|
|
|
proof * ast_manager::mk_symmetry(proof * p) {
|
|
if (!p) return p;
|
|
if (is_reflexivity(p))
|
|
return p;
|
|
if (is_symmetry(p))
|
|
return get_parent(p, 0);
|
|
SASSERT(has_fact(p));
|
|
SASSERT(is_app(get_fact(p)));
|
|
SASSERT(to_app(get_fact(p))->get_num_args() == 2);
|
|
return mk_app(m_basic_family_id, PR_SYMMETRY, p,
|
|
mk_app(to_app(get_fact(p))->get_decl(), to_app(get_fact(p))->get_arg(1), to_app(get_fact(p))->get_arg(0)));
|
|
}
|
|
|
|
proof * ast_manager::mk_transitivity(proof * p1, proof * p2) {
|
|
if (!p1)
|
|
return p2;
|
|
if (!p2)
|
|
return p1;
|
|
SASSERT(has_fact(p1));
|
|
SASSERT(has_fact(p2));
|
|
SASSERT(is_app(get_fact(p1)));
|
|
SASSERT(is_app(get_fact(p2)));
|
|
SASSERT(to_app(get_fact(p1))->get_num_args() == 2);
|
|
SASSERT(to_app(get_fact(p2))->get_num_args() == 2);
|
|
CTRACE("mk_transitivity", to_app(get_fact(p1))->get_decl() != to_app(get_fact(p2))->get_decl(),
|
|
tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n";
|
|
tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n";
|
|
tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";);
|
|
SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() ||
|
|
((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) &&
|
|
(is_iff(get_fact(p2)) || is_eq(get_fact(p2)))) ||
|
|
( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) &&
|
|
(is_eq(get_fact(p2)) || is_oeq(get_fact(p2)))));
|
|
CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0),
|
|
tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n";
|
|
tout << mk_bounded_pp(p1, *this, 5) << "\n\n";
|
|
tout << mk_bounded_pp(p2, *this, 5) << "\n\n";
|
|
);
|
|
SASSERT(to_app(get_fact(p1))->get_arg(1) == to_app(get_fact(p2))->get_arg(0));
|
|
if (is_reflexivity(p1))
|
|
return p2;
|
|
if (is_reflexivity(p2))
|
|
return p1;
|
|
// OEQ is compatible with EQ for transitivity.
|
|
func_decl* f = to_app(get_fact(p1))->get_decl();
|
|
if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl();
|
|
return mk_app(m_basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, to_app(get_fact(p1))->get_arg(0), to_app(get_fact(p2))->get_arg(1)));
|
|
}
|
|
|
|
proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3) {
|
|
return mk_transitivity(mk_transitivity(p1,p2), p3);
|
|
}
|
|
|
|
proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3, proof * p4) {
|
|
return mk_transitivity(mk_transitivity(mk_transitivity(p1,p2), p3), p4);
|
|
}
|
|
|
|
proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
SASSERT(num_proofs > 0);
|
|
proof * r = proofs[0];
|
|
for (unsigned i = 1; i < num_proofs; i++)
|
|
r = mk_transitivity(r, proofs[i]);
|
|
return r;
|
|
}
|
|
|
|
proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
if (proofs_enabled())
|
|
return mk_transitivity(num_proofs, proofs);
|
|
SASSERT(num_proofs > 0);
|
|
if (num_proofs == 1)
|
|
return proofs[0];
|
|
DEBUG_CODE({
|
|
for (unsigned i = 0; i < num_proofs; i++) {
|
|
SASSERT(proofs[i]);
|
|
SASSERT(!is_reflexivity(proofs[i]));
|
|
}
|
|
});
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(mk_eq(n1,n2));
|
|
return mk_app(m_basic_family_id, PR_TRANSITIVITY_STAR, args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
SASSERT(f1->get_num_args() == f2->get_num_args());
|
|
SASSERT(f1->get_decl() == f2->get_decl());
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(mk_app(R, f1, f2));
|
|
return mk_app(m_basic_family_id, PR_MONOTONICITY, args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
SASSERT(get_sort(f1) == get_sort(f2));
|
|
sort * s = get_sort(f1);
|
|
sort * d[2] = { s, s };
|
|
return mk_monotonicity(mk_func_decl(m_basic_family_id, get_eq_op(f1), 0, 0, 2, d), f1, f2, num_proofs, proofs);
|
|
}
|
|
|
|
proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
SASSERT(get_sort(f1) == get_sort(f2));
|
|
sort * s = get_sort(f1);
|
|
sort * d[2] = { s, s };
|
|
return mk_monotonicity(mk_func_decl(m_basic_family_id, OP_OEQ, 0, 0, 2, d), f1, f2, num_proofs, proofs);
|
|
}
|
|
|
|
proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
if (!p) {
|
|
return 0;
|
|
}
|
|
SASSERT(q1->get_num_decls() == q2->get_num_decls());
|
|
SASSERT(has_fact(p));
|
|
SASSERT(is_iff(get_fact(p)));
|
|
return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_iff(q1, q2));
|
|
}
|
|
|
|
proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof * p) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
SASSERT(q1->get_num_decls() == q2->get_num_decls());
|
|
SASSERT(has_fact(p));
|
|
SASSERT(is_oeq(get_fact(p)));
|
|
return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_oeq(q1, q2));
|
|
}
|
|
|
|
proof * ast_manager::mk_distributivity(expr * s, expr * r) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_DISTRIBUTIVITY, mk_eq(s, r));
|
|
}
|
|
|
|
proof * ast_manager::mk_rewrite(expr * s, expr * t) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_REWRITE, mk_eq(s, t));
|
|
}
|
|
|
|
proof * ast_manager::mk_oeq_rewrite(expr * s, expr * t) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_REWRITE, mk_oeq(s, t));
|
|
}
|
|
|
|
proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(mk_eq(s, t));
|
|
return mk_app(m_basic_family_id, PR_REWRITE_STAR, args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof * ast_manager::mk_pull_quant(expr * e, quantifier * q) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_PULL_QUANT, mk_iff(e, q));
|
|
}
|
|
|
|
proof * ast_manager::mk_pull_quant_star(expr * e, quantifier * q) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_PULL_QUANT_STAR, mk_iff(e, q));
|
|
}
|
|
|
|
proof * ast_manager::mk_push_quant(quantifier * q, expr * e) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_PUSH_QUANT, mk_iff(q, e));
|
|
}
|
|
|
|
proof * ast_manager::mk_elim_unused_vars(quantifier * q, expr * e) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_ELIM_UNUSED_VARS, mk_iff(q, e));
|
|
}
|
|
|
|
proof * ast_manager::mk_der(quantifier * q, expr * e) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_DER, mk_iff(q, e));
|
|
}
|
|
|
|
proof * ast_manager::mk_quant_inst(expr * not_q_or_i, unsigned num_bind, expr* const* binding) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
vector<parameter> params;
|
|
for (unsigned i = 0; i < num_bind; ++i) {
|
|
params.push_back(parameter(binding[i]));
|
|
SASSERT(params.back().is_ast());
|
|
}
|
|
return mk_app(m_basic_family_id, PR_QUANT_INST, num_bind, params.c_ptr(), 1, & not_q_or_i);
|
|
}
|
|
|
|
bool ast_manager::is_quant_inst(expr const* e, expr*& not_q_or_i, ptr_vector<expr>& binding) const {
|
|
if (is_quant_inst(e)) {
|
|
not_q_or_i = to_app(e)->get_arg(0);
|
|
func_decl* d = to_app(e)->get_decl();
|
|
SASSERT(binding.empty());
|
|
for (unsigned i = 0; i < d->get_num_parameters(); ++i) {
|
|
binding.push_back(to_expr(d->get_parameter(i).get_ast()));
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ast_manager::is_rewrite(expr const* e, expr*& r1, expr*& r2) const {
|
|
if (is_rewrite(e)) {
|
|
VERIFY (is_eq(to_app(e)->get_arg(0), r1, r2) ||
|
|
is_iff(to_app(e)->get_arg(0), r1, r2));
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
proof * ast_manager::mk_def_axiom(expr * ax) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
return mk_app(m_basic_family_id, PR_DEF_AXIOM, ax);
|
|
}
|
|
|
|
proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs) {
|
|
SASSERT(num_proofs >= 2);
|
|
for (unsigned i = 0; i < num_proofs; i++) {
|
|
SASSERT(has_fact(proofs[i]));
|
|
}
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
expr * fact;
|
|
expr * f1 = get_fact(proofs[0]);
|
|
expr * f2 = get_fact(proofs[1]);
|
|
if (num_proofs == 2 && is_complement(f1, f2)) {
|
|
fact = mk_false();
|
|
}
|
|
else {
|
|
CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_pp(f1, *this) << " " << mk_pp(f2, *this) << "\n";);
|
|
SASSERT(is_or(f1));
|
|
ptr_buffer<expr> new_lits;
|
|
app const * cls = to_app(f1);
|
|
unsigned num_args = cls->get_num_args();
|
|
#ifdef Z3DEBUG
|
|
svector<bool> found;
|
|
#endif
|
|
for (unsigned i = 0; i < num_args; i++) {
|
|
bool found_complement = false;
|
|
expr * lit = cls->get_arg(i);
|
|
for (unsigned j = 1; j < num_proofs; j++) {
|
|
expr const * _fact = get_fact(proofs[j]);
|
|
if (is_complement(lit, _fact)) {
|
|
found_complement = true;
|
|
DEBUG_CODE(found.setx(j, true, false); continue;);
|
|
break;
|
|
}
|
|
}
|
|
if (!found_complement)
|
|
new_lits.push_back(lit);
|
|
}
|
|
DEBUG_CODE({
|
|
for (unsigned i = 1; proofs_enabled() && i < num_proofs; i++) {
|
|
CTRACE("mk_unit_resolution_bug", !found.get(i, false),
|
|
for (unsigned j = 0; j < num_proofs; j++) {
|
|
if (j == i) tout << "Index " << i << " was not found:\n";
|
|
tout << mk_ll_pp(get_fact(proofs[j]), *this);
|
|
});
|
|
SASSERT(found.get(i, false));
|
|
}
|
|
});
|
|
switch (new_lits.size()) {
|
|
case 0:
|
|
fact = mk_false();
|
|
break;
|
|
case 1:
|
|
fact = new_lits[0];
|
|
break;
|
|
default:
|
|
fact = mk_or(new_lits.size(), new_lits.c_ptr());
|
|
break;
|
|
}
|
|
}
|
|
args.push_back(fact);
|
|
proof * pr = mk_app(m_basic_family_id, PR_UNIT_RESOLUTION, args.size(), args.c_ptr());
|
|
TRACE("unit_resolution", tout << "unit_resolution generating fact\n" << mk_ll_pp(pr, *this););
|
|
return pr;
|
|
}
|
|
|
|
proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact) {
|
|
TRACE("unit_bug",
|
|
for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
|
|
tout << "===>\n";
|
|
tout << mk_pp(new_fact, *this) << "\n";);
|
|
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(new_fact);
|
|
#ifdef Z3DEBUG
|
|
expr * f1 = get_fact(proofs[0]);
|
|
expr const * f2 = get_fact(proofs[1]);
|
|
if (num_proofs == 2 && is_complement(f1, f2)) {
|
|
SASSERT(is_false(new_fact));
|
|
}
|
|
else {
|
|
SASSERT(is_or(f1));
|
|
app * cls = to_app(f1);
|
|
unsigned cls_sz = cls->get_num_args();
|
|
CTRACE("cunit_bug", !(num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact))),
|
|
for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n";
|
|
tout << "===>\n";
|
|
tout << mk_pp(new_fact, *this) << "\n";);
|
|
//
|
|
// typically: num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact))
|
|
// but formula could have repeated literals that are merged in the clausal representation.
|
|
//
|
|
unsigned num_matches = 0;
|
|
for (unsigned i = 0; i < cls_sz; i++) {
|
|
expr * lit = cls->get_arg(i);
|
|
unsigned j = 1;
|
|
for (; j < num_proofs; j++) {
|
|
if (is_complement(lit, get_fact(proofs[j]))) {
|
|
num_matches++;
|
|
break;
|
|
}
|
|
}
|
|
if (j == num_proofs) {
|
|
CTRACE("unit_bug1", new_fact != lit, tout << mk_ll_pp(new_fact, *this) << "\n" << mk_ll_pp(lit, *this) << "\n";);
|
|
SASSERT(new_fact == lit);
|
|
}
|
|
}
|
|
SASSERT(num_matches == cls_sz || num_matches == cls_sz - 1);
|
|
SASSERT(num_matches != cls_sz || is_false(new_fact));
|
|
}
|
|
#endif
|
|
proof * pr = mk_app(m_basic_family_id, PR_UNIT_RESOLUTION, args.size(), args.c_ptr());
|
|
TRACE("unit_resolution", tout << "unit_resolution using fact\n" << mk_ll_pp(pr, *this););
|
|
return pr;
|
|
}
|
|
|
|
proof * ast_manager::mk_hypothesis(expr * h) {
|
|
return mk_app(m_basic_family_id, PR_HYPOTHESIS, h);
|
|
}
|
|
|
|
proof * ast_manager::mk_lemma(proof * p, expr * lemma) {
|
|
if (!p) return p;
|
|
SASSERT(has_fact(p));
|
|
CTRACE("mk_lemma", !is_false(get_fact(p)), tout << mk_ll_pp(p, *this) << "\n";);
|
|
SASSERT(is_false(get_fact(p)));
|
|
return mk_app(m_basic_family_id, PR_LEMMA, p, lemma);
|
|
}
|
|
|
|
proof * ast_manager::mk_def_intro(expr * new_def) {
|
|
SASSERT(is_bool(new_def));
|
|
return mk_proof(m_basic_family_id, PR_DEF_INTRO, new_def);
|
|
}
|
|
|
|
proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(mk_oeq(n, def));
|
|
return mk_app(m_basic_family_id, PR_APPLY_DEF, args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof * ast_manager::mk_iff_oeq(proof * p) {
|
|
if (!p) return p;
|
|
|
|
SASSERT(has_fact(p));
|
|
SASSERT(is_iff(get_fact(p)) || is_oeq(get_fact(p)));
|
|
if (is_oeq(get_fact(p)))
|
|
return p;
|
|
|
|
app * iff = to_app(get_fact(p));
|
|
expr * lhs = iff->get_arg(0);
|
|
expr * rhs = iff->get_arg(1);
|
|
return mk_app(m_basic_family_id, PR_IFF_OEQ, p, mk_oeq(lhs, rhs));
|
|
}
|
|
|
|
bool ast_manager::check_nnf_proof_parents(unsigned num_proofs, proof * const * proofs) const {
|
|
for (unsigned i = 0; i < num_proofs; i++) {
|
|
if (!has_fact(proofs[i]))
|
|
return false;
|
|
if (!is_oeq(get_fact(proofs[i])))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
check_nnf_proof_parents(num_proofs, proofs);
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(mk_oeq(s, t));
|
|
return mk_app(m_basic_family_id, PR_NNF_POS, args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
check_nnf_proof_parents(num_proofs, proofs);
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(mk_oeq(mk_not(s), t));
|
|
return mk_app(m_basic_family_id, PR_NNF_NEG, args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(mk_oeq(s, t));
|
|
return mk_app(m_basic_family_id, PR_NNF_STAR, args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof * ast_manager::mk_skolemization(expr * q, expr * e) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
SASSERT(is_bool(q));
|
|
SASSERT(is_bool(e));
|
|
return mk_app(m_basic_family_id, PR_SKOLEMIZE, mk_oeq(q, e));
|
|
}
|
|
|
|
proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
ptr_buffer<expr> args;
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(mk_oeq(s, t));
|
|
return mk_app(m_basic_family_id, PR_CNF_STAR, args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof * ast_manager::mk_and_elim(proof * p, unsigned i) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
SASSERT(has_fact(p));
|
|
SASSERT(is_and(get_fact(p)));
|
|
CTRACE("mk_and_elim", i >= to_app(get_fact(p))->get_num_args(), tout << "i: " << i << "\n" << mk_pp(get_fact(p), *this) << "\n";);
|
|
SASSERT(i < to_app(get_fact(p))->get_num_args());
|
|
expr * f = to_app(get_fact(p))->get_arg(i);
|
|
return mk_app(m_basic_family_id, PR_AND_ELIM, p, f);
|
|
}
|
|
|
|
proof * ast_manager::mk_not_or_elim(proof * p, unsigned i) {
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
SASSERT(has_fact(p));
|
|
SASSERT(is_not(get_fact(p)));
|
|
SASSERT(is_or(to_app(get_fact(p))->get_arg(0)));
|
|
app * or_app = to_app(to_app(get_fact(p))->get_arg(0));
|
|
SASSERT(i < or_app->get_num_args());
|
|
expr * c = or_app->get_arg(i);
|
|
expr * f;
|
|
if (is_not(c))
|
|
f = to_app(c)->get_arg(0);
|
|
else
|
|
f = mk_not(c);
|
|
return mk_app(m_basic_family_id, PR_NOT_OR_ELIM, p, f);
|
|
}
|
|
|
|
|
|
proof * ast_manager::mk_th_lemma(
|
|
family_id tid,
|
|
expr * fact, unsigned num_proofs, proof * const * proofs,
|
|
unsigned num_params, parameter const* params
|
|
)
|
|
{
|
|
if (proofs_disabled())
|
|
return m_undef_proof;
|
|
|
|
ptr_buffer<expr> args;
|
|
vector<parameter> parameters;
|
|
parameters.push_back(parameter(get_family_name(tid)));
|
|
for (unsigned i = 0; i < num_params; ++i) {
|
|
parameters.push_back(params[i]);
|
|
}
|
|
args.append(num_proofs, (expr**) proofs);
|
|
args.push_back(fact);
|
|
return mk_app(m_basic_family_id, PR_TH_LEMMA, num_params+1, parameters.c_ptr(), args.size(), args.c_ptr());
|
|
}
|
|
|
|
proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premises, expr* concl,
|
|
svector<std::pair<unsigned, unsigned> > const& positions,
|
|
vector<expr_ref_vector> const& substs) {
|
|
ptr_vector<expr> fmls;
|
|
SASSERT(positions.size() + 1 == substs.size());
|
|
for (unsigned i = 0; i < num_premises; ++i) {
|
|
TRACE("hyper_res", tout << mk_pp(premises[i], *this) << "\n";);
|
|
fmls.push_back(get_fact(premises[i]));
|
|
}
|
|
SASSERT(is_bool(concl));
|
|
vector<parameter> params;
|
|
for (unsigned i = 0; i < substs.size(); ++i) {
|
|
expr_ref_vector const& vec = substs[i];
|
|
for (unsigned j = 0; j < vec.size(); ++j) {
|
|
params.push_back(parameter(vec[j]));
|
|
}
|
|
if (i + 1 < substs.size()) {
|
|
params.push_back(parameter(positions[i].first));
|
|
params.push_back(parameter(positions[i].second));
|
|
}
|
|
}
|
|
TRACE("hyper_res",
|
|
for (unsigned i = 0; i < params.size(); ++i) {
|
|
params[i].display(tout); tout << "\n";
|
|
});
|
|
ptr_vector<sort> sorts;
|
|
ptr_vector<expr> args;
|
|
for (unsigned i = 0; i < num_premises; ++i) {
|
|
sorts.push_back(mk_proof_sort());
|
|
args.push_back(premises[i]);
|
|
}
|
|
sorts.push_back(mk_bool_sort());
|
|
args.push_back(concl);
|
|
app* result = mk_app(m_basic_family_id, PR_HYPER_RESOLVE, params.size(), params.c_ptr(), args.size(), args.c_ptr());
|
|
SASSERT(result->get_family_id() == m_basic_family_id);
|
|
SASSERT(result->get_decl_kind() == PR_HYPER_RESOLVE);
|
|
return result;
|
|
}
|
|
|
|
bool ast_manager::is_hyper_resolve(
|
|
proof* p,
|
|
proof_ref_vector& premises,
|
|
expr_ref& conclusion,
|
|
svector<std::pair<unsigned, unsigned> > & positions,
|
|
vector<expr_ref_vector> & substs) {
|
|
if (!is_hyper_resolve(p)) {
|
|
return false;
|
|
}
|
|
unsigned sz = p->get_num_args();
|
|
SASSERT(sz > 0);
|
|
for (unsigned i = 0; i + 1 < sz; ++i) {
|
|
premises.push_back(to_app(p->get_arg(i)));
|
|
}
|
|
conclusion = p->get_arg(sz-1);
|
|
func_decl* d = p->get_decl();
|
|
unsigned num_p = d->get_num_parameters();
|
|
parameter const* params = d->get_parameters();
|
|
|
|
substs.push_back(expr_ref_vector(*this));
|
|
for (unsigned i = 0; i < num_p; ++i) {
|
|
if (params[i].is_int()) {
|
|
SASSERT(i + 1 < num_p);
|
|
SASSERT(params[i+1].is_int());
|
|
unsigned x = static_cast<unsigned>(params[i].get_int());
|
|
unsigned y = static_cast<unsigned>(params[i+1].get_int());
|
|
positions.push_back(std::make_pair(x, y));
|
|
substs.push_back(expr_ref_vector(*this));
|
|
++i;
|
|
}
|
|
else {
|
|
SASSERT(params[i].is_ast());
|
|
ast* a = params[i].get_ast();
|
|
SASSERT(is_expr(a));
|
|
substs.back().push_back(to_expr(a));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// -----------------------------------
|
|
//
|
|
// ast_mark
|
|
//
|
|
// -----------------------------------
|
|
|
|
bool ast_mark::is_marked(ast * n) const {
|
|
if (is_decl(n))
|
|
return m_decl_marks.is_marked(to_decl(n));
|
|
else
|
|
return m_expr_marks.is_marked(to_expr(n));
|
|
}
|
|
|
|
void ast_mark::mark(ast * n, bool flag) {
|
|
if (is_decl(n))
|
|
return m_decl_marks.mark(to_decl(n), flag);
|
|
else
|
|
return m_expr_marks.mark(to_expr(n), flag);
|
|
}
|
|
|
|
void ast_mark::reset() {
|
|
m_decl_marks.reset();
|
|
m_expr_marks.reset();
|
|
}
|
|
|
|
// -----------------------------------
|
|
//
|
|
// scoped_mark
|
|
//
|
|
// -----------------------------------
|
|
|
|
void scoped_mark::mark(ast * n, bool flag) {
|
|
SASSERT(flag);
|
|
mark(n);
|
|
}
|
|
|
|
void scoped_mark::mark(ast * n) {
|
|
if (!ast_mark::is_marked(n)) {
|
|
m_stack.push_back(n);
|
|
ast_mark::mark(n, true);
|
|
}
|
|
}
|
|
|
|
void scoped_mark::reset() {
|
|
ast_mark::reset();
|
|
m_stack.reset();
|
|
m_lim.reset();
|
|
}
|
|
|
|
void scoped_mark::push_scope() {
|
|
m_lim.push_back(m_stack.size());
|
|
}
|
|
|
|
void scoped_mark::pop_scope() {
|
|
unsigned new_size = m_stack.size();
|
|
unsigned old_size = m_lim.back();
|
|
for (unsigned i = old_size; i < new_size; ++i) {
|
|
ast_mark::mark(m_stack[i].get(), false);
|
|
}
|
|
m_lim.pop_back();
|
|
m_stack.resize(old_size);
|
|
}
|
|
|
|
void scoped_mark::pop_scope(unsigned num_scopes) {
|
|
for (unsigned i = 0; i < num_scopes; ++i) {
|
|
pop_scope();
|
|
}
|
|
}
|
|
|
|
// Added by KLM for use in GDB
|
|
|
|
// show an expr_ref on stdout
|
|
|
|
void prexpr(expr_ref &e){
|
|
std::cout << mk_pp(e.get(), e.get_manager()) << std::endl;
|
|
}
|
|
|
|
void ast_manager::show_id_gen(){
|
|
std::cout << "id_gen: " << m_expr_id_gen.show_hash() << " " << m_decl_id_gen.show_hash() << "\n";
|
|
}
|