Skip to content

Commit 6764a7d

Browse files
authored
Merge pull request #6408 from tautschnig/empty-union
Handle empty unions via dedicated expression
2 parents 7e976e3 + eff2507 commit 6764a7d

File tree

10 files changed

+104
-40
lines changed

10 files changed

+104
-40
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
union U {
2+
};
3+
4+
struct S
5+
{
6+
int a;
7+
union U u;
8+
int b;
9+
} s;
10+
11+
int main()
12+
{
13+
__CPROVER_assert(0, "");
14+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
^warning: ignoring
9+
^Invariant check failed

src/solvers/flattening/boolbv.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
183183
return convert_struct(to_struct_expr(expr));
184184
else if(expr.id()==ID_union)
185185
return convert_union(to_union_expr(expr));
186+
else if(expr.id() == ID_empty_union)
187+
return convert_empty_union(to_empty_union_expr(expr));
186188
else if(expr.id()==ID_string_constant)
187189
return convert_bitvector(
188190
to_string_constant(expr).to_array_expr());

src/solvers/flattening/boolbv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class boolbvt:public arrayst
162162
virtual bvt convert_let(const let_exprt &);
163163
virtual bvt convert_array_of(const array_of_exprt &expr);
164164
virtual bvt convert_union(const union_exprt &expr);
165+
virtual bvt convert_empty_union(const empty_union_exprt &expr);
165166
virtual bvt convert_bv_typecast(const typecast_exprt &expr);
166167
virtual bvt convert_add_sub(const exprt &expr);
167168
virtual bvt convert_mult(const mult_exprt &expr);

src/solvers/flattening/boolbv_get.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ exprt boolbvt::bv_get_rec(
139139
const union_typet &union_type=to_union_type(type);
140140
const union_typet::componentst &components=union_type.components();
141141

142-
assert(!components.empty());
142+
if(components.empty())
143+
return empty_union_exprt(type);
143144

144145
// Any idea that's better than just returning the first component?
145146
std::size_t component_nr=0;

src/solvers/flattening/boolbv_union.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,8 @@ bvt boolbvt::convert_union(const union_exprt &expr)
3737

3838
return bv;
3939
}
40+
41+
bvt boolbvt::convert_empty_union(const empty_union_exprt &expr)
42+
{
43+
return {};
44+
}

src/solvers/smt2/smt2_conv.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,6 +2126,10 @@ void smt2_convt::convert_expr(const exprt &expr)
21262126
{
21272127
convert_expr(simplify_expr(to_count_trailing_zeros_expr(expr).lower(), ns));
21282128
}
2129+
else if(expr.id() == ID_empty_union)
2130+
{
2131+
out << "()";
2132+
}
21292133
else
21302134
INVARIANT_WITH_DIAGNOSTICS(
21312135
false,
@@ -4403,7 +4407,9 @@ void smt2_convt::set_to(const exprt &expr, bool value)
44034407
if(expr.id() == ID_equal && value)
44044408
{
44054409
const equal_exprt &equal_expr=to_equal_expr(expr);
4406-
if(equal_expr.lhs().type().id() == ID_empty)
4410+
if(
4411+
equal_expr.lhs().type().id() == ID_empty ||
4412+
equal_expr.rhs().id() == ID_empty_union)
44074413
{
44084414
// ignore equality checking over expressions with empty (void) type
44094415
return;
@@ -4891,7 +4897,9 @@ void smt2_convt::convert_type(const typet &type)
48914897
else if(type.id() == ID_union || type.id() == ID_union_tag)
48924898
{
48934899
std::size_t width=boolbv_width(type);
4894-
CHECK_RETURN_WITH_DIAGNOSTICS(width != 0, "failed to get width of union");
4900+
CHECK_RETURN_WITH_DIAGNOSTICS(
4901+
to_union_type(ns.follow(type)).components().empty() || width != 0,
4902+
"failed to get width of union");
48954903

48964904
out << "(_ BitVec " << width << ")";
48974905
}

src/util/expr_initializer.cpp

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -208,51 +208,29 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
208208
}
209209
else if(type_id==ID_union)
210210
{
211-
const union_typet::componentst &components=
212-
to_union_type(type).components();
211+
const union_typet &union_type = to_union_type(type);
213212

214-
union_typet::componentt component;
215-
bool found=false;
216-
mp_integer component_size=0;
217-
218-
// we need to find the largest member
219-
220-
for(const auto &c : components)
221-
{
222-
// skip methods
223-
if(c.type().id() == ID_code)
224-
continue;
225-
226-
auto bits = pointer_offset_bits(c.type(), ns);
227-
228-
if(bits.has_value() && *bits > component_size)
229-
{
230-
component = c;
231-
found=true;
232-
component_size = *bits;
233-
}
234-
}
235-
236-
if(!found)
213+
if(union_type.components().empty())
237214
{
238-
// stupid empty union
239-
union_exprt value(irep_idt(), nil_exprt(), type);
215+
empty_union_exprt value{type};
240216
value.add_source_location() = source_location;
241217
return std::move(value);
242218
}
243-
else
244-
{
245-
auto component_value =
246-
expr_initializer_rec(component.type(), source_location);
247219

248-
if(!component_value.has_value())
249-
return {};
220+
const auto &widest_member = union_type.find_widest_union_component(ns);
221+
if(!widest_member.has_value())
222+
return {};
250223

251-
union_exprt value(component.get_name(), *component_value, type);
252-
value.add_source_location() = source_location;
224+
auto component_value =
225+
expr_initializer_rec(widest_member->first.type(), source_location);
253226

254-
return std::move(value);
255-
}
227+
if(!component_value.has_value())
228+
return {};
229+
230+
union_exprt value{widest_member->first.get_name(), *component_value, type};
231+
value.add_source_location() = source_location;
232+
233+
return std::move(value);
256234
}
257235
else if(type_id==ID_c_enum_tag)
258236
{

src/util/irep_ids.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,7 @@ IREP_ID_TWO(vector_gt, vector->)
862862
IREP_ID_TWO(vector_lt, vector-<)
863863
IREP_ID_ONE(shuffle_vector)
864864
IREP_ID_ONE(count_trailing_zeros)
865+
IREP_ID_ONE(empty_union)
865866

866867
// Projects depending on this code base that wish to extend the list of
867868
// available ids should provide a file local_irep_ids.def in their source tree

src/util/std_expr.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,51 @@ inline union_exprt &to_union_expr(exprt &expr)
16621662
return ret;
16631663
}
16641664

1665+
/// \brief Union constructor to support unions without any member (a GCC/Clang
1666+
/// feature).
1667+
class empty_union_exprt : public nullary_exprt
1668+
{
1669+
public:
1670+
explicit empty_union_exprt(typet _type)
1671+
: nullary_exprt(ID_empty_union, std::move(_type))
1672+
{
1673+
}
1674+
};
1675+
1676+
template <>
1677+
inline bool can_cast_expr<empty_union_exprt>(const exprt &base)
1678+
{
1679+
return base.id() == ID_empty_union;
1680+
}
1681+
1682+
inline void validate_expr(const empty_union_exprt &value)
1683+
{
1684+
validate_operands(
1685+
value, 0, "Empty-union constructor must not have any operand");
1686+
}
1687+
1688+
/// \brief Cast an exprt to an \ref empty_union_exprt
1689+
///
1690+
/// \a expr must be known to be \ref empty_union_exprt.
1691+
///
1692+
/// \param expr: Source expression
1693+
/// \return Object of type \ref empty_union_exprt
1694+
inline const empty_union_exprt &to_empty_union_expr(const exprt &expr)
1695+
{
1696+
PRECONDITION(expr.id() == ID_empty_union);
1697+
const empty_union_exprt &ret = static_cast<const empty_union_exprt &>(expr);
1698+
validate_expr(ret);
1699+
return ret;
1700+
}
1701+
1702+
/// \copydoc to_empty_union_expr(const exprt &)
1703+
inline empty_union_exprt &to_empty_union_expr(exprt &expr)
1704+
{
1705+
PRECONDITION(expr.id() == ID_empty_union);
1706+
empty_union_exprt &ret = static_cast<empty_union_exprt &>(expr);
1707+
validate_expr(ret);
1708+
return ret;
1709+
}
16651710

16661711
/// \brief Struct constructor from list of elements
16671712
class struct_exprt : public multi_ary_exprt

0 commit comments

Comments
 (0)