Skip to content

Commit ed5fbaf

Browse files
ahmed-irfandisteph
authored andcommitted
Fix ef-solve crash and spurious "model error" on UF-in-arithmetic quantifiers (#657)
ef-solve on a forall body with an uninterpreted-function application inside arithmetic (e.g. (f x) in (< x C)) crashed, and on release builds aborted with a bogus "FATAL ERROR: ... model error" bug report. Two independent causes: - quant_pattern.c walked terms with term_child, which only supports composite terms. Arithmetic/bitvector polynomials, products, and projections (which term_num_children still counts) hit term_child's composite path and read a wrong descriptor -> ASan/debug crash, release UB. Add a shared term_ith_subterm() in term_explorer.c that reads those components off the descriptor, and use it in the three quant pattern walkers. - ef_generalize3 built a full model via yices_model_from_map, which rejects function-typed existentials. For a problem with an uninterpreted-function existential and arithmetic universals (unsupported by projection-based generalization) this is now reported as 'unknown' instead of a fatal bug. Adds regression test tests/regress/efsmt/ef-tests/iss657.ys.
1 parent 6435def commit ed5fbaf

7 files changed

Lines changed: 112 additions & 15 deletions

File tree

src/exists_forall/efsolver.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,28 @@ static term_t ef_generalize2(ef_prob_t *prob, term_t cex_cnstr, uint32_t i, term
985985
}
986986

987987

988+
/*
989+
* Check whether constraint i has an existential variable of function type.
990+
* Projection-based generalization can't build a concrete model that assigns
991+
* such a variable a value, so it can't handle these problems.
992+
*/
993+
static bool ef_cnstr_has_function_evar(ef_solver_t *solver, uint32_t i) {
994+
ef_cnstr_t *cnstr;
995+
term_table_t *terms;
996+
uint32_t j, n;
997+
998+
terms = solver->prob->terms;
999+
cnstr = solver->prob->cnstr + i;
1000+
n = ef_constraint_num_evars(cnstr);
1001+
for (j=0; j<n; j++) {
1002+
if (is_function_term(terms, cnstr->evars[j])) {
1003+
return true;
1004+
}
1005+
}
1006+
return false;
1007+
}
1008+
1009+
9881010
/*
9891011
* Option 3: generalize by computing an implicant then
9901012
* applying projection.
@@ -1013,6 +1035,15 @@ static term_t ef_generalize3(ef_solver_t *solver, term_t cex_cnstr, uint32_t i)
10131035
assert(n == solver->all_values.size);
10141036
mdl = yices_model_from_map(n, solver->all_vars.data, solver->all_values.data);
10151037
if (mdl == NULL) {
1038+
if (ef_cnstr_has_function_evar(solver, i)) {
1039+
// An existential variable is an uninterpreted function; its value can't be
1040+
// represented in the concrete map needed for projection, so we can't
1041+
// refine the exists context and make progress. Report 'unknown' rather
1042+
// than a spurious bug: this combination of an uninterpreted-function
1043+
// existential with arithmetic universals is not supported by the ef-solver.
1044+
solver->status = EF_STATUS_UNKNOWN;
1045+
return NULL_TERM;
1046+
}
10161047
// error in the model construction
10171048
solver->status = EF_STATUS_MDL_ERROR;
10181049
solver->error_code = yices_error_code();

src/solvers/quant/quant_pattern.c

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ void quant_process_pattern_term(term_table_t *terms, term_t t, ivector_t *pv, iv
175175
// process all children (if any)
176176
n = term_num_children(terms, x);
177177
for(i=0; i<n; i++) {
178-
u = term_child(terms, x, i);
178+
u = term_ith_subterm(terms, x, i);
179+
if (u == NULL_TERM) continue; // constant monomial of a sum: nothing to recurse into
179180
quant_process_pattern_term(terms, u, pv, f, fa, c);
180181
}
181182

@@ -236,9 +237,9 @@ void quant_process_pattern_term(term_table_t *terms, term_t t, ivector_t *pv, iv
236237
break;
237238

238239
default:
239-
// printf("Unsupported term (kind %d): ", kind);
240-
// yices_pp_term(stdout, x, 120, 1, 0);
241-
assert(false);
240+
// arithmetic/bitvector operators and atoms and other interpreted terms:
241+
// nothing to collect here; their leaves are gathered via the recursion above
242+
break;
242243
}
243244
}
244245

@@ -270,7 +271,8 @@ static bool quant_infer_single_fapps(term_table_t *terms, term_t t, int_hmap_t *
270271
if (n != 0) {
271272
init_int_hmap(childMap, 0);
272273
for(i=0; i<n; i++) {
273-
u = term_child(terms, x, i);
274+
u = term_ith_subterm(terms, x, i);
275+
if (u == NULL_TERM) continue; // constant monomial of a sum
274276
int_hmap_reset(childMap);
275277
skip |= quant_infer_single_fapps(terms, u, childMap, nuvars, out);
276278

@@ -304,9 +306,8 @@ static bool quant_infer_single_fapps(term_table_t *terms, term_t t, int_hmap_t *
304306
break;
305307

306308
default:
307-
// printf("Unsupported term (kind %d): ", kind);
308-
// yices_pp_term(stdout, x, 120, 1, 0);
309-
assert(false);
309+
// interpreted operator/atom: do not propagate its variables upward
310+
break;
310311
}
311312

312313
}
@@ -356,9 +357,9 @@ static bool quant_infer_single_fapps(term_table_t *terms, term_t t, int_hmap_t *
356357
break;
357358

358359
default:
359-
// printf("Unsupported term (kind %d): ", kind);
360-
// yices_pp_term(stdout, x, 120, 1, 0);
361-
assert(false);
360+
// interpreted operator/atom: cannot serve as a single-fapp pattern
361+
skip = true;
362+
break;
362363
}
363364

364365
#if TRACE
@@ -412,7 +413,8 @@ static bool quant_infer_multi_fapps(term_table_t *terms, term_t t, ptr_hmap_t *u
412413
n = term_num_children(terms, x);
413414
if (n != 0) {
414415
for(i=0; i<n; i++) {
415-
u = term_child(terms, x, i);
416+
u = term_ith_subterm(terms, x, i);
417+
if (u == NULL_TERM) continue; // constant monomial of a sum
416418
skip |= quant_infer_multi_fapps(terms, u, uv2fapp, fapp2uv);
417419
}
418420
}
@@ -507,9 +509,9 @@ static bool quant_infer_multi_fapps(term_table_t *terms, term_t t, ptr_hmap_t *u
507509
break;
508510

509511
default:
510-
// printf("Unsupported term (kind %d): ", kind);
511-
// yices_pp_term(stdout, x, 120, 1, 0);
512-
assert(false);
512+
// interpreted operator/atom: cannot host multi-fapp mappings
513+
skip = true;
514+
break;
513515
}
514516

515517
return skip;

src/terms/term_explorer.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,48 @@ term_t term_child(term_table_t *table, term_t t, uint32_t i) {
484484
}
485485

486486

487+
/*
488+
* i-th subterm of t (see header): like term_child, but also supports the
489+
* arithmetic/bitvector sums, products, and projections that term_num_children
490+
* counts. Reads their components straight from the descriptor (no coefficient
491+
* is materialized). Returns NULL_TERM for a sum's constant monomial.
492+
*/
493+
term_t term_ith_subterm(term_table_t *table, term_t t, uint32_t i) {
494+
term_t v;
495+
496+
assert(good_term(table, t) && i < term_num_children(table, t));
497+
498+
if (is_neg_term(t)) {
499+
return term_child(table, t, i); // (not u): a single child u
500+
}
501+
502+
switch (term_kind(table, t)) {
503+
case ARITH_POLY:
504+
v = poly_term_desc(table, t)->mono[i].var;
505+
break;
506+
case ARITH_FF_POLY:
507+
v = finitefield_poly_term_desc(table, t)->mono[i].var;
508+
break;
509+
case BV64_POLY:
510+
v = bvpoly64_term_desc(table, t)->mono[i].var;
511+
break;
512+
case BV_POLY:
513+
v = bvpoly_term_desc(table, t)->mono[i].var;
514+
break;
515+
case POWER_PRODUCT:
516+
return pprod_term_desc(table, t)->prod[i].var;
517+
case SELECT_TERM:
518+
case BIT_TERM:
519+
return proj_term_arg(table, t);
520+
default:
521+
return term_child(table, t, i);
522+
}
523+
524+
// sums store their constant with variable const_idx: no subterm to recurse into
525+
return (v == const_idx) ? NULL_TERM : v;
526+
}
527+
528+
487529
/*
488530
* All children of t:
489531
* - t must be a valid term in table

src/terms/term_explorer.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ extern uint32_t term_num_children(term_table_t *table, term_t t);
8282
extern term_t term_child(term_table_t *table, term_t t, uint32_t i);
8383

8484

85+
/*
86+
* i-th subterm of t, for a generic recursive term traversal.
87+
* - t must be a valid term in table; i must be in [0 ... n-1] where
88+
* n = term_num_children(table, t)
89+
* Unlike term_child, this also handles the kinds that term_num_children counts
90+
* but term_child does not: arithmetic/bitvector sums, products, and projections
91+
* (select/bit). For every other kind it behaves exactly like term_child.
92+
* - returns NULL_TERM for the constant monomial of a sum (no subterm there)
93+
*/
94+
extern term_t term_ith_subterm(term_table_t *table, term_t t, uint32_t i);
95+
96+
8597
/*
8698
* All children of t:
8799
* - t must be a valid term in table
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
;; Issue #657: ef-solve on a problem with an uninterpreted-function
2+
;; existential (cryWidthUnknown) and an arithmetic universal (x).
3+
;; This combination is not supported by the projection-based ef-solver, so
4+
;; the expected answer is 'unknown'. It used to crash / report a spurious bug.
5+
(define cryWidthUnknown::(-> int int))
6+
(define k_2_to_64::int 18446744073709551616)
7+
(assert (forall (x::int) (or (> (cryWidthUnknown x) 64) (< x k_2_to_64))))
8+
(ef-solve)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
unknown
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--mode=ef

0 commit comments

Comments
 (0)