@@ -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,46 @@ 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 (result.front () == loop_id, " Leaving a loop we haven't entered." );
124
+ result.pop_front ();
125
+ }
126
+ }
127
+
128
+ INVARIANT (!result.empty (), " The assignable violation is not in a loop." );
129
+ return result;
130
+ }
131
+
132
+ std::list<loop_idt> cegis_verifiert::get_cause_loop_id (
97
133
const goto_tracet &goto_trace,
98
134
const goto_programt::const_targett violation)
99
135
{
100
- optionalt <loop_idt> result;
136
+ std::list <loop_idt> result;
101
137
102
138
// build the dependence graph
103
139
const namespacet ns (goto_model.symbol_table );
@@ -165,7 +201,8 @@ optionalt<loop_idt> cegis_verifiert::get_cause_loop_id(
165
201
// if it is dependent on the loop havoc.
166
202
if (reachable_set.count (dependence_graph[from].get_node_id ()))
167
203
{
168
- result = loop_idt (step.function_id , original_loop_number_map[step.pc ]);
204
+ result.push_back (
205
+ loop_idt (step.function_id , original_loop_number_map[step.pc ]));
169
206
return result;
170
207
}
171
208
}
@@ -434,9 +471,12 @@ optionalt<cext> cegis_verifiert::verify()
434
471
original_functions[fun_entry.first ].copy_from (fun_entry.second .body );
435
472
}
436
473
437
- // Annotate the candidates tot the goto_model for checking.
474
+ // Annotate the candidates to the goto_model for checking.
438
475
annotate_invariants (invariant_candidates, goto_model);
439
476
477
+ // Annotate assigns
478
+ annotate_assigns (assigns_map, goto_model);
479
+
440
480
// Control verbosity.
441
481
// We allow non-error output message only when verbosity is set to at least 9.
442
482
const unsigned original_verbosity = log .get_message_handler ().get_verbosity ();
@@ -496,14 +536,18 @@ optionalt<cext> cegis_verifiert::verify()
496
536
continue ;
497
537
498
538
first_violation = property_it.first ;
499
- exprt violated_predicate = property_it.second .pc ->condition ();
539
+ const auto &trace = checker->get_traces ()[property_it.first ];
540
+ const exprt &violated_predicate = property_it.second .pc ->condition ();
500
541
501
- // The pointer checked in the null-pointer-check violation.
542
+ // The pointer checked in the null-pointer-check violation and assignable
543
+ // violation.
502
544
exprt checked_pointer = true_exprt ();
503
545
504
546
// Type of the violation
505
547
cext::violation_typet violation_type = cext::violation_typet::cex_other;
506
548
549
+ // Decide the violation type from the description of violation
550
+
507
551
// The violation is a pointer OOB check.
508
552
if ((property_it.second .description .find (
509
553
" dereference failure: pointer outside object bounds in" ) !=
@@ -537,49 +581,86 @@ optionalt<cext> cegis_verifiert::verify()
537
581
violation_type = cext::violation_typet::cex_not_hold_upon_entry;
538
582
}
539
583
584
+ // The violation is an assignable check.
585
+ if (property_it.second .description .find (" assignable" ) != std::string::npos)
586
+ {
587
+ violation_type = cext::violation_typet::cex_assignable;
588
+ }
589
+
590
+ // Compute the cause loop---the loop for which we synthesize loop contracts,
591
+ // and the counterexample.
592
+
593
+ // If the violation is an assignable check, we synthesize assigns targets.
594
+ // In the case, cause loops are all loops the violation is in. We keep
595
+ // adding the new assigns target to the most-inner loop that does not
596
+ // contain the new target until the assignable violation is resolved.
597
+
598
+ // For other cases, we synthesize loop invariant clauses. We synthesize
599
+ // invariants for one loop at a time. So we return only the first cause loop
600
+ // although there can be multiple ones.
601
+
602
+ log .debug () << " Start to compute cause loop ids." << messaget::eom;
603
+
540
604
// 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 );
605
+ std::list<loop_idt> cause_loop_ids;
606
+
607
+ // Doing assigns-synthesis or invariant-synthesis
608
+ if (violation_type == cext::violation_typet::cex_assignable)
609
+ {
610
+ checked_pointer = static_cast <const exprt &>(
611
+ property_it.second .pc ->condition ().find (ID_checked_assigns));
612
+ cause_loop_ids = get_cause_loop_id_for_assigns (trace);
613
+ cext result (violation_type);
614
+ result.cause_loop_ids = cause_loop_ids;
615
+ result.checked_pointer = checked_pointer;
616
+ restore_functions ();
617
+ return result;
618
+ }
619
+
620
+ // We construct the full counterexample only for violations other than
621
+ // assignable checks.
622
+
623
+ // Although there can be multiple cause loop ids. We only synthesize
624
+ // loop invariants for the first cause loop.
625
+ cause_loop_ids = get_cause_loop_id (trace, property_it.second .pc );
545
626
546
- if (!cause_loop_id. has_value ())
627
+ if (cause_loop_ids. empty ())
547
628
{
548
- log .debug () << " No cause loop found!\n " ;
629
+ log .debug () << " No cause loop found!" << messaget::eom ;
549
630
restore_functions ();
550
631
551
632
return cext (violation_type);
552
633
}
553
634
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
635
// 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 ),
636
+ bool is_violation_in_loop = is_instruction_in_transfomed_loop (
637
+ cause_loop_ids. front (),
638
+ goto_model.get_goto_function (cause_loop_ids. front ().function_id ),
563
639
property_it.second .pc ->location_number );
564
640
641
+ log .debug () << " Found cause loop with function id: "
642
+ << cause_loop_ids.front ().function_id
643
+ << " , and loop number: " << cause_loop_ids.front ().loop_number
644
+ << messaget::eom;
645
+
565
646
// We always strengthen in_clause if the violation is
566
647
// invariant-not-preserved.
567
648
if (violation_type == cext::violation_typet::cex_not_preserved)
568
- violation_in_loop = true ;
649
+ is_violation_in_loop = true ;
569
650
570
651
restore_functions ();
571
652
572
653
auto return_cex = build_cex (
573
- checker-> get_traces ()[property_it. first ] ,
654
+ trace ,
574
655
get_loop_head (
575
- cause_loop_id. value ().loop_number ,
656
+ cause_loop_ids. front ().loop_number ,
576
657
goto_model.goto_functions
577
- .function_map [cause_loop_id. value ().function_id ])
658
+ .function_map [cause_loop_ids. front ().function_id ])
578
659
->source_location ());
579
660
return_cex.violated_predicate = violated_predicate;
580
- return_cex.cause_loop_id = cause_loop_id ;
661
+ return_cex.cause_loop_ids = cause_loop_ids ;
581
662
return_cex.checked_pointer = checked_pointer;
582
- return_cex.is_violation_in_loop = violation_in_loop ;
663
+ return_cex.is_violation_in_loop = is_violation_in_loop ;
583
664
return_cex.violation_type = violation_type;
584
665
585
666
return return_cex;
0 commit comments