1
1
/* ******************************************************************\
2
2
3
- Module: Specify write set in function contracts.
3
+ Module: Specify write set in code contracts.
4
4
5
5
Author: Felipe R. Monteiro
6
6
@@ -19,205 +19,156 @@ Date: July 2021
19
19
#include < util/c_types.h>
20
20
#include < util/pointer_predicates.h>
21
21
22
- assigns_clause_targett::assigns_clause_targett (
23
- const exprt &object,
24
- code_contractst &contract,
25
- messaget &log_parameter,
26
- const irep_idt &function_id)
27
- : contract(contract),
28
- init_block(),
29
- log(log_parameter),
30
- target(pointer_for(object)),
31
- target_id(object.id())
22
+ assigns_clauset::targett::targett (
23
+ const assigns_clauset &parent,
24
+ const exprt &expr)
25
+ : address(address_of_exprt(normalize(expr))),
26
+ expr(expr),
27
+ id(expr.id()),
28
+ parent(parent)
32
29
{
33
- INVARIANT (
34
- target.type ().id () == ID_pointer,
35
- " Assigns clause targets should be stored as pointer expressions." );
36
30
}
37
31
38
- assigns_clause_targett::~assigns_clause_targett ( )
32
+ exprt assigns_clauset::targett::normalize ( const exprt &expr )
39
33
{
34
+ if (expr.id () != ID_address_of)
35
+ return expr;
36
+
37
+ const auto &object = to_address_of_expr (expr).object ();
38
+ if (object.id () != ID_index)
39
+ return object;
40
+
41
+ return to_index_expr (object).array ();
40
42
}
41
43
42
- exprt assigns_clause_targett::alias_expression (const exprt &lhs)
44
+ exprt assigns_clauset::targett::generate_containment_check (
45
+ const address_of_exprt &lhs_address) const
43
46
{
44
47
exprt::operandst condition;
45
- exprt lhs_ptr = (lhs.id () == ID_address_of) ? to_address_of_expr (lhs).object ()
46
- : pointer_for (lhs);
47
48
48
49
// __CPROVER_w_ok(target, sizeof(target))
49
50
condition.push_back (w_ok_exprt (
50
- target ,
51
- size_of_expr (dereference_exprt (target ).type (), contract .ns ).value ()));
51
+ address ,
52
+ size_of_expr (dereference_exprt (address ).type (), parent .ns ).value ()));
52
53
53
54
// __CPROVER_same_object(lhs, target)
54
- condition.push_back (same_object (lhs_ptr, target ));
55
+ condition.push_back (same_object (lhs_address, address ));
55
56
56
57
// If assigns target was a dereference, comparing objects is enough
57
- if (target_id == ID_dereference)
58
+ if (id == ID_dereference)
58
59
{
59
60
return conjunction (condition);
60
61
}
61
62
62
- const exprt lhs_offset = pointer_offset (lhs_ptr );
63
- const exprt target_offset = pointer_offset (target );
63
+ const auto lhs_offset = pointer_offset (lhs_address );
64
+ const auto own_offset = pointer_offset (address );
64
65
65
66
// __CPROVER_offset(lhs) >= __CPROVER_offset(target)
66
- condition.push_back (binary_relation_exprt (lhs_offset, ID_ge, target_offset ));
67
+ condition.push_back (binary_relation_exprt (lhs_offset, ID_ge, own_offset ));
67
68
68
- const exprt region_lhs = plus_exprt (
69
+ const auto lhs_region = plus_exprt (
69
70
typecast_exprt::conditional_cast (
70
- size_of_expr (lhs.type (), contract.ns ).value (), lhs_offset.type ()),
71
+ size_of_expr (lhs_address.object ().type (), parent.ns ).value (),
72
+ lhs_offset.type ()),
71
73
lhs_offset);
72
74
73
- const exprt region_target = plus_exprt (
75
+ const exprt own_region = plus_exprt (
74
76
typecast_exprt::conditional_cast (
75
- size_of_expr (dereference_exprt (target ).type (), contract .ns ).value (),
76
- target_offset .type ()),
77
- target_offset );
77
+ size_of_expr (address. object ( ).type (), parent .ns ).value (),
78
+ own_offset .type ()),
79
+ own_offset );
78
80
79
81
// (sizeof(lhs) + __CPROVER_offset(lhs)) <=
80
82
// (sizeof(target) + __CPROVER_offset(target))
81
- condition.push_back (binary_relation_exprt (region_lhs , ID_le, region_target ));
83
+ condition.push_back (binary_relation_exprt (lhs_region , ID_le, own_region ));
82
84
83
85
return conjunction (condition);
84
86
}
85
87
86
- exprt assigns_clause_targett::compatible_expression (
87
- const assigns_clause_targett &called_target)
88
- {
89
- return same_object (called_target.get_target (), target);
90
- }
91
-
92
- const exprt &assigns_clause_targett::get_target () const
93
- {
94
- return target;
95
- }
96
-
97
88
assigns_clauset::assigns_clauset (
98
- const exprt &assigns,
99
- code_contractst &contract,
100
- const irep_idt function_id,
101
- messaget log_parameter)
102
- : assigns(assigns),
103
- parent(contract),
104
- function_id(function_id),
105
- log(log_parameter)
89
+ const exprt &expr,
90
+ const messaget &log,
91
+ const namespacet &ns)
92
+ : expr(expr), log(log), ns(ns)
106
93
{
107
- for (exprt target : assigns .operands ())
94
+ for (const auto &target_expr : expr .operands ())
108
95
{
109
- add_target (target );
96
+ add_target (target_expr );
110
97
}
111
98
}
112
99
113
- assigns_clauset::~assigns_clauset ( )
100
+ void assigns_clauset::add_target ( const exprt &target_expr )
114
101
{
115
- for (assigns_clause_targett *target : targets)
102
+ auto insertion_succeeded = targets.emplace (*this , target_expr).second ;
103
+
104
+ if (!insertion_succeeded)
116
105
{
117
- delete target;
106
+ log .warning () << " Ignored duplicate expression '"
107
+ << from_expr (ns, target_expr.id (), target_expr)
108
+ << " ' in assigns clause at "
109
+ << target_expr.source_location ().as_string () << messaget::eom;
118
110
}
119
111
}
120
112
121
- void assigns_clauset::add_target ( exprt target )
113
+ void assigns_clauset::remove_target ( const exprt &target_expr )
122
114
{
123
- assigns_clause_targett *new_target = new assigns_clause_targett (
124
- (target.id () == ID_address_of)
125
- ? to_index_expr (to_address_of_expr (target).object ()).array ()
126
- : target,
127
- parent,
128
- log ,
129
- function_id);
130
- targets.push_back (new_target);
115
+ targets.erase (targett (*this , targett::normalize (target_expr)));
131
116
}
132
117
133
- goto_programt assigns_clauset::havoc_code ()
118
+ goto_programt assigns_clauset::generate_havoc_code () const
134
119
{
135
120
modifiest modifies;
136
- for (const auto &t : targets)
137
- modifies.insert (to_address_of_expr (t-> get_target ()) .object ());
121
+ for (const auto &target : targets)
122
+ modifies.insert (target. address .object ());
138
123
139
124
goto_programt havoc_statements;
140
- append_havoc_code (assigns .source_location (), modifies, havoc_statements);
125
+ append_havoc_code (expr .source_location (), modifies, havoc_statements);
141
126
return havoc_statements;
142
127
}
143
128
144
- exprt assigns_clauset::alias_expression (const exprt &lhs)
129
+ exprt assigns_clauset::generate_containment_check (const exprt &lhs) const
145
130
{
146
131
// If write set is empty, no assignment is allowed.
147
132
if (targets.empty ())
148
- {
149
133
return false_exprt ();
150
- }
134
+
135
+ const auto lhs_address = address_of_exprt (targett::normalize (lhs));
151
136
152
137
exprt::operandst condition;
153
- for (assigns_clause_targett * target : targets)
138
+ for (const auto & target : targets)
154
139
{
155
- condition.push_back (target-> alias_expression (lhs ));
140
+ condition.push_back (target. generate_containment_check (lhs_address ));
156
141
}
157
142
return disjunction (condition);
158
143
}
159
144
160
- exprt assigns_clauset::compatible_expression (
161
- const assigns_clauset &called_assigns)
145
+ exprt assigns_clauset::generate_subset_check (
146
+ const assigns_clauset &subassigns) const
162
147
{
163
- if (called_assigns.targets .empty ())
164
- {
148
+ if (subassigns.targets .empty ())
165
149
return true_exprt ();
166
- }
167
150
168
- bool first_clause = true ;
169
151
exprt result = true_exprt ();
170
- for (assigns_clause_targett *called_target : called_assigns .targets )
152
+ for (const auto &subtarget : subassigns .targets )
171
153
{
172
- bool first_iter = true ;
173
- exprt current_target_compatible = false_exprt ();
174
- for (assigns_clause_targett *target : targets)
175
- {
176
- if (first_iter)
177
- {
178
- // TODO: Optimize the validation below and remove code duplication
179
- // See GitHub issue #6105 for further details
180
-
181
- // Validating the called target through __CPROVER_w_ok() is
182
- // only useful when the called target is a dereference
183
- const auto &called_target_ptr = called_target->get_target ();
184
- if (
185
- to_address_of_expr (called_target_ptr).object ().id () == ID_dereference)
186
- {
187
- // or_exprt is short-circuited, therefore
188
- // target->compatible_expression(*called_target) would not be
189
- // checked on invalid called_targets.
190
- current_target_compatible = or_exprt (
191
- not_exprt (w_ok_exprt (
192
- called_target_ptr, from_integer (0 , unsigned_int_type ()))),
193
- target->compatible_expression (*called_target));
194
- }
195
- else
196
- {
197
- current_target_compatible =
198
- target->compatible_expression (*called_target);
199
- }
200
- first_iter = false ;
201
- }
202
- else
203
- {
204
- current_target_compatible = or_exprt (
205
- current_target_compatible,
206
- target->compatible_expression (*called_target));
207
- }
208
- }
209
- if (first_clause)
210
- {
211
- result = current_target_compatible;
212
- first_clause = false ;
213
- }
214
- else
154
+ // TODO: Optimize the implication generated due to the validity check.
155
+ // In some cases, e.g. when `subtarget` is known to be `NULL`,
156
+ // the implication can be skipped entirely. See #6105 for more details.
157
+ auto validity_check =
158
+ w_ok_exprt (subtarget.address , from_integer (0 , unsigned_int_type ()));
159
+
160
+ exprt::operandst current_subtarget_found_conditions;
161
+ for (const auto &target : targets)
215
162
{
216
- exprt::operandst conjuncts;
217
- conjuncts.push_back (result);
218
- conjuncts.push_back (current_target_compatible);
219
- result = conjunction (conjuncts);
163
+ current_subtarget_found_conditions.push_back (
164
+ target.generate_containment_check (subtarget.address ));
220
165
}
166
+
167
+ auto current_subtarget_found = or_exprt (
168
+ not_exprt (validity_check),
169
+ disjunction (current_subtarget_found_conditions));
170
+
171
+ result = and_exprt (result, current_subtarget_found);
221
172
}
222
173
223
174
return result;
0 commit comments