Skip to content

Commit 51a8ba2

Browse files
committed
mcsat: trace dropped atoms in preprocessor_build_tuple_model
preprocessor_build_tuple_model has three paths along which a tuple-blasted atom can quietly fail to appear in the reconstructed model: 1. one of the blasted leaf variables has no value in the trail, 2. the function-type merge in merge_blasted_function_value returns null_value for one of several structural reasons (leaf value is neither a function nor an update object, a map's arity does not match the flattened domain, or a sub-call to build_value_from_flat could not rebuild a codomain / domain / default value), 3. the non-function tuple merge in build_value_from_flat itself returns null_value. Under the previous code each of these branches just `continue`'d or returned null_value and the caller conditionally invoked model_map_term only on success. A user inspecting (show-model) would see the atom missing with no signal anywhere as to why. Emit a short line under the existing "mcsat::preprocess" trace tag at each drop site: - the leaf-missing case in preprocessor_build_tuple_model names the atom and the leaf index that is unassigned; - merge_blasted_function_value records a short reason string at each of its five `goto done` exits (via a local fail_reason variable set immediately before the jump), and emits it from the single cleanup label; the caller then adds the concrete atom term; - the tuple (non-function) branch in preprocessor_build_tuple_model names the atom and notes that the leaves did not decompose. trace_enabled is a no-op in NDEBUG, so release builds pay nothing at runtime. mcsat_trace_printf and trace_term_ln are already used from this file under the same tag, so no new headers or dependencies. No semantic change to the rebuilt model; the 31 api tests and the 8 tuple .ys regressions still pass in debug, sanitize, and release.
1 parent a349e1f commit 51a8ba2

1 file changed

Lines changed: 39 additions & 0 deletions

File tree

src/mcsat/preprocessor.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2422,6 +2422,11 @@ value_t merge_blasted_function_value(preprocessor_t* pre, value_table_t* vtbl, t
24222422
ivector_t leaf_maps[nleaves];
24232423
uint32_t flat_n;
24242424
uint32_t maps_init = 0;
2425+
/* Short tag describing why we bailed, so the caller (or a user
2426+
* investigating a missing model entry) can see the cause through a
2427+
* single line of trace output. Set immediately before each `goto done`
2428+
* error exit; the successful path leaves it NULL. */
2429+
const char* fail_reason = NULL;
24252430

24262431
/* All heap-backed scratch vectors are initialized up-front so that every
24272432
* exit through the `done:` label has the same cleanup responsibilities.
@@ -2446,6 +2451,7 @@ value_t merge_blasted_function_value(preprocessor_t* pre, value_table_t* vtbl, t
24462451
init_ivector(&leaf_maps[i], 0);
24472452
maps_init = i + 1;
24482453
if (!object_is_function(vtbl, leaves[i]) && !object_is_update(vtbl, leaves[i])) {
2454+
fail_reason = "leaf value is neither a function nor an update object";
24492455
goto done;
24502456
}
24512457
if (object_is_update(vtbl, leaves[i])) {
@@ -2456,6 +2462,7 @@ value_t merge_blasted_function_value(preprocessor_t* pre, value_table_t* vtbl, t
24562462
for (j = 0; j < vtbl->hset1->nelems; ++j) {
24572463
value_map_t* map = vtbl_map(vtbl, vtbl->hset1->data[j]);
24582464
if (map->arity != flat_n) {
2465+
fail_reason = "update leaf map arity does not match flattened domain";
24592466
goto done;
24602467
}
24612468
add_unique_flat_args(&unique_offsets, &unique_args, map->arg, flat_n);
@@ -2471,6 +2478,7 @@ value_t merge_blasted_function_value(preprocessor_t* pre, value_table_t* vtbl, t
24712478
for (j = 0; j < fun_value->map_size; ++j) {
24722479
value_map_t* map = vtbl_map(vtbl, fun_value->map[j]);
24732480
if (map->arity != flat_n) {
2481+
fail_reason = "function leaf map arity does not match flattened domain";
24742482
goto done;
24752483
}
24762484
add_unique_flat_args(&unique_offsets, &unique_args, map->arg, flat_n);
@@ -2491,6 +2499,7 @@ value_t merge_blasted_function_value(preprocessor_t* pre, value_table_t* vtbl, t
24912499
idx = 0;
24922500
value_t out_val = build_value_from_flat(pre, vtbl, codom, leaf_values, &idx);
24932501
if (out_val == null_value) {
2502+
fail_reason = "could not build codomain value from flat leaf values";
24942503
goto done;
24952504
}
24962505

@@ -2499,6 +2508,7 @@ value_t merge_blasted_function_value(preprocessor_t* pre, value_table_t* vtbl, t
24992508
for (j = 0; j < fun->ndom; ++j) {
25002509
args_orig[j] = build_value_from_flat(pre, vtbl, fun->domain[j], flat_args, &idx);
25012510
if (args_orig[j] == null_value) {
2511+
fail_reason = "could not rebuild a domain argument from flat leaf values";
25022512
goto done;
25032513
}
25042514
}
@@ -2510,12 +2520,17 @@ value_t merge_blasted_function_value(preprocessor_t* pre, value_table_t* vtbl, t
25102520
uint32_t idx = 0;
25112521
value_t def_val = build_value_from_flat(pre, vtbl, codom, leaf_defaults, &idx);
25122522
if (def_val == null_value) {
2523+
fail_reason = "could not rebuild the function default value";
25132524
goto done;
25142525
}
25152526
result = vtbl_mk_function(vtbl, tau, orig_maps.size, (value_t*) orig_maps.data, def_val);
25162527
}
25172528

25182529
done:
2530+
if (fail_reason != NULL && trace_enabled(pre->tracer, "mcsat::preprocess")) {
2531+
mcsat_trace_printf(pre->tracer,
2532+
"merge_blasted_function_value: %s\n", fail_reason);
2533+
}
25192534
/* Single cleanup path for every error exit. All ivectors above are
25202535
* unconditionally initialized before any `goto done`, so unconditional
25212536
* deletes here are correct and stay correct if new error branches are
@@ -2567,6 +2582,18 @@ void preprocessor_build_tuple_model(preprocessor_t* pre, model_t* model) {
25672582
leaf_vals[j] = v;
25682583
}
25692584
if (!ok) {
2585+
/* A blasted leaf was never assigned a value by the mcsat search.
2586+
* We cannot reconstruct a value for the original atom, so it will
2587+
* be absent from the returned model. Log which atom and which leaf
2588+
* index so a user investigating a missing (show-model) entry can
2589+
* pin the gap. */
2590+
if (trace_enabled(pre->tracer, "mcsat::preprocess")) {
2591+
mcsat_trace_printf(pre->tracer,
2592+
"preprocessor_build_tuple_model: dropping atom ");
2593+
trace_term_ln(pre->tracer, pre->terms, atom);
2594+
mcsat_trace_printf(pre->tracer,
2595+
" (blasted leaf %u has no value in the trail model)\n", j);
2596+
}
25702597
delete_ivector(&leaves);
25712598
continue;
25722599
}
@@ -2575,12 +2602,24 @@ void preprocessor_build_tuple_model(preprocessor_t* pre, model_t* model) {
25752602
value_t f = merge_blasted_function_value(pre, vtbl, tau, leaf_vals, n);
25762603
if (f >= 0) {
25772604
model_map_term(model, atom, f);
2605+
} else if (trace_enabled(pre->tracer, "mcsat::preprocess")) {
2606+
/* merge_blasted_function_value has already traced a reason code;
2607+
* complete the message here with the concrete atom identity. */
2608+
mcsat_trace_printf(pre->tracer,
2609+
"preprocessor_build_tuple_model: dropping function atom ");
2610+
trace_term_ln(pre->tracer, pre->terms, atom);
25782611
}
25792612
} else {
25802613
uint32_t idx = 0;
25812614
value_t v = build_value_from_flat(pre, vtbl, tau, leaf_vals, &idx);
25822615
if (v >= 0) {
25832616
model_map_term(model, atom, v);
2617+
} else if (trace_enabled(pre->tracer, "mcsat::preprocess")) {
2618+
mcsat_trace_printf(pre->tracer,
2619+
"preprocessor_build_tuple_model: dropping tuple atom ");
2620+
trace_term_ln(pre->tracer, pre->terms, atom);
2621+
mcsat_trace_printf(pre->tracer,
2622+
" (leaf values did not decompose into a tuple value)\n");
25842623
}
25852624
}
25862625

0 commit comments

Comments
 (0)