Skip to content

Commit f019266

Browse files
committed
SMT2 back-end: improve support for zero-sized components
Kani uses empty structs and unions on a regular basis, which necessitates better support for these in the SMT back-end. As SMT LIB has no provision for zero-sized bit vectors we need to filter out any such expressions. Fixes: #7442
1 parent 1d99b30 commit f019266

File tree

2 files changed

+87
-39
lines changed

2 files changed

+87
-39
lines changed

regression/cbmc/void_pointer5/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE broken-smt-backend gcc-only
1+
CORE gcc-only
22
main.c
33

44
^EXIT=10$

src/solvers/smt2/smt2_conv.cpp

Lines changed: 86 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,34 @@ void smt2_convt::write_footer()
231231
<< "\n";
232232
}
233233

234+
/// Returns true iff \p type has effective width of zero bits.
235+
static bool is_zero_width(const typet &type, const namespacet &ns)
236+
{
237+
if(type.id() == ID_empty)
238+
return true;
239+
else if(type.id() == ID_struct_tag)
240+
return is_zero_width(ns.follow_tag(to_struct_tag_type(type)), ns);
241+
else if(type.id() == ID_union_tag)
242+
return is_zero_width(ns.follow_tag(to_union_tag_type(type)), ns);
243+
else if(type.id() == ID_struct || type.id() == ID_union)
244+
{
245+
for(const auto &comp : to_struct_union_type(type).components())
246+
{
247+
if(!is_zero_width(comp.type(), ns))
248+
return false;
249+
}
250+
return true;
251+
}
252+
else if(auto array_type = type_try_dynamic_cast<array_typet>(type))
253+
{
254+
// we ignore array_type->size().is_zero() for now as there may be
255+
// out-of-bounds accesses that we need to model
256+
return is_zero_width(array_type->element_type(), ns);
257+
}
258+
else
259+
return false;
260+
}
261+
234262
void smt2_convt::define_object_size(
235263
const irep_idt &id,
236264
const object_size_exprt &expr)
@@ -584,14 +612,21 @@ smt2_convt::parse_struct(const irept &src, const struct_typet &type)
584612
{
585613
// Structs look like:
586614
// (mk-struct.1 <component0> <component1> ... <componentN>)
587-
588-
if(src.get_sub().size()!=components.size()+1)
589-
return result; // give up
590-
615+
std::size_t j = 1;
591616
for(std::size_t i=0; i<components.size(); i++)
592617
{
593618
const struct_typet::componentt &c=components[i];
594-
result.operands()[i]=parse_rec(src.get_sub()[i+1], c.type());
619+
if(is_zero_width(components[i].type(), ns))
620+
{
621+
result.operands()[i] = nil_exprt{};
622+
}
623+
else
624+
{
625+
DATA_INVARIANT(
626+
src.get_sub().size() > j, "insufficient number of component values");
627+
result.operands()[i] = parse_rec(src.get_sub()[j], c.type());
628+
++j;
629+
}
595630
}
596631
}
597632
else
@@ -609,6 +644,9 @@ smt2_convt::parse_struct(const irept &src, const struct_typet &type)
609644

610645
for(std::size_t i=0; i<components.size(); i++)
611646
{
647+
if(is_zero_width(components[i].type(), ns))
648+
continue;
649+
612650
std::size_t component_width=boolbv_width(components[i].type());
613651

614652
INVARIANT(
@@ -972,28 +1010,6 @@ std::string smt2_convt::convert_identifier(const irep_idt &identifier)
9721010
return result;
9731011
}
9741012

975-
/// Returns true iff \p type has effective width of zero bits.
976-
static bool is_zero_width(const typet &type, const namespacet &ns)
977-
{
978-
if(type.id() == ID_empty)
979-
return true;
980-
else if(type.id() == ID_struct_tag)
981-
return is_zero_width(ns.follow_tag(to_struct_tag_type(type)), ns);
982-
else if(type.id() == ID_union_tag)
983-
return is_zero_width(ns.follow_tag(to_union_tag_type(type)), ns);
984-
else if(type.id() == ID_struct || type.id() == ID_union)
985-
{
986-
for(const auto &comp : to_struct_union_type(type).components())
987-
{
988-
if(!is_zero_width(comp.type(), ns))
989-
return false;
990-
}
991-
return true;
992-
}
993-
else
994-
return false;
995-
}
996-
9971013
std::string smt2_convt::type2id(const typet &type) const
9981014
{
9991015
if(type.id()==ID_floatbv)
@@ -3224,6 +3240,8 @@ void smt2_convt::convert_struct(const struct_exprt &expr)
32243240
it!=components.end();
32253241
it++, i++)
32263242
{
3243+
if(is_zero_width(it->type(), ns))
3244+
continue;
32273245
out << " ";
32283246
convert_expr(expr.operands()[i]);
32293247
}
@@ -3246,9 +3264,16 @@ void smt2_convt::convert_struct(const struct_exprt &expr)
32463264
else
32473265
{
32483266
// SMT-LIB 2 concat is binary only
3267+
std::size_t n_concat = 0;
32493268
for(std::size_t i=components.size(); i>1; i--)
32503269
{
3251-
out << "(concat ";
3270+
if(is_zero_width(components[i - 1].type(), ns))
3271+
continue;
3272+
else if(i > 2 || !is_zero_width(components[0].type(), ns))
3273+
{
3274+
++n_concat;
3275+
out << "(concat ";
3276+
}
32523277

32533278
exprt op=expr.operands()[i-1];
32543279

@@ -3263,10 +3288,10 @@ void smt2_convt::convert_struct(const struct_exprt &expr)
32633288
out << " ";
32643289
}
32653290

3266-
convert_expr(expr.op0());
3291+
if(!is_zero_width(components[0].type(), ns))
3292+
convert_expr(expr.op0());
32673293

3268-
for(std::size_t i=1; i<components.size(); i++)
3269-
out << ")";
3294+
out << std::string(n_concat, ')');
32703295
}
32713296
}
32723297
}
@@ -4701,19 +4726,30 @@ void smt2_convt::flatten2bv(const exprt &expr)
47014726
struct_type.components();
47024727

47034728
// SMT-LIB 2 concat is binary only
4729+
std::size_t n_concat = 0;
47044730
for(std::size_t i=components.size(); i>1; i--)
47054731
{
4706-
out << "(concat (" << smt_typename << "."
4707-
<< components[i-1].get_name() << " ?sflop)";
4732+
if(is_zero_width(components[i - 1].type(), ns))
4733+
continue;
4734+
else if(i > 2 || !is_zero_width(components[0].type(), ns))
4735+
{
4736+
++n_concat;
4737+
out << "(concat ";
4738+
}
4739+
4740+
out << "(" << smt_typename << "." << components[i - 1].get_name()
4741+
<< " ?sflop)";
47084742

47094743
out << " ";
47104744
}
47114745

4712-
out << "(" << smt_typename << "."
4713-
<< components[0].get_name() << " ?sflop)";
4746+
if(!is_zero_width(components[0].type(), ns))
4747+
{
4748+
out << "(" << smt_typename << "." << components[0].get_name()
4749+
<< " ?sflop)";
4750+
}
47144751

4715-
for(std::size_t i=1; i<components.size(); i++)
4716-
out << ")"; // concat
4752+
out << std::string(n_concat, ')'); // concat
47174753

47184754
out << ")"; // let
47194755
}
@@ -4812,6 +4848,9 @@ void smt2_convt::unflatten(
48124848
it!=components.end();
48134849
it++, i++)
48144850
{
4851+
if(is_zero_width(it->type(), ns))
4852+
continue;
4853+
48154854
std::size_t member_width=boolbv_width(it->type());
48164855

48174856
out << " ";
@@ -5037,6 +5076,9 @@ exprt smt2_convt::prepare_for_convert_expr(const exprt &expr)
50375076

50385077
void smt2_convt::find_symbols(const exprt &expr)
50395078
{
5079+
if(is_zero_width(expr.type(), ns))
5080+
return;
5081+
50405082
// recursive call on type
50415083
find_symbols(expr.type());
50425084

@@ -5821,6 +5863,9 @@ void smt2_convt::find_symbols_rec(
58215863

58225864
for(const auto &component : components)
58235865
{
5866+
if(is_zero_width(component.type(), ns))
5867+
continue;
5868+
58245869
out << "(" << smt_typename << "." << component.get_name()
58255870
<< " ";
58265871
convert_type(component.type());
@@ -5849,6 +5894,9 @@ void smt2_convt::find_symbols_rec(
58495894
it!=components.end();
58505895
++it)
58515896
{
5897+
if(is_zero_width(it->type(), ns))
5898+
continue;
5899+
58525900
const struct_union_typet::componentt &component=*it;
58535901
out << "(define-fun update-" << smt_typename << "."
58545902
<< component.get_name() << " "
@@ -5866,7 +5914,7 @@ void smt2_convt::find_symbols_rec(
58665914
{
58675915
if(it==it2)
58685916
out << "v ";
5869-
else
5917+
else if(!is_zero_width(it2->type(), ns))
58705918
{
58715919
out << "(" << smt_typename << "."
58725920
<< it2->get_name() << " s) ";

0 commit comments

Comments
 (0)