@@ -11,68 +11,68 @@ Author: Diffblue Ltd
1111
1212#include " local_safe_pointers.h"
1313
14+ #include < util/base_type.h>
1415#include < util/expr_iterator.h>
16+ #include < util/expr_util.h>
1517#include < util/format_expr.h>
1618
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`
4221struct goto_null_checkt
4322{
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.
4626 bool checked_when_taken;
4727
4828 // / Null-tested pointer expression
4929 exprt checked_expr;
5030};
5131
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
5434// / \return a `goto_null_checkt` indicating what expression is tested and
5535// / whether the check applies on the taken or not-taken branch, or an empty
5636// / 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)
5938{
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.
6142 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)
6346 {
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 ();
6848 checked_when_taken = !checked_when_taken;
6949 }
7050
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 {};
7676}
7777
7878// / Compute safe dereference expressions for a given GOTO program. This
@@ -82,7 +82,8 @@ get_conditional_checked_expr(const exprt &goto_guard)
8282// / \param goto_program: program to analyse
8383void local_safe_pointerst::operator ()(const goto_programt &goto_program)
8484{
85- std::set<exprt> checked_expressions;
85+ std::set<exprt, base_type_comparet> checked_expressions (
86+ base_type_comparet{ns});
8687
8788 for (const auto &instruction : goto_program.instructions )
8889 {
@@ -91,11 +92,23 @@ void local_safe_pointerst::operator()(const goto_programt &goto_program)
9192 checked_expressions.clear ();
9293 // Retrieve working set for forward GOTOs:
9394 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+ }
95105
96106 // Save the working set at this program point:
97107 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+ }
99112
100113 switch (instruction.type )
101114 {
@@ -113,35 +126,44 @@ void local_safe_pointerst::operator()(const goto_programt &goto_program)
113126
114127 // Possible checks:
115128 case ASSUME:
129+ if (auto assume_check = get_null_checked_expr (instruction.guard ))
116130 {
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 );
123133 }
124134
135+ break ;
136+
125137 case GOTO:
126138 if (!instruction.is_backwards_goto ())
127139 {
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:
134141
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);
139145
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+ }
141162 }
142- // Otherwise fall through to...
143163 }
144164
165+ break ;
166+
145167 default :
146168 // Pessimistically assume all other instructions might overwrite any
147169 // pointer with a possibly-null value.
@@ -157,7 +179,7 @@ void local_safe_pointerst::operator()(const goto_programt &goto_program)
157179// / operator())
158180// / \param ns: global namespace
159181void 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)
161183{
162184 forall_goto_program_instructions (i_it, goto_program)
163185 {
@@ -199,7 +221,7 @@ void local_safe_pointerst::output(
199221// / operator())
200222// / \param ns: global namespace
201223void 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)
203225{
204226 forall_goto_program_instructions (i_it, goto_program)
205227 {
@@ -251,3 +273,12 @@ bool local_safe_pointerst::is_non_null_at_program_point(
251273 tocheck = &tocheck->op0 ();
252274 return findit->second .count (*tocheck) != 0 ;
253275}
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