Skip to content

Commit b16c7d7

Browse files
tautschnigChengyu Zhangandreast271
committed
Support non-heap allocated flexible arrays
This addresses an existing todo in the code base: we need to come up with a new type based on the actual initializer. Fixes: #3653, #4206 Co-authored-by: Chengyu Zhang <[email protected]> Co-authored-by: Andreas Tiemeyer <[email protected]>
1 parent 352be59 commit b16c7d7

File tree

3 files changed

+107
-13
lines changed

3 files changed

+107
-13
lines changed

regression/cbmc/struct16/main.c

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include <assert.h>
2+
#include <stdio.h>
3+
4+
typedef int T[];
5+
6+
struct f
7+
{
8+
int w;
9+
T x;
10+
};
11+
12+
static struct f before = {0xdeadbeef};
13+
static struct f f = {4, {0, 1, 2, 3, 4}};
14+
static struct f after = {0xcafecafe};
15+
16+
struct St
17+
{
18+
char c;
19+
int d[];
20+
};
21+
struct St s = {'a', {11, 5}};
22+
23+
int main()
24+
{
25+
int i;
26+
for(i = 0; i < f.w; ++i)
27+
{
28+
if(f.x[i] != i)
29+
{
30+
assert(0);
31+
}
32+
}
33+
assert(sizeof(f) == sizeof(struct f));
34+
assert(before.w == 0xdeadbeef);
35+
assert(after.w == 0xcafecafe);
36+
printf("%llx\n", &before);
37+
printf("%llx\n", &f);
38+
printf("%llx\n", &after);
39+
40+
unsigned char c;
41+
c = c && 1;
42+
assert(c == 0 || c == 1);
43+
assert(s.d[1] == 5);
44+
s.d[1] += c;
45+
assert(s.d[1] < 8);
46+
assert(s.d[0] == 11);
47+
48+
return 0;
49+
}

regression/cbmc/struct16/test.desc

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test demonstrates support for flexible array members with non-heap allocated
10+
objects.

src/ansi-c/c_typecheck_initializer.cpp

+48-13
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ Author: Daniel Kroening, [email protected]
99
/// \file
1010
/// ANSI-C Conversion / Type Checking
1111

12-
#include "c_typecheck_base.h"
13-
1412
#include <util/arith_tools.h>
1513
#include <util/byte_operators.h>
1614
#include <util/c_types.h>
@@ -22,6 +20,8 @@ Author: Daniel Kroening, [email protected]
2220
#include <util/string_constant.h>
2321

2422
#include "anonymous_member.h"
23+
#include "c_typecheck_base.h"
24+
#include "type2name.h"
2525

2626
void c_typecheck_baset::do_initializer(
2727
exprt &initializer,
@@ -240,13 +240,12 @@ void c_typecheck_baset::do_initializer(symbolt &symbol)
240240
typecheck_expr(symbol.value);
241241
do_initializer(symbol.value, symbol.type, true);
242242

243-
// need to adjust size?
244-
if(
245-
!symbol.is_macro && symbol.type.id() == ID_array &&
246-
to_array_type(symbol.type).size().is_nil())
247-
{
243+
// A flexible array may have been initialized, which entails a type change.
244+
// Note that the type equality test is important: we want to preserve
245+
// annotations like typedefs or const-ness when the type is otherwise the
246+
// same.
247+
if(!symbol.is_macro && symbol.type != symbol.value.type())
248248
symbol.type = symbol.value.type();
249-
}
250249
}
251250

252251
if(symbol.is_macro)
@@ -428,8 +427,6 @@ exprt::operandst::const_iterator c_typecheck_baset::do_designated_initializer(
428427
}
429428
dest->operands().resize(
430429
numeric_cast_v<std::size_t>(index) + 1, *zero);
431-
432-
// todo: adjust type!
433430
}
434431
else
435432
{
@@ -1007,11 +1004,49 @@ exprt c_typecheck_baset::do_initializer_list(
10071004
increment_designator(current_designator);
10081005
}
10091006

1010-
// make sure we didn't mess up index computation
10111007
if(full_type.id()==ID_struct)
10121008
{
1013-
assert(result.operands().size()==
1014-
to_struct_type(full_type).components().size());
1009+
const struct_typet &full_struct_type = to_struct_type(full_type);
1010+
const struct_typet::componentst &components = full_struct_type.components();
1011+
// make sure we didn't mess up index computation
1012+
CHECK_RETURN(result.operands().size() == components.size());
1013+
1014+
if(
1015+
!components.empty() &&
1016+
components.back().type().get_bool(ID_C_flexible_array_member))
1017+
{
1018+
const auto array_size = numeric_cast<mp_integer>(
1019+
to_array_type(components.back().type()).size());
1020+
array_exprt &init_array = to_array_expr(result.operands().back());
1021+
if(
1022+
!array_size.has_value() ||
1023+
(*array_size <= 1 && init_array.operands().size() != *array_size))
1024+
{
1025+
struct_typet actual_struct_type = full_struct_type;
1026+
array_typet &actual_array_type =
1027+
to_array_type(actual_struct_type.components().back().type());
1028+
actual_array_type.size() = from_integer(
1029+
init_array.operands().size(), actual_array_type.index_type());
1030+
actual_array_type.set(ID_C_flexible_array_member, true);
1031+
init_array.type() = actual_array_type;
1032+
1033+
// mimic bits of typecheck_compound_type to produce a new struct tag
1034+
actual_struct_type.remove(ID_tag);
1035+
type_symbolt compound_symbol{actual_struct_type};
1036+
compound_symbol.mode = mode;
1037+
compound_symbol.location = value.source_location();
1038+
std::string typestr = type2name(compound_symbol.type, *this);
1039+
compound_symbol.base_name = "#anon#" + typestr;
1040+
compound_symbol.name = "tag-#anon#" + typestr;
1041+
irep_idt tag_identifier = compound_symbol.name;
1042+
1043+
// We might already have the same anonymous struct, which is fine as it
1044+
// will be exactly the same type.
1045+
symbol_table.insert(std::move(compound_symbol));
1046+
1047+
result.type() = struct_tag_typet{tag_identifier};
1048+
}
1049+
}
10151050
}
10161051

10171052
if(full_type.id()==ID_array &&

0 commit comments

Comments
 (0)