@@ -32,6 +32,7 @@ Author: Qinheping Hu
32
32
#include < goto-checker/all_properties_verifier_with_trace_storage.h>
33
33
#include < goto-checker/multi_path_symex_checker.h>
34
34
#include < goto-instrument/contracts/contracts.h>
35
+ #include < goto-instrument/contracts/instrument_spec_assigns.h>
35
36
#include < goto-instrument/contracts/utils.h>
36
37
#include < goto-instrument/havoc_utils.h>
37
38
#include < langapi/language_util.h>
@@ -93,11 +94,47 @@ optionst cegis_verifiert::get_options()
93
94
return options;
94
95
}
95
96
96
- optionalt<loop_idt> cegis_verifiert::get_cause_loop_id (
97
+ std::list<loop_idt>
98
+ cegis_verifiert::get_cause_loop_id_for_assigns (const goto_tracet &goto_trace)
99
+ {
100
+ std::list<loop_idt> result;
101
+
102
+ // We say a loop is the cause loop of an assignable-violation if the inclusion
103
+ // check is in the loop.
104
+
105
+ // So we check what loops the last step of the trace is in.
106
+ // Transformed loop head:
107
+ // ASSIGN entered_loop = false
108
+ // Transformed loop end:
109
+ // ASSIGN entered_loop = true
110
+ for (const auto &step : goto_trace.steps )
111
+ {
112
+ // We are entering a loop.
113
+ if (is_transformed_loop_head (step.pc ))
114
+ {
115
+ result.push_front (
116
+ loop_idt (step.function_id , original_loop_number_map[step.pc ]));
117
+ }
118
+ // We are leaving a loop.
119
+ else if (is_transformed_loop_end (step.pc ))
120
+ {
121
+ const loop_idt loop_id (
122
+ step.function_id , original_loop_number_map[step.pc ]);
123
+ INVARIANT (
124
+ result.front () == loop_id, " Leaving a loop we haven't entered." );
125
+ result.pop_front ();
126
+ }
127
+ }
128
+
129
+ INVARIANT (!result.empty (), " The assignable violation is not in a loop." );
130
+ return result;
131
+ }
132
+
133
+ std::list<loop_idt> cegis_verifiert::get_cause_loop_id (
97
134
const goto_tracet &goto_trace,
98
135
const goto_programt::const_targett violation)
99
136
{
100
- optionalt <loop_idt> result;
137
+ std::list <loop_idt> result;
101
138
102
139
// build the dependence graph
103
140
const namespacet ns (goto_model.symbol_table );
@@ -165,7 +202,8 @@ optionalt<loop_idt> cegis_verifiert::get_cause_loop_id(
165
202
// if it is dependent on the loop havoc.
166
203
if (reachable_set.count (dependence_graph[from].get_node_id ()))
167
204
{
168
- result = loop_idt (step.function_id , original_loop_number_map[step.pc ]);
205
+ result.push_back (
206
+ loop_idt (step.function_id , original_loop_number_map[step.pc ]));
169
207
return result;
170
208
}
171
209
}
@@ -434,9 +472,12 @@ optionalt<cext> cegis_verifiert::verify()
434
472
original_functions[fun_entry.first ].copy_from (fun_entry.second .body );
435
473
}
436
474
437
- // Annotate the candidates tot the goto_model for checking.
475
+ // Annotate the candidates to the goto_model for checking.
438
476
annotate_invariants (invariant_candidates, goto_model);
439
477
478
+ // Annotate assigns
479
+ annotate_assigns (assigns_map, goto_model);
480
+
440
481
// Control verbosity.
441
482
// We allow non-error output message only when verbosity is set to at least 9.
442
483
const unsigned original_verbosity = log .get_message_handler ().get_verbosity ();
@@ -495,15 +536,15 @@ optionalt<cext> cegis_verifiert::verify()
495
536
if (property_it.second .status != property_statust::FAIL)
496
537
continue ;
497
538
539
+ // Store the violation that we want to fix with synthesized
540
+ // assigns/invariant.
498
541
first_violation = property_it.first ;
499
- exprt violated_predicate = property_it.second .pc ->condition ();
500
-
501
- // The pointer checked in the null-pointer-check violation.
502
- exprt checked_pointer = true_exprt ();
503
542
504
543
// Type of the violation
505
544
cext::violation_typet violation_type = cext::violation_typet::cex_other;
506
545
546
+ // Decide the violation type from the description of violation
547
+
507
548
// The violation is a pointer OOB check.
508
549
if ((property_it.second .description .find (
509
550
" dereference failure: pointer outside object bounds in" ) !=
@@ -516,11 +557,6 @@ optionalt<cext> cegis_verifiert::verify()
516
557
if (property_it.second .description .find (" pointer NULL" ) != std::string::npos)
517
558
{
518
559
violation_type = cext::violation_typet::cex_null_pointer;
519
- checked_pointer = property_it.second .pc ->condition ()
520
- .operands ()[0 ]
521
- .operands ()[1 ]
522
- .operands ()[0 ];
523
- INVARIANT (checked_pointer.id () == ID_symbol, " Checking pointer symbol" );
524
560
}
525
561
526
562
// The violation is a loop-invariant-preservation check.
@@ -537,51 +573,97 @@ optionalt<cext> cegis_verifiert::verify()
537
573
violation_type = cext::violation_typet::cex_not_hold_upon_entry;
538
574
}
539
575
540
- // The loop which could be the cause of the violation .
541
- // We say a loop is the cause loop if the violated predicate is dependent
542
- // upon the write set of the loop.
543
- optionalt<loop_idt> cause_loop_id = get_cause_loop_id (
544
- checker-> get_traces ()[property_it. first ], property_it. second . pc );
576
+ // The violation is an assignable check .
577
+ if (property_it. second . description . find ( " assignable " ) != std::string::npos)
578
+ {
579
+ violation_type = cext::violation_typet::cex_assignable;
580
+ }
545
581
546
- if (!cause_loop_id.has_value ())
582
+ // Compute the cause loop---the loop for which we synthesize loop contracts,
583
+ // and the counterexample.
584
+
585
+ // If the violation is an assignable check, we synthesize assigns targets.
586
+ // In the case, cause loops are all loops the violation is in. We keep
587
+ // adding the new assigns target to the most-inner loop that does not
588
+ // contain the new target until the assignable violation is resolved.
589
+
590
+ // For other cases, we synthesize loop invariant clauses. We synthesize
591
+ // invariants for one loop at a time. So we return only the first cause loop
592
+ // although there can be multiple ones.
593
+
594
+ log .debug () << " Start to compute cause loop ids." << messaget::eom;
595
+
596
+ const auto &trace = checker->get_traces ()[property_it.first ];
597
+
598
+ // Doing assigns-synthesis or invariant-synthesis
599
+ if (violation_type == cext::violation_typet::cex_assignable)
600
+ {
601
+ cext result (violation_type);
602
+ result.cause_loop_ids = get_cause_loop_id_for_assigns (trace);
603
+ result.checked_pointer = static_cast <const exprt &>(
604
+ property_it.second .pc ->condition ().find (ID_checked_assigns));
605
+ restore_functions ();
606
+ return result;
607
+ }
608
+
609
+ // We construct the full counterexample only for violations other than
610
+ // assignable checks.
611
+
612
+ // Although there can be multiple cause loop ids. We only synthesize
613
+ // loop invariants for the first cause loop.
614
+ const std::list<loop_idt> cause_loop_ids =
615
+ get_cause_loop_id (trace, property_it.second .pc );
616
+
617
+ if (cause_loop_ids.empty ())
547
618
{
548
- log .debug () << " No cause loop found!\n " ;
619
+ log .debug () << " No cause loop found!" << messaget::eom ;
549
620
restore_functions ();
550
621
551
622
return cext (violation_type);
552
623
}
553
624
554
- log .debug () << " Found cause loop with function id: "
555
- << cause_loop_id.value ().function_id
556
- << " , and loop number: " << cause_loop_id.value ().loop_number
557
- << " \n " ;
558
-
559
625
// Decide whether the violation is in the cause loop.
560
- bool violation_in_loop = is_instruction_in_transfomed_loop (
561
- cause_loop_id. value (),
562
- goto_model.get_goto_function (cause_loop_id. value ().function_id ),
626
+ bool is_violation_in_loop = is_instruction_in_transfomed_loop (
627
+ cause_loop_ids. front (),
628
+ goto_model.get_goto_function (cause_loop_ids. front ().function_id ),
563
629
property_it.second .pc ->location_number );
564
630
631
+ log .debug () << " Found cause loop with function id: "
632
+ << cause_loop_ids.front ().function_id
633
+ << " , and loop number: " << cause_loop_ids.front ().loop_number
634
+ << messaget::eom;
635
+
565
636
// We always strengthen in_clause if the violation is
566
637
// invariant-not-preserved.
567
638
if (violation_type == cext::violation_typet::cex_not_preserved)
568
- violation_in_loop = true ;
639
+ is_violation_in_loop = true ;
569
640
570
641
restore_functions ();
571
642
572
643
auto return_cex = build_cex (
573
- checker-> get_traces ()[property_it. first ] ,
644
+ trace ,
574
645
get_loop_head (
575
- cause_loop_id. value ().loop_number ,
646
+ cause_loop_ids. front ().loop_number ,
576
647
goto_model.goto_functions
577
- .function_map [cause_loop_id. value ().function_id ])
648
+ .function_map [cause_loop_ids. front ().function_id ])
578
649
->source_location ());
579
- return_cex.violated_predicate = violated_predicate;
580
- return_cex.cause_loop_id = cause_loop_id;
581
- return_cex.checked_pointer = checked_pointer;
582
- return_cex.is_violation_in_loop = violation_in_loop;
650
+ return_cex.violated_predicate = property_it.second .pc ->condition ();
651
+ return_cex.cause_loop_ids = cause_loop_ids;
652
+ return_cex.is_violation_in_loop = is_violation_in_loop;
583
653
return_cex.violation_type = violation_type;
584
654
655
+ // The pointer checked in the null-pointer-check violation.
656
+ if (violation_type == cext::violation_typet::cex_null_pointer)
657
+ {
658
+ return_cex.checked_pointer = property_it.second .pc ->condition ()
659
+ .operands ()[0 ]
660
+ .operands ()[1 ]
661
+ .operands ()[0 ];
662
+ INVARIANT (
663
+ return_cex.checked_pointer .id () == ID_symbol,
664
+ " Checking pointer symbol" );
665
+ }
666
+
585
667
return return_cex;
586
668
}
587
669
0 commit comments