Skip to content

Commit 7773ca5

Browse files
committed
Refactor assigns clause implementation
+ usused fields (e.g., `function_id`, `init_block`) are removed, + redundant functions (e.g. `pointer_for`) are removed, + const references are used throughout consistently + targets are tracked in an `unordered_set` as opposed to `vector` + introduced a "normalize" method for extracting base object of arrays + API renamed for clarity
1 parent b4c3a12 commit 7773ca5

File tree

5 files changed

+139
-141
lines changed

5 files changed

+139
-141
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
int foo(int *x) __CPROVER_assigns(*x, *x)
2+
{
3+
*x = *x + 0;
4+
return *x + 5;
5+
}
6+
7+
int main()
8+
{
9+
int n = 4;
10+
n = foo(&n);
11+
12+
return 0;
13+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^Ignored duplicate expression '\*x' in assigns clause at file main\.c line \d+$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
--
10+
This test checks that repeated expressions in assigns clauses are
11+
correctly detected and ignored.

src/goto-instrument/contracts/assigns.cpp

Lines changed: 65 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*******************************************************************\
22
3-
Module: Specify write set in function contracts.
3+
Module: Specify write set in code contracts.
44
55
Author: Felipe R. Monteiro
66
@@ -19,129 +19,112 @@ 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+
: expr(address_of_exprt(expr)), id(expr.id()), parent(parent)
3226
{
33-
INVARIANT(
34-
target.type().id() == ID_pointer,
35-
"Assigns clause targets should be stored as pointer expressions.");
3627
}
3728

38-
assigns_clause_targett::~assigns_clause_targett()
29+
exprt assigns_clauset::targett::normalize(const exprt &expr)
3930
{
31+
if(expr.id() != ID_address_of)
32+
return expr;
33+
34+
const auto &object = to_address_of_expr(expr).object();
35+
if(object.id() != ID_index)
36+
return object;
37+
38+
return to_index_expr(object).array();
4039
}
4140

42-
exprt assigns_clause_targett::alias_expression(const exprt &lhs)
41+
exprt assigns_clauset::targett::generate_alias_check(const exprt &lhs) const
4342
{
4443
exprt::operandst condition;
45-
exprt lhs_ptr = (lhs.id() == ID_address_of) ? to_address_of_expr(lhs).object()
46-
: pointer_for(lhs);
44+
const auto lhs_ptr =
45+
(lhs.id() == ID_address_of) ? lhs : address_of_exprt(lhs);
4746

4847
// __CPROVER_w_ok(target, sizeof(target))
4948
condition.push_back(w_ok_exprt(
50-
target,
51-
size_of_expr(dereference_exprt(target).type(), contract.ns).value()));
49+
expr, size_of_expr(dereference_exprt(expr).type(), parent.ns).value()));
5250

5351
// __CPROVER_same_object(lhs, target)
54-
condition.push_back(same_object(lhs_ptr, target));
52+
condition.push_back(same_object(lhs_ptr, expr));
5553

5654
// If assigns target was a dereference, comparing objects is enough
57-
if(target_id == ID_dereference)
55+
if(id == ID_dereference)
5856
{
5957
return conjunction(condition);
6058
}
6159

62-
const exprt lhs_offset = pointer_offset(lhs_ptr);
63-
const exprt target_offset = pointer_offset(target);
60+
const auto lhs_offset = pointer_offset(lhs_ptr);
61+
const auto own_offset = pointer_offset(expr);
6462

6563
// __CPROVER_offset(lhs) >= __CPROVER_offset(target)
66-
condition.push_back(binary_relation_exprt(lhs_offset, ID_ge, target_offset));
64+
condition.push_back(binary_relation_exprt(lhs_offset, ID_ge, own_offset));
6765

68-
const exprt region_lhs = plus_exprt(
66+
const auto lhs_region = plus_exprt(
6967
typecast_exprt::conditional_cast(
70-
size_of_expr(lhs.type(), contract.ns).value(), lhs_offset.type()),
68+
size_of_expr(lhs.type(), parent.ns).value(), lhs_offset.type()),
7169
lhs_offset);
7270

73-
const exprt region_target = plus_exprt(
71+
const exprt own_region = plus_exprt(
7472
typecast_exprt::conditional_cast(
75-
size_of_expr(dereference_exprt(target).type(), contract.ns).value(),
76-
target_offset.type()),
77-
target_offset);
73+
size_of_expr(dereference_exprt(expr).type(), parent.ns).value(),
74+
own_offset.type()),
75+
own_offset);
7876

7977
// (sizeof(lhs) + __CPROVER_offset(lhs)) <=
8078
// (sizeof(target) + __CPROVER_offset(target))
81-
condition.push_back(binary_relation_exprt(region_lhs, ID_le, region_target));
79+
condition.push_back(binary_relation_exprt(lhs_region, ID_le, own_region));
8280

8381
return conjunction(condition);
8482
}
8583

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
84+
exprt assigns_clauset::targett::generate_compatibility_check(
85+
const assigns_clauset::targett &other_target) const
9386
{
94-
return target;
87+
return same_object(other_target.expr, expr);
9588
}
9689

9790
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)
91+
const exprt &expr,
92+
const messaget &log,
93+
const namespacet &ns)
94+
: expr(expr), log(log), ns(ns)
10695
{
107-
for(exprt target : assigns.operands())
96+
for(const auto &target_expr : expr.operands())
10897
{
109-
add_target(target);
98+
add_target(target_expr);
11099
}
111100
}
112101

113-
assigns_clauset::~assigns_clauset()
102+
void assigns_clauset::add_target(const exprt &target_expr)
114103
{
115-
for(assigns_clause_targett *target : targets)
104+
auto insertion_succeeded =
105+
targets.emplace(*this, targett::normalize(target_expr)).second;
106+
107+
if(!insertion_succeeded)
116108
{
117-
delete target;
109+
log.warning() << "Ignored duplicate expression '"
110+
<< from_expr(ns, target_expr.id(), target_expr)
111+
<< "' in assigns clause at "
112+
<< target_expr.source_location().as_string() << messaget::eom;
118113
}
119114
}
120115

121-
void assigns_clauset::add_target(exprt target)
122-
{
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);
131-
}
132-
133-
goto_programt assigns_clauset::havoc_code()
116+
goto_programt assigns_clauset::generate_havoc_code() const
134117
{
135118
modifiest modifies;
136-
for(const auto &t : targets)
137-
modifies.insert(to_address_of_expr(t->get_target()).object());
119+
for(const auto &target : targets)
120+
modifies.insert(to_address_of_expr(target.expr).object());
138121

139122
goto_programt havoc_statements;
140-
append_havoc_code(assigns.source_location(), modifies, havoc_statements);
123+
append_havoc_code(expr.source_location(), modifies, havoc_statements);
141124
return havoc_statements;
142125
}
143126

144-
exprt assigns_clauset::alias_expression(const exprt &lhs)
127+
exprt assigns_clauset::generate_alias_check(const exprt &lhs) const
145128
{
146129
// If write set is empty, no assignment is allowed.
147130
if(targets.empty())
@@ -150,15 +133,15 @@ exprt assigns_clauset::alias_expression(const exprt &lhs)
150133
}
151134

152135
exprt::operandst condition;
153-
for(assigns_clause_targett *target : targets)
136+
for(const auto &target : targets)
154137
{
155-
condition.push_back(target->alias_expression(lhs));
138+
condition.push_back(target.generate_alias_check(lhs));
156139
}
157140
return disjunction(condition);
158141
}
159142

160-
exprt assigns_clauset::compatible_expression(
161-
const assigns_clauset &called_assigns)
143+
exprt assigns_clauset::generate_compatibility_check(
144+
const assigns_clauset &called_assigns) const
162145
{
163146
if(called_assigns.targets.empty())
164147
{
@@ -167,11 +150,11 @@ exprt assigns_clauset::compatible_expression(
167150

168151
bool first_clause = true;
169152
exprt result = true_exprt();
170-
for(assigns_clause_targett *called_target : called_assigns.targets)
153+
for(const auto &called_target : called_assigns.targets)
171154
{
172155
bool first_iter = true;
173156
exprt current_target_compatible = false_exprt();
174-
for(assigns_clause_targett *target : targets)
157+
for(const auto &target : targets)
175158
{
176159
if(first_iter)
177160
{
@@ -180,30 +163,30 @@ exprt assigns_clauset::compatible_expression(
180163

181164
// Validating the called target through __CPROVER_w_ok() is
182165
// only useful when the called target is a dereference
183-
const auto &called_target_ptr = called_target->get_target();
166+
const auto &called_target_ptr = called_target.expr;
184167
if(
185168
to_address_of_expr(called_target_ptr).object().id() == ID_dereference)
186169
{
187170
// or_exprt is short-circuited, therefore
188-
// target->compatible_expression(*called_target) would not be
171+
// target.generate_compatibility_check(*called_target) would not be
189172
// checked on invalid called_targets.
190173
current_target_compatible = or_exprt(
191174
not_exprt(w_ok_exprt(
192175
called_target_ptr, from_integer(0, unsigned_int_type()))),
193-
target->compatible_expression(*called_target));
176+
target.generate_compatibility_check(called_target));
194177
}
195178
else
196179
{
197180
current_target_compatible =
198-
target->compatible_expression(*called_target);
181+
target.generate_compatibility_check(called_target);
199182
}
200183
first_iter = false;
201184
}
202185
else
203186
{
204187
current_target_compatible = or_exprt(
205188
current_target_compatible,
206-
target->compatible_expression(*called_target));
189+
target.generate_compatibility_check(called_target));
207190
}
208191
}
209192
if(first_clause)
@@ -213,10 +196,7 @@ exprt assigns_clauset::compatible_expression(
213196
}
214197
else
215198
{
216-
exprt::operandst conjuncts;
217-
conjuncts.push_back(result);
218-
conjuncts.push_back(current_target_compatible);
219-
result = conjunction(conjuncts);
199+
result = and_exprt(result, current_target_compatible);
220200
}
221201
}
222202

0 commit comments

Comments
 (0)