@@ -140,62 +140,15 @@ void code_contractst::check_apply_loop_contracts(
140
140
goto_functionst::goto_functiont &goto_function,
141
141
const local_may_aliast &local_may_alias,
142
142
goto_programt::targett loop_head,
143
+ goto_programt::targett loop_end,
143
144
const loopt &loop,
145
+ exprt assigns_clause,
146
+ exprt invariant,
147
+ exprt decreases_clause,
144
148
const irep_idt &mode)
145
149
{
146
- PRECONDITION (!loop.empty ());
147
-
148
150
const auto loop_head_location = loop_head->source_location ();
149
151
150
- // find the last back edge
151
- goto_programt::targett loop_end = loop_head;
152
- for (const auto &t : loop)
153
- {
154
- if (
155
- t->is_goto () && t->get_target () == loop_head &&
156
- t->location_number > loop_end->location_number )
157
- loop_end = t;
158
- }
159
-
160
- // check for assigns, invariant, and decreases clauses
161
- auto assigns_clause = static_cast <const exprt &>(
162
- loop_end->get_condition ().find (ID_C_spec_assigns));
163
- auto invariant = static_cast <const exprt &>(
164
- loop_end->get_condition ().find (ID_C_spec_loop_invariant));
165
- auto decreases_clause = static_cast <const exprt &>(
166
- loop_end->get_condition ().find (ID_C_spec_decreases));
167
-
168
- if (invariant.is_nil ())
169
- {
170
- if (decreases_clause.is_nil () && assigns_clause.is_nil ())
171
- {
172
- // no contracts to enforce, so return
173
- return ;
174
- }
175
- else
176
- {
177
- invariant = true_exprt ();
178
- // assigns clause is missing; we will try to automatic inference
179
- log .warning () << " The loop at " << loop_head_location.as_string ()
180
- << " does not have an invariant in its contract.\n "
181
- << " Hence, a default invariant ('true') is being used.\n "
182
- << " This choice is sound, but verification may fail"
183
- << " if it is be too weak to prove the desired properties."
184
- << messaget::eom;
185
- }
186
- }
187
- else
188
- {
189
- invariant = conjunction (invariant.operands ());
190
- if (decreases_clause.is_nil ())
191
- {
192
- log .warning () << " The loop at " << loop_head_location.as_string ()
193
- << " does not have a decreases clause in its contract.\n "
194
- << " Termination of this loop will not be verified."
195
- << messaget::eom;
196
- }
197
- }
198
-
199
152
// Vector representing a (possibly multidimensional) decreases clause
200
153
const auto &decreases_clause_exprs = decreases_clause.operands ();
201
154
@@ -1022,20 +975,144 @@ void code_contractst::apply_loop_contract(
1022
975
1023
976
struct loop_graph_nodet : public graph_nodet <empty_edget>
1024
977
{
1025
- typedef const goto_programt::targett &targett;
1026
- typedef const typename natural_loops_mutablet::loopt &loopt;
1027
-
1028
- targett target;
1029
- loopt loop;
1030
-
1031
- loop_graph_nodet (targett t, loopt l) : target(t), loop(l)
978
+ const typename natural_loops_mutablet::loopt &content;
979
+ const goto_programt::targett head_target, end_target;
980
+ exprt assigns_clause, invariant, decreases_clause;
981
+
982
+ loop_graph_nodet (
983
+ const typename natural_loops_mutablet::loopt &loop,
984
+ const goto_programt::targett head,
985
+ const goto_programt::targett end,
986
+ const exprt &assigns,
987
+ const exprt &inv,
988
+ const exprt &decreases)
989
+ : content(loop),
990
+ head_target (head),
991
+ end_target(end),
992
+ assigns_clause(assigns),
993
+ invariant(inv),
994
+ decreases_clause(decreases)
1032
995
{
1033
996
}
1034
997
};
1035
998
grapht<loop_graph_nodet> loop_nesting_graph;
1036
999
1037
- for (const auto &loop : natural_loops.loop_map )
1038
- loop_nesting_graph.add_node (loop.first , loop.second );
1000
+ std::list<size_t > to_check_contracts_on_children;
1001
+
1002
+ for (const auto &loop_head_and_content : natural_loops.loop_map)
1003
+ {
1004
+ const auto &loop_content = loop_head_and_content.second ;
1005
+ if (loop_content.empty ())
1006
+ continue ;
1007
+
1008
+ auto loop_head = loop_head_and_content.first ;
1009
+ auto loop_end = loop_head;
1010
+
1011
+ // Find the last back edge to `loop_head`
1012
+ for (const auto &t : loop_content)
1013
+ {
1014
+ if (
1015
+ t->is_goto () && t->get_target () == loop_head &&
1016
+ t->location_number > loop_end->location_number )
1017
+ loop_end = t;
1018
+ }
1019
+
1020
+ if (loop_end == loop_head)
1021
+ {
1022
+ log .error () << " Could not find end of the loop starting at: "
1023
+ << loop_head->source_location () << messaget::eom;
1024
+ throw 0 ;
1025
+ }
1026
+
1027
+ exprt assigns_clause =
1028
+ static_cast <const exprt &>(loop_end->condition ().find (ID_C_spec_assigns));
1029
+ exprt invariant = static_cast <const exprt &>(
1030
+ loop_end->get_condition ().find (ID_C_spec_loop_invariant));
1031
+ exprt decreases_clause = static_cast <const exprt &>(
1032
+ loop_end->get_condition ().find (ID_C_spec_decreases));
1033
+
1034
+ if (invariant.is_nil ())
1035
+ {
1036
+ if (decreases_clause.is_not_nil () || assigns_clause.is_not_nil ())
1037
+ {
1038
+ invariant = true_exprt{};
1039
+ // assigns clause is missing; we will try to automatic inference
1040
+ log .warning ()
1041
+ << " The loop at " << loop_head->source_location ().as_string ()
1042
+ << " does not have an invariant in its contract.\n "
1043
+ << " Hence, a default invariant ('true') is being used.\n "
1044
+ << " This choice is sound, but verification may fail"
1045
+ << " if it is be too weak to prove the desired properties."
1046
+ << messaget::eom;
1047
+ }
1048
+ }
1049
+ else
1050
+ {
1051
+ invariant = conjunction (invariant.operands ());
1052
+ if (decreases_clause.is_nil ())
1053
+ {
1054
+ log .warning () << " The loop at "
1055
+ << loop_head->source_location ().as_string ()
1056
+ << " does not have a decreases clause in its contract.\n "
1057
+ << " Termination of this loop will not be verified."
1058
+ << messaget::eom;
1059
+ }
1060
+ }
1061
+
1062
+ const auto idx = loop_nesting_graph.add_node (
1063
+ loop_content,
1064
+ loop_head,
1065
+ loop_end,
1066
+ assigns_clause,
1067
+ invariant,
1068
+ decreases_clause);
1069
+
1070
+ if (
1071
+ assigns_clause.is_nil () && invariant.is_nil () &&
1072
+ decreases_clause.is_nil ())
1073
+ continue ;
1074
+
1075
+ to_check_contracts_on_children.push_back (idx);
1076
+
1077
+ // If the loop has contracts to be enforced, then
1078
+ // reject irregular loops that have "holes" within their body,
1079
+ // while allowing instructions that are okay to be "out of loop".
1080
+ for (auto i = loop_head; i < loop_end; ++i)
1081
+ {
1082
+ if (loop_content.contains (i))
1083
+ continue ;
1084
+
1085
+ // `break` or `goto` out of loop, are okay
1086
+ if (i->is_goto () && !loop_content.contains (i->get_target ()))
1087
+ continue ;
1088
+
1089
+ // `assume false`, is okay
1090
+ if (i->is_assume () && i->get_condition ().is_false ())
1091
+ continue ;
1092
+
1093
+ // `set return value` followed by `dead`s, are okay
1094
+ if (i->is_set_return_value ())
1095
+ {
1096
+ do
1097
+ i++;
1098
+ while (i->is_dead ());
1099
+
1100
+ // because we increment `i` in the outer `for` loop
1101
+ i--;
1102
+
1103
+ // the `goto` for `return` will be handled in next iteration
1104
+ continue ;
1105
+ }
1106
+
1107
+ log .error () << " Cannot enforce contracts on irregular loop at: "
1108
+ << loop_head->source_location ()
1109
+ << " .\n Loop body must be a contiguous set of instructions."
1110
+ << messaget::eom;
1111
+ log .debug () << " Out-of-loop-body instruction:" << messaget::eom;
1112
+ goto_function.body .output_instruction (ns, function_name, log .debug (), *i);
1113
+ throw 0 ;
1114
+ }
1115
+ }
1039
1116
1040
1117
for (size_t outer = 0 ; outer < loop_nesting_graph.size(); ++outer)
1041
1118
{
@@ -1044,23 +1121,69 @@ void code_contractst::apply_loop_contract(
1044
1121
if (inner == outer)
1045
1122
continue ;
1046
1123
1047
- if (loop_nesting_graph[outer].loop .contains (
1048
- loop_nesting_graph[inner].target ))
1124
+ if (loop_nesting_graph[outer].content .contains (
1125
+ loop_nesting_graph[inner].head_target ))
1126
+ {
1127
+ if (!loop_nesting_graph[outer].content .contains (
1128
+ loop_nesting_graph[inner].end_target ))
1129
+ {
1130
+ log .error ()
1131
+ << " Overlapping loops at:\n "
1132
+ << loop_nesting_graph[outer].head_target ->source_location ()
1133
+ << " \n and\n "
1134
+ << loop_nesting_graph[inner].head_target ->source_location ()
1135
+ << " \n Loops must be nested or sequential for contracts to be "
1136
+ " enforced."
1137
+ << messaget::eom;
1138
+ }
1049
1139
loop_nesting_graph.add_edge (inner, outer);
1140
+ }
1141
+ }
1142
+ }
1143
+
1144
+ // make sure all children of a contractified loop also have contracts
1145
+ while (!to_check_contracts_on_children.empty())
1146
+ {
1147
+ const auto loop_idx = to_check_contracts_on_children.front ();
1148
+ to_check_contracts_on_children.pop_front ();
1149
+
1150
+ const auto &loop_node = loop_nesting_graph[loop_idx];
1151
+ if (
1152
+ loop_node.assigns_clause .is_nil () && loop_node.invariant .is_nil () &&
1153
+ loop_node.decreases_clause .is_nil ())
1154
+ {
1155
+ log .error ()
1156
+ << " Inner loop at: " << loop_node.head_target ->source_location ()
1157
+ << " does not have contracts, but an enclosing loop does.\n "
1158
+ << " Please provide contracts for this loop, or unwind it first."
1159
+ << messaget::eom;
1160
+ throw 0 ;
1050
1161
}
1162
+
1163
+ for (const auto child_idx : loop_nesting_graph.get_predecessors (loop_idx))
1164
+ to_check_contracts_on_children.push_back (child_idx);
1051
1165
}
1052
1166
1053
1167
// Iterate over the (natural) loops in the function, in topo-sorted order,
1054
1168
// and apply any loop contracts that we find.
1055
1169
for (const auto &idx : loop_nesting_graph.topsort())
1056
1170
{
1057
1171
const auto &loop_node = loop_nesting_graph[idx];
1172
+ if (
1173
+ loop_node.assigns_clause .is_nil () && loop_node.invariant .is_nil () &&
1174
+ loop_node.decreases_clause .is_nil ())
1175
+ continue ;
1176
+
1058
1177
check_apply_loop_contracts (
1059
1178
function_name,
1060
1179
goto_function,
1061
1180
local_may_alias,
1062
- loop_node.target ,
1063
- loop_node.loop ,
1181
+ loop_node.head_target ,
1182
+ loop_node.end_target ,
1183
+ loop_node.content ,
1184
+ loop_node.assigns_clause ,
1185
+ loop_node.invariant ,
1186
+ loop_node.decreases_clause ,
1064
1187
symbol_table.lookup_ref (function_name).mode );
1065
1188
}
1066
1189
}
0 commit comments