3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-02 01:13:18 +00:00

wip - alpha support for polymorphism

An initial update to support polymorphism from SMTLIB3 and the API (so far C, Python).

The WIP SMTLIB3 format is assumed to be supporting the following declaration

```
(declare-type-var A)
```
Whenever A is used in a type signature of a function/constant or bound quantified variable, it is taken to mean that all instantiations of A are included in the signature and assertions.
For example, if the function f is declared with signature A -> A, then there is a version of f for all instances of A.
The semantics of polymorphism appears to follow previous proposals: the instances are effectively different functions.
This may clash with some other notions, such as the type signature forall 'a . 'a -> 'a would be inhabited by a unique function (the identity), while this is not enforced in this version (and hopefully never because it is more busy work).

The C API has the function 'Z3_mk_type_variable' to create a type variable and applying functions modulo polymorphic type signatures is possible.
The kind Z3_TYPE_VAR is added to sort discriminators.

This version is considered as early alpha. It passes a first rudimentary unit test involving quantified axioms, declare-fun, define-fun, and define-fun-rec.
This commit is contained in:
Nikolaj Bjorner 2023-07-12 18:09:02 -07:00
parent d6f2c23627
commit 939bf1c725
16 changed files with 987 additions and 42 deletions

View file

@ -42,6 +42,7 @@ Notes:
#include "ast/for_each_expr.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/rewriter/recfun_replace.h"
#include "ast/polymorphism_util.h"
#include "model/model_evaluator.h"
#include "model/model_smt2_pp.h"
#include "model/model_v2_pp.h"
@ -223,12 +224,48 @@ bool func_decls::check_signature(ast_manager& m, func_decl* f, unsigned arity, s
return true;
}
func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * domain, sort * range) const {
bool func_decls::check_poly_signature(ast_manager& m, func_decl* f, unsigned arity, sort* const* domain, sort* range, func_decl*& g) {
polymorphism::substitution sub(m);
arith_util au(m);
sort_ref range_ref(range, m);
if (range != nullptr && !sub.match(f->get_range(), range))
return false;
if (f->get_arity() != arity)
return false;
for (unsigned i = 0; i < arity; i++)
if (!sub.match(f->get_domain(i), domain[i]))
return false;
if (!range)
range_ref = sub(f->get_range());
recfun::util u(m);
auto& p = u.get_plugin();
if (!u.has_def(f)) {
g = m.instantiate_polymorphic(f, arity, domain, range_ref);
return true;
}
// this is an instantiation of a recursive polymorphic function.
// create a self-contained polymorphic definition for the instantiation.
auto def = u.get_def(f);
auto promise_def = p.mk_def(f->get_name(), arity, domain, range_ref, false);
recfun_replace replace(m);
expr_ref tt = sub(def.get_rhs());
p.set_definition(replace, promise_def, def.is_macro(), def.get_vars().size(), def.get_vars().data(), tt);
g = promise_def.get_def()->get_decl();
insert(m, g);
return true;
}
func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * domain, sort * range) {
bool coerced = false;
func_decl* g = nullptr;
if (!more_than_one()) {
func_decl* f = first();
if (check_signature(m, f, arity, domain, range, coerced))
return f;
return f;
if (check_poly_signature(m, f, arity, domain, range, g))
return g;
return nullptr;
}
func_decl_set * fs = UNTAG(func_decl_set *, m_decls);
@ -241,10 +278,15 @@ func_decl * func_decls::find(ast_manager& m, unsigned arity, sort * const * doma
return f;
}
}
return best_f;
if (best_f != nullptr)
return best_f;
for (func_decl* f : *fs)
if (check_poly_signature(m, f, arity, domain, range, g))
return g;
return nullptr;
}
func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const {
func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) {
if (!more_than_one())
first();
ptr_buffer<sort> sorts;
@ -376,12 +418,13 @@ void cmd_context::erase_macro(symbol const& s) {
decls.erase_last(m());
}
bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr*& t) const {
bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, expr_ref_vector& coerced_args, expr_ref& t) {
macro_decls decls;
if (!m_macros.find(s, decls))
return false;
for (macro_decl const& d : decls) {
if (d.m_domain.size() != n) continue;
if (d.m_domain.size() != n)
continue;
bool eq = true;
coerced_args.reset();
for (unsigned i = 0; eq && i < n; ++i) {
@ -406,6 +449,26 @@ bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, exp
return true;
}
}
for (macro_decl const& d : decls) {
if (d.m_domain.size() != n)
continue;
polymorphism::substitution sub(m());
bool eq = true;
for (unsigned i = 0; eq && i < n; ++i) {
if (!sub.match(d.m_domain[i], args[i]->get_sort()))
eq = false;
}
if (eq) {
t = d.m_body;
t = sub(t);
verbose_stream() << "macro " << t << "\n";
ptr_buffer<sort> domain;
for (unsigned i = 0; i < n; ++i)
domain.push_back(args[i]->get_sort());
insert_macro(s, n, domain.data(), t);
return true;
}
}
return false;
}
@ -939,18 +1002,16 @@ void cmd_context::insert(cmd * c) {
void cmd_context::insert_user_tactic(symbol const & s, sexpr * d) {
sm().inc_ref(d);
sexpr * old_d;
if (m_user_tactic_decls.find(s, old_d)) {
sm().dec_ref(old_d);
}
if (m_user_tactic_decls.find(s, old_d))
sm().dec_ref(old_d);
m_user_tactic_decls.insert(s, d);
}
void cmd_context::insert(symbol const & s, object_ref * r) {
r->inc_ref(*this);
object_ref * old_r = nullptr;
if (m_object_refs.find(s, old_r)) {
old_r->dec_ref(*this);
}
if (m_object_refs.find(s, old_r))
old_r->dec_ref(*this);
m_object_refs.insert(s, r);
}
@ -1054,16 +1115,17 @@ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family
}
func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices,
unsigned arity, sort * const * domain, sort * range) const {
unsigned arity, sort * const * domain, sort * range) {
if (domain && contains_macro(s, arity, domain))
throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s);
func_decl * f = nullptr;
func_decls fs;
if (num_indices == 0 && m_func_decls.find(s, fs))
if (num_indices == 0 && m_func_decls.contains(s)) {
auto& fs = m_func_decls.find(s);
f = fs.find(m(), arity, domain, range);
if (f)
}
if (f)
return f;
builtin_decl d;
if ((arity == 0 || domain) && m_builtin_decls.find(s, d)) {
@ -1089,11 +1151,12 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices,
throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s);
return f;
}
if (num_indices > 0 && m_func_decls.find(s, fs))
if (num_indices > 0 && m_func_decls.contains(s)) {
auto& fs = m_func_decls.find(s);
f = fs.find(m(), arity, domain, range);
if (f)
}
if (f)
return f;
throw cmd_exception("invalid function declaration reference, unknown indexed function ", s);
}
@ -1125,7 +1188,7 @@ object_ref * cmd_context::find_object_ref(symbol const & s) const {
#define CHECK_SORT(T) if (well_sorted_check_enabled()) m().check_sorts_core(T)
void cmd_context::mk_const(symbol const & s, expr_ref & result) const {
void cmd_context::mk_const(symbol const & s, expr_ref & result) {
mk_app(s, 0, nullptr, 0, nullptr, nullptr, result);
}
@ -1153,9 +1216,10 @@ bool cmd_context::try_mk_builtin_app(symbol const & s, unsigned num_args, expr *
bool cmd_context::try_mk_declared_app(symbol const & s, unsigned num_args, expr * const * args,
unsigned num_indices, parameter const * indices, sort * range,
func_decls& fs, expr_ref & result) const {
if (!m_func_decls.find(s, fs))
expr_ref & result) {
if (!m_func_decls.contains(s))
return false;
func_decls& fs = m_func_decls.find(s);
if (num_args == 0 && !range) {
if (fs.more_than_one())
@ -1180,8 +1244,8 @@ bool cmd_context::try_mk_declared_app(symbol const & s, unsigned num_args, expr
bool cmd_context::try_mk_macro_app(symbol const & s, unsigned num_args, expr * const * args,
unsigned num_indices, parameter const * indices, sort * range,
expr_ref & result) const {
expr* _t;
expr_ref & result) {
expr_ref _t(m());
expr_ref_vector coerced_args(m());
if (macros_find(s, num_args, args, coerced_args, _t)) {
TRACE("macro_bug", tout << "well_sorted_check_enabled(): " << well_sorted_check_enabled() << "\n";
@ -1256,19 +1320,21 @@ bool cmd_context::try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * c
void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args,
unsigned num_indices, parameter const * indices, sort * range,
expr_ref & result) const {
expr_ref & result) {
func_decls fs;
if (try_mk_macro_app(s, num_args, args, num_indices, indices, range, result))
return;
if (try_mk_declared_app(s, num_args, args, num_indices, indices, range, fs, result))
return;
if (try_mk_declared_app(s, num_args, args, num_indices, indices, range, result))
return;
if (try_mk_builtin_app(s, num_args, args, num_indices, indices, range, result))
return;
if (!range && try_mk_pdecl_app(s, num_args, args, num_indices, indices, result))
return;
func_decls fs;
m_func_decls.find(s, fs);
std::ostringstream buffer;
buffer << "unknown constant " << s;
if (num_args > 0) {