-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Implement finite_set_decl_plugin with complete operator support and polymorphism infrastructure #7961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Copilot
wants to merge
11
commits into
finite-sets
Choose a base branch
from
copilot/fix-8720d50e-3ea8-4d98-8aed-98393b4b96aa
base: finite-sets
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Implement finite_set_decl_plugin with complete operator support and polymorphism infrastructure #7961
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
008c921
Initial plan
Copilot d8beab9
Implement finite_sets_decl_plugin with all specified operations
Copilot 1217392
Add tests for finite_sets_decl_plugin
Copilot 980ea35
Add set.singleton operator to finite_sets_decl_plugin
Copilot aa9cb71
Refactor finite_sets_decl_plugin to use polymorphic signatures and Ar…
Copilot 495cdba
Rename finite_sets to finite_set everywhere including file names
Copilot 5135fd3
Rename set.filter to set.select
Copilot 0362f0a
Refactor finite_set_decl_plugin to use polymorphism_util
Copilot dd9662f
Move psig and match method to polymorphism_util
Copilot 86949bf
Add MATCH macros and fix is_fully_interp return value
Copilot b53896f
Add is_finite_set helper and parameter count validation
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, ¶mA); | ||
sort* setB = m.mk_sort(m_family_id, FINITE_SET_SORT, 1, ¶mB); | ||
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, ¶mInt); | ||
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; | ||
} | ||
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, ¶m); | ||
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, ¶m)); | ||
} | ||
|
||
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, ¶m, 0, nullptr); | ||
} | ||
} | ||
return nullptr; | ||
} | ||
|
||
bool finite_set_decl_plugin::is_fully_interp(sort * s) const { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change this to return false There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed |
||
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); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, ¶m, 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.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 checksget_num_parameters() != 1
and returnsnullptr
if invalid. Commit: 86949bf