Skip to content

Commit 345d7eb

Browse files
martinmartin
authored andcommitted
Verify per history rather than combining them
When a history more sophisticated than --ahistorical is used there may be multiple histories that reach the same location. We can get more precise results by verifying against each and then merging rather than merging and verifying once.
1 parent 2a9be98 commit 345d7eb

File tree

1 file changed

+136
-14
lines changed

1 file changed

+136
-14
lines changed

src/goto-analyzer/static_verifier.cpp

Lines changed: 136 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,31 @@ static void static_verifier_console(
262262
m.result() << '\n';
263263
}
264264

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+
265290
/// Runs the analyzer and then prints out the domain
266291
/// \param goto_model: the program analyzed
267292
/// \param ai: the abstract interpreter after it has been run to fix point
@@ -300,32 +325,129 @@ bool static_verifier(
300325
continue;
301326

302327
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);
306328

307329
results.push_back(static_verifier_resultt());
308330
auto &result = results.back();
309331

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
311340
{
312341
result.status = static_verifier_resultt::BOTTOM;
313342
++pass;
314343
}
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)
321345
{
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+
}
324366
}
325367
else
326368
{
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+
}
329451
}
330452

331453
result.source_location = i_it->source_location;

0 commit comments

Comments
 (0)