mirror of
https://github.com/Z3Prover/z3
synced 2025-04-06 17:44:08 +00:00
163 lines
4.9 KiB
Plaintext
163 lines
4.9 KiB
Plaintext
/*++
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
error_handling
|
|
|
|
Abstract:
|
|
|
|
Error handling in the OCaml API for Z3.
|
|
|
|
The wrapper of each Z3 API routine that takes a Z3_context or a Z3_theory
|
|
argument calls check_error_code before returning. (These calls are added
|
|
in generate_mlapi.cmd using the build.sed script.)
|
|
|
|
There are two error handling schemes implemented, depending on whether
|
|
(UN)SAFE_ERRORS is set.
|
|
|
|
- SAFE_ERRORS checks Z3_error_code after each call and raises an OCaml
|
|
exception in error conditions. Z3_set_error_handler is not exposed by
|
|
the SAFE_ERRORS version.
|
|
|
|
- UNSAFE_ERRORS sets a Z3 error handler routine that either calls a
|
|
globally registered OCaml function or, by default, raises an OCaml
|
|
exception. This avoids overhead of repeatedly checking
|
|
Z3_get_error_code, but leaves Z3 in a broken state.
|
|
|
|
Notes:
|
|
|
|
The current SAFE_ERRORS implementation interacts badly with theory plugin
|
|
callbacks. Z3 errors are converted into OCaml exceptions, which the
|
|
wrappers of theory plugin callbacks are not expecting. Therefore, if a
|
|
theory plugin calls a Z3 API routine that triggers an error, an OCaml
|
|
exception will be raised and bypass any C++ destructors pushed onto the
|
|
stack by Z3 before the call to the plugin and after the preceding OCaml
|
|
exception handler. One solution to this would be to modify the theory
|
|
plugin callback registration functions to wrap callbacks in an OCaml
|
|
exception handler. Since OCaml exceptions are cheap to raise at the
|
|
expense of some cost to install a handler, this may not be desirable.
|
|
Another solution would be to modify check_error_code to detect if it is
|
|
executing in a plugin callback and simply maintain the Z3_error_code, or
|
|
raise a C++ exception, instead of raising an OCaml exception.
|
|
|
|
Author:
|
|
|
|
Josh Berdine (jjb) 2012-03-21
|
|
|
|
--*/
|
|
|
|
|
|
#if !defined(UNSAFE_ERRORS) && !defined(SAFE_ERRORS)
|
|
#define SAFE_ERRORS
|
|
#endif
|
|
|
|
|
|
#ifdef SAFE_ERRORS
|
|
|
|
quote(mlmli,"
|
|
(** Exceptions raised by Z3. It is safe to continue interacting with Z3 after
|
|
catching [Error] exceptions.
|
|
|
|
- {b See also}: {!get_error_msg}
|
|
*)
|
|
exception Error of context * error_code
|
|
");
|
|
quote(ml,"
|
|
/* Register dynamically-generated exception tag for use from C */
|
|
let _ = Callback.register_exception \"Z3.Error\" (Error (Obj.magic None, OK))
|
|
");
|
|
|
|
quote(c,"
|
|
value camlidl_c2ml_z3_Z3_error_code(Z3_error_code * _c2, camlidl_ctx _ctx);
|
|
|
|
/* Error checking routine that raises OCaml Error exceptions */
|
|
void check_error_code (Z3_context c)
|
|
{
|
|
static struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL };
|
|
value* exn_tag = NULL;
|
|
value ctx_err[2];
|
|
Z3_error_code e;
|
|
e = Z3_get_error_code(c);
|
|
if (e != Z3_OK) {
|
|
ctx_err[0] = c2ml_Z3_context(&c);
|
|
ctx_err[1] = camlidl_c2ml_z3_Z3_error_code(&e, &_ctxs);
|
|
exn_tag = caml_named_value(\"Z3.Error\");
|
|
if (*exn_tag == 0) {
|
|
fprintf(stderr, \"Z3.Error not found\");
|
|
exit(1);
|
|
}
|
|
caml_raise_with_args(*exn_tag, 2, ctx_err);
|
|
}
|
|
}
|
|
|
|
/* Disable default error handler, all error checking is done by check_error_code */
|
|
void* error_handler_static = NULL;
|
|
");
|
|
|
|
#else
|
|
|
|
quote(mlmli,"
|
|
(** Exceptions raised by Z3. {b Warning}: It is unsafe to continue
|
|
interacting with Z3 after catching [Error] exceptions. To recover from
|
|
error conditions, use {!set_error_handler} to set an error handler that
|
|
does nothing, and then test {!get_error_code} after every call to Z3.
|
|
|
|
- {b See also}: {!get_error_msg}
|
|
*)
|
|
exception Error of context * error_code
|
|
");
|
|
quote(ml,"
|
|
/* Register dynamically-generated exception tag for use from C */
|
|
let _ = Callback.register_exception \"Z3.Error\" (Error (Obj.magic None, OK))
|
|
");
|
|
|
|
quote(c,"
|
|
/* Error checking routine that does nothing */
|
|
void check_error_code(Z3_context c) {}
|
|
|
|
/* All contexts share the same handler */
|
|
static value caml_error_handler = 0;
|
|
|
|
static void error_handler_static (Z3_context c, Z3_error_code e)
|
|
{
|
|
static struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL };
|
|
value* exn_tag = NULL;
|
|
value ctx_err[2];
|
|
ctx_err[0] = c2ml_Z3_context(&c);
|
|
ctx_err[1] = camlidl_c2ml_z3_Z3_error_code(&e, &_ctxs);
|
|
if (caml_error_handler) {
|
|
caml_callback2(caml_error_handler, ctx_err[0], ctx_err[1]);
|
|
} else {
|
|
/* if no handler set, raise OCaml Error exception */
|
|
exn_tag = caml_named_value(\"Z3.Error\");
|
|
if (*exn_tag == 0) {
|
|
fprintf(stderr, \"Z3.Error not found\");
|
|
exit(1);
|
|
}
|
|
caml_raise_with_args(*exn_tag, 2, ctx_err);
|
|
}
|
|
}
|
|
|
|
void ml2c_Z3_error_handler (value ml_handler, void* c_handler)
|
|
{
|
|
caml_error_handler = ml_handler;
|
|
c_handler = (void*)error_handler_static;
|
|
}
|
|
|
|
/* Never called */
|
|
value c2ml_Z3_error_handler (void* _)
|
|
{
|
|
return 0;
|
|
}
|
|
");
|
|
|
|
typedef [mltype("context -> error_code -> unit"),
|
|
ml2c(ml2c_Z3_error_handler),
|
|
c2ml(c2ml_Z3_error_handler)
|
|
] void Z3_error_handler;
|
|
|
|
quote(c,"#define Z3_error_handler void*");
|
|
|
|
#endif
|