Skip to content

Commit 8cf524f

Browse files
committed
Expression initialiser: do not generate nil for empty union
Empty unions do not have a meaningful union_exprt as an initialiser, and thus the expression initialiser should make use of returning an optionalt{} instead. This test was generated using C-Reduce based on an example initially generated by CSmith.
1 parent 1ab5de1 commit 8cf524f

File tree

7 files changed

+80
-22
lines changed

7 files changed

+80
-22
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
union {
2+
} a;
3+
main()
4+
{
5+
__assert_fail("", 2, __PRETTY_FUNCTION__);
6+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
^warning: ignoring
9+
--
10+
CBMC would previously report "warning: ignoring nil" and eventually fail an
11+
invariant in src/solvers/flattening/boolbv_width.cpp:199 function: get_entry.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
union empty {
2+
};
3+
4+
struct S
5+
{
6+
int x;
7+
union empty e;
8+
int y;
9+
};
10+
11+
struct S s = {1};
12+
13+
int main()
14+
{
15+
assert(s.x == 1 && s.y == 0);
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/ansi-c/c_typecheck_initializer.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -953,16 +953,21 @@ exprt c_typecheck_baset::do_initializer_list(
953953
full_type.id()==ID_union ||
954954
full_type.id()==ID_vector)
955955
{
956-
// start with zero everywhere
957-
const auto zero = zero_initializer(type, value.source_location(), *this);
958-
if(!zero.has_value())
956+
if(
957+
full_type.id() != ID_union ||
958+
!to_union_type(full_type).components().empty())
959959
{
960-
error().source_location = value.source_location();
961-
error() << "cannot zero-initialize '" << to_string(full_type) << "'"
962-
<< eom;
963-
throw 0;
960+
// start with zero everywhere
961+
const auto zero = zero_initializer(type, value.source_location(), *this);
962+
if(!zero.has_value())
963+
{
964+
error().source_location = value.source_location();
965+
error() << "cannot zero-initialize '" << to_string(full_type) << "'"
966+
<< eom;
967+
throw 0;
968+
}
969+
result = *zero;
964970
}
965-
result = *zero;
966971
}
967972
else if(full_type.id()==ID_array)
968973
{

src/linking/static_lifetime_init.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,30 +65,36 @@ static_lifetime_init(const irep_idt &identifier, symbol_tablet &symbol_table)
6565
return {}; // do not initialize
6666
}
6767

68-
exprt rhs;
68+
optionalt<exprt> rhs;
6969

7070
if((symbol.value.is_nil() && symbol.is_extern) ||
7171
symbol.value.id() == ID_nondet)
7272
{
7373
// Nondet initialise if not linked, or if explicitly requested.
7474
// Compilers would usually complain about the unlinked symbol case.
75-
const auto nondet = nondet_initializer(symbol.type, symbol.location, ns);
76-
CHECK_RETURN(nondet.has_value());
77-
rhs = *nondet;
75+
rhs = nondet_initializer(symbol.type, symbol.location, ns);
7876
}
7977
else if(symbol.value.is_nil())
8078
{
81-
const auto zero = zero_initializer(symbol.type, symbol.location, ns);
82-
CHECK_RETURN(zero.has_value());
83-
rhs = *zero;
79+
rhs = zero_initializer(symbol.type, symbol.location, ns);
8480
}
8581
else
8682
rhs = symbol.value;
8783

88-
code_assignt code(symbol.symbol_expr(), rhs);
89-
code.add_source_location() = symbol.location;
84+
if(rhs.has_value())
85+
{
86+
code_assignt code(symbol.symbol_expr(), *rhs);
87+
code.add_source_location() = symbol.location;
88+
89+
return std::move(code);
90+
}
91+
else
92+
{
93+
code_expressiont code{typecast_exprt{symbol.symbol_expr(), empty_typet{}}};
94+
code.add_source_location() = symbol.location;
9095

91-
return std::move(code);
96+
return std::move(code);
97+
}
9298
}
9399

94100
void static_lifetime_init(

src/util/expr_initializer.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,14 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
192192
code_value.add_source_location()=source_location;
193193
value.add_to_operands(std::move(code_value));
194194
}
195+
else if(
196+
c.type().id() == ID_union_tag &&
197+
ns.follow_tag(to_union_tag_type(c.type())).components().empty())
198+
{
199+
union_exprt empty_union{irep_idt{}, nil_exprt{}, c.type()};
200+
empty_union.add_source_location() = source_location;
201+
value.add_to_operands(std::move(empty_union));
202+
}
195203
else
196204
{
197205
const auto member = expr_initializer_rec(c.type(), source_location);
@@ -235,10 +243,8 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
235243

236244
if(!found)
237245
{
238-
// stupid empty union
239-
union_exprt value(irep_idt(), nil_exprt(), type);
240-
value.add_source_location() = source_location;
241-
return std::move(value);
246+
// empty union or no member of known size
247+
return {};
242248
}
243249
else
244250
{

0 commit comments

Comments
 (0)