@@ -258,7 +258,10 @@ void goto_symext::symex_function_call_code(
258
258
if (symex_config.unwinding_assertions )
259
259
vcc (false_exprt (), " recursion unwinding assertion" , state);
260
260
261
- // add to state guard to prevent further assignments
261
+ // Rule out this path:
262
+ symex_assume_l2 (state, false_exprt ());
263
+ // Disable processing instructions until we next encounter one reachable
264
+ // without passing this instruction:
262
265
state.guard .add (false_exprt ());
263
266
}
264
267
@@ -303,7 +306,7 @@ void goto_symext::symex_function_call_code(
303
306
304
307
// produce a new frame
305
308
PRECONDITION (!state.call_stack ().empty ());
306
- framet &frame = state.call_stack ().new_frame (state.source );
309
+ framet &frame = state.call_stack ().new_frame (state.source , state. guard );
307
310
308
311
// preserve locality of local variables
309
312
locality (identifier, state, path_storage, goto_function, ns);
@@ -332,8 +335,10 @@ void goto_symext::symex_function_call_code(
332
335
}
333
336
334
337
// / pop one call frame
335
- static void
336
- pop_frame (goto_symext::statet &state, const path_storaget &path_storage)
338
+ static void pop_frame (
339
+ goto_symext::statet &state,
340
+ const path_storaget &path_storage,
341
+ bool doing_path_exploration)
337
342
{
338
343
PRECONDITION (!state.call_stack ().empty ());
339
344
@@ -347,6 +352,26 @@ pop_frame(goto_symext::statet &state, const path_storaget &path_storage)
347
352
// restore L1 renaming
348
353
state.level1 .restore_from (frame.old_level1 );
349
354
355
+ // If the program is multi-threaded then the state guard is used to
356
+ // accumulate assumptions (in symex_assume_l2) and must be left alone.
357
+ // If however it is single-threaded then we should restore the guard, as the
358
+ // guard coming out of the function may be more complex (e.g. if the callee
359
+ // was { if(x) __CPROVER_assume(false); } then the guard may still be `!x`),
360
+ // but at this point all control-flow paths have either converged or been
361
+ // proven unviable, so we can stop specifying the callee's constraints when
362
+ // we generate an assumption or VCC.
363
+
364
+ // If the guard is false, *this* path is unviable and we shouldn't discard
365
+ // that knowledge. If we're doing path exploration then we do
366
+ // tail-duplication, and we actually *are* in a more-restricted context
367
+ // than we were when the function began.
368
+ if (
369
+ state.threads .size () == 1 && !state.guard .is_false () &&
370
+ !doing_path_exploration)
371
+ {
372
+ state.guard = frame.guard_at_function_start ;
373
+ }
374
+
350
375
symex_renaming_levelt::viewt view;
351
376
state.get_level2 ().current_names .get_view (view);
352
377
@@ -387,7 +412,7 @@ void goto_symext::symex_end_of_function(statet &state)
387
412
state.guard .as_expr (), state.source .function_id , state.source , hidden);
388
413
389
414
// then get rid of the frame
390
- pop_frame (state, path_storage);
415
+ pop_frame (state, path_storage, symex_config. doing_path_exploration );
391
416
}
392
417
393
418
// / Preserves locality of parameters of a given function by applying L1
0 commit comments