Skip to content

Commit 2859f9f

Browse files
committed
Refactor assigns clause implementation
+ usused fields (e.g., `function_id`) 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`
1 parent f0ab57e commit 2859f9f

File tree

5 files changed

+131
-132
lines changed

5 files changed

+131
-132
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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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 verification succeeds if only expressions inside the assigns clause are assigned within the function.
11+
12+
Note: For all 'enforce' tests, nothing can be assumed about the return value of the function (as the function call is not replaced at this point).
13+
14+
To make such assumptions would cause verification to fail.

src/goto-instrument/contracts/assigns.cpp

Lines changed: 61 additions & 83 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,107 @@ 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()
39-
{
40-
}
41-
42-
exprt assigns_clause_targett::alias_expression(const exprt &lhs)
29+
exprt assigns_clauset::targett::alias_expression(const exprt &lhs) const
4330
{
4431
exprt::operandst condition;
45-
exprt lhs_ptr = (lhs.id() == ID_address_of) ? to_address_of_expr(lhs).object()
46-
: pointer_for(lhs);
32+
const auto lhs_ptr = (lhs.id() == ID_address_of)
33+
? to_address_of_expr(lhs).object()
34+
: address_of_exprt(lhs);
4735

4836
// __CPROVER_w_ok(target, sizeof(target))
4937
condition.push_back(w_ok_exprt(
50-
target,
51-
size_of_expr(dereference_exprt(target).type(), contract.ns).value()));
38+
expr, size_of_expr(dereference_exprt(expr).type(), parent.ns).value()));
5239

5340
// __CPROVER_same_object(lhs, target)
54-
condition.push_back(same_object(lhs_ptr, target));
41+
condition.push_back(same_object(lhs_ptr, expr));
5542

5643
// If assigns target was a dereference, comparing objects is enough
57-
if(target_id == ID_dereference)
44+
if(id == ID_dereference)
5845
{
5946
return conjunction(condition);
6047
}
6148

62-
const exprt lhs_offset = pointer_offset(lhs_ptr);
63-
const exprt target_offset = pointer_offset(target);
49+
const auto lhs_offset = pointer_offset(lhs_ptr);
50+
const auto own_offset = pointer_offset(expr);
6451

6552
// __CPROVER_offset(lhs) >= __CPROVER_offset(target)
66-
condition.push_back(binary_relation_exprt(lhs_offset, ID_ge, target_offset));
53+
condition.push_back(binary_relation_exprt(lhs_offset, ID_ge, own_offset));
6754

68-
const exprt region_lhs = plus_exprt(
55+
const auto lhs_region = plus_exprt(
6956
typecast_exprt::conditional_cast(
70-
size_of_expr(lhs.type(), contract.ns).value(), lhs_offset.type()),
57+
size_of_expr(lhs.type(), parent.ns).value(), lhs_offset.type()),
7158
lhs_offset);
7259

73-
const exprt region_target = plus_exprt(
60+
const exprt own_region = plus_exprt(
7461
typecast_exprt::conditional_cast(
75-
size_of_expr(dereference_exprt(target).type(), contract.ns).value(),
76-
target_offset.type()),
77-
target_offset);
62+
size_of_expr(dereference_exprt(expr).type(), parent.ns).value(),
63+
own_offset.type()),
64+
own_offset);
7865

7966
// (sizeof(lhs) + __CPROVER_offset(lhs)) <=
8067
// (sizeof(target) + __CPROVER_offset(target))
81-
condition.push_back(binary_relation_exprt(region_lhs, ID_le, region_target));
68+
condition.push_back(binary_relation_exprt(lhs_region, ID_le, own_region));
8269

8370
return conjunction(condition);
8471
}
8572

86-
exprt assigns_clause_targett::compatible_expression(
87-
const assigns_clause_targett &called_target)
73+
exprt assigns_clauset::targett::compatible_expression(
74+
const assigns_clauset::targett &other_target) const
8875
{
89-
return same_object(called_target.get_target(), target);
90-
}
91-
92-
const exprt &assigns_clause_targett::get_target() const
93-
{
94-
return target;
76+
return same_object(other_target.expr, expr);
9577
}
9678

9779
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)
80+
const exprt &expr,
81+
const messaget &log,
82+
const namespacet &ns)
83+
: expr(expr), log(log), ns(ns)
10684
{
107-
for(exprt target : assigns.operands())
85+
for(const auto &target_expr : expr.operands())
10886
{
109-
add_target(target);
87+
add_target(target_expr);
11088
}
11189
}
11290

113-
assigns_clauset::~assigns_clauset()
91+
void assigns_clauset::add_target(const exprt &target_expr)
11492
{
115-
for(assigns_clause_targett *target : targets)
93+
auto insertion_succeeded =
94+
targets
95+
.emplace(
96+
*this,
97+
(target_expr.id() == ID_address_of)
98+
? to_index_expr(to_address_of_expr(target_expr).object()).array()
99+
: target_expr)
100+
.second;
101+
102+
if(!insertion_succeeded)
116103
{
117-
delete target;
104+
log.warning() << "Ignored duplicate expression '"
105+
<< from_expr(ns, target_expr.id(), target_expr)
106+
<< "' in assigns clause at "
107+
<< target_expr.source_location().as_string() << messaget::eom;
118108
}
119109
}
120110

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()
111+
goto_programt assigns_clauset::havoc_code() const
134112
{
135113
modifiest modifies;
136-
for(const auto &t : targets)
137-
modifies.insert(to_address_of_expr(t->get_target()).object());
114+
for(const auto &target : targets)
115+
modifies.insert(to_address_of_expr(target.expr).object());
138116

139117
goto_programt havoc_statements;
140-
append_havoc_code(assigns.source_location(), modifies, havoc_statements);
118+
append_havoc_code(expr.source_location(), modifies, havoc_statements);
141119
return havoc_statements;
142120
}
143121

144-
exprt assigns_clauset::alias_expression(const exprt &lhs)
122+
exprt assigns_clauset::alias_expression(const exprt &lhs) const
145123
{
146124
// If write set is empty, no assignment is allowed.
147125
if(targets.empty())
@@ -150,15 +128,15 @@ exprt assigns_clauset::alias_expression(const exprt &lhs)
150128
}
151129

152130
exprt::operandst condition;
153-
for(assigns_clause_targett *target : targets)
131+
for(const auto &target : targets)
154132
{
155-
condition.push_back(target->alias_expression(lhs));
133+
condition.push_back(target.alias_expression(lhs));
156134
}
157135
return disjunction(condition);
158136
}
159137

160138
exprt assigns_clauset::compatible_expression(
161-
const assigns_clauset &called_assigns)
139+
const assigns_clauset &called_assigns) const
162140
{
163141
if(called_assigns.targets.empty())
164142
{
@@ -167,11 +145,11 @@ exprt assigns_clauset::compatible_expression(
167145

168146
bool first_clause = true;
169147
exprt result = true_exprt();
170-
for(assigns_clause_targett *called_target : called_assigns.targets)
148+
for(const auto &called_target : called_assigns.targets)
171149
{
172150
bool first_iter = true;
173151
exprt current_target_compatible = false_exprt();
174-
for(assigns_clause_targett *target : targets)
152+
for(const auto &target : targets)
175153
{
176154
if(first_iter)
177155
{
@@ -180,30 +158,30 @@ exprt assigns_clauset::compatible_expression(
180158

181159
// Validating the called target through __CPROVER_w_ok() is
182160
// only useful when the called target is a dereference
183-
const auto &called_target_ptr = called_target->get_target();
161+
const auto &called_target_ptr = called_target.expr;
184162
if(
185163
to_address_of_expr(called_target_ptr).object().id() == ID_dereference)
186164
{
187165
// or_exprt is short-circuited, therefore
188-
// target->compatible_expression(*called_target) would not be
166+
// target.compatible_expression(*called_target) would not be
189167
// checked on invalid called_targets.
190168
current_target_compatible = or_exprt(
191169
not_exprt(w_ok_exprt(
192170
called_target_ptr, from_integer(0, unsigned_int_type()))),
193-
target->compatible_expression(*called_target));
171+
target.compatible_expression(called_target));
194172
}
195173
else
196174
{
197175
current_target_compatible =
198-
target->compatible_expression(*called_target);
176+
target.compatible_expression(called_target);
199177
}
200178
first_iter = false;
201179
}
202180
else
203181
{
204182
current_target_compatible = or_exprt(
205183
current_target_compatible,
206-
target->compatible_expression(*called_target));
184+
target.compatible_expression(called_target));
207185
}
208186
}
209187
if(first_clause)
Lines changed: 40 additions & 45 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
@@ -18,58 +18,53 @@ Date: July 2021
1818

1919
#include <util/pointer_offset_size.h>
2020

21-
/// \brief A base class for assigns clause targets
22-
class assigns_clause_targett
21+
/// \brief A base class for assigns clause in code contracts
22+
class assigns_clauset
2323
{
2424
public:
25-
assigns_clause_targett(
26-
const exprt &object,
27-
code_contractst &contract,
28-
messaget &log_parameter,
29-
const irep_idt &function_id);
30-
~assigns_clause_targett();
31-
32-
exprt alias_expression(const exprt &lhs);
33-
exprt compatible_expression(const assigns_clause_targett &called_target);
34-
const exprt &get_target() const;
35-
36-
static exprt pointer_for(const exprt &object)
25+
/// \brief A base class for assigns clause targets
26+
class targett
3727
{
38-
return address_of_exprt(object);
39-
}
28+
public:
29+
targett(const assigns_clauset &, const exprt &);
4030

41-
protected:
42-
const code_contractst &contract;
43-
goto_programt init_block;
44-
messaget &log;
45-
exprt target;
46-
const irep_idt &target_id;
47-
};
31+
exprt alias_expression(const exprt &) const;
32+
exprt compatible_expression(const targett &) const;
4833

49-
class assigns_clauset
50-
{
51-
public:
52-
assigns_clauset(
53-
const exprt &assigns,
54-
code_contractst &contract,
55-
const irep_idt function_id,
56-
messaget log_parameter);
57-
~assigns_clauset();
58-
59-
void add_target(exprt target);
60-
goto_programt havoc_code();
61-
exprt alias_expression(const exprt &lhs);
62-
exprt compatible_expression(const assigns_clauset &called_assigns);
34+
bool operator==(const targett &other) const
35+
{
36+
return expr == other.expr;
37+
}
6338

64-
protected:
65-
const exprt &assigns;
39+
struct hasht
40+
{
41+
bool operator()(const targett &target) const
42+
{
43+
return irep_hash{}(target.expr);
44+
}
45+
};
46+
47+
const exprt expr;
48+
const irep_idt &id;
49+
const assigns_clauset &parent;
50+
51+
protected:
52+
goto_programt init_block;
53+
};
6654

67-
std::vector<assigns_clause_targett *> targets;
68-
goto_programt standin_declarations;
55+
assigns_clauset(const exprt &, const messaget &, const namespacet &);
6956

70-
code_contractst &parent;
71-
const irep_idt function_id;
72-
messaget log;
57+
void add_target(const exprt &);
58+
goto_programt havoc_code() const;
59+
exprt alias_expression(const exprt &) const;
60+
exprt compatible_expression(const assigns_clauset &) const;
61+
62+
const exprt &expr;
63+
const messaget &log;
64+
const namespacet &ns;
65+
66+
protected:
67+
std::unordered_set<targett, targett::hasht> targets;
7368
};
7469

7570
#endif // CPROVER_GOTO_INSTRUMENT_CONTRACTS_ASSIGNS_H

0 commit comments

Comments
 (0)