3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-01-28 21:08:43 +00:00

Subterms Theory (#8115)

* somewhaat failed attempt at declaring subterm predicate

I can't really figure out how to link the smt parser to the rest of the
machinenery, so I will stop here and try from the other side. I'll start
implmenting the logic and see if it brings me back to the parser.

* initial logic implmentation

Very primitive, but I don't like have that much work uncommitted.

* parser implementation

* more theory

* Working base

* subterm reflexivity

* a few optimization

Skip adding obvious equalities or disequality

* removed some optimisations

* better handling of backtracking

* stupid segfault

Add m_subterm to the trail

* Update src/smt/theory_datatype.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/ast/rewriter/datatype_rewriter.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/smt/theory_datatype.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/smt/theory_datatype.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/smt/theory_datatype.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* review

* forgot to update `iterate_subterm`'s signature

* fix iterator segfault

* Remove duplicate include statement

Removed duplicate include of 'theory_datatype.h'.

* Replace 'optional' with 'std::option' in datatype_decl_plugin.h

* Add is_subterm_predicate matcher to datatype_decl_plugin

* Change std::option to std::optional for m_subterm

* Update pdecl.h

* Change has_subterm to use has_value method

* Update pdecl.cpp

---------

Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Simon Jeanteur 2026-01-13 19:53:17 +01:00 committed by GitHub
parent 7377d28c30
commit deaced1711
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 563 additions and 32 deletions

View file

@ -57,6 +57,23 @@ namespace datatype {
return alloc(accessor, tr.to(), name(), to_sort(tr(m_range.get())));
}
def const& subterm::get_def() const { return *m_def; }
util& subterm::u() const { return m_def->u(); }
func_decl_ref subterm::instantiate(sort_ref_vector const& ps) const {
ast_manager& m = ps.get_manager();
sort_ref dt_sort = get_def().instantiate(ps);
sort* domain[2] = { dt_sort, dt_sort };
sort_ref range(m.mk_bool_sort(), m);
parameter p(name());
return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_SUBTERM, 1, &p, 2, domain, range), m);
}
func_decl_ref subterm::instantiate(sort* dt) const {
sort_ref_vector sorts = get_def().u().datatype_params(dt);
return instantiate(sorts);
}
constructor::~constructor() {
for (accessor* a : m_accessors) dealloc(a);
m_accessors.reset();
@ -235,6 +252,7 @@ namespace datatype {
void plugin::reset() {
m_datatype2constructors.reset();
m_datatype2subterm.reset();
m_datatype2nonrec_constructor.reset();
m_constructor2accessors.reset();
m_constructor2recognizer.reset();
@ -443,6 +461,18 @@ namespace datatype {
return m.mk_func_decl(name, arity, domain, range, info);
}
func_decl * decl::plugin::mk_subterm(unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort* range)
{
ast_manager& m = *m_manager;
VALIDATE_PARAM(num_parameters == 1 && parameters[0].is_symbol());
VALIDATE_PARAM(arity == 2 && u().is_datatype(domain[0]) && domain[0] == domain[1] && m.is_bool(range));
func_decl_info info(m_family_id, OP_DT_SUBTERM, num_parameters, parameters);
info.m_private_parameters = true;
symbol name = parameters[0].get_symbol();
return m.mk_func_decl(name, arity, domain, range, info);
}
func_decl * decl::plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) {
switch (k) {
@ -453,7 +483,9 @@ namespace datatype {
case OP_DT_IS:
return mk_is(num_parameters, parameters, arity, domain, range);
case OP_DT_ACCESSOR:
return mk_accessor(num_parameters, parameters, arity, domain, range);
return mk_accessor(num_parameters, parameters, arity, domain, range);
case OP_DT_SUBTERM:
return mk_subterm(num_parameters, parameters, arity, domain, range);
case OP_DT_UPDATE_FIELD:
return mk_update_field(num_parameters, parameters, arity, domain, range);
default:
@ -1040,6 +1072,22 @@ namespace datatype {
return m_family_id;
}
func_decl * util::get_datatype_subterm(sort * ty) {
SASSERT(is_datatype(ty));
func_decl * r = nullptr;
if (plugin().m_datatype2subterm.find(ty, r))
return r;
def const& d = get_def(ty);
if (d.has_subterm()) {
func_decl_ref f = d.get_subterm().instantiate(ty);
r = f;
plugin().add_ast(r);
plugin().m_datatype2subterm.insert(ty, r);
}
return r;
}
ptr_vector<func_decl> const * util::get_datatype_constructors(sort * ty) {
SASSERT(is_datatype(ty));
ptr_vector<func_decl> * r = nullptr;
@ -1482,11 +1530,14 @@ namespace datatype {
}
datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs) {
datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs, symbol const& subterm_name) {
datatype::decl::plugin& p = u.plugin();
datatype::def* d = p.mk(n, num_params, params);
for (unsigned i = 0; i < num_constructors; ++i) {
d->add(cs[i]);
}
if (subterm_name != symbol::null) {
d->attach_subterm(subterm_name, u.get_manager().mk_bool_sort());
}
return d;
}

View file

@ -39,6 +39,7 @@ enum op_kind {
OP_DT_IS,
OP_DT_ACCESSOR,
OP_DT_UPDATE_FIELD,
OP_DT_SUBTERM,
LAST_DT_OP
};
@ -48,6 +49,22 @@ namespace datatype {
class def;
class accessor;
class constructor;
class subterm;
class subterm {
symbol m_name;
sort_ref m_range;
def* m_def = nullptr;
public:
subterm(ast_manager& m, symbol const& n, sort* r) : m_name(n), m_range(r, m) {}
sort* range() const { return m_range; }
symbol const& name() const { return m_name; }
func_decl_ref instantiate(sort_ref_vector const& ps) const;
func_decl_ref instantiate(sort* dt) const;
util& u() const;
void attach(def* d) { m_def = d; }
def const& get_def() const;
};
class accessor {
@ -166,6 +183,7 @@ namespace datatype {
mutable sort_ref m_sort;
ptr_vector<constructor> m_constructors;
mutable dictionary<constructor*> m_name2constructor;
std::optional<subterm> m_subterm;
public:
def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params):
m(m),
@ -185,6 +203,10 @@ namespace datatype {
m_constructors.push_back(c);
c->attach(this);
}
void attach_subterm(symbol const& n, sort* range) {
m_subterm = subterm(m, n, range);
m_subterm->attach(this);
}
symbol const& name() const { return m_name; }
unsigned id() const { return m_class_id; }
sort_ref instantiate(sort_ref_vector const& ps) const;
@ -222,6 +244,8 @@ namespace datatype {
SASSERT(result); // Post-condition: get_constructor_by_name returns a non-null result
return result;
}
bool has_subterm() const { return m_subterm.has_value(); }
subterm const& get_subterm() const { return *m_subterm; }
def* translate(ast_translation& tr, util& u);
};
@ -293,6 +317,7 @@ namespace datatype {
obj_map<sort, ptr_vector<func_decl>*> m_datatype2constructors;
obj_map<sort, func_decl*> m_datatype2subterm;
obj_map<sort, cnstr_depth> m_datatype2nonrec_constructor;
obj_map<func_decl, ptr_vector<func_decl>*> m_constructor2accessors;
obj_map<func_decl, func_decl*> m_constructor2recognizer;
@ -324,6 +349,16 @@ namespace datatype {
unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range);
/**
* \brief declares a subterm predicate
*
* Subterms have the signature `sort -> sort -> bool` and are only
* supported for non-mutually recursive datatypes
*/
func_decl * mk_subterm(
unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range);
func_decl * mk_recognizer(
unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range);
@ -379,6 +414,8 @@ namespace datatype {
bool is_is(func_decl * f) const { return is_decl_of(f, fid(), OP_DT_IS); }
bool is_accessor(func_decl * f) const { return is_decl_of(f, fid(), OP_DT_ACCESSOR); }
bool is_update_field(func_decl * f) const { return is_decl_of(f, fid(), OP_DT_UPDATE_FIELD); }
bool is_subterm_predicate(func_decl * f) const { return is_decl_of(f, fid(), OP_DT_SUBTERM); }
bool is_subterm_predicate(expr* e) const { return is_app(e) && is_subterm_predicate(to_app(e)->get_decl()); }
bool is_constructor(app const * f) const { return is_app_of(f, fid(), OP_DT_CONSTRUCTOR); }
bool is_constructor(expr const * e) const { return is_app(e) && is_constructor(to_app(e)); }
bool is_recognizer0(app const* f) const { return is_app_of(f, fid(), OP_DT_RECOGNISER);}
@ -393,6 +430,7 @@ namespace datatype {
bool is_update_field(expr * f) const { return is_app(f) && is_app_of(to_app(f), fid(), OP_DT_UPDATE_FIELD); }
app* mk_is(func_decl * c, expr *f);
ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
func_decl * get_datatype_subterm(sort * ty);
unsigned get_datatype_num_constructors(sort * ty);
unsigned get_datatype_num_parameter_sorts(sort * ty);
sort* get_datatype_parameter_sort(sort * ty, unsigned idx);
@ -468,7 +506,7 @@ inline constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r
// Remark: the datatype becomes the owner of the constructor_decls
datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs);
datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs, symbol const& subterm_name = symbol::null);
inline void del_datatype_decl(datatype_decl * d) {}
inline void del_datatype_decls(unsigned num, datatype_decl * const * ds) {}

View file

@ -121,6 +121,9 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr
result = m().mk_app(c_decl, num, new_args.data());
return BR_DONE;
}
case OP_DT_SUBTERM:
// No rewrite yet for subterms
return BR_FAILED;
default:
UNREACHABLE();
}