Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ast/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ z3_add_component(ast
expr_map.cpp
expr_stat.cpp
expr_substitution.cpp
finite_set_decl_plugin.cpp
for_each_ast.cpp
for_each_expr.cpp
format.cpp
Expand Down
192 changes: 192 additions & 0 deletions src/ast/finite_set_decl_plugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*++
Copyright (c) 2025 Microsoft Corporation

Module Name:

finite_set_decl_plugin.cpp

Abstract:

Declaration plugin for finite sets

Author:

GitHub Copilot Agent 2025

Revision History:

--*/
#include<sstream>
#include "ast/finite_set_decl_plugin.h"
#include "ast/arith_decl_plugin.h"
#include "ast/array_decl_plugin.h"
#include "ast/polymorphism_util.h"
#include "util/warning.h"

finite_set_decl_plugin::finite_set_decl_plugin():
m_init(false) {
}

finite_set_decl_plugin::~finite_set_decl_plugin() {
for (polymorphism::psig* s : m_sigs)
dealloc(s);
}

void finite_set_decl_plugin::init() {
if (m_init) return;
ast_manager& m = *m_manager;
array_util autil(m);
m_init = true;

sort* A = m.mk_type_var(symbol("A"));
sort* B = m.mk_type_var(symbol("B"));
parameter paramA(A);
parameter paramB(B);
sort* setA = m.mk_sort(m_family_id, FINITE_SET_SORT, 1, &paramA);
sort* setB = m.mk_sort(m_family_id, FINITE_SET_SORT, 1, &paramB);
sort* boolT = m.mk_bool_sort();
sort* intT = arith_util(m).mk_int();
parameter paramInt(intT);
sort* setInt = m.mk_sort(m_family_id, FINITE_SET_SORT, 1, &paramInt);
sort* arrAB = autil.mk_array_sort(A, B);
sort* arrABool = autil.mk_array_sort(A, boolT);

sort* setAsetA[2] = { setA, setA };
sort* AsetA[2] = { A, setA };
sort* arrABsetA[2] = { arrAB, setA };
sort* arrABoolsetA[2] = { arrABool, setA };
sort* intintT[2] = { intT, intT };

m_sigs.resize(LAST_FINITE_SET_OP);
m_sigs[OP_FINITE_SET_EMPTY] = alloc(polymorphism::psig, m, "set.empty", 1, 0, nullptr, setA);
m_sigs[OP_FINITE_SET_SINGLETON] = alloc(polymorphism::psig, m, "set.singleton", 1, 1, &A, setA);
m_sigs[OP_FINITE_SET_UNION] = alloc(polymorphism::psig, m, "set.union", 1, 2, setAsetA, setA);
m_sigs[OP_FINITE_SET_INTERSECT] = alloc(polymorphism::psig, m, "set.intersect", 1, 2, setAsetA, setA);
m_sigs[OP_FINITE_SET_DIFFERENCE] = alloc(polymorphism::psig, m, "set.difference", 1, 2, setAsetA, setA);
m_sigs[OP_FINITE_SET_IN] = alloc(polymorphism::psig, m, "set.in", 1, 2, AsetA, boolT);
m_sigs[OP_FINITE_SET_SIZE] = alloc(polymorphism::psig, m, "set.size", 1, 1, &setA, intT);
m_sigs[OP_FINITE_SET_SUBSET] = alloc(polymorphism::psig, m, "set.subset", 1, 2, setAsetA, boolT);
m_sigs[OP_FINITE_SET_MAP] = alloc(polymorphism::psig, m, "set.map", 2, 2, arrABsetA, setB);
m_sigs[OP_FINITE_SET_SELECT] = alloc(polymorphism::psig, m, "set.select", 1, 2, arrABoolsetA, setA);
m_sigs[OP_FINITE_SET_RANGE] = alloc(polymorphism::psig, m, "set.range", 0, 2, intintT, setInt);
}

sort * finite_set_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
if (k == FINITE_SET_SORT) {
if (num_parameters != 1) {
m_manager->raise_exception("FiniteSet sort expects exactly one parameter (element sort)");
return nullptr;
}
if (!parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) {
m_manager->raise_exception("FiniteSet sort parameter must be a sort");
return nullptr;
}
sort * element_sort = to_sort(parameters[0].get_ast());
sort_size sz = sort_size::mk_very_big();
sort_info info(m_family_id, FINITE_SET_SORT, sz, num_parameters, parameters);
return m_manager->mk_sort(symbol("FiniteSet"), info);
}
m_manager->raise_exception("unknown finite set sort");
return nullptr;
}

bool finite_set_decl_plugin::is_finite_set(sort* s) const {
return s->get_family_id() == m_family_id && s->get_decl_kind() == FINITE_SET_SORT;
}

sort * finite_set_decl_plugin::get_element_sort(sort* finite_set_sort) const {
if (!is_finite_set(finite_set_sort)) {
return nullptr;
}
if (finite_set_sort->get_num_parameters() != 1) {
return nullptr;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot - check that the number of parameters is 1. Return nullptr if it is not 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added parameter count validation to get_element_sort(). Now checks get_num_parameters() != 1 and returns nullptr if invalid. Commit: 86949bf

parameter const* params = finite_set_sort->get_parameters();
if (!params[0].is_ast() || !is_sort(params[0].get_ast())) {
return nullptr;
}
return to_sort(params[0].get_ast());
}

func_decl * finite_set_decl_plugin::mk_empty(sort* element_sort) {
parameter param(element_sort);
sort * finite_set_sort = m_manager->mk_sort(m_family_id, FINITE_SET_SORT, 1, &param);
sort * const * no_domain = nullptr;
return m_manager->mk_func_decl(m_sigs[OP_FINITE_SET_EMPTY]->m_name, 0u, no_domain, finite_set_sort,
func_decl_info(m_family_id, OP_FINITE_SET_EMPTY, 1, &param));
}

func_decl * finite_set_decl_plugin::mk_finite_set_op(decl_kind k, unsigned arity, sort * const * domain, sort* range) {
ast_manager& m = *m_manager;
polymorphism::util poly_util(m);
sort_ref rng(m);
poly_util.match(*m_sigs[k], arity, domain, range, rng);
return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k));
}

func_decl * finite_set_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters,
parameter const * parameters,
unsigned arity, sort * const * domain,
sort * range) {
init();

switch (k) {
case OP_FINITE_SET_EMPTY:
if (num_parameters != 1 || !parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) {
m_manager->raise_exception("set.empty requires one sort parameter");
return nullptr;
}
return mk_empty(to_sort(parameters[0].get_ast()));
case OP_FINITE_SET_SINGLETON:
case OP_FINITE_SET_UNION:
case OP_FINITE_SET_INTERSECT:
case OP_FINITE_SET_DIFFERENCE:
case OP_FINITE_SET_IN:
case OP_FINITE_SET_SIZE:
case OP_FINITE_SET_SUBSET:
case OP_FINITE_SET_MAP:
case OP_FINITE_SET_SELECT:
case OP_FINITE_SET_RANGE:
return mk_finite_set_op(k, arity, domain, range);
default:
return nullptr;
}
}

void finite_set_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol const & logic) {
init();
for (unsigned i = 0; i < m_sigs.size(); ++i) {
if (m_sigs[i])
op_names.push_back(builtin_name(m_sigs[i]->m_name.str(), i));
}
}

void finite_set_decl_plugin::get_sort_names(svector<builtin_name>& sort_names, symbol const & logic) {
sort_names.push_back(builtin_name("FiniteSet", FINITE_SET_SORT));
}

expr * finite_set_decl_plugin::get_some_value(sort * s) {
if (is_finite_set(s)) {
// Return empty set for the given sort
sort* element_sort = get_element_sort(s);
if (element_sort) {
parameter param(element_sort);
return m_manager->mk_app(m_family_id, OP_FINITE_SET_EMPTY, 1, &param, 0, nullptr);
}
}
return nullptr;
}

bool finite_set_decl_plugin::is_fully_interp(sort * s) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change this to return false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed is_fully_interp() to return false. Commit: dd9662f

return false;
}

bool finite_set_decl_plugin::is_value(app * e) const {
// Empty set is a value
return is_app_of(e, m_family_id, OP_FINITE_SET_EMPTY);
}

bool finite_set_decl_plugin::is_unique_value(app* e) const {
// Empty set is a unique value for its sort
return is_value(e);
}
188 changes: 188 additions & 0 deletions src/ast/finite_set_decl_plugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*++
Copyright (c) 2025 Microsoft Corporation

Module Name:

finite_set_decl_plugin.h

Abstract:
Declaration plugin for finite sets signatures

Sort:
FiniteSet(S)

Operators:
set.empty : (FiniteSet S)
set.singleton : S -> (FiniteSet S)
set.union : (FiniteSet S) (FiniteSet S) -> (FiniteSet S)
set.intersect : (FiniteSet S) (FiniteSet S) -> (FiniteSet S)
set.difference : (FiniteSet S) (FiniteSet S) -> (FiniteSet S)
set.in : S (FiniteSet S) -> Bool
set.size : (FiniteSet S) -> Int
set.subset : (FiniteSet S) (FiniteSet S) -> Bool
set.map : (S -> T) (FiniteSet S) -> (FiniteSet T)
set.select : (S -> Bool) (FiniteSet S) -> (FiniteSet S)
set.range : Int Int -> (FiniteSet Int)

--*/
#pragma once

#include "ast/ast.h"
#include "ast/polymorphism_util.h"

enum finite_set_sort_kind {
FINITE_SET_SORT
};

enum finite_set_op_kind {
OP_FINITE_SET_EMPTY,
OP_FINITE_SET_SINGLETON,
OP_FINITE_SET_UNION,
OP_FINITE_SET_INTERSECT,
OP_FINITE_SET_DIFFERENCE,
OP_FINITE_SET_IN,
OP_FINITE_SET_SIZE,
OP_FINITE_SET_SUBSET,
OP_FINITE_SET_MAP,
OP_FINITE_SET_SELECT,
OP_FINITE_SET_RANGE,
LAST_FINITE_SET_OP
};

class finite_set_decl_plugin : public decl_plugin {
ptr_vector<polymorphism::psig> m_sigs;
bool m_init;

void init();
func_decl * mk_empty(sort* element_sort);
func_decl * mk_finite_set_op(decl_kind k, unsigned arity, sort * const * domain, sort* range);
sort * get_element_sort(sort* finite_set_sort) const;
bool is_finite_set(sort* s) const;

public:
finite_set_decl_plugin();
~finite_set_decl_plugin() override;

decl_plugin * mk_fresh() override {
return alloc(finite_set_decl_plugin);
}

void finalize() override {
for (polymorphism::psig* s : m_sigs)
dealloc(s);
m_sigs.reset();
}

//
// Contract for sort:
// parameters[0] - element sort
//
sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override;

//
// Contract for func_decl:
// For OP_FINITE_SET_MAP and OP_FINITE_SET_FILTER:
// parameters[0] - function declaration
// For others:
// no parameters
func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) override;

void get_op_names(svector<builtin_name> & op_names, symbol const & logic) override;

void get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) override;

expr * get_some_value(sort * s) override;

bool is_fully_interp(sort * s) const override;

bool is_value(app * e) const override;

bool is_unique_value(app* e) const override;
};

class finite_set_recognizers {
protected:
family_id m_fid;
public:
finite_set_recognizers(family_id fid):m_fid(fid) {}
family_id get_family_id() const { return m_fid; }
bool is_finite_set(sort* s) const { return is_sort_of(s, m_fid, FINITE_SET_SORT); }
bool is_finite_set(expr const* n) const { return is_finite_set(n->get_sort()); }
bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_EMPTY); }
bool is_singleton(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_SINGLETON); }
bool is_union(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_UNION); }
bool is_intersect(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_INTERSECT); }
bool is_difference(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_DIFFERENCE); }
bool is_in(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_IN); }
bool is_size(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_SIZE); }
bool is_subset(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_SUBSET); }
bool is_map(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_MAP); }
bool is_select(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_SELECT); }
bool is_range(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_RANGE); }

MATCH_UNARY(is_singleton);
MATCH_UNARY(is_size);
MATCH_BINARY(is_union);
MATCH_BINARY(is_intersect);
MATCH_BINARY(is_difference);
MATCH_BINARY(is_in);
MATCH_BINARY(is_subset);
MATCH_BINARY(is_map);
MATCH_BINARY(is_select);
MATCH_BINARY(is_range);
};

class finite_set_util : public finite_set_recognizers {
ast_manager& m_manager;
public:
finite_set_util(ast_manager& m):
finite_set_recognizers(m.mk_family_id("finite_set")), m_manager(m) {}

ast_manager& get_manager() const { return m_manager; }

app * mk_empty(sort* element_sort) {
parameter param(element_sort);
return m_manager.mk_app(m_fid, OP_FINITE_SET_EMPTY, 1, &param, 0, nullptr);
}

app * mk_singleton(expr* elem) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_SINGLETON, elem);
}

app * mk_union(expr* s1, expr* s2) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_UNION, s1, s2);
}

app * mk_intersect(expr* s1, expr* s2) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_INTERSECT, s1, s2);
}

app * mk_difference(expr* s1, expr* s2) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_DIFFERENCE, s1, s2);
}

app * mk_in(expr* elem, expr* set) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_IN, elem, set);
}

app * mk_size(expr* set) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_SIZE, set);
}

app * mk_subset(expr* s1, expr* s2) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_SUBSET, s1, s2);
}

app * mk_map(expr* arr, expr* set) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_MAP, arr, set);
}

app * mk_select(expr* arr, expr* set) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_SELECT, arr, set);
}

app * mk_range(expr* low, expr* high) {
return m_manager.mk_app(m_fid, OP_FINITE_SET_RANGE, low, high);
}
};
Empty file.
Loading