Skip to content

Commit 6b9108d

Browse files
committed
Refactor lvalue-retainment in replace_symbolt into a separate class
1 parent 1d1f54e commit 6b9108d

File tree

6 files changed

+183
-39
lines changed

6 files changed

+183
-39
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
int i = 0;
6+
int *p = &i;
7+
assert(*p == 0);
8+
return 0;
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--constant-propagator
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
VERIFICATION SUCCESSFUL
7+
--
8+
^warning: ignoring

src/analyses/constant_propagator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class constant_propagator_domaint:public ai_domain_baset
7171
struct valuest
7272
{
7373
// maps variables to constants
74-
unchecked_replace_symbolt replace_const;
74+
address_of_aware_replace_symbolt replace_const;
7575
bool is_bottom = true;
7676

7777
bool merge(const valuest &src);

src/util/replace_symbol.cpp

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,19 @@ void replace_symbolt::insert(
2727
old_expr.get_identifier(), new_expr));
2828
}
2929

30-
bool replace_symbolt::replace(
31-
exprt &dest,
32-
const bool replace_with_const) const
30+
bool replace_symbolt::replace_symbol_expr(symbol_exprt &s) const
31+
{
32+
expr_mapt::const_iterator it = expr_map.find(s.get_identifier());
33+
34+
if(it == expr_map.end())
35+
return true;
36+
37+
static_cast<exprt &>(s) = it->second;
38+
39+
return false;
40+
}
41+
42+
bool replace_symbolt::replace(exprt &dest) const
3343
{
3444
bool result=true; // unchanged
3545

@@ -49,14 +59,14 @@ bool replace_symbolt::replace(
4959
{
5060
member_exprt &me=to_member_expr(dest);
5161

52-
if(!replace(me.struct_op(), replace_with_const)) // Could give non l-value.
62+
if(!replace(me.struct_op()))
5363
result=false;
5464
}
5565
else if(dest.id()==ID_index)
5666
{
5767
index_exprt &ie=to_index_expr(dest);
5868

59-
if(!replace(ie.array(), replace_with_const)) // Could give non l-value.
69+
if(!replace(ie.array()))
6070
result=false;
6171

6272
if(!replace(ie.index()))
@@ -66,27 +76,13 @@ bool replace_symbolt::replace(
6676
{
6777
address_of_exprt &aoe=to_address_of_expr(dest);
6878

69-
if(!replace(aoe.object(), false))
79+
if(!replace(aoe.object()))
7080
result=false;
7181
}
7282
else if(dest.id()==ID_symbol)
7383
{
74-
const symbol_exprt &s=to_symbol_expr(dest);
75-
76-
expr_mapt::const_iterator it=
77-
expr_map.find(s.get_identifier());
78-
79-
if(it!=expr_map.end())
80-
{
81-
const exprt &e=it->second;
82-
83-
if(!replace_with_const && e.is_constant()) // Would give non l-value.
84-
return true;
85-
86-
dest=e;
87-
84+
if(!replace_symbol_expr(to_symbol_expr(dest)))
8885
return false;
89-
}
9086
}
9187
else
9288
{
@@ -291,3 +287,85 @@ void unchecked_replace_symbolt::insert(
291287
{
292288
type_map.emplace(identifier, new_type);
293289
}
290+
291+
bool address_of_aware_replace_symbolt::replace(exprt &dest) const
292+
{
293+
const exprt &const_dest(dest);
294+
if(!require_lvalue && const_dest.id() != ID_address_of)
295+
return unchecked_replace_symbolt::replace(dest);
296+
297+
bool result=true; // unchanged
298+
299+
// first look at type
300+
if(have_to_replace(const_dest.type()))
301+
{
302+
const set_require_lvalue_and_backupt backup(require_lvalue, false);
303+
if(!unchecked_replace_symbolt::replace(dest.type()))
304+
result=false;
305+
}
306+
307+
// now do expression itself
308+
309+
if(!have_to_replace(dest))
310+
return result;
311+
312+
if(dest.id()==ID_index)
313+
{
314+
index_exprt &ie=to_index_expr(dest);
315+
316+
// Could yield non l-value.
317+
if(!replace(ie.array()))
318+
result=false;
319+
320+
const set_require_lvalue_and_backupt backup(require_lvalue, false);
321+
if(!replace(ie.index()))
322+
result=false;
323+
}
324+
else if(dest.id()==ID_address_of)
325+
{
326+
address_of_exprt &aoe=to_address_of_expr(dest);
327+
328+
const set_require_lvalue_and_backupt backup(require_lvalue, true);
329+
if(!replace(aoe.object()))
330+
result=false;
331+
}
332+
else
333+
{
334+
if(!unchecked_replace_symbolt::replace(dest))
335+
return false;
336+
}
337+
338+
const set_require_lvalue_and_backupt backup(require_lvalue, false);
339+
340+
const typet &c_sizeof_type =
341+
static_cast<const typet&>(dest.find(ID_C_c_sizeof_type));
342+
if(c_sizeof_type.is_not_nil() && have_to_replace(c_sizeof_type))
343+
result &= unchecked_replace_symbolt::replace(
344+
static_cast<typet&>(dest.add(ID_C_c_sizeof_type)));
345+
346+
const typet &va_arg_type =
347+
static_cast<const typet&>(dest.find(ID_C_va_arg_type));
348+
if(va_arg_type.is_not_nil() && have_to_replace(va_arg_type))
349+
result &= unchecked_replace_symbolt::replace(
350+
static_cast<typet&>(dest.add(ID_C_va_arg_type)));
351+
352+
return result;
353+
}
354+
355+
bool address_of_aware_replace_symbolt::replace_symbol_expr(
356+
symbol_exprt &s) const
357+
{
358+
expr_mapt::const_iterator it = expr_map.find(s.get_identifier());
359+
360+
if(it == expr_map.end())
361+
return true;
362+
363+
const exprt &e = it->second;
364+
365+
if(require_lvalue && e.is_constant()) // Would give non l-value.
366+
return true;
367+
368+
static_cast<exprt &>(s) = e;
369+
370+
return false;
371+
}

src/util/replace_symbol.h

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,8 @@ class replace_symbolt
4141
type_map.insert(std::pair<irep_idt, typet>(identifier, type));
4242
}
4343

44-
/// \brief Replaces a symbol with a constant
45-
/// If you are replacing symbols with constants in an l-value, you can
46-
/// create something that is not an l-value. For example if your
47-
/// l-value is "a[i]" then substituting i for a constant results in an
48-
/// l-value but substituting a for a constant (array) wouldn't. This
49-
/// only applies to the "top level" of the expression, for example, it
50-
/// is OK to replace b with a constant array in a[b[0]].
51-
///
52-
/// \param dest The expression in which to do the replacement
53-
/// \param replace_with_const If false then it disables the rewrites that
54-
/// could result in something that is not an l-value.
55-
///
56-
virtual bool replace(
57-
exprt &dest,
58-
const bool replace_with_const=true) const;
5944

45+
virtual bool replace(exprt &dest) const;
6046
virtual bool replace(typet &dest) const;
6147

6248
void operator()(exprt &dest) const
@@ -108,7 +94,8 @@ class replace_symbolt
10894
expr_mapt expr_map;
10995
type_mapt type_map;
11096

111-
protected:
97+
virtual bool replace_symbol_expr(symbol_exprt &dest) const;
98+
11299
bool have_to_replace(const exprt &dest) const;
113100
bool have_to_replace(const typet &type) const;
114101
};
@@ -127,4 +114,42 @@ class unchecked_replace_symbolt : public replace_symbolt
127114
void insert(const irep_idt &identifier, const typet &new_type);
128115
};
129116

117+
/// Replace symbols with constants while maintaining syntactically valid
118+
/// expressions.
119+
/// If you are replacing symbols with constants in an l-value, you can
120+
/// create something that is not an l-value. For example if your
121+
/// l-value is "a[i]" then substituting i for a constant results in an
122+
/// l-value but substituting a for a constant (array) wouldn't. This
123+
/// only applies to the "top level" of the expression, for example, it
124+
/// is OK to replace b with a constant array in a[b[0]].
125+
class address_of_aware_replace_symbolt : public unchecked_replace_symbolt
126+
{
127+
public:
128+
bool replace(exprt &dest) const override;
129+
130+
private:
131+
mutable bool require_lvalue = false;
132+
133+
class set_require_lvalue_and_backupt
134+
{
135+
public:
136+
set_require_lvalue_and_backupt(bool &require_lvalue, const bool value)
137+
: require_lvalue(require_lvalue), prev_value(require_lvalue)
138+
{
139+
require_lvalue = value;
140+
}
141+
142+
~set_require_lvalue_and_backupt()
143+
{
144+
require_lvalue = prev_value;
145+
}
146+
147+
private:
148+
bool &require_lvalue;
149+
bool prev_value;
150+
};
151+
152+
bool replace_symbol_expr(symbol_exprt &dest) const override;
153+
};
154+
130155
#endif // CPROVER_UTIL_REPLACE_SYMBOL_H

unit/util/replace_symbol.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ TEST_CASE("Lvalue only", "[core][util][replace_symbol]")
6161

6262
constant_exprt c("some_value", typet("some_type"));
6363

64-
unchecked_replace_symbolt r;
64+
address_of_aware_replace_symbolt r;
6565
r.insert("a", c);
6666

6767
REQUIRE(r.replace(binary) == false);
@@ -71,3 +71,27 @@ TEST_CASE("Lvalue only", "[core][util][replace_symbol]")
7171
REQUIRE(to_array_type(index_expr.array().type()).size() == c);
7272
REQUIRE(index_expr.index() == c);
7373
}
74+
75+
TEST_CASE("Replace always", "[core][util][replace_symbol]")
76+
{
77+
symbol_exprt s1("a", typet("some_type"));
78+
array_typet array_type(typet("some_type"), s1);
79+
symbol_exprt array("b", array_type);
80+
index_exprt index(array, s1);
81+
82+
exprt binary("binary", typet("some_type"));
83+
binary.copy_to_operands(address_of_exprt(s1));
84+
binary.copy_to_operands(address_of_exprt(index));
85+
86+
constant_exprt c("some_value", typet("some_type"));
87+
88+
unchecked_replace_symbolt r;
89+
r.insert("a", c);
90+
91+
REQUIRE(r.replace(binary) == false);
92+
REQUIRE(binary.op0() == address_of_exprt(c));
93+
const index_exprt &index_expr =
94+
to_index_expr(to_address_of_expr(binary.op1()).object());
95+
REQUIRE(to_array_type(index_expr.array().type()).size() == c);
96+
REQUIRE(index_expr.index() == c);
97+
}

0 commit comments

Comments
 (0)