mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
wip: add recursive functions
This commit is contained in:
parent
fba22d2fac
commit
d5e134dd94
19 changed files with 1362 additions and 4 deletions
|
@ -36,6 +36,7 @@ z3_add_component(ast
|
|||
occurs.cpp
|
||||
pb_decl_plugin.cpp
|
||||
pp.cpp
|
||||
recfun_decl_plugin.cpp
|
||||
reg_decl_plugins.cpp
|
||||
seq_decl_plugin.cpp
|
||||
shared_occs.cpp
|
||||
|
|
|
@ -32,5 +32,27 @@ struct mk_pp : public mk_ismt2_pp {
|
|||
}
|
||||
};
|
||||
|
||||
//<! print vector of ASTs
|
||||
class mk_pp_vec {
|
||||
ast_manager & m;
|
||||
ast_ref_vector vec;
|
||||
public:
|
||||
mk_pp_vec(unsigned len, ast ** vec0, ast_manager & m) : m(m), vec(m) {
|
||||
for (unsigned i=0; i<len; ++i) vec.push_back(vec0[i]);
|
||||
}
|
||||
void display(std::ostream & out) const {
|
||||
bool first = true;
|
||||
for (ast* e : vec) {
|
||||
if (first) { first = false; } else { out << " "; }
|
||||
out << mk_pp(e, m);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream & out, mk_pp_vec const & pp) {
|
||||
pp.display(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
469
src/ast/recfun_decl_plugin.cpp
Normal file
469
src/ast/recfun_decl_plugin.cpp
Normal file
|
@ -0,0 +1,469 @@
|
|||
/*++
|
||||
Module Name:
|
||||
|
||||
recfun_decl_plugin.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Declaration and definition of (potentially recursive) functions
|
||||
|
||||
Author:
|
||||
|
||||
Simon Cruanes 2017-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include "ast/expr_functors.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "ast/recfun_decl_plugin.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "util/scoped_ptr_vector.h"
|
||||
|
||||
#define DEBUG(x) do { auto& out = std::cout; out << "recfun: "; x; out << '\n' << std::flush; } while(0)
|
||||
#define VALIDATE_PARAM(m, _pred_) if (!(_pred_)) m.raise_exception("invalid parameter to recfun " #_pred_);
|
||||
|
||||
|
||||
namespace recfun {
|
||||
case_pred::case_pred(ast_manager & m, family_id fid, std::string const & s, sort_ref_vector const & domain)
|
||||
: m_name(), m_name_buf(s), m_domain(domain), m_decl(m)
|
||||
{
|
||||
m_name = symbol(m_name_buf.c_str());
|
||||
func_decl_info info(fid, OP_FUN_CASE_PRED);
|
||||
m_decl = m.mk_func_decl(m_name, domain.size(), domain.c_ptr(), m.mk_bool_sort(), info);
|
||||
}
|
||||
|
||||
case_def::case_def(ast_manager &m,
|
||||
family_id fid,
|
||||
def * d,
|
||||
std::string & name,
|
||||
sort_ref_vector const & arg_sorts,
|
||||
unsigned num_guards, expr ** guards, expr* rhs)
|
||||
: m_pred(m, fid, name, arg_sorts), m_guards(m), m_rhs(expr_ref(rhs,m)), m_def(d) {
|
||||
for (unsigned i=0; i<num_guards; ++i) {
|
||||
m_guards.push_back(expr_ref(guards[i], m));
|
||||
}
|
||||
}
|
||||
|
||||
def::def(ast_manager &m, family_id fid, symbol const & s,
|
||||
unsigned arity, sort *const * domain, sort* range)
|
||||
: m_manager(m), m_name(s),
|
||||
m_domain(m), m_range(range, m), m_vars(m), m_cases(),
|
||||
m_decl(m), m_fid(fid), m_macro(false)
|
||||
{
|
||||
for (unsigned i=0; i < arity; ++i)
|
||||
m_domain.push_back(domain[i]);
|
||||
|
||||
SASSERT(arity == get_arity());
|
||||
|
||||
func_decl_info info(fid, OP_FUN_DEFINED);
|
||||
m_decl = m.mk_func_decl(m_name, arity, domain, range, info);
|
||||
}
|
||||
|
||||
// does `e` contain any `ite` construct?
|
||||
bool def::contains_ite(expr * e) {
|
||||
struct ite_find_p : public i_expr_pred {
|
||||
ast_manager & m;
|
||||
ite_find_p(ast_manager & m) : m(m) {}
|
||||
virtual bool operator()(expr * e) { return m.is_ite(e); }
|
||||
};
|
||||
ite_find_p p(m());
|
||||
check_pred cp(p, m());
|
||||
return cp(e);
|
||||
}
|
||||
|
||||
/*
|
||||
* compilation of functions to a list of cases.
|
||||
*
|
||||
* We use a backtracking algorithm in a relatively functional style,
|
||||
* where the multiple states (corresponding to alternatives) are stored in
|
||||
* a region, and deallocated at the end
|
||||
*/
|
||||
|
||||
// immutable list of choices of `ite` terms (mapping each one's condition to true/false)
|
||||
struct choice_lst {
|
||||
app * ite;
|
||||
bool sign;
|
||||
choice_lst const * next; // or null for the last one
|
||||
choice_lst(app * ite, bool sign, choice_lst const * next) : ite(ite), sign(sign), next(next) {}
|
||||
};
|
||||
|
||||
struct ite_lst {
|
||||
app * ite; // invariant: `is_ite(e)`
|
||||
ite_lst const * next;
|
||||
ite_lst(app * ite, ite_lst const * next) : ite(ite), next(next) {}
|
||||
};
|
||||
|
||||
// immutable stack of expressions to unfold
|
||||
struct unfold_lst {
|
||||
expr * e;
|
||||
unfold_lst const * next; // or null for last one
|
||||
};
|
||||
|
||||
// main state for one branch of the search tree.
|
||||
struct branch {
|
||||
choice_lst const * path; // choices made so far
|
||||
ite_lst const * to_split; // `ite` terms to make a choice on
|
||||
unfold_lst const * to_unfold; // terms yet to unfold
|
||||
|
||||
branch(choice_lst const * path, ite_lst const * to_split, unfold_lst const * to_unfold) : path(path), to_split(to_split), to_unfold(to_unfold) {}
|
||||
branch(branch const & from) : path(from.path), to_split(from.to_split), to_unfold(from.to_unfold) {}
|
||||
};
|
||||
|
||||
// state for computing cases from the RHS of a functions' definition
|
||||
class case_state {
|
||||
region m_reg;
|
||||
ast_manager & m_manager;
|
||||
vector<branch> m_branches;
|
||||
|
||||
public:
|
||||
case_state(ast_manager & m) : m_reg(), m_manager(m), m_branches() {}
|
||||
|
||||
bool empty() const { return m_branches.empty(); }
|
||||
ast_manager & m() const { return m_manager; }
|
||||
region & reg() { return m_reg; }
|
||||
|
||||
branch pop_branch() {
|
||||
branch res = m_branches.back();
|
||||
m_branches.pop_back();
|
||||
return res;
|
||||
}
|
||||
|
||||
void push_branch(branch const & b) { m_branches.push_back(b); }
|
||||
|
||||
|
||||
unfold_lst const * cons_unfold(expr * e, unfold_lst const * next) {
|
||||
return new (reg()) unfold_lst{e, next};
|
||||
}
|
||||
unfold_lst const * cons_unfold(expr * e1, expr * e2, unfold_lst const * next) {
|
||||
return cons_unfold(e1, cons_unfold(e2, next));
|
||||
}
|
||||
unfold_lst const * mk_unfold_lst(expr * e) {
|
||||
return cons_unfold(e, nullptr);
|
||||
}
|
||||
|
||||
ite_lst const * cons_ite(app * ite, ite_lst const * next) {
|
||||
return new (reg()) ite_lst{ite, next};
|
||||
}
|
||||
|
||||
choice_lst const * cons_choice(app * ite, bool sign, choice_lst const * next) {
|
||||
return new (reg()) choice_lst{ite, sign, next};
|
||||
}
|
||||
};
|
||||
|
||||
//<! build a substitution and a list of conditions from a path
|
||||
void convert_path(ast_manager & m,
|
||||
choice_lst const * choices,
|
||||
expr_ref_vector & conditions /* out */,
|
||||
expr_substitution & subst /* out */)
|
||||
{
|
||||
for (; choices != nullptr; choices = choices->next) {
|
||||
app * ite = choices->ite;
|
||||
SASSERT(m.is_ite(ite));
|
||||
|
||||
// condition to add to the guard
|
||||
expr * cond0 = ite->get_arg(0);
|
||||
conditions.push_back(choices->sign ? cond0 : m.mk_not(cond0));
|
||||
|
||||
// binding to add to the substitution
|
||||
subst.insert(ite, choices->sign ? ite->get_arg(1) : ite->get_arg(2));
|
||||
}
|
||||
}
|
||||
|
||||
// substitute `subst` in `e`
|
||||
expr_ref replace_subst(th_rewriter & th_rw, ast_manager & m,
|
||||
expr_substitution & subst, expr * e) {
|
||||
th_rw.reset();
|
||||
th_rw.set_substitution(&subst);
|
||||
expr_ref res(m);
|
||||
th_rw(e, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void def::add_case(std::string & name, unsigned n_conditions, expr ** conditions, expr * rhs, bool is_imm) {
|
||||
case_def c(m(), m_fid, this, name, get_domain(), n_conditions, conditions, rhs);
|
||||
c.set_is_immediate(is_imm);
|
||||
TRACE("recfun", tout << "add_case " << name << " " << mk_pp(rhs, m())
|
||||
<< " :is_imm " << is_imm
|
||||
<< " :guards " << mk_pp_vec(n_conditions, (ast**)conditions, m()););
|
||||
DEBUG(out << "add_case " << name << " " << mk_pp(rhs, m())
|
||||
<< " :is_imm " << is_imm
|
||||
<< " :guards " << mk_pp_vec(n_conditions, (ast**)conditions, m()));
|
||||
m_cases.push_back(c);
|
||||
}
|
||||
|
||||
|
||||
// Compute a set of cases, given the RHS
|
||||
void def::compute_cases(is_immediate_pred & is_i, th_rewriter & th_rw,
|
||||
unsigned n_vars, var *const * vars, expr* rhs0)
|
||||
{
|
||||
if (m_cases.size() != 0) {
|
||||
TRACE("recfun", tout << "bug: cases for " << m_name << " has cases already";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
SASSERT(n_vars = m_domain.size());
|
||||
|
||||
DEBUG(out << "compute cases " << mk_pp(rhs0, m()));
|
||||
|
||||
unsigned case_idx = 0;
|
||||
std::string name;
|
||||
|
||||
name.append("case_");
|
||||
name.append(m_name.bare_str());
|
||||
name.append("_");
|
||||
|
||||
for (unsigned i=0; i<n_vars; ++i)
|
||||
m_vars.push_back(vars[i]);
|
||||
|
||||
#if 0
|
||||
// simplify `rhs`
|
||||
expr_ref simplified_rhs(m());
|
||||
expr* rhs;
|
||||
th_rw.reset();
|
||||
th_rw(rhs0, simplified_rhs);
|
||||
rhs = simplified_rhs.get();
|
||||
|
||||
DEBUG(out << "simplified into " << mk_pp(rhs, m()));
|
||||
#else
|
||||
expr* rhs = rhs0;
|
||||
#endif
|
||||
|
||||
// is the function a macro (unconditional body)?
|
||||
m_macro = n_vars == 0 || !contains_ite(rhs);
|
||||
|
||||
if (m_macro) {
|
||||
// constant function or trivial control flow, only one (dummy) case
|
||||
name.append("dummy");
|
||||
add_case(name, 0, 0, rhs);
|
||||
return;
|
||||
}
|
||||
|
||||
// analyze control flow of `rhs`, accumulating guards and
|
||||
// rebuilding a `ite`-free RHS on the fly for each path in `rhs`.
|
||||
// Each such `ite`-free term is converted into a case_def and added to definition.
|
||||
|
||||
case_state st(m());
|
||||
{
|
||||
branch b(nullptr, nullptr, st.mk_unfold_lst(rhs));
|
||||
st.push_branch(b);
|
||||
}
|
||||
|
||||
while (! st.empty()) {
|
||||
DEBUG(out << "main loop iter");
|
||||
|
||||
branch b = st.pop_branch();
|
||||
|
||||
// first: unfold expressions, stopping when we meet subterms that are `ite`
|
||||
while (b.to_unfold != nullptr) {
|
||||
|
||||
ptr_vector<expr> stack;
|
||||
stack.push_back(b.to_unfold->e);
|
||||
|
||||
b.to_unfold = b.to_unfold->next;
|
||||
|
||||
while (! stack.empty()) {
|
||||
expr * e = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
if (m().is_ite(e)) {
|
||||
// need to do a case split on `e`, forking the search space
|
||||
b.to_split = st.cons_ite(to_app(e), b.to_split);
|
||||
} else if (is_app(e)) {
|
||||
// explore arguments
|
||||
app * a = to_app(e);
|
||||
|
||||
for (unsigned i=0; i < a->get_num_args(); ++i)
|
||||
stack.push_back(a->get_arg(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (b.to_split != nullptr) {
|
||||
// split one `ite`, which will lead to distinct (sets of) cases
|
||||
app * ite = b.to_split->ite;
|
||||
SASSERT(m().is_ite(ite));
|
||||
|
||||
/* explore both positive choice and negative choice.
|
||||
* each contains a longer path, with `ite` mapping to `true` (resp. `false),
|
||||
* and must unfold the `then` (resp. `else`) branch.
|
||||
* We must also unfold the test itself, for it could contain
|
||||
* tests.
|
||||
*/
|
||||
|
||||
branch b_pos(st.cons_choice(ite, true, b.path),
|
||||
b.to_split->next,
|
||||
st.cons_unfold(ite->get_arg(0), ite->get_arg(1), b.to_unfold));
|
||||
branch b_neg(st.cons_choice(ite, false, b.path),
|
||||
b.to_split->next,
|
||||
st.cons_unfold(ite->get_arg(0), ite->get_arg(2), b.to_unfold));
|
||||
|
||||
st.push_branch(b_neg);
|
||||
st.push_branch(b_pos);
|
||||
}
|
||||
else {
|
||||
// leaf of the search tree
|
||||
|
||||
expr_ref_vector conditions_raw(m());
|
||||
expr_substitution subst(m());
|
||||
convert_path(m(), b.path, conditions_raw, subst);
|
||||
|
||||
// substitute, to get rid of `ite` terms
|
||||
expr_ref case_rhs = replace_subst(th_rw, m(), subst, rhs);
|
||||
expr_ref_vector conditions(m());
|
||||
for (expr * g : conditions_raw) {
|
||||
expr_ref g_subst(replace_subst(th_rw, m(), subst, g), m());
|
||||
conditions.push_back(g_subst);
|
||||
}
|
||||
|
||||
|
||||
unsigned old_name_len = name.size();
|
||||
{ // TODO: optimize? this does many copies
|
||||
std::ostringstream sout;
|
||||
sout << ((unsigned long) case_idx);
|
||||
name.append(sout.str());
|
||||
}
|
||||
case_idx ++;
|
||||
|
||||
// yield new case
|
||||
bool is_imm = is_i(case_rhs);
|
||||
add_case(name, conditions.size(), conditions.c_ptr(), case_rhs, is_imm);
|
||||
name.resize(old_name_len);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("recfun", tout << "done analysing " << get_name(););
|
||||
}
|
||||
|
||||
/*
|
||||
* Main manager for defined functions
|
||||
*/
|
||||
|
||||
util::util(ast_manager & m, family_id id)
|
||||
: m_manager(m), m_family_id(id), m_th_rw(m), m_plugin(0) {
|
||||
m_plugin = dynamic_cast<decl::plugin*>(m.get_plugin(m_family_id));
|
||||
}
|
||||
|
||||
def * util::decl_fun(symbol const& name, unsigned n, sort *const * domain, sort * range) {
|
||||
return alloc(def, m(), m_family_id, name, n, domain, range);
|
||||
}
|
||||
|
||||
void util::set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs) {
|
||||
d.set_definition(n_vars, vars, rhs);
|
||||
}
|
||||
|
||||
|
||||
// used to know which `app` are from this theory
|
||||
struct is_imm_pred : is_immediate_pred {
|
||||
util & u;
|
||||
is_imm_pred(util & u) : u(u) {}
|
||||
bool operator()(expr * rhs) {
|
||||
// find an `app` that is an application of a defined function
|
||||
struct find : public i_expr_pred {
|
||||
util & u;
|
||||
find(util & u) : u(u) {}
|
||||
bool operator()(expr * e) override {
|
||||
//return is_app(e) ? u.owns_app(to_app(e)) : false;
|
||||
if (! is_app(e)) return false;
|
||||
|
||||
app * a = to_app(e);
|
||||
return u.is_defined(a);
|
||||
}
|
||||
};
|
||||
find f(u);
|
||||
check_pred cp(f, u.m());
|
||||
bool contains_defined_fun = cp(rhs);
|
||||
return ! contains_defined_fun;
|
||||
}
|
||||
};
|
||||
|
||||
// set definition
|
||||
void promise_def::set_definition(unsigned n_vars, var * const * vars, expr * rhs) {
|
||||
SASSERT(n_vars == d->get_arity());
|
||||
|
||||
is_imm_pred is_i(*u);
|
||||
d->compute_cases(is_i, u->get_th_rewriter(), n_vars, vars, rhs);
|
||||
}
|
||||
|
||||
namespace decl {
|
||||
plugin::plugin() : decl_plugin(), m_defs(), m_case_defs(), m_def_block() {}
|
||||
plugin::~plugin() { finalize(); }
|
||||
|
||||
void plugin::finalize() {
|
||||
for (auto& kv : m_defs) {
|
||||
dealloc(kv.m_value);
|
||||
}
|
||||
m_defs.reset();
|
||||
// m_case_defs does not own its data, no need to deallocate
|
||||
m_case_defs.reset();
|
||||
m_util = 0; // force deletion
|
||||
}
|
||||
|
||||
util & plugin::u() const {
|
||||
SASSERT(m_manager);
|
||||
SASSERT(m_family_id != null_family_id);
|
||||
if (m_util.get() == 0) {
|
||||
m_util = alloc(util, *m_manager, m_family_id);
|
||||
}
|
||||
return *(m_util.get());
|
||||
}
|
||||
|
||||
promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range) {
|
||||
SASSERT(! m_defs.contains(name));
|
||||
def* d = u().decl_fun(name, n, params, range);
|
||||
m_defs.insert(name, d);
|
||||
return promise_def(&u(), d);
|
||||
}
|
||||
|
||||
void plugin::set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs) {
|
||||
u().set_definition(d, n_vars, vars, rhs);
|
||||
for (case_def & c : d.get_def()->get_cases()) {
|
||||
m_case_defs.insert(c.get_name(), &c);
|
||||
}
|
||||
}
|
||||
|
||||
def* plugin::mk_def(symbol const& name, unsigned n, sort ** params, sort * range,
|
||||
unsigned n_vars, var ** vars, expr * rhs) {
|
||||
SASSERT(! m_defs.contains(name));
|
||||
promise_def d = mk_def(name, n, params, range);
|
||||
set_definition(d, n_vars, vars, rhs);
|
||||
return d.get_def();
|
||||
}
|
||||
|
||||
func_decl * plugin::mk_fun_pred_decl(unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range)
|
||||
{
|
||||
VALIDATE_PARAM(m(), m().is_bool(range) && num_parameters == 1 && parameters[0].is_ast());
|
||||
func_decl_info info(m_family_id, OP_FUN_CASE_PRED, num_parameters, parameters);
|
||||
info.m_private_parameters = true;
|
||||
return m().mk_func_decl(symbol(parameters[0].get_symbol()), arity, domain, range, info);
|
||||
}
|
||||
|
||||
func_decl * plugin::mk_fun_defined_decl(decl_kind k, unsigned num_parameters,
|
||||
parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range)
|
||||
{
|
||||
VALIDATE_PARAM(m(), num_parameters == 1 && parameters[0].is_ast());
|
||||
func_decl_info info(m_family_id, k, num_parameters, parameters);
|
||||
info.m_private_parameters = true;
|
||||
return m().mk_func_decl(symbol(parameters[0].get_symbol()), arity,
|
||||
domain, range, info);
|
||||
}
|
||||
|
||||
// generic declaration of symbols
|
||||
func_decl * plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range)
|
||||
{
|
||||
switch(k) {
|
||||
case OP_FUN_CASE_PRED:
|
||||
return mk_fun_pred_decl(num_parameters, parameters, arity, domain, range);
|
||||
case OP_FUN_DEFINED:
|
||||
return mk_fun_defined_decl(k, num_parameters, parameters, arity, domain, range);
|
||||
default:
|
||||
UNREACHABLE(); return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
246
src/ast/recfun_decl_plugin.h
Normal file
246
src/ast/recfun_decl_plugin.h
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*++
|
||||
Module Name:
|
||||
|
||||
recfun_decl_plugin.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Declaration and definition of (potentially recursive) functions
|
||||
|
||||
Author:
|
||||
|
||||
Simon Cruanes 2017-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
|
||||
namespace recfun {
|
||||
class case_def; //<! one possible control path of a function
|
||||
class case_pred; //<! a predicate guarding a given control flow path of a function
|
||||
class util; //<! util for other modules
|
||||
class def; //!< definition of a (recursive) function
|
||||
class promise_def; //!< definition to be complete
|
||||
|
||||
enum op_kind {
|
||||
OP_FUN_DEFINED, // defined function with one or more cases, possibly recursive
|
||||
OP_FUN_CASE_PRED, // predicate guarding a given control flow path
|
||||
};
|
||||
|
||||
/*! A predicate `p(t1…tn)`, that, if true, means `f(t1…tn)` is following
|
||||
a given path of its control flow and can be unrolled.
|
||||
|
||||
For example, `fact n := if n<2 then 1 else n * fact(n-1)` would have two cases,
|
||||
and therefore two case predicates `C_fact_0` and `C_fact_1`, where
|
||||
`C_fact_0(t)=true` means `t<2` (first path) and `C_fact_1(t)=true` means `¬(t<2)` (second path).
|
||||
*/
|
||||
class case_pred {
|
||||
friend class case_def;
|
||||
symbol m_name; //<! symbol for the predicate
|
||||
std::string m_name_buf; //<! memory for m_name
|
||||
sort_ref_vector const & m_domain;
|
||||
func_decl_ref m_decl; //<! declaration for the predicate
|
||||
|
||||
case_pred(ast_manager & m, family_id fid, std::string const & s, sort_ref_vector const & args);
|
||||
public:
|
||||
symbol const & get_name() const { return m_name; }
|
||||
sort_ref_vector const & get_domain() const { return m_domain; }
|
||||
func_decl * get_decl() const { return m_decl.get(); }
|
||||
unsigned get_arity() const { return m_domain.size(); }
|
||||
};
|
||||
|
||||
typedef var_ref_vector vars;
|
||||
|
||||
class case_def {
|
||||
friend class def;
|
||||
case_pred m_pred; //<! predicate used for this case
|
||||
expr_ref_vector m_guards; //<! conjunction that is equivalent to this case
|
||||
expr_ref m_rhs; //<! if guard is true, `f(t1…tn) = rhs` holds
|
||||
def * m_def; //<! definition this is a part of
|
||||
bool m_immediate; //<! does `rhs` contain no defined_fun/case_pred?
|
||||
|
||||
case_def(ast_manager & m,
|
||||
family_id fid,
|
||||
def * d,
|
||||
std::string & name,
|
||||
sort_ref_vector const & arg_sorts,
|
||||
unsigned num_guards,
|
||||
expr** guards,
|
||||
expr* rhs);
|
||||
|
||||
void add_guard(expr_ref && e) { m_guards.push_back(e); }
|
||||
public:
|
||||
symbol const& get_name() const { return m_pred.get_name(); }
|
||||
case_pred const & get_pred() const { return m_pred; }
|
||||
def * get_def() const { return m_def; }
|
||||
expr_ref_vector const & get_guards() const { return m_guards; }
|
||||
expr * get_guards_c_ptr() const { return *m_guards.c_ptr(); }
|
||||
expr * get_guard(unsigned i) const { return m_guards[i]; }
|
||||
expr * get_rhs() const { return m_rhs; }
|
||||
unsigned num_guards() const { return m_guards.size(); }
|
||||
bool is_immediate() const { return m_immediate; };
|
||||
void set_is_immediate(bool b) { m_immediate = b; }
|
||||
};
|
||||
|
||||
// closure for computing whether a `rhs` expression is immediate
|
||||
struct is_immediate_pred {
|
||||
virtual bool operator()(expr * rhs) = 0;
|
||||
};
|
||||
|
||||
class def {
|
||||
friend class util;
|
||||
friend class promise_def;
|
||||
typedef vector<case_def> cases;
|
||||
|
||||
ast_manager & m_manager;
|
||||
symbol m_name; //<! name of function
|
||||
sort_ref_vector m_domain; //<! type of arguments
|
||||
sort_ref m_range; //<! return type
|
||||
vars m_vars; //<! variables of the function
|
||||
cases m_cases; //!< possible cases
|
||||
func_decl_ref m_decl; //!< generic declaration
|
||||
family_id m_fid;
|
||||
bool m_macro;
|
||||
|
||||
def(ast_manager &m, family_id fid, symbol const & s, unsigned arity, sort *const * domain, sort* range);
|
||||
|
||||
// compute cases for a function, given its RHS (possibly containing `ite`).
|
||||
void compute_cases(is_immediate_pred &, th_rewriter & th_rw,
|
||||
unsigned n_vars, var *const * vars, expr* rhs);
|
||||
void add_case(std::string & name, unsigned n_conds, expr ** conditions, expr* rhs, bool is_imm = false);
|
||||
bool contains_ite(expr* e); // expression contains a test?
|
||||
public:
|
||||
ast_manager & m() const { return m_manager; }
|
||||
symbol const & get_name() const { return m_name; }
|
||||
vars const & get_vars() const { return m_vars; }
|
||||
cases & get_cases() { return m_cases; }
|
||||
unsigned get_arity() const { return m_domain.size(); }
|
||||
sort_ref_vector const & get_domain() const { return m_domain; }
|
||||
sort_ref const & get_range() const { return m_range; }
|
||||
func_decl * get_decl() const { return m_decl.get(); }
|
||||
|
||||
bool is_fun_macro() const { return m_macro; }
|
||||
bool is_fun_defined() const { return !is_fun_macro(); }
|
||||
|
||||
expr * get_macro_rhs() const {
|
||||
SASSERT(is_fun_macro());
|
||||
return m_cases[0].get_rhs();
|
||||
}
|
||||
};
|
||||
|
||||
// definition to be complete (missing RHS)
|
||||
class promise_def {
|
||||
friend class util;
|
||||
util * u;
|
||||
def * d;
|
||||
void set_definition(unsigned n_vars, var * const * vars, expr * rhs); // call only once
|
||||
public:
|
||||
promise_def(util * u, def * d) : u(u), d(d) {}
|
||||
promise_def(promise_def const & from) : u(from.u), d(from.d) {}
|
||||
def * get_def() const { return d; }
|
||||
};
|
||||
|
||||
namespace decl {
|
||||
|
||||
class plugin : public decl_plugin {
|
||||
typedef map<symbol, def*, symbol_hash_proc, symbol_eq_proc> def_map;
|
||||
typedef map<symbol, case_def*, symbol_hash_proc, symbol_eq_proc> case_def_map;
|
||||
|
||||
mutable scoped_ptr<util> m_util;
|
||||
def_map m_defs; // function->def
|
||||
case_def_map m_case_defs; // case_pred->def
|
||||
svector<symbol> m_def_block;
|
||||
|
||||
ast_manager & m() { return *m_manager; }
|
||||
public:
|
||||
plugin();
|
||||
virtual ~plugin() override;
|
||||
virtual void finalize() override;
|
||||
|
||||
util & u() const; // build or return util
|
||||
|
||||
virtual bool is_fully_interp(sort * s) const override { return false; } // might depend on unin sorts
|
||||
|
||||
virtual decl_plugin * mk_fresh() override { return alloc(plugin); }
|
||||
|
||||
virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override { UNREACHABLE(); return 0; }
|
||||
|
||||
virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range) override;
|
||||
|
||||
promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range);
|
||||
|
||||
void set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs);
|
||||
|
||||
def* mk_def(symbol const& name, unsigned n, sort ** params, sort * range, unsigned n_vars, var ** vars, expr * rhs);
|
||||
|
||||
bool has_def(const symbol& s) const { return m_defs.contains(s); }
|
||||
def const& get_def(const symbol& s) const { return *(m_defs[s]); }
|
||||
promise_def get_promise_def(const symbol &s) const { return promise_def(&u(), m_defs[s]); }
|
||||
def& get_def(symbol const& s) { return *(m_defs[s]); }
|
||||
bool has_case_def(const symbol& s) const { return m_case_defs.contains(s); }
|
||||
case_def& get_case_def(symbol const& s) { SASSERT(has_case_def(s)); return *(m_case_defs[s]); }
|
||||
bool is_declared(symbol const& s) const { return m_defs.contains(s); }
|
||||
private:
|
||||
func_decl * mk_fun_pred_decl(unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range);
|
||||
func_decl * mk_fun_defined_decl(decl_kind k,
|
||||
unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range);
|
||||
};
|
||||
}
|
||||
|
||||
// Varus utils for recursive functions
|
||||
class util {
|
||||
friend class decl::plugin;
|
||||
|
||||
ast_manager & m_manager;
|
||||
family_id m_family_id;
|
||||
th_rewriter m_th_rw;
|
||||
decl::plugin * m_plugin;
|
||||
|
||||
bool compute_is_immediate(expr * rhs);
|
||||
void set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs);
|
||||
public:
|
||||
util(ast_manager &m, family_id);
|
||||
|
||||
ast_manager & m() { return m_manager; }
|
||||
th_rewriter & get_th_rewriter() { return m_th_rw; }
|
||||
bool is_case_pred(app * e) const { return is_app_of(e, m_family_id, OP_FUN_CASE_PRED); }
|
||||
bool is_defined(app * e) const { return is_app_of(e, m_family_id, OP_FUN_DEFINED); }
|
||||
bool owns_app(app * e) const { return e->get_family_id() == m_family_id; }
|
||||
|
||||
//<! add a function declaration
|
||||
def * decl_fun(symbol const & s, unsigned n_args, sort *const * args, sort * range);
|
||||
|
||||
def& get_def(symbol const & s) {
|
||||
SASSERT(m_plugin->has_def(s));
|
||||
return m_plugin->get_def(s);
|
||||
}
|
||||
|
||||
case_def& get_case_def(symbol const & s) {
|
||||
SASSERT(m_plugin->has_case_def(s));
|
||||
return m_plugin->get_case_def(s);
|
||||
}
|
||||
|
||||
app* mk_fun_defined(def const & d, unsigned n_args, expr * const * args) {
|
||||
return m().mk_app(d.get_decl(), n_args, args);
|
||||
}
|
||||
app* mk_fun_defined(def const & d, ptr_vector<expr> const & args) {
|
||||
return mk_fun_defined(d, args.size(), args.c_ptr());
|
||||
}
|
||||
app* mk_case_pred(case_pred const & p, ptr_vector<expr> const & args) {
|
||||
return m().mk_app(p.get_decl(), args.size(), args.c_ptr());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
typedef recfun::def recfun_def;
|
||||
typedef recfun::case_def recfun_case_def;
|
||||
typedef recfun::decl::plugin recfun_decl_plugin;
|
||||
typedef recfun::util recfun_util;
|
|
@ -22,6 +22,7 @@ Revision History:
|
|||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/recfun_decl_plugin.h"
|
||||
#include "ast/dl_decl_plugin.h"
|
||||
#include "ast/seq_decl_plugin.h"
|
||||
#include "ast/pb_decl_plugin.h"
|
||||
|
@ -40,6 +41,9 @@ void reg_decl_plugins(ast_manager & m) {
|
|||
if (!m.get_plugin(m.mk_family_id(symbol("datatype")))) {
|
||||
m.register_plugin(symbol("datatype"), alloc(datatype_decl_plugin));
|
||||
}
|
||||
if (!m.get_plugin(m.mk_family_id(symbol("recfun")))) {
|
||||
m.register_plugin(symbol("recfun"), alloc(recfun_decl_plugin));
|
||||
}
|
||||
if (!m.get_plugin(m.mk_family_id(symbol("datalog_relation")))) {
|
||||
m.register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue