@@ -262,6 +262,31 @@ static void static_verifier_console(
262
262
m.result () << ' \n ' ;
263
263
}
264
264
265
+ static static_verifier_resultt::statust
266
+ check_assertion (const ai_domain_baset &domain, exprt e, const namespacet &ns)
267
+ {
268
+ if (domain.is_bottom ())
269
+ {
270
+ return static_verifier_resultt::BOTTOM;
271
+ }
272
+
273
+ domain.ai_simplify (e, ns);
274
+ if (e.is_true ())
275
+ {
276
+ return static_verifier_resultt::TRUE ;
277
+ }
278
+ else if (e.is_false ())
279
+ {
280
+ return static_verifier_resultt::FALSE ;
281
+ }
282
+ else
283
+ {
284
+ return static_verifier_resultt::UNKNOWN;
285
+ }
286
+
287
+ UNREACHABLE;
288
+ }
289
+
265
290
// / Runs the analyzer and then prints out the domain
266
291
// / \param goto_model: the program analyzed
267
292
// / \param ai: the abstract interpreter after it has been run to fix point
@@ -300,32 +325,129 @@ bool static_verifier(
300
325
continue ;
301
326
302
327
exprt e (i_it->get_condition ());
303
- auto dp = ai.abstract_state_before (i_it);
304
- const ai_domain_baset &domain (*dp);
305
- domain.ai_simplify (e, ns);
306
328
307
329
results.push_back (static_verifier_resultt ());
308
330
auto &result = results.back ();
309
331
310
- if (domain.is_bottom ())
332
+ // If there are multiple, distinct histories that reach the same location
333
+ // we can get better results by checking with each individually rather
334
+ // than merging all of them and doing one check.
335
+ const auto trace_set_pointer =
336
+ ai.abstract_traces_before (i_it); // Keep a pointer so refcount > 0
337
+ const auto &trace_set = *trace_set_pointer;
338
+
339
+ if (trace_set.size () == 0 ) // i.e. unreachable
311
340
{
312
341
result.status = static_verifier_resultt::BOTTOM;
313
342
++pass;
314
343
}
315
- else if (e.is_true ())
316
- {
317
- result.status = static_verifier_resultt::TRUE ;
318
- ++pass;
319
- }
320
- else if (e.is_false ())
344
+ else if (trace_set.size () == 1 )
321
345
{
322
- result.status = static_verifier_resultt::FALSE ;
323
- ++fail;
346
+ auto dp = ai.abstract_state_before (i_it);
347
+
348
+ result.status = check_assertion (*dp, e, ns);
349
+ switch (result.status )
350
+ {
351
+ case static_verifier_resultt::BOTTOM:
352
+ ++pass;
353
+ break ;
354
+ case static_verifier_resultt::TRUE :
355
+ ++pass;
356
+ break ;
357
+ case static_verifier_resultt::FALSE :
358
+ ++fail;
359
+ break ;
360
+ case static_verifier_resultt::UNKNOWN:
361
+ ++unknown;
362
+ break ;
363
+ default :
364
+ UNREACHABLE;
365
+ }
324
366
}
325
367
else
326
368
{
327
- result.status = static_verifier_resultt::UNKNOWN;
328
- ++unknown;
369
+ // Multiple traces, verify against each one
370
+ std::size_t unreachable_traces = 0 ;
371
+ std::size_t true_traces = 0 ;
372
+ std::size_t false_traces = 0 ;
373
+ std::size_t unknown_traces = 0 ;
374
+
375
+ for (const auto &trace_ptr : trace_set)
376
+ {
377
+ auto dp = ai.abstract_state_before (trace_ptr);
378
+
379
+ result.status = check_assertion (*dp, e, ns);
380
+ switch (result.status )
381
+ {
382
+ case static_verifier_resultt::BOTTOM:
383
+ ++unreachable_traces;
384
+ break ;
385
+ case static_verifier_resultt::TRUE :
386
+ ++true_traces;
387
+ break ;
388
+ case static_verifier_resultt::FALSE :
389
+ ++false_traces;
390
+ break ;
391
+ case static_verifier_resultt::UNKNOWN:
392
+ ++unknown_traces;
393
+ break ;
394
+ default :
395
+ UNREACHABLE;
396
+ }
397
+ }
398
+
399
+ // Join the results
400
+ if (unknown_traces != 0 )
401
+ {
402
+ // If any trace is unknown, the final result must be unknown
403
+ result.status = static_verifier_resultt::UNKNOWN;
404
+ ++unknown;
405
+ }
406
+ else
407
+ {
408
+ if (false_traces == 0 )
409
+ {
410
+ // Definitely true; the only question is how
411
+ ++pass;
412
+ if (true_traces == 0 )
413
+ {
414
+ // Definitely not reachable
415
+ INVARIANT (
416
+ unreachable_traces == trace_set.size (),
417
+ " All traces must not reach the assertion" );
418
+ result.status = static_verifier_resultt::BOTTOM;
419
+ }
420
+ else
421
+ {
422
+ // At least one trace (may) reach it.
423
+ // All traces that reach it are safe.
424
+ result.status = static_verifier_resultt::TRUE ;
425
+ }
426
+ }
427
+ else
428
+ {
429
+ // At lease one trace (may) reach it and it is false on that trace
430
+ if (true_traces == 0 )
431
+ {
432
+ // All traces that (may) reach it are false
433
+ ++fail;
434
+ result.status = static_verifier_resultt::FALSE ;
435
+ }
436
+ else
437
+ {
438
+ // The awkward case, there are traces that (may) reach it and
439
+ // some are true, some are false. It is not entirely fair to say
440
+ // "FAILURE (if reachable)" because it's a bit more complex than
441
+ // that, "FAILURE (if reachable via a particular trace)" would be
442
+ // more accurate summary of what we know at this point.
443
+ // Given that all results of FAILURE from this analysis are
444
+ // caveated with some reachability questions, the following is not
445
+ // entirely unreasonable.
446
+ ++fail;
447
+ result.status = static_verifier_resultt::FALSE ;
448
+ }
449
+ }
450
+ }
329
451
}
330
452
331
453
result.source_location = i_it->source_location ;
0 commit comments