3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-09 02:41:52 +00:00
z3/ml/z3.idl
Leonardo de Moura bcca613cb2 Added ml component
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 12:44:06 -07:00

608 lines
15 KiB
Plaintext

/*++
Copyright (c) Microsoft Corporation
Module Name:
Z3
Abstract:
OCaml API for Z3.
The following design is used for the treatment of reference counting:
- The conversion of Z3_context from ML to C remembers the Z3 context, and
registers a finalizer using Gc.finalize that calls Z3_del_context.
- The conversion of Z3_ast and other reference counted types from C to ML:
+ stores the last translated context with the Z3_ast in the wrapper
object;
+ registers a finalizer using Gc.finalize that decrements the reference
counter of the Z3_ast;
+ increments the reference count of the Z3_ast.
The finalizers are registered using (the C interface to) Gc.finalize,
not attaching a finalizer to a custom block. The finalizers registered
by Gc.finalize are guaranteed to be called in reverse
registration-order, which is necessary to ensure that Z3_context's are
finalized only after all the Z3_ast's within them.
- ML Z3.ast (and subtypes) are given generic hash and comparison
operations using Z3_get_ast_hash and Z3_get_ast_id. Other types could
be handled similarly if analogous hash and id operations were exported
by the C API.
- The wrapper for Z3_mk_context is customized (using quote(call,...) in
z3_api.patched.h) to call Z3_mk_context_rc, and the ML API does not
include mk_context_rc.
This scheme relies on the property that all reference counted values
returned from C to ML are in the Z3_context that was last sent from ML to
C. This is normally straightforward, but note that it depends on the
argument order of e.g. the Z3_*translate functions.
Non-reference counted Z3 types that have delete operations have finalizers
that call the delete operations. The exposed delete operations are
shadowed by nop functions. The types whose delete operation accepts a
context use Gc.finalize while those that do not use custom block
finalizers.
Custom c2ml functions check the Z3 error code prior to allocating ML
values or registering finalizers. Other functions check the Z3 error code
after making a Z3 library call.
Some operations return NULL pointers when operations fail, or accept NULL
pointers. To handle these cases Z3_{ast,func_interp,sort}_opt types are
introduced. These are synonyms of Z3_{ast,func_interp,sort} but are
translated into OCaml option types. If the NULL pointers were passed to
ML, even if the user does not access them, they will have finalizers
registered, so when they die the OCaml GC will crash trying to call
dec_ref on NULL.
There is an alternate implementation, enabled by setting LEAK_CONTEXTS,
that avoids the overhead of Gc.finalize finalizers, but at the price of
leaking Z3_context objects.
Notes:
OCaml does not support unsigned types, so CamlIDL conflates signed and
unsigned types of the same size. Therefore, functions in the C API
operating on unsigned values that become redundant after this conflation
are excluded from the ML API using [#ifndef CAMLIDL] in z3_api.h.
CamlIDL does not support function pointers, so functions in the C API with
function pointer arguments are handled manually.
Author:
Jakob Lichtenberg (JakobL) 2007-08-08
Josh Berdine (jjb) 2012-03-21
--*/
// cpp trick to include expanded macro arguments in string literals
#define xstr(s) str(s)
#define str(s) #s
quote(c,"#define xstr(s) str(s)");
quote(c,"#define str(s) #s");
// CamlIDL (1.05) has a bug where it does not accept [unsigned] as a type,
// only as a specifier, so unsigned is defined to be unsigned int.
#define unsigned unsigned int
// Suppress "warning C4090: 'function' : different 'const' qualifiers" as
// CamlIDL does not seem to get this right.
quote(c,"#pragma warning(disable:4090)");
#ifndef MLAPIV3
#define DEFINE_TYPE(T) typedef [abstract] void* T
#define DEFINE_VOID(T) typedef [abstract] void* T
#define BEGIN_MLAPI_EXCLUDE quote(mli,"(*");
#define END_MLAPI_EXCLUDE quote(mli,"*)");
#ifdef LEAK_CONTEXTS
// Declare pointer type with custom conversion functions.
#define DEFINE_CUST_TYPE(T) \
typedef [abstract, ml2c(ml2c_Z3_ ## T), c2ml(c2ml_Z3_ ## T)] void* Z3_ ## T
#else
// Declare pointer type with custom conversion functions.
// Register an OCaml closure that just calls a C finalization function.
#define DEFINE_CUST_TYPE(T) \
quote(ml,xstr(\
external finalize_Z3_ ## T : T -> unit = xstr(finalize_Z3_ ## T);; \
let _ = Callback.register xstr(finalize_Z3_ ## T) finalize_Z3_ ## T \
)); \
typedef [abstract, ml2c(ml2c_Z3_ ## T), c2ml(c2ml_Z3_ ## T)] void* Z3_ ## T
#endif
// Z3_context
quote(c,"
void check_error_code (Z3_context c);
Z3_context last_ctx;
");
#ifdef LEAK_CONTEXTS
quote(c,"
value c2ml_Z3_context(Z3_context* c)
{
value v;
v = caml_alloc_small(1, Abstract_tag);
*((Z3_context *) Bp_val(v)) = *c;
return v;
}
void ml2c_Z3_context(value v, Z3_context* c)
{
*c = *((Z3_context *) Bp_val(v));
last_ctx = *c;
}
");
#else
quote(c,"
// caml_final_register is the implementation of Gc.finalize
value caml_final_register (value f, value v);
void register_finalizer(value** closure, char* name, Z3_context ctx, value v)
{
if (*closure == NULL) {
*closure = caml_named_value(name);
if (*closure == NULL) {
Z3_set_error(ctx, Z3_INTERNAL_FATAL);
return;
}
}
caml_final_register(**closure, v);
}
value c2ml_Z3_context (Z3_context* c)
{
static value* finalize_Z3_context_closure = NULL;
value v;
v = caml_alloc_small(1, Abstract_tag);
Field(v, 0) = (value) *c;
register_finalizer(&finalize_Z3_context_closure, \"finalize_Z3_context\",
(Z3_context) *c, v);
return v;
}
void ml2c_Z3_context (value v, Z3_context* c)
{
*c = (Z3_context) Field(v, 0);
last_ctx = *c;
}
value finalize_Z3_context (value v)
{
Z3_context c;
c = (Z3_context) Field(v, 0);
Z3_del_context(c);
return Val_unit;
}
");
#endif
DEFINE_CUST_TYPE(context);
// Z3_symbol
typedef [abstract] void* Z3_symbol;
// Z3_ast: reference counted type with hashing and comparison
quote(c,"
typedef struct _Z3_ast_context {
Z3_ast ast;
Z3_context ctx;
} Z3_ast_context;
void ml2c_Z3_ast (value v, Z3_ast* c)
{
*c = ((Z3_ast_context*) Data_custom_val(v))->ast;
}
static int compare_Z3_ast (value v1, value v2)
{
Z3_ast_context* ac1;
Z3_ast_context* ac2;
unsigned id1, id2;
ac1 = Data_custom_val(v1);
ac2 = Data_custom_val(v2);
id1 = Z3_get_ast_id(ac1->ctx, ac1->ast);
check_error_code(ac1->ctx);
id2 = Z3_get_ast_id(ac2->ctx, ac2->ast);
check_error_code(ac2->ctx);
return id2 - id1;
}
static intnat hash_Z3_ast (value v)
{
Z3_ast_context* ac;
unsigned hash;
ac = Data_custom_val(v);
hash = Z3_get_ast_hash(ac->ctx, ac->ast);
check_error_code(ac->ctx);
return hash;
}
");
#ifdef LEAK_CONTEXTS
quote(c,"
static void finalize_Z3_ast (value v)
{
Z3_ast_context* ac;
ac = Data_custom_val(v);
Z3_dec_ref(ac->ctx, ac->ast);
check_error_code(ac->ctx);
}
static struct custom_operations cops_Z3_ast = {
NULL,
finalize_Z3_ast,
compare_Z3_ast,
hash_Z3_ast,
custom_serialize_default,
custom_deserialize_default
};
value c2ml_Z3_ast (Z3_ast* c)
{
value v;
Z3_ast_context* ac;
check_error_code(last_ctx);
v = alloc_custom(&cops_Z3_ast, sizeof(Z3_ast_context), 0, 1);
ac = Data_custom_val(v);
ac->ctx = last_ctx;
ac->ast = *c;
Z3_inc_ref(ac->ctx, ac->ast);
return v;
}
");
#else
quote(c,"
value finalize_Z3_ast (value v)
{
Z3_ast_context* ac;
ac = Data_custom_val(v);
Z3_dec_ref(ac->ctx, ac->ast);
check_error_code(ac->ctx);
return Val_unit;
}
static struct custom_operations cops_Z3_ast = {
NULL,
custom_finalize_default,
compare_Z3_ast,
hash_Z3_ast,
custom_serialize_default,
custom_deserialize_default
};
value c2ml_Z3_ast (Z3_ast* c)
{
static value* finalize_Z3_ast_closure = NULL;
value v;
Z3_ast_context* ac;
check_error_code(last_ctx);
v = caml_alloc_custom(&cops_Z3_ast, sizeof(Z3_ast_context), 0, 1);
ac = Data_custom_val(v);
ac->ast = *c;
ac->ctx = last_ctx;
register_finalizer(&finalize_Z3_ast_closure, \"finalize_Z3_ast\",
(Z3_context) *c, v);
Z3_inc_ref(last_ctx, *c);
return v;
}
");
#endif
DEFINE_CUST_TYPE(ast);
// subtypes of Z3_ast
quote(c,"\
#define DEFINE_SUBAST_OPS(T) \
void ml2c_ ## T (value v, T * a) \
{ \
ml2c_Z3_ast(v, (Z3_ast*) a); \
} \
\
value c2ml_ ## T (T * a) \
{ \
return c2ml_Z3_ast((Z3_ast*) a); \
} \
");
#define DEFINE_SUBAST(T) \
typedef [mltype("private ast"), ml2c(ml2c_ ## T), c2ml(c2ml_ ## T)] Z3_ast T
quote(c,"DEFINE_SUBAST_OPS(Z3_sort)"); DEFINE_SUBAST(Z3_sort);
quote(c,"DEFINE_SUBAST_OPS(Z3_func_decl)"); DEFINE_SUBAST(Z3_func_decl);
quote(c,"DEFINE_SUBAST_OPS(Z3_app)"); DEFINE_SUBAST(Z3_app);
quote(c,"DEFINE_SUBAST_OPS(Z3_pattern)"); DEFINE_SUBAST(Z3_pattern);
// reference counted types without hashing and comparison
#ifdef LEAK_CONTEXTS
quote(c,"\
#define DEFINE_RC_OPS(T) \
typedef struct _ ## T ## _context { \
T dat; \
Z3_context ctx; \
} T ## _context; \
\
static void finalize_ ## T (value v) \
{ \
T ## _context* ac; \
ac = Data_custom_val(v); \
T ## _dec_ref(ac->ctx, ac->dat); \
check_error_code(ac->ctx); \
} \
\
static struct custom_operations cops_ ## T = { \
NULL, \
finalize_ ## T, \
custom_compare_default, \
custom_hash_default, \
custom_serialize_default, \
custom_deserialize_default \
}; \
\
value c2ml_ ## T (T * c) \
{ \
value v; \
T ## _context* ac; \
check_error_code(last_ctx); \
v = alloc_custom(&cops_ ## T, sizeof(T ## _context), 0, 1); \
ac = Data_custom_val(v); \
ac->dat = *c; \
ac->ctx = last_ctx; \
T ## _inc_ref(ac->ctx, ac->dat); \
return v; \
} \
\
void ml2c_ ## T (value v, T * c) \
{ \
*c = ((T ## _context*) Data_custom_val(v))->dat; \
} \
");
#else
quote(c,"\
#define DEFINE_RC_OPS(T) \
value c2ml_ ## T (T * c) \
{ \
static value* finalize_ ## T ## _closure = NULL; \
value v; \
check_error_code(last_ctx); \
v = caml_alloc_small(2, Abstract_tag); \
Field(v, 0) = (value) *c; \
Field(v, 1) = (value) last_ctx; \
register_finalizer(&finalize_ ## T ## _closure, xstr(finalize_ ## T), \
(Z3_context) *c, v); \
T ## _inc_ref(last_ctx, *c); \
return v; \
} \
\
void ml2c_ ## T (value v, T * c) \
{ \
*c = (T) Field(v, 0); \
} \
\
value finalize_ ## T (value v) \
{ \
Z3_context c; \
c = (Z3_context) Field(v, 1); \
T ## _dec_ref(c, (T) Field(v, 0)); \
check_error_code(c); \
return Val_unit; \
} \
");
#endif
quote(c,"DEFINE_RC_OPS(Z3_params)"); DEFINE_CUST_TYPE(params);
quote(c,"DEFINE_RC_OPS(Z3_param_descrs)"); DEFINE_CUST_TYPE(param_descrs);
quote(c,"DEFINE_RC_OPS(Z3_model)"); DEFINE_CUST_TYPE(model);
quote(c,"DEFINE_RC_OPS(Z3_func_interp)"); DEFINE_CUST_TYPE(func_interp);
quote(c,"DEFINE_RC_OPS(Z3_func_entry)"); DEFINE_CUST_TYPE(func_entry);
quote(c,"DEFINE_RC_OPS(Z3_fixedpoint)"); DEFINE_CUST_TYPE(fixedpoint);
quote(c,"DEFINE_RC_OPS(Z3_ast_vector)"); DEFINE_CUST_TYPE(ast_vector);
quote(c,"DEFINE_RC_OPS(Z3_ast_map)"); DEFINE_CUST_TYPE(ast_map);
quote(c,"DEFINE_RC_OPS(Z3_goal)"); DEFINE_CUST_TYPE(goal);
quote(c,"DEFINE_RC_OPS(Z3_tactic)"); DEFINE_CUST_TYPE(tactic);
quote(c,"DEFINE_RC_OPS(Z3_probe)"); DEFINE_CUST_TYPE(probe);
quote(c,"DEFINE_RC_OPS(Z3_apply_result)"); DEFINE_CUST_TYPE(apply_result);
quote(c,"DEFINE_RC_OPS(Z3_solver)"); DEFINE_CUST_TYPE(solver);
quote(c,"DEFINE_RC_OPS(Z3_stats)"); DEFINE_CUST_TYPE(stats);
// possibly-NULL pointer types, translated to OCaml option types
quote(c,"\
#define DEFINE_OPT_OPS(T) \
void ml2c_ ## T ## _opt (value v, T* c) \
{ \
struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; \
camlidl_ctx _ctx = &_ctxs; \
if (v != Val_int(0)) { \
camlidl_ml2c_z3_ ## T(Field(v, 0), c, _ctx); \
} else { \
*c = NULL; \
} \
} \
\
value c2ml_ ## T ## _opt (T* c) \
{ \
struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; \
camlidl_ctx _ctx = &_ctxs; \
value v; \
value a; \
if (*c) { \
a = camlidl_c2ml_z3_ ## T(c, _ctx); \
Begin_root(a) \
v = caml_alloc_small(1, 0); \
Field(v, 0) = a; \
End_roots(); \
} else { \
v = Val_int(0); \
} \
return v; \
}
");
#define DEFINE_OPT_TYPE(T) \
typedef [mltype(xstr(T option)), \
ml2c(ml2c_Z3_ ## T ## _opt), \
c2ml(c2ml_Z3_ ## T ## _opt) \
] Z3_ ## T Z3_ ## T ## _opt
quote(c,"DEFINE_OPT_OPS(Z3_ast)");
DEFINE_OPT_TYPE(ast);
quote(c,"DEFINE_OPT_OPS(Z3_sort)");
DEFINE_OPT_TYPE(sort);
quote(c,"DEFINE_OPT_OPS(Z3_func_interp)");
DEFINE_OPT_TYPE(func_interp);
// ToDo: these unnecessarily appear in the API documentation
DEFINE_TYPE(Z3_constructor);
DEFINE_TYPE(Z3_constructor_list);
// shadow delete operations with nops
quote(ml,"
let del_constructor _ _ = ()
let del_constructor_list _ _ = ()
let del_model _ _ = ()
let del_context _ = ()
let reset_memory () = ()
");
#else // MLAPIV3
// Provide custom error handler:
quote (c,"Z3_error_handler caml_z3_error_handler;");
quote (c,"void caml_z3_error_handler(Z3_context c, Z3_error_code e) { static char buffer[128]; char * msg = Z3_get_error_msg_ex(c, e); if (strlen(msg) > 100) { failwith(\"Z3: error message is too big to fit in buffer\"); } else { sprintf(buffer, \"Z3: %s\", msg); failwith(buffer); } }");
#define DEFINE_TYPE(T) typedef [abstract] void* T
#define DEFINE_VOID(T) typedef [abstract] void* T
#define BEGIN_MLAPI_EXCLUDE
#define END_MLAPI_EXCLUDE
#endif // MLAPIV3
#ifndef __in
#define __in [in]
#endif
#ifndef __out
#define __out [out]
#endif
#ifndef __out_opt
#define __out_opt [out,unique]
#endif
#ifndef __ecount
#define __ecount(num_args) [NOT_SUPPORTED]
#endif
#ifndef __in_ecount
#define __in_ecount(num_args) [in, size_is(num_args), length_is(num_args)]
#endif
#ifndef __out_ecount
#define __out_ecount(num_args) [out, size_is(num_args), length_is(num_args)]
#endif
#ifndef __inout_ecount
#define __inout_ecount(num_args) [in, out, size_is(num_args), length_is(num_args)]
#endif
#ifndef __inout
#define __inout [in, out]
#endif
#ifndef Z3_bool_opt
#define Z3_bool_opt void
#endif
#define Z3_API
#ifdef MLAPIV3
#include "z3V3_api.idl"
#include "x3V3.mli"
#include "x3V3.ml"
#else
#include "z3_api.idl"
#include "x3.ml"
quote(ml,"
let _ =
Printexc.register_printer (function
| Error(c,e) -> Some (\"Z3 \"^(get_error_msg c e))
| _ -> None
)
");
quote(mli,"
(**
{2 {L Legacy V3 API}}
*)
module V3 : sig
(**
{2 {L Legacy V3 API}}
*)
");
quote(ml,"
module V3 = struct
");
#endif
#ifdef MLAPIV3
quote(mlmli,"
end
");
#endif