@@ -11,68 +11,68 @@ Author: Diffblue Ltd
11
11
12
12
#include " local_safe_pointers.h"
13
13
14
+ #include < util/base_type.h>
14
15
#include < util/expr_iterator.h>
16
+ #include < util/expr_util.h>
15
17
#include < util/format_expr.h>
16
18
17
- // / If `expr` is of the form `x != nullptr`, return x. Otherwise return null
18
- static const exprt *get_null_checked_expr (const exprt &expr)
19
- {
20
- if (expr.id () == ID_notequal)
21
- {
22
- const exprt *op0 = &expr.op0 (), *op1 = &expr.op1 ();
23
- if (op0->type ().id () == ID_pointer &&
24
- *op0 == null_pointer_exprt (to_pointer_type (op0->type ())))
25
- {
26
- std::swap (op0, op1);
27
- }
28
-
29
- if (op1->type ().id () == ID_pointer &&
30
- *op1 == null_pointer_exprt (to_pointer_type (op1->type ())))
31
- {
32
- while (op0->id () == ID_typecast)
33
- op0 = &op0->op0 ();
34
- return op0;
35
- }
36
- }
37
-
38
- return nullptr ;
39
- }
40
-
41
- // / Return structure for `get_conditional_checked_expr`
19
+ // / Return structure for `get_null_checked_expr` and
20
+ // / `get_conditional_checked_expr`
42
21
struct goto_null_checkt
43
22
{
44
- // / If true, the given GOTO tests that a pointer expression is non-null on the
45
- // / taken branch; otherwise, on the not-taken branch.
23
+ // / If true, the given GOTO/ASSUME tests that a pointer expression is non-null
24
+ // / on the taken branch or passing case; otherwise, on the not-taken branch
25
+ // / or on failure.
46
26
bool checked_when_taken;
47
27
48
28
// / Null-tested pointer expression
49
29
exprt checked_expr;
50
30
};
51
31
52
- // / Check if a GOTO guard expression tests if a pointer is null
53
- // / \param goto_guard : expression to check
32
+ // / Check if `expr` tests if a pointer is null
33
+ // / \param expr : expression to check
54
34
// / \return a `goto_null_checkt` indicating what expression is tested and
55
35
// / whether the check applies on the taken or not-taken branch, or an empty
56
36
// / optionalt if this isn't a null check.
57
- static optionalt<goto_null_checkt>
58
- get_conditional_checked_expr (const exprt &goto_guard)
37
+ static optionalt<goto_null_checkt> get_null_checked_expr (const exprt &expr)
59
38
{
60
- exprt normalized_guard = goto_guard;
39
+ exprt normalized_expr = expr;
40
+ // If true, then a null check is made when test `expr` passes; if false,
41
+ // one is made when it fails.
61
42
bool checked_when_taken = true ;
62
- while (normalized_guard.id () == ID_not || normalized_guard.id () == ID_equal)
43
+
44
+ // Reduce some roundabout ways of saying "x != null", e.g. "!(x == null)".
45
+ while (normalized_expr.id () == ID_not)
63
46
{
64
- if (normalized_guard.id () == ID_not)
65
- normalized_guard = normalized_guard.op0 ();
66
- else
67
- normalized_guard.id (ID_notequal);
47
+ normalized_expr = normalized_expr.op0 ();
68
48
checked_when_taken = !checked_when_taken;
69
49
}
70
50
71
- const exprt *checked_expr = get_null_checked_expr (normalized_guard);
72
- if (!checked_expr)
73
- return {};
74
- else
75
- return goto_null_checkt { checked_when_taken, *checked_expr };
51
+ if (normalized_expr.id () == ID_equal)
52
+ {
53
+ normalized_expr.id (ID_notequal);
54
+ checked_when_taken = !checked_when_taken;
55
+ }
56
+
57
+ if (normalized_expr.id () == ID_notequal)
58
+ {
59
+ const exprt &op0 = skip_typecast (normalized_expr.op0 ());
60
+ const exprt &op1 = skip_typecast (normalized_expr.op1 ());
61
+
62
+ if (op0.type ().id () == ID_pointer &&
63
+ op0 == null_pointer_exprt (to_pointer_type (op0.type ())))
64
+ {
65
+ return { { checked_when_taken, op1 } };
66
+ }
67
+
68
+ if (op1.type ().id () == ID_pointer &&
69
+ op1 == null_pointer_exprt (to_pointer_type (op1.type ())))
70
+ {
71
+ return { { checked_when_taken, op0 } };
72
+ }
73
+ }
74
+
75
+ return {};
76
76
}
77
77
78
78
// / Compute safe dereference expressions for a given GOTO program. This
@@ -82,7 +82,8 @@ get_conditional_checked_expr(const exprt &goto_guard)
82
82
// / \param goto_program: program to analyse
83
83
void local_safe_pointerst::operator ()(const goto_programt &goto_program)
84
84
{
85
- std::set<exprt> checked_expressions;
85
+ std::set<exprt, base_type_comparet> checked_expressions (
86
+ base_type_comparet{ns});
86
87
87
88
for (const auto &instruction : goto_program.instructions )
88
89
{
@@ -91,11 +92,23 @@ void local_safe_pointerst::operator()(const goto_programt &goto_program)
91
92
checked_expressions.clear ();
92
93
// Retrieve working set for forward GOTOs:
93
94
else if (instruction.is_target ())
94
- checked_expressions = non_null_expressions[instruction.location_number ];
95
+ {
96
+ auto findit = non_null_expressions.find (instruction.location_number );
97
+ if (findit != non_null_expressions.end ())
98
+ checked_expressions = findit->second ;
99
+ else
100
+ {
101
+ checked_expressions =
102
+ std::set<exprt, base_type_comparet>(base_type_comparet{ns});
103
+ }
104
+ }
95
105
96
106
// Save the working set at this program point:
97
107
if (!checked_expressions.empty ())
98
- non_null_expressions[instruction.location_number ] = checked_expressions;
108
+ {
109
+ non_null_expressions.emplace (
110
+ instruction.location_number , checked_expressions);
111
+ }
99
112
100
113
switch (instruction.type )
101
114
{
@@ -113,35 +126,44 @@ void local_safe_pointerst::operator()(const goto_programt &goto_program)
113
126
114
127
// Possible checks:
115
128
case ASSUME:
129
+ if (auto assume_check = get_null_checked_expr (instruction.guard ))
116
130
{
117
- const exprt *checked_expr;
118
- if ((checked_expr = get_null_checked_expr (instruction.guard )) != nullptr )
119
- {
120
- checked_expressions.insert (*checked_expr);
121
- }
122
- break ;
131
+ if (assume_check->checked_when_taken )
132
+ checked_expressions.insert (assume_check->checked_expr );
123
133
}
124
134
135
+ break ;
136
+
125
137
case GOTO:
126
138
if (!instruction.is_backwards_goto ())
127
139
{
128
- if (auto conditional_check =
129
- get_conditional_checked_expr (instruction.guard ))
130
- {
131
- auto &taken_checked_expressions =
132
- non_null_expressions[instruction.get_target ()->location_number ];
133
- taken_checked_expressions = checked_expressions;
140
+ // Copy current state to GOTO target:
134
141
135
- if (conditional_check->checked_when_taken )
136
- taken_checked_expressions.insert (conditional_check->checked_expr );
137
- else
138
- checked_expressions.insert (conditional_check->checked_expr );
142
+ auto target_emplace_result =
143
+ non_null_expressions.emplace (
144
+ instruction.get_target ()->location_number , checked_expressions);
139
145
140
- break ;
146
+ // If the target already has a state entry then it is a control-flow
147
+ // merge point and everything will be assumed maybe-null in any case.
148
+ if (target_emplace_result.second )
149
+ {
150
+ if (auto conditional_check = get_null_checked_expr (instruction.guard ))
151
+ {
152
+ // Add the GOTO condition to either the target or current state,
153
+ // as appropriate:
154
+ if (conditional_check->checked_when_taken )
155
+ {
156
+ target_emplace_result.first ->second .insert (
157
+ conditional_check->checked_expr );
158
+ }
159
+ else
160
+ checked_expressions.insert (conditional_check->checked_expr );
161
+ }
141
162
}
142
- // Otherwise fall through to...
143
163
}
144
164
165
+ break ;
166
+
145
167
default :
146
168
// Pessimistically assume all other instructions might overwrite any
147
169
// pointer with a possibly-null value.
@@ -157,7 +179,7 @@ void local_safe_pointerst::operator()(const goto_programt &goto_program)
157
179
// / operator())
158
180
// / \param ns: global namespace
159
181
void local_safe_pointerst::output (
160
- std::ostream &out, const goto_programt &goto_program, const namespacet &ns )
182
+ std::ostream &out, const goto_programt &goto_program)
161
183
{
162
184
forall_goto_program_instructions (i_it, goto_program)
163
185
{
@@ -199,7 +221,7 @@ void local_safe_pointerst::output(
199
221
// / operator())
200
222
// / \param ns: global namespace
201
223
void local_safe_pointerst::output_safe_dereferences (
202
- std::ostream &out, const goto_programt &goto_program, const namespacet &ns )
224
+ std::ostream &out, const goto_programt &goto_program)
203
225
{
204
226
forall_goto_program_instructions (i_it, goto_program)
205
227
{
@@ -251,3 +273,12 @@ bool local_safe_pointerst::is_non_null_at_program_point(
251
273
tocheck = &tocheck->op0 ();
252
274
return findit->second .count (*tocheck) != 0 ;
253
275
}
276
+
277
+ bool local_safe_pointerst::base_type_comparet::operator ()(
278
+ const exprt &e1 , const exprt &e2 ) const
279
+ {
280
+ if (base_type_eq (e1 , e2 , ns))
281
+ return false ;
282
+ else
283
+ return e1 < e2 ;
284
+ }
0 commit comments