Skip to content

Commit ee2690d

Browse files
authored
Merge pull request #6594 from tautschnig/bugfixes/6586-enum-check
Do not generate enum-value checks for function call left-hand sides
2 parents 8b7bed6 + 7e0f829 commit ee2690d

File tree

10 files changed

+138
-8
lines changed

10 files changed

+138
-8
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
enum_test3.c
3+
--show-goto-functions
4+
0 ≤ cast\(main::1::ev1, (un)?signedbv\[\d+\]\) ∧ cast\(main::1::ev1, (un)?signedbv\[\d+\]\) ≤ 4$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
main::1::ev1 = 0
9+
--
10+
This test is to demonstrate simplification of enumerated ranges of integers.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
no_duplicate.c
3+
--show-goto-functions
4+
main::1::e = 1 ∨ main::1::e = 2 ∨ main::1::e = 4 ∨ main::1::e = 8
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
type: c_enum_tag
9+
--
10+
This test is to show that format(expr) also works for enum-typed constants.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
enum enm
2+
{
3+
first = 1,
4+
second = 1 << 1,
5+
third = 1 << 2,
6+
fourth = 1 << 3,
7+
};
8+
9+
static enum enm efunc(void)
10+
{
11+
return second;
12+
}
13+
14+
int main(void)
15+
{
16+
enum enm e = efunc();
17+
__CPROVER_assert(__CPROVER_enum_is_in_range(e), "enum failure");
18+
return (e);
19+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
no_duplicate.c
3+
--enum-range-check
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
line 17 enum range check in e: SUCCESS$
9+
--
10+
Test that enum range checks aren't applied to function-call left-hand sides, and
11+
that no extra checks are generated for uses of __CPROVER_enum_is_in_range.

regression/validate-trace-xml-schema/check.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
['integer-assignments1', 'integer-typecheck.desc'],
2626
['destructors', 'compound_literal.desc'],
2727
['destructors', 'enter_lexical_block.desc'],
28+
['enum_is_in_range', 'enum_test3-simplified.desc'],
29+
['enum_is_in_range', 'format.desc'],
2830
['r_w_ok9', 'simplify.desc'],
2931
['reachability-slice-interproc2', 'test.desc'],
3032
# this one wants show-properties instead producing a trace

src/analyses/goto_check_c.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,7 @@ void goto_check_ct::enum_range_check(const exprt &expr, const guardt &guard)
517517
return;
518518

519519
const c_enum_tag_typet &c_enum_tag_type = to_c_enum_tag_type(expr.type());
520-
symbolt enum_type = ns.lookup(c_enum_tag_type.get_identifier());
521-
const c_enum_typet &c_enum_type = to_c_enum_type(enum_type.type);
520+
const c_enum_typet &c_enum_type = ns.follow_tag(c_enum_tag_type);
522521

523522
const c_enum_typet::memberst enum_values = c_enum_type.members();
524523

@@ -2148,7 +2147,13 @@ void goto_check_ct::goto_check(
21482147
}
21492148
else if(i.is_function_call())
21502149
{
2151-
check(i.call_lhs());
2150+
// Disable enum range checks for left-hand sides as their values are yet
2151+
// to be set (by this function call).
2152+
{
2153+
flag_overridet resetter(i.source_location());
2154+
resetter.disable_flag(enable_enum_range_check, "enum_range_check");
2155+
check(i.call_lhs());
2156+
}
21522157
check(i.call_function());
21532158

21542159
for(const auto &arg : i.call_arguments())

src/goto-programs/builtin_functions.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,8 +636,9 @@ void goto_convertt::do_enum_is_in_range(
636636
disjuncts.push_back(equal_exprt(enum_expr, std::move(val)));
637637
}
638638

639-
code_assignt assignment(lhs, disjunction(disjuncts));
639+
code_assignt assignment(lhs, simplify_expr(disjunction(disjuncts), ns));
640640
assignment.add_source_location() = function.source_location();
641+
assignment.add_source_location().add_pragma("disable:enum-range-check");
641642
copy(assignment, ASSIGN, dest);
642643
}
643644

src/util/arith_tools.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ bool to_integer(const constant_exprt &expr, mp_integer &int_value)
5959
const typet &underlying_type = to_c_enum_type(type).underlying_type();
6060
if(underlying_type.id() == ID_signedbv)
6161
{
62-
const auto width = to_signedbv_type(type).get_width();
62+
const auto width = to_signedbv_type(underlying_type).get_width();
6363
int_value = bvrep2integer(value, width, true);
6464
return false;
6565
}
6666
else if(underlying_type.id() == ID_unsignedbv)
6767
{
68-
const auto width = to_unsignedbv_type(type).get_width();
68+
const auto width = to_unsignedbv_type(underlying_type).get_width();
6969
int_value = bvrep2integer(value, width, false);
7070
return false;
7171
}

src/util/format_expr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ static std::ostream &format_rec(std::ostream &os, const constant_exprt &src)
204204
<< format(pointer_type.base_type()) << ')';
205205
}
206206
}
207+
else if(type == ID_c_enum_tag)
208+
{
209+
return os << string2integer(id2string(src.get_value()), 16);
210+
}
207211
else
208212
return os << src.pretty();
209213
}

src/util/simplify_expr_boolean.cpp

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ Author: Daniel Kroening, [email protected]
88

99
#include "simplify_expr_class.h"
1010

11-
#include <unordered_set>
12-
11+
#include "arith_tools.h"
12+
#include "c_types.h"
1313
#include "expr_util.h"
1414
#include "mathematical_expr.h"
15+
#include "namespace.h"
1516
#include "std_expr.h"
1617

18+
#include <unordered_set>
19+
1720
simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
1821
{
1922
if(!expr.has_operands())
@@ -96,6 +99,8 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
9699
std::unordered_set<exprt, irep_hash> expr_set;
97100

98101
bool no_change = true;
102+
bool may_be_reducible_to_interval =
103+
expr.id() == ID_or && expr.operands().size() > 2;
99104

100105
exprt::operandst new_operands = expr.operands();
101106

@@ -127,7 +132,70 @@ simplify_exprt::resultt<> simplify_exprt::simplify_boolean(const exprt &expr)
127132
no_change = false;
128133
}
129134
else
135+
{
136+
if(may_be_reducible_to_interval)
137+
may_be_reducible_to_interval = it->id() == ID_equal;
130138
it++;
139+
}
140+
}
141+
142+
if(may_be_reducible_to_interval)
143+
{
144+
optionalt<symbol_exprt> symbol_opt;
145+
std::set<mp_integer> values;
146+
for(const exprt &op : new_operands)
147+
{
148+
equal_exprt eq = to_equal_expr(op);
149+
if(eq.lhs().is_constant())
150+
std::swap(eq.lhs(), eq.rhs());
151+
if(auto s = expr_try_dynamic_cast<symbol_exprt>(eq.lhs()))
152+
{
153+
if(!symbol_opt.has_value())
154+
symbol_opt = *s;
155+
156+
if(*s == *symbol_opt)
157+
{
158+
if(auto c = expr_try_dynamic_cast<constant_exprt>(eq.rhs()))
159+
{
160+
constant_exprt c_tmp = *c;
161+
if(c_tmp.type().id() == ID_c_enum_tag)
162+
c_tmp.type() = ns.follow_tag(to_c_enum_tag_type(c_tmp.type()));
163+
if(auto int_opt = numeric_cast<mp_integer>(c_tmp))
164+
{
165+
values.insert(*int_opt);
166+
continue;
167+
}
168+
}
169+
}
170+
}
171+
172+
symbol_opt.reset();
173+
break;
174+
}
175+
176+
if(symbol_opt.has_value() && values.size() >= 3)
177+
{
178+
mp_integer lower = *values.begin();
179+
mp_integer upper = *std::prev(values.end());
180+
if(upper - lower + 1 == mp_integer{values.size()})
181+
{
182+
typet type = symbol_opt->type();
183+
if(symbol_opt->type().id() == ID_c_enum_tag)
184+
{
185+
type = ns.follow_tag(to_c_enum_tag_type(symbol_opt->type()))
186+
.underlying_type();
187+
}
188+
189+
less_than_or_equal_exprt lb{
190+
from_integer(lower, type),
191+
typecast_exprt::conditional_cast(*symbol_opt, type)};
192+
less_than_or_equal_exprt ub{
193+
typecast_exprt::conditional_cast(*symbol_opt, type),
194+
from_integer(upper, type)};
195+
196+
return and_exprt{lb, ub};
197+
}
198+
}
131199
}
132200

133201
// search for a and !a

0 commit comments

Comments
 (0)