Skip to content

Commit e34f4fd

Browse files
committed
introduce binding_exprt::instantiate
This introduces a helper method to substitute variables that are bound in a binding_exprt by a vector of given values. The substitution algorithm preserves sharing when no substitution is made and is aware of hiding when nesting bindings.
1 parent d52022d commit e34f4fd

File tree

3 files changed

+158
-9
lines changed

3 files changed

+158
-9
lines changed

src/solvers/flattening/boolbv_quantifier.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ Author: Daniel Kroening, [email protected]
1414
#include <util/optional.h>
1515
#include <util/range.h>
1616
#include <util/replace_expr.h>
17-
#include <util/replace_symbol.h>
1817
#include <util/simplify_expr.h>
1918

2019
/// A method to detect equivalence between experts that can contain typecast
@@ -212,17 +211,11 @@ literalt boolbvt::convert_quantifier(const quantifier_exprt &src)
212211
auto fresh_symbols = fresh_binding(src);
213212

214213
// replace in where()
215-
replace_symbolt replace_symbol;
216-
217-
for(const auto &pair : make_range(src.variables()).zip(fresh_symbols))
218-
replace_symbol.insert(pair.first, pair.second);
219-
220-
exprt renamed_where = src.where();
221-
replace_symbol(renamed_where);
214+
auto where_replaced = src.instantiate(fresh_symbols);
222215

223216
// produce new quantifier expression
224217
auto new_src =
225-
quantifier_exprt(src.id(), std::move(fresh_symbols), renamed_where);
218+
quantifier_exprt(src.id(), std::move(fresh_symbols), where_replaced);
226219

227220
const auto res = instantiate_quantifier(src, ns);
228221

src/util/std_expr.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Author: Daniel Kroening, [email protected]
1111
#include "namespace.h"
1212
#include "range.h"
1313

14+
#include <map>
15+
1416
bool constant_exprt::value_is_zero_string() const
1517
{
1618
const std::string val=id2string(get_value());
@@ -134,3 +136,130 @@ void let_exprt::validate(const exprt &expr, const validation_modet vm)
134136
"let bindings must be type consistent");
135137
}
136138
}
139+
140+
static optionalt<exprt> substitute_symbols_rec(
141+
const std::map<irep_idt, exprt> &substitutions,
142+
exprt src)
143+
{
144+
if(src.id() == ID_symbol)
145+
{
146+
auto s_it = substitutions.find(to_symbol_expr(src).get_identifier());
147+
if(s_it == substitutions.end())
148+
return {};
149+
else
150+
return s_it->second;
151+
}
152+
else if(src.id() == ID_forall || src.id() == ID_exists ||
153+
src.id() == ID_lambda)
154+
{
155+
const auto &binding_expr = to_binding_expr(src);
156+
157+
// bindings may be nested,
158+
// which may hide some of our substitutions
159+
auto new_substitutions = substitutions;
160+
for(const auto &variable : binding_expr.variables())
161+
new_substitutions.erase(variable.get_identifier());
162+
163+
auto op_result = substitute_symbols_rec(new_substitutions, binding_expr.where());
164+
if(op_result.has_value())
165+
return binding_exprt(src.id(), binding_expr.variables(), op_result.value(), binding_expr.type());
166+
else
167+
return {};
168+
}
169+
else if(src.id() == ID_let)
170+
{
171+
auto new_let_expr = to_let_expr(src); // copy
172+
const auto &binding_expr = to_let_expr(src).binding();
173+
174+
// bindings may be nested,
175+
// which may hide some of our substitutions
176+
auto new_substitutions = substitutions;
177+
for(const auto &variable : binding_expr.variables())
178+
new_substitutions.erase(variable.get_identifier());
179+
180+
bool op_changed = false;
181+
182+
for(auto &op : new_let_expr.values())
183+
{
184+
auto op_result = substitute_symbols_rec(new_substitutions, op);
185+
186+
if(op_result.has_value())
187+
{
188+
op = op_result.value();
189+
op_changed = true;
190+
}
191+
}
192+
193+
auto op_result = substitute_symbols_rec(new_substitutions, binding_expr.where());
194+
if(op_result.has_value())
195+
{
196+
new_let_expr.where() = op_result.value();
197+
op_changed = true;
198+
}
199+
200+
if(op_changed)
201+
return std::move(new_let_expr);
202+
else
203+
return {};
204+
}
205+
206+
if(!src.has_operands())
207+
return {};
208+
209+
bool op_changed = false;
210+
211+
for(auto &op : src.operands())
212+
{
213+
auto op_result = substitute_symbols_rec(substitutions, op);
214+
215+
if(op_result.has_value())
216+
{
217+
op = op_result.value();
218+
op_changed = true;
219+
}
220+
}
221+
222+
if(op_changed)
223+
return src;
224+
else
225+
return {};
226+
}
227+
228+
exprt binding_exprt::instantiate(const operandst &values) const
229+
{
230+
// number of values must match the number of bound variables
231+
auto &variables = this->variables();
232+
PRECONDITION(variables.size() == values.size());
233+
234+
std::map<symbol_exprt, exprt> value_map;
235+
236+
for(std::size_t i = 0; i < variables.size(); i++)
237+
{
238+
// types must match
239+
PRECONDITION(variables[i].type() == values[i].type());
240+
value_map[variables[i]] = values[i];
241+
}
242+
243+
// build a subsitution map
244+
std::map<irep_idt, exprt> substitutions;
245+
246+
for(std::size_t i = 0; i < variables.size(); i++)
247+
substitutions[variables[i].get_identifier()] = values[i];
248+
249+
// now recurse downwards and substitute in 'where'
250+
auto substitute_result = substitute_symbols_rec(substitutions, where());
251+
252+
if(substitute_result.has_value())
253+
return substitute_result.value();
254+
else
255+
return where(); // trivial case, variables not used
256+
}
257+
258+
exprt binding_exprt::instantiate(const variablest &new_variables) const
259+
{
260+
std::vector<exprt> values;
261+
values.reserve(new_variables.size());
262+
for(const auto &new_variable : new_variables)
263+
values.push_back(new_variable);
264+
return instantiate(values);
265+
}

src/util/std_expr.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2797,8 +2797,35 @@ class binding_exprt : public binary_exprt
27972797
{
27982798
return op1();
27992799
}
2800+
2801+
/// substitute free occurrences of the variables in where()
2802+
/// by the given values
2803+
exprt instantiate(const exprt::operandst &) const;
2804+
2805+
/// substitute free occurrences of the variables in where()
2806+
/// by a set of different symbols
2807+
exprt instantiate(const variablest &) const;
28002808
};
28012809

2810+
inline void validate_expr(const binding_exprt &binding_expr)
2811+
{
2812+
validate_operands(binding_expr, 2, "Binding expressions must have two operands");
2813+
}
2814+
2815+
/// \brief Cast an exprt to a \ref binding_exprt
2816+
///
2817+
/// \a expr must be known to be \ref binding_exprt.
2818+
///
2819+
/// \param expr: Source expression
2820+
/// \return Object of type \ref binding_exprt
2821+
inline const binding_exprt &to_binding_expr(const exprt &expr)
2822+
{
2823+
PRECONDITION(expr.id()==ID_forall || expr.id() == ID_exists || expr.id() == ID_lambda);
2824+
const binding_exprt &ret = static_cast<const binding_exprt &>(expr);
2825+
validate_expr(ret);
2826+
return ret;
2827+
}
2828+
28022829
/// \brief A let expression
28032830
class let_exprt : public binary_exprt
28042831
{

0 commit comments

Comments
 (0)