Skip to content

Commit 4e8f9ca

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 fd8af8a commit 4e8f9ca

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
@@ -908,16 +908,21 @@ exprt c_typecheck_baset::do_initializer_list(
908908
full_type.id()==ID_union ||
909909
full_type.id()==ID_vector)
910910
{
911-
// start with zero everywhere
912-
const auto zero = zero_initializer(type, value.source_location(), *this);
913-
if(!zero.has_value())
911+
if(
912+
full_type.id() != ID_union ||
913+
!to_union_type(full_type).components().empty())
914914
{
915-
error().source_location = value.source_location();
916-
error() << "cannot zero-initialize '" << to_string(full_type) << "'"
917-
<< eom;
918-
throw 0;
915+
// start with zero everywhere
916+
const auto zero = zero_initializer(type, value.source_location(), *this);
917+
if(!zero.has_value())
918+
{
919+
error().source_location = value.source_location();
920+
error() << "cannot zero-initialize '" << to_string(full_type) << "'"
921+
<< eom;
922+
throw 0;
923+
}
924+
result = *zero;
919925
}
920-
result = *zero;
921926
}
922927
else if(full_type.id()==ID_array)
923928
{

src/linking/static_lifetime_init.cpp

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

71-
exprt rhs;
71+
optionalt<exprt> rhs;
7272

7373
if((symbol.value.is_nil() && symbol.is_extern) ||
7474
symbol.value.id() == ID_nondet)
7575
{
7676
// Nondet initialise if not linked, or if explicitly requested.
7777
// Compilers would usually complain about the unlinked symbol case.
78-
const auto nondet = nondet_initializer(symbol.type, symbol.location, ns);
79-
CHECK_RETURN(nondet.has_value());
80-
rhs = *nondet;
78+
rhs = nondet_initializer(symbol.type, symbol.location, ns);
8179
}
8280
else if(symbol.value.is_nil())
8381
{
84-
const auto zero = zero_initializer(symbol.type, symbol.location, ns);
85-
CHECK_RETURN(zero.has_value());
86-
rhs = *zero;
82+
rhs = zero_initializer(symbol.type, symbol.location, ns);
8783
}
8884
else
8985
rhs = symbol.value;
9086

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

94-
return std::move(code);
99+
return std::move(code);
100+
}
95101
}
96102

97103
void static_lifetime_init(

src/util/expr_initializer.cpp

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

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

0 commit comments

Comments
 (0)