mirror of
https://github.com/Z3Prover/z3
synced 2025-11-28 00:09:49 +00:00
Update Z3_mk_datatype_sort API to accept array of sort parameters and add Z3_mk_polymorphic_datatype (#7966)
* Initial plan * Update Z3_mk_datatype_sort API to accept array of parameters Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Update language bindings for parametric datatype sort API - Python: Updated DatatypeSort() to accept optional params list - OCaml: Added mk_sort_ref_p/mk_sort_ref_ps for parametric datatypes - .NET: Added MkDatatypeSortRef() methods with optional params - Java: Added mkDatatypeSortRef() methods with optional params - All changes maintain backward compatibility Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Fix .NET build error - rename params to parameters The 'params' keyword is reserved in C#, causing compilation errors. Renamed parameter from 'params' to 'parameters' in MkDatatypeSortRef methods. Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add unit test for parametric datatypes Added test_parametric_datatype.cpp to demonstrate polymorphic datatypes. The test creates two concrete instantiations of a generic pair concept: - pair_int_real with fields (first:Int, second:Real) - pair_real_int with fields (first:Real, second:Int) Then verifies that accessors work correctly and type checking is enforced. Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Implement polymorphic type variables support for parametric datatypes - Modified datatype_decl_plugin to allow type variables as parameters - Updated mk_datatype_decl to extract type variables from field sorts - Type variables are collected in order of first appearance - Revised unit test to use Z3_mk_type_variable for polymorphic datatypes - Test creates pair datatype with type variables alpha and beta - Successfully instantiates with concrete types (pair Int Real) and (pair Real Int) - Verifies accessor types match and equality terms are well-typed - All tests pass Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add Z3_mk_polymorphic_datatype API and refactor datatype creation - Added new API Z3_mk_polymorphic_datatype to z3_api.h - Renamed static mk_datatype_decl to api_datatype_decl in api_datatype.cpp - Modified api_datatype_decl to accept explicit type parameters - Updated all callers to use renamed function - Added test_polymorphic_datatype_api demonstrating new API usage - Both tests pass successfully Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Remove type variable collection logic from constructors Removed the logic for collecting type variables from field sorts based on constructors. * Update comments on parameter handling in api_datatype.cpp Clarify usage of parameters in API documentation. * Fix OCaml build error - use list instead of array for mk_datatype_sort Changed mk_sort_ref to pass empty list [] instead of empty array [||]. Changed mk_sort_ref_p to pass params list directly instead of converting to array. Z3native.mk_datatype_sort expects a list, not an array. Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add polymorphic datatype example to C++ examples Added polymorphic_datatype_example() demonstrating: - Creating type variables alpha and beta with Z3_mk_type_variable - Defining parametric Pair datatype with fields of type alpha and beta - Instantiating with concrete types (Pair Int Real) and (Pair Real Int) - Getting constructors and accessors from instantiated datatypes - Creating constants and expressions using the polymorphic types - Verifying type correctness with equality (= (first p1) (second p2)) Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
e669fbe557
commit
5163411f9b
13 changed files with 554 additions and 18 deletions
|
|
@ -21,6 +21,7 @@ add_executable(test-z3
|
|||
api_polynomial.cpp
|
||||
api_pb.cpp
|
||||
api_datalog.cpp
|
||||
parametric_datatype.cpp
|
||||
arith_rewriter.cpp
|
||||
arith_simplifier_plugin.cpp
|
||||
ast.cpp
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ int main(int argc, char ** argv) {
|
|||
TST(api_polynomial);
|
||||
TST(api_pb);
|
||||
TST(api_datalog);
|
||||
TST(parametric_datatype);
|
||||
TST(cube_clause);
|
||||
TST(old_interval);
|
||||
TST(get_implied_equalities);
|
||||
|
|
|
|||
229
src/test/parametric_datatype.cpp
Normal file
229
src/test/parametric_datatype.cpp
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/*++
|
||||
Copyright (c) 2025 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
parametric_datatype.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Test parametric datatypes with type variables.
|
||||
|
||||
Author:
|
||||
|
||||
Copilot 2025-10-12
|
||||
|
||||
--*/
|
||||
|
||||
#include "api/z3.h"
|
||||
#include "util/util.h"
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* Test polymorphic type variables with algebraic datatype definitions.
|
||||
*
|
||||
* This test uses Z3_mk_type_variable to create polymorphic type parameters alpha and beta,
|
||||
* defines a generic pair datatype, then instantiates it with concrete types using
|
||||
* Z3_mk_datatype_sort with parameters.
|
||||
*/
|
||||
static void test_parametric_pair() {
|
||||
std::cout << "test_parametric_pair\n";
|
||||
|
||||
Z3_config cfg = Z3_mk_config();
|
||||
Z3_context ctx = Z3_mk_context(cfg);
|
||||
Z3_del_config(cfg);
|
||||
|
||||
// Create type variables alpha and beta for polymorphic datatype
|
||||
Z3_symbol alpha_sym = Z3_mk_string_symbol(ctx, "alpha");
|
||||
Z3_symbol beta_sym = Z3_mk_string_symbol(ctx, "beta");
|
||||
Z3_sort alpha = Z3_mk_type_variable(ctx, alpha_sym);
|
||||
Z3_sort beta = Z3_mk_type_variable(ctx, beta_sym);
|
||||
|
||||
// Define parametric pair datatype with constructor mk-pair(first: alpha, second: beta)
|
||||
Z3_symbol pair_name = Z3_mk_string_symbol(ctx, "pair");
|
||||
Z3_symbol mk_pair_name = Z3_mk_string_symbol(ctx, "mk-pair");
|
||||
Z3_symbol is_pair_name = Z3_mk_string_symbol(ctx, "is-pair");
|
||||
Z3_symbol first_name = Z3_mk_string_symbol(ctx, "first");
|
||||
Z3_symbol second_name = Z3_mk_string_symbol(ctx, "second");
|
||||
|
||||
Z3_symbol field_names[2] = {first_name, second_name};
|
||||
Z3_sort field_sorts[2] = {alpha, beta}; // Use type variables
|
||||
unsigned sort_refs[2] = {0, 0}; // Not recursive references
|
||||
|
||||
Z3_constructor mk_pair_con = Z3_mk_constructor(
|
||||
ctx, mk_pair_name, is_pair_name, 2, field_names, field_sorts, sort_refs
|
||||
);
|
||||
|
||||
// Create the parametric datatype
|
||||
Z3_constructor constructors[1] = {mk_pair_con};
|
||||
Z3_sort pair = Z3_mk_datatype(ctx, pair_name, 1, constructors);
|
||||
|
||||
Z3_del_constructor(ctx, mk_pair_con);
|
||||
|
||||
std::cout << "Created parametric pair datatype\n";
|
||||
std::cout << "pair sort: " << Z3_sort_to_string(ctx, pair) << "\n";
|
||||
|
||||
// Now instantiate the datatype with concrete types
|
||||
Z3_sort int_sort = Z3_mk_int_sort(ctx);
|
||||
Z3_sort real_sort = Z3_mk_real_sort(ctx);
|
||||
|
||||
// Create (pair Int Real)
|
||||
Z3_sort params_int_real[2] = {int_sort, real_sort};
|
||||
Z3_sort pair_int_real = Z3_mk_datatype_sort(ctx, pair_name, 2, params_int_real);
|
||||
|
||||
// Create (pair Real Int)
|
||||
Z3_sort params_real_int[2] = {real_sort, int_sort};
|
||||
Z3_sort pair_real_int = Z3_mk_datatype_sort(ctx, pair_name, 2, params_real_int);
|
||||
|
||||
std::cout << "Instantiated pair with Int and Real\n";
|
||||
std::cout << "pair_int_real: " << Z3_sort_to_string(ctx, pair_int_real) << "\n";
|
||||
std::cout << "pair_real_int: " << Z3_sort_to_string(ctx, pair_real_int) << "\n";
|
||||
|
||||
// Get constructors and accessors from the instantiated datatypes
|
||||
Z3_func_decl mk_pair_int_real = Z3_get_datatype_sort_constructor(ctx, pair_int_real, 0);
|
||||
Z3_func_decl first_int_real = Z3_get_datatype_sort_constructor_accessor(ctx, pair_int_real, 0, 0);
|
||||
Z3_func_decl second_int_real = Z3_get_datatype_sort_constructor_accessor(ctx, pair_int_real, 0, 1);
|
||||
|
||||
Z3_func_decl mk_pair_real_int = Z3_get_datatype_sort_constructor(ctx, pair_real_int, 0);
|
||||
Z3_func_decl first_real_int = Z3_get_datatype_sort_constructor_accessor(ctx, pair_real_int, 0, 0);
|
||||
Z3_func_decl second_real_int = Z3_get_datatype_sort_constructor_accessor(ctx, pair_real_int, 0, 1);
|
||||
|
||||
std::cout << "Got constructors and accessors from instantiated datatypes\n";
|
||||
|
||||
// Create constants p1 : (pair Int Real) and p2 : (pair Real Int)
|
||||
Z3_symbol p1_sym = Z3_mk_string_symbol(ctx, "p1");
|
||||
Z3_symbol p2_sym = Z3_mk_string_symbol(ctx, "p2");
|
||||
Z3_ast p1 = Z3_mk_const(ctx, p1_sym, pair_int_real);
|
||||
Z3_ast p2 = Z3_mk_const(ctx, p2_sym, pair_real_int);
|
||||
|
||||
// Create (first p1) - should be Int
|
||||
Z3_ast first_p1 = Z3_mk_app(ctx, first_int_real, 1, &p1);
|
||||
|
||||
// Create (second p2) - should be Int
|
||||
Z3_ast second_p2 = Z3_mk_app(ctx, second_real_int, 1, &p2);
|
||||
|
||||
// Create the equality (= (first p1) (second p2))
|
||||
Z3_ast eq = Z3_mk_eq(ctx, first_p1, second_p2);
|
||||
|
||||
std::cout << "Created term: " << Z3_ast_to_string(ctx, eq) << "\n";
|
||||
|
||||
// Verify the term was created successfully
|
||||
ENSURE(eq != nullptr);
|
||||
|
||||
// Check that first_p1 and second_p2 have the same sort (Int)
|
||||
Z3_sort first_p1_sort = Z3_get_sort(ctx, first_p1);
|
||||
Z3_sort second_p2_sort = Z3_get_sort(ctx, second_p2);
|
||||
|
||||
std::cout << "Sort of (first p1): " << Z3_sort_to_string(ctx, first_p1_sort) << "\n";
|
||||
std::cout << "Sort of (second p2): " << Z3_sort_to_string(ctx, second_p2_sort) << "\n";
|
||||
|
||||
// Both should be Int
|
||||
ENSURE(Z3_is_eq_sort(ctx, first_p1_sort, int_sort));
|
||||
ENSURE(Z3_is_eq_sort(ctx, second_p2_sort, int_sort));
|
||||
|
||||
std::cout << "test_parametric_pair passed!\n";
|
||||
|
||||
Z3_del_context(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Z3_mk_polymorphic_datatype API with explicit parameters.
|
||||
*
|
||||
* This test demonstrates the new API that explicitly accepts type parameters.
|
||||
*/
|
||||
static void test_polymorphic_datatype_api() {
|
||||
std::cout << "test_polymorphic_datatype_api\n";
|
||||
|
||||
Z3_config cfg = Z3_mk_config();
|
||||
Z3_context ctx = Z3_mk_context(cfg);
|
||||
Z3_del_config(cfg);
|
||||
|
||||
// Create type variables alpha and beta for polymorphic datatype
|
||||
Z3_symbol alpha_sym = Z3_mk_string_symbol(ctx, "alpha");
|
||||
Z3_symbol beta_sym = Z3_mk_string_symbol(ctx, "beta");
|
||||
Z3_sort alpha = Z3_mk_type_variable(ctx, alpha_sym);
|
||||
Z3_sort beta = Z3_mk_type_variable(ctx, beta_sym);
|
||||
|
||||
// Define parametric triple datatype with constructor mk-triple(first: alpha, second: beta, third: alpha)
|
||||
Z3_symbol triple_name = Z3_mk_string_symbol(ctx, "triple");
|
||||
Z3_symbol mk_triple_name = Z3_mk_string_symbol(ctx, "mk-triple");
|
||||
Z3_symbol is_triple_name = Z3_mk_string_symbol(ctx, "is-triple");
|
||||
Z3_symbol first_name = Z3_mk_string_symbol(ctx, "first");
|
||||
Z3_symbol second_name = Z3_mk_string_symbol(ctx, "second");
|
||||
Z3_symbol third_name = Z3_mk_string_symbol(ctx, "third");
|
||||
|
||||
Z3_symbol field_names[3] = {first_name, second_name, third_name};
|
||||
Z3_sort field_sorts[3] = {alpha, beta, alpha}; // Use type variables
|
||||
unsigned sort_refs[3] = {0, 0, 0}; // Not recursive references
|
||||
|
||||
Z3_constructor mk_triple_con = Z3_mk_constructor(
|
||||
ctx, mk_triple_name, is_triple_name, 3, field_names, field_sorts, sort_refs
|
||||
);
|
||||
|
||||
// Create the parametric datatype using Z3_mk_polymorphic_datatype
|
||||
Z3_constructor constructors[1] = {mk_triple_con};
|
||||
Z3_sort type_params[2] = {alpha, beta};
|
||||
Z3_sort triple = Z3_mk_polymorphic_datatype(ctx, triple_name, 2, type_params, 1, constructors);
|
||||
|
||||
Z3_del_constructor(ctx, mk_triple_con);
|
||||
|
||||
std::cout << "Created parametric triple datatype using Z3_mk_polymorphic_datatype\n";
|
||||
std::cout << "triple sort: " << Z3_sort_to_string(ctx, triple) << "\n";
|
||||
|
||||
// Now instantiate the datatype with concrete types
|
||||
Z3_sort int_sort = Z3_mk_int_sort(ctx);
|
||||
Z3_sort bool_sort = Z3_mk_bool_sort(ctx);
|
||||
|
||||
// Create (triple Int Bool)
|
||||
Z3_sort params_int_bool[2] = {int_sort, bool_sort};
|
||||
Z3_sort triple_int_bool = Z3_mk_datatype_sort(ctx, triple_name, 2, params_int_bool);
|
||||
|
||||
std::cout << "Instantiated triple with Int and Bool\n";
|
||||
std::cout << "triple_int_bool: " << Z3_sort_to_string(ctx, triple_int_bool) << "\n";
|
||||
|
||||
// Get constructors and accessors from the instantiated datatype
|
||||
Z3_func_decl mk_triple_int_bool = Z3_get_datatype_sort_constructor(ctx, triple_int_bool, 0);
|
||||
Z3_func_decl first_int_bool = Z3_get_datatype_sort_constructor_accessor(ctx, triple_int_bool, 0, 0);
|
||||
Z3_func_decl second_int_bool = Z3_get_datatype_sort_constructor_accessor(ctx, triple_int_bool, 0, 1);
|
||||
Z3_func_decl third_int_bool = Z3_get_datatype_sort_constructor_accessor(ctx, triple_int_bool, 0, 2);
|
||||
|
||||
std::cout << "Got constructors and accessors from instantiated datatype\n";
|
||||
|
||||
// Create a constant t : (triple Int Bool)
|
||||
Z3_symbol t_sym = Z3_mk_string_symbol(ctx, "t");
|
||||
Z3_ast t = Z3_mk_const(ctx, t_sym, triple_int_bool);
|
||||
|
||||
// Create (first t) - should be Int
|
||||
Z3_ast first_t = Z3_mk_app(ctx, first_int_bool, 1, &t);
|
||||
|
||||
// Create (third t) - should also be Int
|
||||
Z3_ast third_t = Z3_mk_app(ctx, third_int_bool, 1, &t);
|
||||
|
||||
// Create the equality (= (first t) (third t))
|
||||
Z3_ast eq = Z3_mk_eq(ctx, first_t, third_t);
|
||||
|
||||
std::cout << "Created term: " << Z3_ast_to_string(ctx, eq) << "\n";
|
||||
|
||||
// Verify the term was created successfully
|
||||
ENSURE(eq != nullptr);
|
||||
|
||||
// Check that first_t and third_t have the same sort (Int)
|
||||
Z3_sort first_t_sort = Z3_get_sort(ctx, first_t);
|
||||
Z3_sort third_t_sort = Z3_get_sort(ctx, third_t);
|
||||
|
||||
std::cout << "Sort of (first t): " << Z3_sort_to_string(ctx, first_t_sort) << "\n";
|
||||
std::cout << "Sort of (third t): " << Z3_sort_to_string(ctx, third_t_sort) << "\n";
|
||||
|
||||
// Both should be Int
|
||||
ENSURE(Z3_is_eq_sort(ctx, first_t_sort, int_sort));
|
||||
ENSURE(Z3_is_eq_sort(ctx, third_t_sort, int_sort));
|
||||
|
||||
std::cout << "test_polymorphic_datatype_api passed!\n";
|
||||
|
||||
Z3_del_context(ctx);
|
||||
}
|
||||
|
||||
void tst_parametric_datatype() {
|
||||
test_parametric_pair();
|
||||
test_polymorphic_datatype_api();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue