Skip to content

Commit d52022d

Browse files
authored
Merge pull request #6189 from diffblue/quantifier_scoping
Quantifier scoping
2 parents 523622a + 62e04e4 commit d52022d

File tree

6 files changed

+131
-91
lines changed

6 files changed

+131
-91
lines changed

src/solvers/flattening/boolbv.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,29 @@ bool boolbvt::is_unbounded_array(const typet &type) const
552552
return false;
553553
}
554554

555+
binding_exprt::variablest boolbvt::fresh_binding(const binding_exprt &binding)
556+
{
557+
// to ensure freshness of the new identifiers
558+
scope_counter++;
559+
560+
binding_exprt::variablest result;
561+
result.reserve(binding.variables().size());
562+
563+
for(const auto &binding : binding.variables())
564+
{
565+
const auto &old_identifier = binding.get_identifier();
566+
567+
// produce a new identifier
568+
const irep_idt new_identifier =
569+
"boolbvt::scope::" + std::to_string(scope_counter) +
570+
"::" + id2string(old_identifier);
571+
572+
result.emplace_back(new_identifier, binding.type());
573+
}
574+
575+
return result;
576+
}
577+
555578
void boolbvt::print_assignment(std::ostream &out) const
556579
{
557580
arrayst::print_assignment(out);

src/solvers/flattening/boolbv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ class boolbvt:public arrayst
274274

275275
// scopes
276276
std::size_t scope_counter = 0;
277+
278+
/// create new, unique variables for the given binding
279+
binding_exprt::variablest fresh_binding(const binding_exprt &);
277280
};
278281

279282
#endif // CPROVER_SOLVERS_FLATTENING_BOOLBV_H

src/solvers/flattening/boolbv_let.cpp

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,31 @@ bvt boolbvt::convert_let(const let_exprt &expr)
4040
for(auto &value : values)
4141
converted_values.push_back(convert_bv(value));
4242

43-
scope_counter++;
44-
45-
// for renaming the bound symbols
46-
replace_symbolt replace_symbol;
43+
// get fresh bound symbols
44+
auto fresh_variables = fresh_binding(expr.binding());
4745

4846
// Now assign
49-
for(const auto &binding : make_range(variables).zip(converted_values))
47+
for(const auto &binding : make_range(fresh_variables).zip(converted_values))
5048
{
5149
bool is_boolean = binding.first.type().id() == ID_bool;
52-
const auto &old_identifier = binding.first.get_identifier();
53-
54-
// produce a new identifier
55-
const irep_idt new_identifier =
56-
"boolbvt::scope::" + std::to_string(scope_counter) +
57-
"::" + id2string(old_identifier);
50+
const auto &identifier = binding.first.get_identifier();
5851

5952
// make the symbol visible
6053
if(is_boolean)
6154
{
6255
DATA_INVARIANT(binding.second.size() == 1, "boolean values have one bit");
63-
symbols[new_identifier] = binding.second[0];
56+
symbols[identifier] = binding.second[0];
6457
}
6558
else
66-
map.set_literals(new_identifier, binding.first.type(), binding.second);
67-
68-
// remember the renaming
69-
replace_symbol.insert(
70-
binding.first, symbol_exprt(new_identifier, binding.first.type()));
59+
map.set_literals(identifier, binding.first.type(), binding.second);
7160
}
7261

62+
// for renaming the bound symbols
63+
replace_symbolt replace_symbol;
64+
65+
for(const auto &pair : make_range(variables).zip(fresh_variables))
66+
replace_symbol.insert(pair.first, pair.second);
67+
7368
// rename the bound symbols in 'where'
7469
exprt where_renamed = expr.where();
7570
replace_symbol(where_renamed);
@@ -78,14 +73,13 @@ bvt boolbvt::convert_let(const let_exprt &expr)
7873
bvt result_bv = convert_bv(where_renamed);
7974

8075
// the mapping can now be deleted
81-
for(const auto &entry : replace_symbol.get_expr_map())
76+
for(const auto &entry : fresh_variables)
8277
{
83-
const auto &new_symbol = to_symbol_expr(entry.second);
84-
const auto &type = new_symbol.type();
78+
const auto &type = entry.type();
8579
if(type.id() == ID_bool)
86-
symbols.erase(new_symbol.get_identifier());
80+
symbols.erase(entry.get_identifier());
8781
else
88-
map.erase_literals(new_symbol.get_identifier(), type);
82+
map.erase_literals(entry.get_identifier(), type);
8983
}
9084

9185
return result_bv;

src/solvers/flattening/boolbv_quantifier.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ Author: Daniel Kroening, [email protected]
1212
#include <util/expr_util.h>
1313
#include <util/invariant.h>
1414
#include <util/optional.h>
15+
#include <util/range.h>
1516
#include <util/replace_expr.h>
17+
#include <util/replace_symbol.h>
1618
#include <util/simplify_expr.h>
1719

1820
/// A method to detect equivalence between experts that can contain typecast
@@ -206,6 +208,22 @@ literalt boolbvt::convert_quantifier(const quantifier_exprt &src)
206208
{
207209
PRECONDITION(src.id() == ID_forall || src.id() == ID_exists);
208210

211+
// We first worry about the scoping of the symbols bound by the quantifier.
212+
auto fresh_symbols = fresh_binding(src);
213+
214+
// 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);
222+
223+
// produce new quantifier expression
224+
auto new_src =
225+
quantifier_exprt(src.id(), std::move(fresh_symbols), renamed_where);
226+
209227
const auto res = instantiate_quantifier(src, ns);
210228

211229
if(res)

src/solvers/smt2/smt2_parser.cpp

Lines changed: 48 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -131,30 +131,6 @@ exprt::operandst smt2_parsert::operands()
131131
return result;
132132
}
133133

134-
irep_idt smt2_parsert::add_fresh_id(
135-
const irep_idt &id,
136-
idt::kindt kind,
137-
const exprt &expr)
138-
{
139-
auto &count=renaming_counters[id];
140-
irep_idt new_id;
141-
do
142-
{
143-
new_id=id2string(id)+'#'+std::to_string(count);
144-
count++;
145-
} while(!id_map
146-
.emplace(
147-
std::piecewise_construct,
148-
std::forward_as_tuple(new_id),
149-
std::forward_as_tuple(kind, expr))
150-
.second);
151-
152-
// record renaming
153-
renaming_map[id] = new_id;
154-
155-
return new_id;
156-
}
157-
158134
void smt2_parsert::add_unique_id(const irep_idt &id, const exprt &expr)
159135
{
160136
if(!id_map
@@ -169,16 +145,6 @@ void smt2_parsert::add_unique_id(const irep_idt &id, const exprt &expr)
169145
}
170146
}
171147

172-
irep_idt smt2_parsert::rename_id(const irep_idt &id) const
173-
{
174-
auto it=renaming_map.find(id);
175-
176-
if(it==renaming_map.end())
177-
return id;
178-
else
179-
return it->second;
180-
}
181-
182148
exprt smt2_parsert::let_expression()
183149
{
184150
if(next_token() != smt2_tokenizert::OPEN)
@@ -254,7 +220,7 @@ exprt smt2_parsert::quantifier_expression(irep_idt id)
254220
if(next_token() != smt2_tokenizert::OPEN)
255221
throw error() << "expected bindings after " << id;
256222

257-
std::vector<symbol_exprt> bindings;
223+
binding_exprt::variablest bindings;
258224

259225
while(smt2_tokenizer.peek() == smt2_tokenizert::OPEN)
260226
{
@@ -276,18 +242,23 @@ exprt smt2_parsert::quantifier_expression(irep_idt id)
276242
if(next_token() != smt2_tokenizert::CLOSE)
277243
throw error("expected ')' at end of bindings");
278244

279-
// save the renaming map
280-
renaming_mapt old_renaming_map = renaming_map;
245+
// we may hide identifiers in outer scopes
246+
std::vector<std::pair<irep_idt, idt>> saved_ids;
281247

282-
// go forwards, add to id_map, renaming if need be
248+
// add the bindings to the id_map
283249
for(auto &b : bindings)
284250
{
285-
const irep_idt id =
286-
add_fresh_id(b.get_identifier(), idt::BINDING, exprt(ID_nil, b.type()));
287-
288-
b.set_identifier(id);
251+
auto insert_result =
252+
id_map.insert({b.get_identifier(), idt{idt::BINDING, b.type()}});
253+
if(!insert_result.second) // already there
254+
{
255+
auto &id_entry = *insert_result.first;
256+
saved_ids.emplace_back(id_entry.first, std::move(id_entry.second));
257+
id_entry.second = idt{idt::BINDING, b.type()};
258+
}
289259
}
290260

261+
// now parse, with bindings in place
291262
exprt expr=expression();
292263

293264
if(next_token() != smt2_tokenizert::CLOSE)
@@ -299,8 +270,9 @@ exprt smt2_parsert::quantifier_expression(irep_idt id)
299270
for(const auto &b : bindings)
300271
id_map.erase(b.get_identifier());
301272

302-
// restore renaming map
303-
renaming_map = old_renaming_map;
273+
// restore any previous ids
274+
for(auto &saved_id : saved_ids)
275+
id_map.insert(std::move(saved_id));
304276

305277
// go backwards, build quantified expression
306278
for(auto r_it=bindings.rbegin(); r_it!=bindings.rend(); r_it++)
@@ -603,20 +575,18 @@ exprt smt2_parsert::function_application()
603575
auto op = operands();
604576

605577
// rummage through id_map
606-
const irep_idt final_id = rename_id(id);
607-
auto id_it = id_map.find(final_id);
578+
auto id_it = id_map.find(id);
608579
if(id_it != id_map.end())
609580
{
610581
if(id_it->second.type.id() == ID_mathematical_function)
611582
{
612-
return function_application(
613-
symbol_exprt(final_id, id_it->second.type), op);
583+
return function_application(symbol_exprt(id, id_it->second.type), op);
614584
}
615585
else
616-
return symbol_exprt(final_id, id_it->second.type);
586+
return symbol_exprt(id, id_it->second.type);
617587
}
618-
619-
throw error() << "unknown function symbol '" << id << '\'';
588+
else
589+
throw error() << "unknown function symbol '" << id << '\'';
620590
}
621591
break;
622592

@@ -916,11 +886,10 @@ exprt smt2_parsert::expression()
916886
return e_it->second();
917887

918888
// rummage through id_map
919-
const irep_idt final_id = rename_id(identifier);
920-
auto id_it = id_map.find(final_id);
889+
auto id_it = id_map.find(identifier);
921890
if(id_it != id_map.end())
922891
{
923-
symbol_exprt symbol_expr(final_id, id_it->second.type);
892+
symbol_exprt symbol_expr(identifier, id_it->second.type);
924893
if(smt2_tokenizer.token_is_quoted_symbol())
925894
symbol_expr.set(ID_C_quoted, true);
926895
return std::move(symbol_expr);
@@ -1393,9 +1362,7 @@ smt2_parsert::function_signature_definition()
13931362

13941363
irep_idt id = smt2_tokenizer.get_buffer();
13951364
domain.push_back(sort());
1396-
1397-
parameters.push_back(
1398-
add_fresh_id(id, idt::PARAMETER, exprt(ID_nil, domain.back())));
1365+
parameters.push_back(id);
13991366

14001367
if(next_token() != smt2_tokenizert::CLOSE)
14011368
throw error("expected ')' at end of parameter");
@@ -1497,16 +1464,35 @@ void smt2_parsert::setup_commands()
14971464
if(next_token() != smt2_tokenizert::SYMBOL)
14981465
throw error("expected a symbol after define-fun");
14991466

1500-
// save the renaming map
1501-
renaming_mapt old_renaming_map = renaming_map;
1502-
15031467
const irep_idt id = smt2_tokenizer.get_buffer();
15041468

15051469
const auto signature = function_signature_definition();
1470+
1471+
// put the parameters into the scope and take care of hiding
1472+
std::vector<std::pair<irep_idt, idt>> hidden_ids;
1473+
1474+
for(const auto &pair : signature.ids_and_types())
1475+
{
1476+
auto insert_result =
1477+
id_map.insert({pair.first, idt{idt::PARAMETER, pair.second}});
1478+
if(!insert_result.second) // already there
1479+
{
1480+
auto &id_entry = *insert_result.first;
1481+
hidden_ids.emplace_back(id_entry.first, std::move(id_entry.second));
1482+
id_entry.second = idt{idt::PARAMETER, pair.second};
1483+
}
1484+
}
1485+
1486+
// now parse body with parameter ids in place
15061487
const auto body = expression();
15071488

1508-
// restore renamings
1509-
std::swap(renaming_map, old_renaming_map);
1489+
// remove the parameter ids
1490+
for(auto &id : signature.parameters)
1491+
id_map.erase(id);
1492+
1493+
// restore the hidden ids, if any
1494+
for(auto &hidden_id : hidden_ids)
1495+
id_map.insert(std::move(hidden_id));
15101496

15111497
// check type of body
15121498
if(signature.type.id() == ID_mathematical_function)

src/solvers/smt2/smt2_parser.h

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class smt2_parsert
4242
{
4343
}
4444

45+
idt(kindt _kind, typet __type)
46+
: kind(_kind), type(std::move(__type)), definition(nil_exprt())
47+
{
48+
}
49+
4550
kindt kind;
4651
typet type;
4752
exprt definition;
@@ -88,14 +93,9 @@ class smt2_parsert
8893
std::size_t parenthesis_level;
8994
smt2_tokenizert::tokent next_token();
9095

91-
// for let/quantifier bindings, function parameters
92-
using renaming_mapt=std::map<irep_idt, irep_idt>;
93-
renaming_mapt renaming_map;
94-
using renaming_counterst=std::map<irep_idt, unsigned>;
95-
renaming_counterst renaming_counters;
96-
irep_idt add_fresh_id(const irep_idt &, idt::kindt, const exprt &);
96+
// add the given identifier to the id_map but
97+
// complain if that identifier is used already
9798
void add_unique_id(const irep_idt &, const exprt &);
98-
irep_idt rename_id(const irep_idt &) const;
9999

100100
struct signature_with_parameter_idst
101101
{
@@ -117,6 +117,22 @@ class smt2_parsert
117117
_parameters.size()) ||
118118
(_type.id() != ID_mathematical_function && _parameters.empty()));
119119
}
120+
121+
// a convenience helper for iterating over identifiers and types
122+
std::vector<std::pair<irep_idt, typet>> ids_and_types() const
123+
{
124+
if(parameters.empty())
125+
return {};
126+
else
127+
{
128+
std::vector<std::pair<irep_idt, typet>> result;
129+
result.reserve(parameters.size());
130+
const auto &domain = to_mathematical_function_type(type).domain();
131+
for(std::size_t i = 0; i < parameters.size(); i++)
132+
result.emplace_back(parameters[i], domain[i]);
133+
return result;
134+
}
135+
}
120136
};
121137

122138
// expressions

0 commit comments

Comments
 (0)