Skip to content

Commit 678edb4

Browse files
authored
Merge pull request #624 from SRI-CSL/fix-issue-615-interpolation-crash
Fix issue #615: crash in mcsat_value_construct_from_value during interpolation.
2 parents 3dffd6e + c7754ce commit 678edb4

9 files changed

Lines changed: 835 additions & 47 deletions

File tree

src/api/yices_api.c

Lines changed: 104 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2534,6 +2534,63 @@ static bool check_good_vars_or_uninterpreted(term_manager_t *mngr, uint32_t n, c
25342534
return true;
25352535
}
25362536

2537+
static bool mcsat_assumption_type_supported(type_table_t *types, type_t tau) {
2538+
type_kind_t kind = type_kind(types, tau);
2539+
uint32_t i;
2540+
2541+
switch (kind) {
2542+
case BOOL_TYPE:
2543+
case INT_TYPE:
2544+
case REAL_TYPE:
2545+
case SCALAR_TYPE:
2546+
case BITVECTOR_TYPE:
2547+
return true;
2548+
2549+
case TUPLE_TYPE: {
2550+
tuple_type_t *tuple = tuple_type_desc(types, tau);
2551+
for (i = 0; i < tuple->nelem; i++) {
2552+
if (! mcsat_assumption_type_supported(types, tuple->elem[i])) {
2553+
return false;
2554+
}
2555+
}
2556+
return true;
2557+
}
2558+
2559+
default:
2560+
return false;
2561+
}
2562+
}
2563+
2564+
// Check that the type of every term in v is one that the MCSAT solver can
2565+
// decide on as an assumption value. Tuple types are supported if all their
2566+
// recursively flattened leaves are supported scalar decision types. Other
2567+
// type kinds (UNINTERPRETED_TYPE, FUNCTION_TYPE, FF_TYPE, etc.) reach the
2568+
// MCSAT solver but trigger an assertion failure inside
2569+
// mcsat_value_construct_from_value (or a NULL decide_assignment dispatch),
2570+
// so we reject them here with a clear error code (issue #615).
2571+
//
2572+
// All elements of v must already be good terms.
2573+
static bool check_mcsat_assumption_types(term_manager_t *mngr, uint32_t n, const term_t *v) {
2574+
term_table_t *tbl;
2575+
type_table_t *types;
2576+
uint32_t i;
2577+
2578+
tbl = term_manager_get_terms(mngr);
2579+
types = tbl->types;
2580+
for (i=0; i<n; i++) {
2581+
type_t tau = term_type(tbl, v[i]);
2582+
if (! mcsat_assumption_type_supported(types, tau)) {
2583+
error_report_t *error = get_yices_error();
2584+
error->code = MCSAT_ERROR_ASSUMPTION_TYPE_NOT_SUPPORTED;
2585+
error->term1 = v[i];
2586+
error->type1 = tau;
2587+
return false;
2588+
}
2589+
}
2590+
2591+
return true;
2592+
}
2593+
25372594
// Check whether arrays v and a define a valid substitution
25382595
// both must be arrays of n elements
25392596
static bool check_good_substitution(term_manager_t *mngr, uint32_t n, const term_t *v, const term_t *a) {
@@ -9571,6 +9628,18 @@ static bool good_terms_for_check_with_model(uint32_t n, const term_t t[]) {
95719628
MT_PROTECT(bool, __yices_globals.lock, _o_good_terms_for_check_with_model(n, t));
95729629
}
95739630

9631+
/*
9632+
* Same as _o_good_terms_for_check_with_model, but additionally checks that
9633+
* the type of each assumption term is one MCSAT can decide on. This guards
9634+
* against the crash reported in issue #615. Tuple assumptions are allowed
9635+
* only if every recursively flattened leaf type is supported by an MCSAT
9636+
* decision plugin.
9637+
*/
9638+
static bool _o_good_assumption_terms_for_mcsat(uint32_t n, const term_t t[]) {
9639+
return _o_good_terms_for_check_with_model(n, t)
9640+
&& check_mcsat_assumption_types(__yices_globals.manager, n, t);
9641+
}
9642+
95749643
/*
95759644
* Check context with model
95769645
* - param = parameter for check sat (or NULL for default parameters)
@@ -9588,10 +9657,16 @@ static smt_status_t _o_yices_check_context_with_model(context_t *ctx, const para
95889657
return YICES_STATUS_ERROR;
95899658
}
95909659

9591-
if (! _o_good_terms_for_check_with_model(n, t)) {
9592-
// this sets the error code already (to VARIABLE_REQUIRED)
9593-
// but Dejan created another error code that means the same thing here.
9594-
set_error_code(MCSAT_ERROR_ASSUMPTION_TERM_NOT_SUPPORTED);
9660+
if (! _o_good_assumption_terms_for_mcsat(n, t)) {
9661+
// Normalize the error code: VARIABLE_REQUIRED (set by the
9662+
// term-shape check) becomes MCSAT_ERROR_ASSUMPTION_TERM_NOT_SUPPORTED,
9663+
// matching the long-standing convention here. The new
9664+
// MCSAT_ERROR_ASSUMPTION_TYPE_NOT_SUPPORTED (set by the type-kind
9665+
// check) is left as-is so callers can distinguish "wrong shape" from
9666+
// "wrong type". term1/type1 fields are preserved.
9667+
if (yices_error_code() == VARIABLE_REQUIRED) {
9668+
set_error_code(MCSAT_ERROR_ASSUMPTION_TERM_NOT_SUPPORTED);
9669+
}
95959670
return YICES_STATUS_ERROR;
95969671
}
95979672

@@ -9669,10 +9744,11 @@ static smt_status_t _o_yices_check_context_with_model_and_hint(context_t *ctx, c
96699744
return YICES_STATUS_ERROR;
96709745
}
96719746

9672-
if (! _o_good_terms_for_check_with_model(n, t)) {
9673-
// this sets the error code already (to VARIABLE_REQUIRED)
9674-
// but Dejan created another error code that means the same thing here.
9675-
set_error_code(MCSAT_ERROR_ASSUMPTION_TERM_NOT_SUPPORTED);
9747+
if (! _o_good_assumption_terms_for_mcsat(n, t)) {
9748+
// See note in _o_yices_check_context_with_model.
9749+
if (yices_error_code() == VARIABLE_REQUIRED) {
9750+
set_error_code(MCSAT_ERROR_ASSUMPTION_TERM_NOT_SUPPORTED);
9751+
}
96769752
return YICES_STATUS_ERROR;
96779753
}
96789754

@@ -9880,16 +9956,28 @@ EXPORTED smt_status_t yices_check_context_with_interpolation(interpolation_conte
98809956
ctx->model = yices_get_model(ctx->ctx_A, true);
98819957
}
98829958

9883-
// Pop both contexts
9884-
if (result != YICES_STATUS_ERROR) {
9959+
// Pop both contexts. Preserve the original error report if the loop above
9960+
// failed (for example, via model-refutation assumption validation).
9961+
{
9962+
bool preserve_error = result == YICES_STATUS_ERROR;
9963+
error_report_t saved_error;
9964+
if (preserve_error) {
9965+
saved_error = *get_yices_error();
9966+
}
9967+
98859968
ret = yices_pop(ctx->ctx_B);
9886-
if (ret) {
9969+
if (ret && !preserve_error) {
98879970
result = YICES_STATUS_ERROR;
9888-
} else {
9889-
ret = yices_pop(ctx->ctx_A);
9890-
if (ret) {
9891-
result = YICES_STATUS_ERROR;
9892-
}
9971+
}
9972+
// Pop A even if popping B failed: both contexts were pushed above and
9973+
// must be balanced independently.
9974+
ret = yices_pop(ctx->ctx_A);
9975+
if (ret && !preserve_error) {
9976+
result = YICES_STATUS_ERROR;
9977+
}
9978+
9979+
if (preserve_error) {
9980+
*get_yices_error() = saved_error;
98939981
}
98949982
}
98959983

src/api/yices_error.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,10 @@ int32_t print_error(FILE *f) {
608608
code = fprintf(f, "mcsat: checking with assumptions only supports variables as assumptions\n");
609609
break;
610610

611+
case MCSAT_ERROR_ASSUMPTION_TYPE_NOT_SUPPORTED:
612+
code = fprintf(f, "mcsat: assumption variable has a type that mcsat cannot decide on\n");
613+
break;
614+
611615
case INTERNAL_EXCEPTION:
612616
default:
613617
code = fprintf(f, "internal error\n");
@@ -1137,6 +1141,10 @@ char *error_string(void) {
11371141
nchar = snprintf(buffer, BUFFER_SIZE, "mcsat: checking with assumptions only supports variables as assumptions\n");
11381142
break;
11391143

1144+
case MCSAT_ERROR_ASSUMPTION_TYPE_NOT_SUPPORTED:
1145+
nchar = snprintf(buffer, BUFFER_SIZE, "mcsat: assumption variable has a type that mcsat cannot decide on\n");
1146+
break;
1147+
11401148
case INTERNAL_EXCEPTION:
11411149
default:
11421150
nchar = snprintf(buffer, BUFFER_SIZE, "internal error");

src/frontend/smt2/smt2_commands.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,10 @@ static void print_yices_error(bool full) {
11021102
print_out("mcsat: checking with assumptions only supports variables as assumptions");
11031103
break;
11041104

1105+
case MCSAT_ERROR_ASSUMPTION_TYPE_NOT_SUPPORTED:
1106+
print_out("mcsat: assumption variable has a type that mcsat cannot decide on");
1107+
break;
1108+
11051109
case OUTPUT_ERROR:
11061110
print_out(" IO error");
11071111
break;

src/include/yices.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3370,6 +3370,11 @@ __YICES_DLLSPEC__ extern smt_status_t yices_check_context_with_assumptions(conte
33703370
* if one of the terms t[i] is not an uninterpreted term
33713371
* code = MCSAT_ERROR_ASSUMPTION_TERM_NOT_SUPPORTED
33723372
*
3373+
* if one of the terms t[i] has a type that MCSAT cannot decide on
3374+
* (i.e. not Bool, Int, Real, scalar, BitVector, or a tuple whose
3375+
* recursively flattened leaves all have one of these types)
3376+
* code = MCSAT_ERROR_ASSUMPTION_TYPE_NOT_SUPPORTED
3377+
*
33733378
* If the context does not have the MCSAT solver enabled
33743379
* code = CTX_OPERATION_NOT_SUPPORTED
33753380
*
@@ -3418,6 +3423,11 @@ __YICES_DLLSPEC__ extern smt_status_t yices_check_context_with_model(context_t *
34183423
* if one of the terms t[i] is not an uninterpreted term
34193424
* code = MCSAT_ERROR_ASSUMPTION_TERM_NOT_SUPPORTED
34203425
*
3426+
* if one of the terms t[i] has a type that MCSAT cannot decide on
3427+
* (i.e. not Bool, Int, Real, scalar, BitVector, or a tuple whose
3428+
* recursively flattened leaves all have one of these types)
3429+
* code = MCSAT_ERROR_ASSUMPTION_TYPE_NOT_SUPPORTED
3430+
*
34213431
* If the context does not have the MCSAT solver enabled
34223432
* code = CTX_OPERATION_NOT_SUPPORTED
34233433
*
@@ -3506,8 +3516,9 @@ __YICES_DLLSPEC__ extern smt_status_t yices_mcsat_set_initial_var_order(context_
35063516
* a model is returned in ctx->model. This model must be freed when no-longer needed by
35073517
* calling yices_free_model.
35083518
*
3509-
* If something is wrong, the function returns YICES_STATUS_ERROR and sets the yices error report
3510-
* (code = CTX_INVALID_OPERATION).
3519+
* If something is wrong, the function returns YICES_STATUS_ERROR and sets the yices error report.
3520+
* This includes CTX_INVALID_OPERATION and any error propagated from the internal
3521+
* yices_check_context_with_model call used for model refutation.
35113522
*
35123523
* Since 2.6.4.
35133524
*/

src/include/yices_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ typedef enum error_code {
480480
*/
481481
MCSAT_ERROR_UNSUPPORTED_THEORY = 1000,
482482
MCSAT_ERROR_ASSUMPTION_TERM_NOT_SUPPORTED = 1001,
483+
MCSAT_ERROR_ASSUMPTION_TYPE_NOT_SUPPORTED = 1002,
483484

484485
/*
485486
* Input/output and system errors

src/mcsat/preprocessor.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,12 @@ term_t preprocessor_apply(preprocessor_t* pre, term_t t, ivector_t* out, bool is
23972397
return t_pre;
23982398
}
23992399

2400+
void preprocessor_tuple_blast(preprocessor_t* pre, term_t t, ivector_t* out) {
2401+
ivector_reset(out);
2402+
tuple_blast_term(pre, t);
2403+
tuple_blast_get(pre, t, out);
2404+
}
2405+
24002406
void preprocessor_set_exception_handler(preprocessor_t* pre, jmp_buf* handler) {
24012407
pre->exception = handler;
24022408
}

src/mcsat/preprocessor.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ void preprocessor_destruct(preprocessor_t* pre);
111111
/** Preprocess the term, add any additional assertions to output vector. */
112112
term_t preprocessor_apply(preprocessor_t* pre, term_t t, ivector_t* out, bool is_assertion);
113113

114+
/*
115+
* Tuple-blast term t and copy its flattened leaves into out.
116+
* - out is reset first.
117+
* - leaf order matches type_collect_blasted_atom_types/type leaf order.
118+
* - leaves are memoized and stable across later tuple-blast calls.
119+
*/
120+
void preprocessor_tuple_blast(preprocessor_t* pre, term_t t, ivector_t* out);
121+
114122
/** Set tracer */
115123
void preprocessor_set_tracer(preprocessor_t* pre, tracer_t* tracer);
116124

0 commit comments

Comments
 (0)