Skip to content

Commit ab12de4

Browse files
qinhepingtautschnig
authored andcommitted
Make sure free symbols are declared in SMT2_conv after quantifier rewriting
A bound variable may shadow another free or bound variable.
1 parent 1ed46d5 commit ab12de4

File tree

5 files changed

+64
-44
lines changed

5 files changed

+64
-44
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stddef.h>
4+
5+
#define N 16
6+
7+
void main()
8+
{
9+
int a[N];
10+
a[10] = 0;
11+
bool flag = true;
12+
for(size_t j = 0; j < N; ++j)
13+
// clang-format off
14+
__CPROVER_assigns(j, __CPROVER_object_whole(a))
15+
__CPROVER_loop_invariant(j <= N)
16+
__CPROVER_loop_invariant((j != 0) ==> __CPROVER_forall {
17+
int k;
18+
(0 <= k && k < N) ==> (a[k] == 1)
19+
})
20+
// clang-format on
21+
{
22+
for(size_t i = 0; i < N; ++i)
23+
// clang-format off
24+
__CPROVER_assigns(i, __CPROVER_object_whole(a))
25+
__CPROVER_loop_invariant(0 <= i && i <= N)
26+
__CPROVER_loop_invariant(__CPROVER_forall { int k; k < i ==> a[k] == 1 })
27+
28+
// clang-format on
29+
{
30+
a[i] = 1;
31+
}
32+
}
33+
assert(a[10] == 1);
34+
}

regression/contracts-dfcc/quantifiers-loop-fail/test.desc renamed to regression/contracts-dfcc/quantifiers-loop-05/test.desc

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
CORE dfcc-only
22
main.c
33
--dfcc main --apply-loop-contracts _ --smt2
4-
^EXIT=6$
4+
^EXIT=0$
55
^SIGNAL=0$
6-
^SMT2 solver returned error message:$
7-
^.*\"line \d+ column \d+: unknown constant .*$
8-
^VERIFICATION ERROR$
6+
^VERIFICATION SUCCESSFUL$
97
--
108
^warning: ignoring
119
--

regression/contracts-dfcc/quantifiers-loop-fail/main.c

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/solvers/smt2/smt2_conv.cpp

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4721,11 +4721,11 @@ void smt2_convt::set_to(const exprt &expr, bool value)
47214721
identifier_map.find(identifier) == identifier_map.end() &&
47224722
equal_expr.lhs() != equal_expr.rhs())
47234723
{
4724-
identifiert &id=identifier_map[identifier];
4725-
CHECK_RETURN(id.type.is_nil());
4724+
auto id_entry = identifier_map.insert(
4725+
{identifier, identifiert{equal_expr.lhs().type(), false}});
4726+
CHECK_RETURN(id_entry.second);
47264727

4727-
id.type=equal_expr.lhs().type();
4728-
find_symbols(id.type);
4728+
find_symbols(id_entry.first->second.type);
47294729
exprt prepared_rhs = prepare_for_convert_expr(equal_expr.rhs());
47304730

47314731
std::string smt2_identifier=convert_identifier(identifier);
@@ -4908,17 +4908,30 @@ void smt2_convt::find_symbols(const exprt &expr)
49084908

49094909
if(expr.id() == ID_exists || expr.id() == ID_forall)
49104910
{
4911+
std::unordered_map<irep_idt, std::optional<identifiert>> shadowed_syms;
4912+
49114913
// do not declare the quantified symbol, but record
49124914
// as 'bound symbol'
49134915
const auto &q_expr = to_quantifier_expr(expr);
49144916
for(const auto &symbol : q_expr.variables())
49154917
{
49164918
const auto identifier = symbol.get_identifier();
4917-
identifiert &id = identifier_map[identifier];
4918-
id.type = symbol.type();
4919-
id.is_bound = true;
4919+
auto id_entry =
4920+
identifier_map.insert({identifier, identifiert{symbol.type(), true}});
4921+
shadowed_syms.insert(
4922+
{identifier,
4923+
id_entry.second ? std::nullopt
4924+
: std::optional{id_entry.first->second}});
49204925
}
49214926
find_symbols(q_expr.where());
4927+
for(const auto &[id, shadowed_val] : shadowed_syms)
4928+
{
4929+
auto previous_entry = identifier_map.find(id);
4930+
if(!shadowed_val.has_value())
4931+
identifier_map.erase(previous_entry);
4932+
else
4933+
previous_entry->second = std::move(*shadowed_val);
4934+
}
49224935
return;
49234936
}
49244937

@@ -4941,12 +4954,11 @@ void smt2_convt::find_symbols(const exprt &expr)
49414954
identifier="nondet_"+
49424955
id2string(to_nondet_symbol_expr(expr).get_identifier());
49434956

4944-
identifiert &id=identifier_map[identifier];
4957+
auto id_entry =
4958+
identifier_map.insert({identifier, identifiert{expr.type(), false}});
49454959

4946-
if(id.type.is_nil())
4960+
if(id_entry.second)
49474961
{
4948-
id.type=expr.type();
4949-
49504962
std::string smt2_identifier=convert_identifier(identifier);
49514963
smt2_identifiers.insert(smt2_identifier);
49524964

src/solvers/smt2/smt2_conv.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,16 @@ class smt2_convt : public stack_decision_proceduret
242242
// keeps track of all non-Boolean symbols and their value
243243
struct identifiert
244244
{
245+
// We do not currently read any of the following members, but might do so in
246+
// future. At this time, we just care about (not) having an entry in
247+
// `identifier_map`.
245248
bool is_bound;
246249
typet type;
247250
exprt value;
248251

249-
identifiert() : is_bound(false)
252+
identifiert(typet type, bool is_bound)
253+
: is_bound(is_bound), type(std::move(type))
250254
{
251-
type.make_nil();
252255
value.make_nil();
253256
}
254257
};

0 commit comments

Comments
 (0)