Skip to content

Commit 4fa790b

Browse files
authored
Merge pull request #4385 from smowton/smowton/fix/expr-iterator-mutated
Expr-depth-iterator: support iterating over mutated expressions
2 parents 8334ac8 + 17bee52 commit 4fa790b

File tree

3 files changed

+120
-24
lines changed

3 files changed

+120
-24
lines changed

src/util/expr_iterator.h

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,18 @@ class const_unique_depth_iteratort;
4242
struct depth_iterator_expr_statet final
4343
{
4444
typedef exprt::operandst::const_iterator operands_iteratort;
45-
inline depth_iterator_expr_statet(
46-
const exprt &expr,
47-
operands_iteratort it,
48-
operands_iteratort end):
49-
expr(expr), it(it), end(end) { }
45+
explicit depth_iterator_expr_statet(const exprt &expr) : expr(expr), op_idx(0)
46+
{
47+
}
5048
std::reference_wrapper<const exprt> expr;
51-
operands_iteratort it;
52-
operands_iteratort end;
49+
std::size_t op_idx;
5350
};
5451

5552
inline bool operator==(
5653
const depth_iterator_expr_statet &left,
5754
const depth_iterator_expr_statet &right)
5855
{
59-
return distance(left.it, left.end) == distance(right.it, right.end) &&
60-
left.expr.get() == right.expr.get();
56+
return left.op_idx == right.op_idx && left.expr.get() == right.expr.get();
6157
}
6258

6359
/// Depth first search iterator base - iterates over supplied expression
@@ -100,16 +96,19 @@ class depth_iterator_baset
10096
PRECONDITION(!m_stack.empty());
10197
while(true)
10298
{
103-
if(m_stack.back().it==m_stack.back().end)
99+
if(m_stack.back().op_idx == m_stack.back().expr.get().operands().size())
104100
{
105101
m_stack.pop_back();
106102
if(m_stack.empty())
107103
break;
108104
}
109105
// Check eg. if we haven't seen this node before
110-
else if(this->downcast().push_expr(*m_stack.back().it))
106+
else if(this->downcast().push_expr(
107+
m_stack.back().expr.get().operands()[m_stack.back().op_idx]))
108+
{
111109
break;
112-
m_stack.back().it++;
110+
}
111+
++m_stack.back().op_idx;
113112
}
114113
return this->downcast();
115114
}
@@ -120,7 +119,7 @@ class depth_iterator_baset
120119
m_stack.pop_back();
121120
if(!m_stack.empty())
122121
{
123-
++m_stack.back().it;
122+
++m_stack.back().op_idx;
124123
return ++(*this);
125124
}
126125
return this->downcast();
@@ -185,18 +184,11 @@ class depth_iterator_baset
185184
for(auto &state : m_stack)
186185
{
187186
// This deliberately breaks sharing as expr is now non-const
188-
auto &operands = expr->operands();
189-
// Get iterators into the operands of the new expr corresponding to the
190-
// ones into the operands of the old expr
191-
const auto it = operands.end() - (state.end - state.it);
187+
(void)expr->write();
192188
state.expr = *expr;
193-
state.it=it;
194-
state.end=operands.end();
195189
// Get the expr for the next level down to use in the next iteration
196-
if(!(state==m_stack.back()))
197-
{
198-
expr = &*it;
199-
}
190+
if(!(state == m_stack.back()))
191+
expr = &expr->operands()[state.op_idx];
200192
}
201193
return *expr;
202194
}
@@ -208,7 +200,7 @@ class depth_iterator_baset
208200
/// false otherwise. If returning false, child will not be iterated over.
209201
bool push_expr(const exprt &expr)
210202
{
211-
m_stack.emplace_back(expr, expr.operands().begin(), expr.operands().end());
203+
m_stack.emplace_back(expr);
212204
return true;
213205
}
214206

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ SRC += analyses/ai/ai.cpp \
5050
util/cmdline.cpp \
5151
util/expr_cast/expr_cast.cpp \
5252
util/expr.cpp \
53+
util/expr_iterator.cpp \
5354
util/file_util.cpp \
5455
util/format_number_range.cpp \
5556
util/get_base_name.cpp \

unit/util/expr_iterator.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests for expr_iterator
4+
5+
Author: Diffblue Ltd
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// expr_iterator unit tests
11+
12+
#include <testing-utils/use_catch.h>
13+
14+
#include <util/expr_iterator.h>
15+
#include <util/std_expr.h>
16+
17+
SCENARIO("expr_iterator", "[core][utils][expr_iterator]")
18+
{
19+
GIVEN("An expression of depth 3")
20+
{
21+
symbol_exprt symbol("whatever", bool_typet());
22+
notequal_exprt middle1(symbol, symbol);
23+
equal_exprt middle2(symbol, symbol);
24+
implies_exprt top(middle1, middle2);
25+
26+
WHEN("Visiting the expressions with a depth iterator")
27+
{
28+
std::vector<irep_idt> ids;
29+
for(auto it = top.depth_begin(), itend = top.depth_end(); it != itend;
30+
++it)
31+
{
32+
ids.push_back(it->id());
33+
}
34+
35+
THEN("We expect to see parents before children")
36+
{
37+
REQUIRE(
38+
ids == std::vector<irep_idt>{ID_implies,
39+
ID_notequal,
40+
ID_symbol,
41+
ID_symbol,
42+
ID_equal,
43+
ID_symbol,
44+
ID_symbol});
45+
}
46+
}
47+
48+
WHEN("Replacing one of the middle nodes mid-walk")
49+
{
50+
std::vector<irep_idt> ids;
51+
for(auto it = top.depth_begin(), itend = top.depth_end(); it != itend;
52+
++it)
53+
{
54+
if(it->id() == ID_notequal)
55+
it.mutate() = not_exprt(equal_exprt(symbol, symbol));
56+
57+
ids.push_back(it->id());
58+
}
59+
60+
THEN("We expect to see the newly added child nodes")
61+
{
62+
REQUIRE(
63+
ids == std::vector<irep_idt>{ID_implies,
64+
ID_not,
65+
ID_equal,
66+
ID_symbol,
67+
ID_symbol,
68+
ID_equal,
69+
ID_symbol,
70+
ID_symbol});
71+
}
72+
}
73+
74+
WHEN(
75+
"Replacing one of the middle nodes mid-walk and skipping the new "
76+
"children")
77+
{
78+
std::vector<irep_idt> ids;
79+
for(auto it = top.depth_begin(), itend = top.depth_end(); it != itend;
80+
/* no ++it */)
81+
{
82+
bool replace_here = it->id() == ID_notequal;
83+
84+
if(replace_here)
85+
it.mutate() = not_exprt(equal_exprt(symbol, symbol));
86+
87+
ids.push_back(it->id());
88+
89+
if(replace_here)
90+
it.next_sibling_or_parent();
91+
else
92+
++it;
93+
}
94+
95+
THEN("We expect to skip the newly added child nodes")
96+
{
97+
REQUIRE(
98+
ids == std::vector<irep_idt>{
99+
ID_implies, ID_not, ID_equal, ID_symbol, ID_symbol});
100+
}
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)