Skip to content

Commit 8df2a13

Browse files
committed
C front-end: initializer lists do not initialise anonymous members
Compilers differ in their handling of anonymous members, also depending on the type of the anonymous member. Make sure we accurately model this while type checking. Consequently, regression tests need to be amened to either be compiler-specific or not rely on anonymous or empty compound types, which dump-c also must not spuriously generate (from incomplete structs or unions). New tests are based on a case found by C-Reduce when starting from an SV-COMP task. Behaviour across different compilers confirmed with Compiler Explorer (https://godbolt.org/).
1 parent bc6b109 commit 8df2a13

File tree

27 files changed

+189
-38
lines changed

27 files changed

+189
-38
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
int blah(void);
22
; // empty!
33

4+
#ifndef _MSC_VER
45
struct some
56
{
67
; // empty
78
};
9+
#endif
810

911
int main() {
1012
}

regression/ansi-c/Union_Initialization2/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
CORE
22
main.c
33

4-
union member designator found for empty union
4+
(union member designator found for empty union|C requires that a struct or union has at least one member)
55
^SIGNAL=0$
66
^EXIT=(1|64)$
77
--

regression/ansi-c/_Generic1/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
struct some: 5 \
1414
)
1515

16+
#ifdef __GNUC__
1617
struct some
1718
{
1819
} s;
@@ -22,7 +23,6 @@ char ch;
2223
long double ld;
2324
short sh;
2425

25-
#ifdef __GNUC__
2626
STATIC_ASSERT(G(i)==3);
2727
STATIC_ASSERT(G(sh)==10);
2828
STATIC_ASSERT(G(ld)==1);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
1+
#ifdef _MSC_VER
2+
// No _Static_assert in Visual Studio
3+
# define _Static_assert(condition, message) static_assert(condition, message)
4+
#endif
5+
16
struct S
27
{
38
struct
49
{
510
int : 1;
11+
#ifndef _MSC_VER
612
int;
13+
#endif
714
int named;
815
};
916
};
1017

18+
_Static_assert(sizeof(struct S) == sizeof(int) * 2, "ignore int;");
19+
1120
struct S s = {.named = 0};
1221

22+
struct S1
23+
{
24+
struct S2
25+
{
26+
int : 1;
27+
int named;
28+
};
29+
};
30+
31+
#ifdef _MSC_VER
32+
_Static_assert(sizeof(struct S1) == sizeof(int) * 2, "");
33+
#else
34+
_Static_assert(sizeof(struct S1) == 0, "");
35+
#endif
36+
1337
int main()
1438
{
1539
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#define static_assert(x) struct {char some[(x) ? 1 : -1]; }
2+
3+
struct a
4+
{
5+
};
6+
struct c
7+
{
8+
struct a;
9+
void *d;
10+
} e =
11+
{
12+
#ifdef not_permitted
13+
{},
14+
#endif
15+
0},
16+
e2;
17+
struct
18+
{
19+
struct c f;
20+
} * g;
21+
int main()
22+
{
23+
g->f;
24+
static_assert(sizeof(e) == sizeof(void *));
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE gcc-only
2+
main.c
3+
-Dnot_permitted
4+
cannot initialize 'void \*' with an initializer list
5+
^CONVERSION ERROR$
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
^warning: ignoring
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE gcc-only
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
^CONVERSION ERROR$
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
typedef void(keyhandler_fn_t)(void);
2+
typedef void(irq_keyhandler_fn_t)(int);
3+
4+
void foo()
5+
{
6+
}
7+
8+
struct keyhandler
9+
{
10+
union {
11+
keyhandler_fn_t *fn;
12+
irq_keyhandler_fn_t *irq_fn;
13+
};
14+
const char *desc;
15+
_Bool x, y;
16+
} key_table[3] = {[0] = {{(keyhandler_fn_t *)(foo)}, "text", 1, 0}};
17+
18+
int main()
19+
{
20+
}
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+
--
7+
^warning: ignoring
8+
^CONVERSION ERROR$

regression/ansi-c/sizeof3/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#define STATIC_ASSERT(condition) \
55
int CONCAT2(some_array, __LINE__)[(condition) ? 1 : -1]
66

7+
#ifndef _MSC_VER
78
struct empty_struct { };
89
union empty_union { };
910

@@ -16,6 +17,7 @@ struct combination {
1617
STATIC_ASSERT(sizeof(struct empty_struct)==0);
1718
STATIC_ASSERT(sizeof(union empty_union)==0);
1819
STATIC_ASSERT(sizeof(struct combination)==sizeof(int));
20+
#endif
1921

2022
int main()
2123
{

regression/ansi-c/struct5/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
CORE
22
main.c
3-
3+
--i386-win32
44
^EXIT=0$
55
^SIGNAL=0$
66
--

regression/cbmc-concurrency/invalid_object1/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ struct OpenEthState
134134

135135
struct device
136136
{
137+
int dummy;
137138
};
138139

139140
struct napi_struct

regression/cbmc-library/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ add_test_pl_tests(
77
"$<TARGET_FILE:cbmc>"
88
"cbmc-library"
99
"$<TARGET_FILE:cbmc>"
10-
"-C;-X;pthread"
10+
"-C;-X;pthread;-X;gcc-only"
1111
"CORE"
1212
)
1313
endif()

regression/cbmc-library/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ include ../../src/common
55

66
ifeq ($(BUILD_ENV_),MSVC)
77
no_pthread = -X pthread
8+
gcc_only = -X gcc-only
89
endif
910

1011
test:
11-
@../test.pl -e -p -c ../../../src/cbmc/cbmc $(no_pthread)
12+
@../test.pl -e -p -c ../../../src/cbmc/cbmc $(no_pthread) $(gcc_only)
1213

1314
tests.log: ../test.pl
14-
@../test.pl -e -p -c ../../../src/cbmc/cbmc $(no_pthread)
15+
@../test.pl -e -p -c ../../../src/cbmc/cbmc $(no_pthread) $(gcc_only)
1516

1617
show:
1718
@for dir in *; do \

regression/cbmc-library/memcpy-06/test.desc

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

44
function 'memcpy' is not declared
@@ -8,3 +8,6 @@ parameter "memcpy::dst" type mismatch
88
--
99
^warning: ignoring
1010
Invariant check failed
11+
--
12+
This test requires support for empty structs, which aren't supported by Visual
13+
Studio.

regression/cbmc/Anonymous_Struct2/main.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// The member without name is a Visual Studio feature
22
// https://msdn.microsoft.com/en-us/library/z2cx9y4f.aspx
33

4+
#ifdef _MSC_VER
5+
# include <assert.h>
6+
47
struct X
58
{
69
struct
@@ -47,3 +50,8 @@ int main()
4750
assert(s2.y==1);
4851
assert(s2.z==1);
4952
}
53+
#else
54+
int main()
55+
{
56+
}
57+
#endif

regression/cbmc/Anonymous_Struct3/main.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#include <assert.h>
2-
31
// The member without name is a Visual Studio feature
42
// https://msdn.microsoft.com/en-us/library/z2cx9y4f.aspx
53
typedef union my_U {
@@ -19,7 +17,7 @@ int main()
1917
x.f1 = 1;
2018

2119
if(*(char *)&word==1)
22-
assert(x.raw==2); // little endian
20+
__CPROVER_assert(x.raw == 2, "little endian");
2321
else
24-
assert(x.raw==64); // big endian
22+
__CPROVER_assert(x.raw == 64, "big endian");
2523
}

regression/cbmc/Anonymous_Struct3/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
CORE
22
main.c
3-
3+
-win32
44
^EXIT=0$
55
^SIGNAL=0$
66
^VERIFICATION SUCCESSFUL$

regression/cbmc/Empty_struct1/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
1+
CORE broken-smt-backend gcc-only
22
main.c
33

44
^EXIT=0$

regression/cbmc/Empty_struct2/test.desc

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

44
^EXIT=0$

regression/cbmc/atomic_section_seq1/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ struct OpenEthState$link4
200200

201201
struct device
202202
{
203+
int dummy;
203204
};
204205

205206
struct napi_struct

regression/cbmc/empty_compound_type1/test.desc

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

44
^EXIT=0$

regression/cbmc/empty_compound_type2/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
1+
CORE broken-smt-backend gcc-only
22
main.c
33
--pointer-check
44
^EXIT=10$

regression/cbmc/struct1/test.desc

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

44
^EXIT=0$

src/ansi-c/c_typecheck_initializer.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,9 @@ void c_typecheck_baset::designator_enter(
285285

286286
for(const auto &c : struct_type.components())
287287
{
288-
if(c.type().id() != ID_code && !c.get_is_padding())
288+
if(
289+
c.type().id() != ID_code && !c.get_is_padding() &&
290+
(c.type().id() != ID_c_bit_field || !c.get_anonymous()))
289291
{
290292
entry.subtype = c.type();
291293
break;
@@ -721,12 +723,11 @@ void c_typecheck_baset::increment_designator(designatort &designator)
721723
assert(components.size()==entry.size);
722724

723725
// we skip over any padding or code
724-
// we also skip over anonymous members
726+
// we also skip over anonymous members bit fields
725727
while(entry.index < entry.size &&
726728
(components[entry.index].get_is_padding() ||
727729
(components[entry.index].get_anonymous() &&
728-
components[entry.index].type().id() != ID_struct_tag &&
729-
components[entry.index].type().id() != ID_union_tag) ||
730+
components[entry.index].type().id() == ID_c_bit_field) ||
730731
components[entry.index].type().id() == ID_code))
731732
{
732733
entry.index++;
@@ -826,8 +827,9 @@ designatort c_typecheck_baset::make_designator(
826827
}
827828
else
828829
{
829-
// We will search for anonymous members,
830-
// in a loop. This isn't supported by gcc, but icc does allow it.
830+
// We will search for anonymous members, in a loop. This isn't supported
831+
// by GCC (unless the anonymous member is within an unnamed union or
832+
// struct), but Visual Studio does allow it.
831833

832834
bool found=false, repeat;
833835
typet tmp_type=entry.type;
@@ -853,6 +855,9 @@ designatort c_typecheck_baset::make_designator(
853855
c.get_anonymous() &&
854856
(c.type().id() == ID_struct_tag ||
855857
c.type().id() == ID_union_tag) &&
858+
(config.ansi_c.mode ==
859+
configt::ansi_ct::flavourt::VISUAL_STUDIO ||
860+
follow(c.type()).find(ID_tag).is_nil()) &&
856861
has_component_rec(c.type(), component_name, *this))
857862
{
858863
entry.index=number;

0 commit comments

Comments
 (0)