
GNU and MSVC have extensions where flexible array members (or their equivalent) can be in unions or alone in structs. This is already fully supported in Clang through the 0-sized array ("fake flexible array") extension or when C99 flexible array members have been syntactically obfuscated. Clang needs to explicitly allow these extensions directly for C99 flexible arrays, since they are common code patterns in active use by the Linux kernel (and other projects). Such projects have been using either 0-sized arrays (which is considered deprecated in favor of C99 flexible array members) or via obfuscated syntax, both of which complicate their code bases. For example, these do not error by default: ``` union one { int a; int b[0]; }; union two { int a; struct { struct { } __empty; int b[]; }; }; ``` But this does: ``` union three { int a; int b[]; }; ``` Remove the default error diagnostics for this but continue to provide warnings under Microsoft or GNU extensions checks. This will allow for a seamless transition for code bases away from 0-sized arrays without losing existing code patterns. Add explicit checking for the warnings under various constructions. Additionally fixes a CodeGen bug with flexible array members in unions in C++, which was found when adding a testcase for: ``` union { char x[]; } z = {0}; ``` which only had Sema tests originally. Fixes #84565
154 lines
5.1 KiB
C
154 lines
5.1 KiB
C
// RUN: %clang_cc1 -fsyntax-only -verify -Wgnu-flexible-array-union-member %s
|
|
typedef union {
|
|
int *ip;
|
|
float *fp;
|
|
long *__restrict rlp;
|
|
void *vpa[1];
|
|
} TU __attribute__((transparent_union));
|
|
|
|
void f(TU); // expected-note{{passing argument to parameter here}}
|
|
|
|
void g(int *ip, float *fp, char *cp) {
|
|
f(ip);
|
|
f(fp);
|
|
f(cp); // expected-error{{incompatible type}}
|
|
f(0);
|
|
|
|
TU tu_ip = ip; // expected-error{{incompatible type}}
|
|
TU tu;
|
|
tu.ip = ip;
|
|
}
|
|
|
|
/* Test ability to redeclare a function taking a transparent_union arg
|
|
with various compatible and incompatible argument types. */
|
|
|
|
void fip(TU);
|
|
void fip(int *i) {}
|
|
|
|
void ffp(TU);
|
|
void ffp(float *f) {}
|
|
|
|
void flp(TU);
|
|
void flp(long *l) {}
|
|
|
|
void fvp(TU); // expected-note{{previous declaration is here}}
|
|
void fvp(void *p) {} // expected-error{{conflicting types}}
|
|
|
|
void fsp(TU); // expected-note{{previous declaration is here}}
|
|
void fsp(short *s) {} // expected-error{{conflicting types}}
|
|
|
|
void fi(TU); // expected-note{{previous declaration is here}}
|
|
void fi(int i) {} // expected-error{{conflicting types}}
|
|
|
|
void fvpp(TU); // expected-note{{previous declaration is here}}
|
|
void fvpp(void **v) {} // expected-error{{conflicting types}}
|
|
|
|
/* Test redeclaring a function taking a transparent_union arg more than twice.
|
|
Merging different declarations depends on their order, vary order too. */
|
|
|
|
void f_triple0(TU tu) {}
|
|
void f_triple0(int *); // expected-note{{previous declaration is here}}
|
|
void f_triple0(float *f); // expected-error{{conflicting types}}
|
|
|
|
void f_triple1(int *);
|
|
void f_triple1(TU tu) {} // expected-note{{previous definition is here}}
|
|
void f_triple1(float *f); // expected-error{{conflicting types}}
|
|
|
|
void f_triple2(int *); // expected-note{{previous declaration is here}}
|
|
void f_triple2(float *f); // expected-error{{conflicting types}}
|
|
void f_triple2(TU tu) {}
|
|
|
|
/* Test calling redeclared function taking a transparent_union arg. */
|
|
|
|
void f_callee(TU);
|
|
void f_callee(int *i) {} // expected-note{{passing argument to parameter 'i' here}}
|
|
|
|
void caller(void) {
|
|
TU tu;
|
|
f_callee(tu); // expected-error{{passing 'TU' to parameter of incompatible type 'int *'}}
|
|
|
|
int *i;
|
|
f_callee(i);
|
|
}
|
|
|
|
|
|
/* FIXME: we'd like to just use an "int" here and align it differently
|
|
from the normal "int", but if we do so we lose the alignment
|
|
information from the typedef within the compiler. */
|
|
typedef struct { int x, y; } __attribute__((aligned(8))) aligned_struct8;
|
|
|
|
typedef struct { int x, y; } __attribute__((aligned(4))) aligned_struct4;
|
|
typedef union {
|
|
aligned_struct4 s4; // expected-note{{alignment of first field}}
|
|
aligned_struct8 s8; // expected-warning{{alignment of field}}
|
|
} TU1 __attribute__((transparent_union));
|
|
|
|
typedef union __attribute__((transparent_union)) {
|
|
aligned_struct4 s4; // expected-note{{alignment of first field}}
|
|
aligned_struct8 s8; // expected-warning{{alignment of field}}
|
|
} TU1b ;
|
|
|
|
typedef union {
|
|
char c; // expected-note{{size of first field is 8 bits}}
|
|
int i; // expected-warning{{size of field}}
|
|
} TU2 __attribute__((transparent_union));
|
|
|
|
typedef union __attribute__((transparent_union)){
|
|
char c; // expected-note{{size of first field is 8 bits}}
|
|
int i; // expected-warning{{size of field}}
|
|
} TU2b;
|
|
|
|
typedef union {
|
|
float f; // expected-warning{{floating}}
|
|
} TU3 __attribute__((transparent_union));
|
|
|
|
typedef union { } TU4 __attribute__((transparent_union)); // expected-warning{{field}}
|
|
|
|
typedef int int4 __attribute__((ext_vector_type(4)));
|
|
typedef union {
|
|
int4 vec; // expected-warning{{first field of a transparent union cannot have vector type 'int4' (vector of 4 'int' values); transparent_union attribute ignored}}
|
|
} TU5 __attribute__((transparent_union));
|
|
|
|
union pr15134 {
|
|
unsigned int u;
|
|
struct {
|
|
unsigned int expo:2;
|
|
unsigned int mant:30;
|
|
} __attribute__((packed));
|
|
// The packed attribute is acceptable because it defines a less strict
|
|
// alignment than required by the first field of the transparent union.
|
|
} __attribute__((transparent_union));
|
|
|
|
union pr15134v2 {
|
|
struct { // expected-note {{alignment of first field is 32 bits}}
|
|
unsigned int u1;
|
|
unsigned int u2;
|
|
};
|
|
struct { // expected-warning-re {{alignment of field 'struct pr15134v2::(anonymous at {{.+}})' (64 bits) does not match the alignment of the first field in transparent union; transparent_union attribute ignored}}
|
|
unsigned int u3;
|
|
} __attribute__((aligned(8)));
|
|
} __attribute__((transparent_union));
|
|
|
|
union pr30520v { void b; } __attribute__((transparent_union)); // expected-error {{field has incomplete type 'void'}}
|
|
|
|
union pr30520a { int b[]; } __attribute__((transparent_union)); // expected-warning {{flexible array member 'b' in a union is a GNU extension}}
|
|
|
|
// expected-note@+1 2 {{forward declaration of 'struct stb'}}
|
|
union pr30520s { struct stb b; } __attribute__((transparent_union)); // expected-error {{field has incomplete type 'struct stb'}}
|
|
|
|
union pr30520s2 { int *v; struct stb b; } __attribute__((transparent_union)); // expected-error {{field has incomplete type 'struct stb'}}
|
|
|
|
typedef union __attribute__((__transparent_union__)) {
|
|
int *i;
|
|
struct st *s;
|
|
} TU6;
|
|
|
|
void bar(TU6);
|
|
|
|
void foo11(int *i) {
|
|
bar(i);
|
|
}
|
|
void foo2(struct st *s) {
|
|
bar(s);
|
|
}
|