11/* ******************************************************************\
22
3- Module: Specify write set in function contracts.
3+ Module: Specify write set in code contracts.
44
55Author: Felipe R. Monteiro
66
@@ -19,205 +19,156 @@ Date: July 2021
1919#include < util/c_types.h>
2020#include < util/pointer_predicates.h>
2121
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)
3229{
33- INVARIANT (
34- target.type ().id () == ID_pointer,
35- " Assigns clause targets should be stored as pointer expressions." );
3630}
3731
38- assigns_clause_targett::~assigns_clause_targett ( )
32+ exprt assigns_clauset::targett::normalize ( const exprt &expr )
3933{
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 ();
4042}
4143
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
4346{
4447 exprt::operandst condition;
45- exprt lhs_ptr = (lhs.id () == ID_address_of) ? to_address_of_expr (lhs).object ()
46- : pointer_for (lhs);
4748
4849 // __CPROVER_w_ok(target, sizeof(target))
4950 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 ()));
5253
5354 // __CPROVER_same_object(lhs, target)
54- condition.push_back (same_object (lhs_ptr, target ));
55+ condition.push_back (same_object (lhs_address, address ));
5556
5657 // If assigns target was a dereference, comparing objects is enough
57- if (target_id == ID_dereference)
58+ if (id == ID_dereference)
5859 {
5960 return conjunction (condition);
6061 }
6162
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 );
6465
6566 // __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 ));
6768
68- const exprt region_lhs = plus_exprt (
69+ const auto lhs_region = plus_exprt (
6970 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 ()),
7173 lhs_offset);
7274
73- const exprt region_target = plus_exprt (
75+ const exprt own_region = plus_exprt (
7476 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 );
7880
7981 // (sizeof(lhs) + __CPROVER_offset(lhs)) <=
8082 // (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 ));
8284
8385 return conjunction (condition);
8486}
8587
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-
9788assigns_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)
10693{
107- for (exprt target : assigns .operands ())
94+ for (const auto &target_expr : expr .operands ())
10895 {
109- add_target (target );
96+ add_target (target_expr );
11097 }
11198}
11299
113- assigns_clauset::~assigns_clauset ( )
100+ void assigns_clauset::add_target ( const exprt &target_expr )
114101{
115- for (assigns_clause_targett *target : targets)
102+ auto insertion_succeeded = targets.emplace (*this , target_expr).second ;
103+
104+ if (!insertion_succeeded)
116105 {
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;
118110 }
119111}
120112
121- void assigns_clauset::add_target ( exprt target )
113+ void assigns_clauset::remove_target ( const exprt &target_expr )
122114{
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)));
131116}
132117
133- goto_programt assigns_clauset::havoc_code ()
118+ goto_programt assigns_clauset::generate_havoc_code () const
134119{
135120 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 ());
138123
139124 goto_programt havoc_statements;
140- append_havoc_code (assigns .source_location (), modifies, havoc_statements);
125+ append_havoc_code (expr .source_location (), modifies, havoc_statements);
141126 return havoc_statements;
142127}
143128
144- exprt assigns_clauset::alias_expression (const exprt &lhs)
129+ exprt assigns_clauset::generate_containment_check (const exprt &lhs) const
145130{
146131 // If write set is empty, no assignment is allowed.
147132 if (targets.empty ())
148- {
149133 return false_exprt ();
150- }
134+
135+ const auto lhs_address = address_of_exprt (targett::normalize (lhs));
151136
152137 exprt::operandst condition;
153- for (assigns_clause_targett * target : targets)
138+ for (const auto & target : targets)
154139 {
155- condition.push_back (target-> alias_expression (lhs ));
140+ condition.push_back (target. generate_containment_check (lhs_address ));
156141 }
157142 return disjunction (condition);
158143}
159144
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
162147{
163- if (called_assigns.targets .empty ())
164- {
148+ if (subassigns.targets .empty ())
165149 return true_exprt ();
166- }
167150
168- bool first_clause = true ;
169151 exprt result = true_exprt ();
170- for (assigns_clause_targett *called_target : called_assigns .targets )
152+ for (const auto &subtarget : subassigns .targets )
171153 {
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)
215162 {
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 ));
220165 }
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);
221172 }
222173
223174 return result;
0 commit comments