Note that there is an additional change in parser_xref_tag to address the issue regarding completeness in redefinition which affects also structs / unions. The test c23-tag-6.c was changed accordingly.
c23: tag compatibility rules for struct and unions Implement redeclaration and compatibility rules for structures and unions in C23. gcc/c/: * c-decl.cc (previous_tag): New function. (parser_xref_tag): Find earlier definition. (get_parm_info): Turn off warning for C23. (start_struct): Allow redefinitons. (finish_struct): Diagnose conflicts. * c-tree.h (comptypes_same_p): Add prototype. * c-typeck.cc (comptypes_same_p): New function (comptypes_internal): Activate comparison of tagged types. (convert_for_assignment): Ignore qualifiers. (digest_init): Add error. (initialized_elementwise_p): Allow compatible types. gcc/testsuite/: * gcc.dg/c23-enum-7.c: Remove warning. * gcc.dg/c23-tag-1.c: New test. * gcc.dg/c23-tag-2.c: New deactivated test. * gcc.dg/c23-tag-3.c: New test. * gcc.dg/c23-tag-4.c: New test. * gcc.dg/c23-tag-5.c: New deactivated test. * gcc.dg/c23-tag-6.c: New test. * gcc.dg/c23-tag-7.c: New test. * gcc.dg/c23-tag-8.c: New test. * gcc.dg/gnu23-tag-1.c: New test. * gcc.dg/gnu23-tag-2.c: New test. * gcc.dg/gnu23-tag-3.c: New test. * gcc.dg/gnu23-tag-4.c: New test. --- gcc/c/c-decl.cc | 72 +++++++++++++++++++++++++++--- gcc/c/c-tree.h | 1 + gcc/c/c-typeck.cc | 38 +++++++++++++--- gcc/testsuite/gcc.dg/c23-enum-7.c | 6 +-- gcc/testsuite/gcc.dg/c23-tag-1.c | 67 +++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/c23-tag-2.c | 43 ++++++++++++++++++ gcc/testsuite/gcc.dg/c23-tag-3.c | 16 +++++++ gcc/testsuite/gcc.dg/c23-tag-4.c | 26 +++++++++++ gcc/testsuite/gcc.dg/c23-tag-5.c | 33 ++++++++++++++ gcc/testsuite/gcc.dg/c23-tag-6.c | 58 ++++++++++++++++++++++++ gcc/testsuite/gcc.dg/c23-tag-7.c | 12 +++++ gcc/testsuite/gcc.dg/c23-tag-8.c | 10 +++++ gcc/testsuite/gcc.dg/gnu23-tag-1.c | 10 +++++ gcc/testsuite/gcc.dg/gnu23-tag-2.c | 19 ++++++++ gcc/testsuite/gcc.dg/gnu23-tag-3.c | 28 ++++++++++++ gcc/testsuite/gcc.dg/gnu23-tag-4.c | 31 +++++++++++++ 16 files changed, 454 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/c23-tag-1.c create mode 100644 gcc/testsuite/gcc.dg/c23-tag-2.c create mode 100644 gcc/testsuite/gcc.dg/c23-tag-3.c create mode 100644 gcc/testsuite/gcc.dg/c23-tag-4.c create mode 100644 gcc/testsuite/gcc.dg/c23-tag-5.c create mode 100644 gcc/testsuite/gcc.dg/c23-tag-6.c create mode 100644 gcc/testsuite/gcc.dg/c23-tag-7.c create mode 100644 gcc/testsuite/gcc.dg/c23-tag-8.c create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-1.c create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-2.c create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-3.c create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-4.c diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 64d3a941cb9..ebe1708b977 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -2039,6 +2039,28 @@ locate_old_decl (tree decl) decl, TREE_TYPE (decl)); } + +/* Helper function. For a tagged type, it finds the declaration + for a visible tag declared in the the same scope if such a + declaration exists. */ +static tree +previous_tag (tree type) +{ + struct c_binding *b = NULL; + tree name = TYPE_NAME (type); + + if (name) + b = I_TAG_BINDING (name); + + if (b) + b = b->shadowed; + + if (b && B_IN_CURRENT_SCOPE (b)) + return b->decl; + + return NULL_TREE; +} + /* Subroutine of duplicate_decls. Compare NEWDECL to OLDDECL. Returns true if the caller should proceed to merge the two, false if OLDDECL should simply be discarded. As a side effect, issues @@ -8573,11 +8595,14 @@ get_parm_info (bool ellipsis, tree expr) if (TREE_CODE (decl) != UNION_TYPE || b->id != NULL_TREE) { if (b->id) - /* The %s will be one of 'struct', 'union', or 'enum'. */ - warning_at (b->locus, 0, - "%<%s %E%> declared inside parameter list" - " will not be visible outside of this definition or" - " declaration", keyword, b->id); + { + /* The %s will be one of 'struct', 'union', or 'enum'. */ + if (!flag_isoc23) + warning_at (b->locus, 0, + "%<%s %E%> declared inside parameter list" + " will not be visible outside of this definition or" + " declaration", keyword, b->id); + } else /* The %s will be one of 'struct', 'union', or 'enum'. */ warning_at (b->locus, 0, @@ -8668,6 +8693,16 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name, present, only a definition in the current scope is relevant. */ ref = lookup_tag (code, name, has_enum_type_specifier, &refloc); + + /* If the visble type is still being defined, see if there is + an earlier definition (which may be complete). */ + if (flag_isoc23 && ref && C_TYPE_BEING_DEFINED (ref)) + { + tree vis = previous_tag (ref); + if (vis) + ref = vis; + } + /* If this is the right type of tag, return what we found. (This reference will be shadowed by shadow_tag later if appropriate.) If this is the wrong type of tag, do not return it. If it was the @@ -8782,6 +8817,14 @@ start_struct (location_t loc, enum tree_code code, tree name, if (name != NULL_TREE) ref = lookup_tag (code, name, true, &refloc); + + /* For C23, even if we already have a completed definition, + we do not use it. We will check for consistency later. + If we are in a nested redefinition the type is not + complete. We will then detect this below. */ + if (flag_isoc23 && ref && TYPE_SIZE (ref)) + ref = NULL_TREE; + if (ref && TREE_CODE (ref) == code) { if (TYPE_STUB_DECL (ref)) @@ -9576,6 +9619,25 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, warning_at (loc, 0, "union cannot be made transparent"); } + /* Check for consistency with previous definition. */ + if (flag_isoc23) + { + tree vistype = previous_tag (t); + if (vistype + && TREE_CODE (vistype) == TREE_CODE (t) + && !C_TYPE_BEING_DEFINED (vistype)) + { + TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (t); + if (c_type_variably_modified_p (t)) + error ("redefinition of struct or union %qT with variably " + "modified type", t); + else if (!comptypes_same_p (t, vistype)) + error ("redefinition of struct or union %qT", t); + } + } + + C_TYPE_BEING_DEFINED (t) = 0; + tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t)); for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x)) { diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index c6f38ec94a0..7df4d65bf7a 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -756,6 +756,7 @@ extern tree c_objc_common_truthvalue_conversion (location_t, tree); extern tree require_complete_type (location_t, tree); extern bool same_translation_unit_p (const_tree, const_tree); extern int comptypes (tree, tree); +extern bool comptypes_same_p (tree, tree); extern int comptypes_check_different_types (tree, tree, bool *); extern int comptypes_check_enum_int (tree, tree, bool *); extern bool c_mark_addressable (tree, bool = false); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 1dbb4471a88..dc8a16df272 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -1080,6 +1080,23 @@ comptypes (tree type1, tree type2) return ret ? (data.warning_needed ? 2 : 1) : 0; } + +/* Like comptypes, but it returns non-zero only for identical + types. */ + +bool +comptypes_same_p (tree type1, tree type2) +{ + struct comptypes_data data = { }; + bool ret = comptypes_internal (type1, type2, &data); + + if (data.different_types_p) + return false; + + return ret; +} + + /* Like comptypes, but if it returns non-zero because enum and int are compatible, it sets *ENUM_AND_INT_P to true. */ @@ -1266,11 +1283,11 @@ comptypes_internal (const_tree type1, const_tree type2, case ENUMERAL_TYPE: case RECORD_TYPE: case UNION_TYPE: - if (false) - { - return tagged_types_tu_compatible_p (t1, t2, data); - } - return false; + + if (!flag_isoc23) + return false; + + return tagged_types_tu_compatible_p (t1, t2, data); case VECTOR_TYPE: return known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2)) @@ -7031,7 +7048,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, /* Aggregates in different TUs might need conversion. */ if ((codel == RECORD_TYPE || codel == UNION_TYPE) && codel == coder - && comptypes (type, rhstype)) + && comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (rhstype))) return convert_and_check (expr_loc != UNKNOWN_LOCATION ? expr_loc : location, type, rhs); @@ -8401,6 +8418,13 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype, conversion. */ inside_init = convert (type, inside_init); + if ((code == RECORD_TYPE || code == UNION_TYPE) + && !comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (TREE_TYPE (inside_init)))) + { + error_init (init_loc, "invalid initializer"); + return error_mark_node; + } + if (require_constant && TREE_CODE (inside_init) == COMPOUND_LITERAL_EXPR) { @@ -10486,7 +10510,7 @@ initialize_elementwise_p (tree type, tree value) return !VECTOR_TYPE_P (value_type); if (AGGREGATE_TYPE_P (type)) - return type != TYPE_MAIN_VARIANT (value_type); + return !comptypes (type, TYPE_MAIN_VARIANT (value_type)); return false; } diff --git a/gcc/testsuite/gcc.dg/c23-enum-7.c b/gcc/testsuite/gcc.dg/c23-enum-7.c index c9ef0882b41..ff8e145c2a8 100644 --- a/gcc/testsuite/gcc.dg/c23-enum-7.c +++ b/gcc/testsuite/gcc.dg/c23-enum-7.c @@ -26,17 +26,15 @@ enum e13 : short x13; /* { dg-error "'enum' underlying type may not be specified enum e14 : short f14 (); /* { dg-error "'enum' underlying type may not be specified here" } */ typeof (enum e15 : long) x15; /* { dg-error "'enum' underlying type may not be specified here" } */ int f16 (enum e16 : char p); /* { dg-error "'enum' underlying type may not be specified here" } */ -/* { dg-warning "will not be visible outside of this definition or declaration" "warning" { target *-*-* } .-1 } */ int f17 (enum e17 : char); /* { dg-error "'enum' underlying type may not be specified here" } */ -/* { dg-warning "will not be visible outside of this definition or declaration" "warning" { target *-*-* } .-1 } */ struct s18 { enum e18 : int x; }; /* { dg-error "'enum' underlying type may not be specified here" } */ /* But those are OK if the enum content is defined. */ enum e19 : short { E19 } x19; enum e20 : long { E20 } f20 (); typeof (enum e21 : long { E21 }) x21; -int f22 (enum e22 : long long { E22 } p); /* { dg-warning "will not be visible outside of this definition or declaration" } */ -int f23 (enum e23 : long long { E23 } p); /* { dg-warning "will not be visible outside of this definition or declaration" } */ +int f22 (enum e22 : long long { E22 } p); +int f23 (enum e23 : long long { E23 } p); struct s24 { enum e24 : int { E24 } x; }; /* Incompatible kinds of tags in the same scope are errors. */ diff --git a/gcc/testsuite/gcc.dg/c23-tag-1.c b/gcc/testsuite/gcc.dg/c23-tag-1.c new file mode 100644 index 00000000000..4a6207ec626 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-1.c @@ -0,0 +1,67 @@ +/* + * { dg-do compile } + * { dg-options "-std=c23" } + */ + +// allowed and forbidden redefinitions of the same struct/union in the same scope + +typedef struct p { int a; } pd_t; +typedef struct p { int a; } pd_t; + +typedef struct bar { int x; } X; +typedef struct bar { float x; } Y; /* { dg-error "redefinition of struct or union" } */ + +void test(void) +{ + struct foo { int x; }; + struct foo { float x; }; /* { dg-error "redefinition of struct or union" } */ +} + +struct aa { int a; }; + +void f(void) +{ + typedef struct aa A; + struct bb { struct aa a; } x; + struct aa { int a; }; + typedef struct aa A; /* { dg-error "redefinition" } */ + struct bb { struct aa a; } y; /* { dg-error "redefinition of struct or union" } */ + (void)x; (void)y; +} + + + +void h(void) +{ + struct a2 { int a; }; + { + typedef struct a2 A; + struct b2 { struct a2 a; } x; + struct a2 { int a; }; + typedef struct a2 A; /* { dg-error "redefinition" } */ + struct b2 { struct a2 a; } y; /* { dg-error "redefinition of struct or union" } */ + (void)x; (void)y; + } +} + + +union cc { int x; float y; } z; +union cc { int x; float y; } z1; +union cc { float y; int x; } z2; /* { dg-error "redefinition of struct or union" } */ + +void g(void) +{ + struct s { int a; }; + struct s { int a; } x0; + struct p { struct s c; } y1 = { x0 }; + struct p { struct s { int a; } c; } y = { x0 }; +} + +struct q { struct { int a; }; }; +struct q { struct { int a; }; }; +struct q { int a; }; /* { dg-error "redefinition of struct or union" } */ + +struct r { int a; char b[]; }; +struct r { int a; char b[]; }; +struct r { int a; char b[1]; }; /* { dg-error "redefinition of struct or union" } */ + diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c new file mode 100644 index 00000000000..5dd4a21e9df --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-2.c @@ -0,0 +1,43 @@ +/* { dg-do compile { target { ! "*-*-*" } } } + * { dg-options "-std=c23" } + */ + +// compatibility of structs in assignment + +typedef struct p { int a; } pd_t; + +void test1(void) +{ + pd_t y0; + struct p { int a; } x; + y0 = x; +} + +void test2(void) +{ + struct p { int a; } x; + struct p y0 = x; +} + +void test3(void) +{ + struct p { int a; } x; + pd_t y0 = x; +} + +typedef struct p { int a; } p2_t; + +void test4(void) +{ + p2_t x; + pd_t y0 = x; +} + +void test5(void) +{ + struct q { int a; } a; + struct q { int a; } b; + a = b; +} + + diff --git a/gcc/testsuite/gcc.dg/c23-tag-3.c b/gcc/testsuite/gcc.dg/c23-tag-3.c new file mode 100644 index 00000000000..4847e783c82 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-3.c @@ -0,0 +1,16 @@ +/* + * { dg-do compile } + * { dg-options "-std=c23" } + */ + +// conflicting types via linkage + +extern struct foo { int x; } x; +extern struct bar { float x; } y; + +void test(void) +{ + extern struct foo { int x; } x; + extern struct bar { int x; } y; /* { dg-error "conflicting types" } */ +} + diff --git a/gcc/testsuite/gcc.dg/c23-tag-4.c b/gcc/testsuite/gcc.dg/c23-tag-4.c new file mode 100644 index 00000000000..8083c43f607 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-4.c @@ -0,0 +1,26 @@ +/* + * { dg-do compile } + * { dg-options "-std=c23" } + */ + +// conflicting types for anonymous structs / unions + +extern struct { int x; } a; +extern struct { int x; } a; /* { dg-error "conflicting types" } */ + +extern union { int x; } b; +extern union { int x; } b; /* { dg-error "conflicting types" } */ + +typedef struct { int x; } u; +typedef struct { int x; } v; + +u c; +v c; /* { dg-error "conflicting types" } */ + +typedef union { int x; } q; +typedef union { int x; } r; + +q d; +r d; /* { dg-error "conflicting types" } */ + + diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c new file mode 100644 index 00000000000..ff40d07aef1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-5.c @@ -0,0 +1,33 @@ +/* { dg-do run { target { ! "*-*-*" } } } + * { dg-options "-std=c23" } + */ + +// nesting and parameters + +#define product_type(T, A, B) \ +struct product_ ## T { A a ; B b ; } +#define sum_type(T, A, B) \ +struct sum_ ## T { _Bool flag ; union { A a ; B b ; }; } + +float foo1(product_type(iSfd_, int, sum_type(fd, float, double)) x) +{ + return x.b.a; +} + +static void test1(void) +{ + product_type(iSfd_, int, sum_type(fd, float, double)) y = { 3, { 1, { .a = 1. } } }; + product_type(iSfd_, int, sum_type(fd, float, double)) z = y; + product_type(iSfd_, int, sum_type(fd, float, double)) *zp = &y; + float a = foo1(y); + product_type(iSid_, int, sum_type(id, int, double)) *wp = &y; /* { dg-warning "incompatible pointer type" } */ + float b = foo1(y); + product_type(iSid_, int, sum_type(id, int, double)) w = *wp; + (void)a; (void)b; (void)z; (void)zp; (void)w; (void)wp; +} + +int main() +{ + test1(); +} + diff --git a/gcc/testsuite/gcc.dg/c23-tag-6.c b/gcc/testsuite/gcc.dg/c23-tag-6.c new file mode 100644 index 00000000000..1b65ed3e35d --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-6.c @@ -0,0 +1,58 @@ +/* + * { dg-do compile } + * { dg-options "-std=c23" } + */ + +// (in-)completeness + +struct foo { + char x[10]; +} x; + +// complete, same type + +struct foo { + _Static_assert(_Generic(&x, struct foo*: 1, default: 0)); + char x[_Generic(&x, struct foo*: 10, default: 1)]; + _Static_assert(_Generic(0, struct foo: 0, default: 1)); +}; + +// incomplete, same type + +struct bar* p; +struct bar { + _Static_assert(_Generic(p, struct bar*: 1, default: 0)); + char x[_Generic(p, struct bar*: 10, default: 1)]; + _Static_assert(_Generic(0, struct bar: 0, default: 1)); /* { dg-error "incomplete type" } */ +}; + +struct bar { + char x[10]; +}; + +struct h *hp; + +void f(void) +{ + // again incomplete, different type + + struct foo { + char x[_Generic(&x, struct foo*: 1, default: 10)]; + _Static_assert(_Generic(0, struct foo: 0, default: 1)); /* { dg-error "incomplete type" } */ + }; + + struct foo z; + _Static_assert(10 == sizeof(z.x), ""); + + // still incomplete, different type + + struct h { + char x[_Generic(hp, struct h*: 1, default: 10)]; + _Static_assert(_Generic(0, struct h: 0, default: 1)); /* { dg-error "incomplete type" } */ + }; + + struct h y; + _Static_assert(10 == sizeof(y.x), ""); +} + + diff --git a/gcc/testsuite/gcc.dg/c23-tag-7.c b/gcc/testsuite/gcc.dg/c23-tag-7.c new file mode 100644 index 00000000000..dd3b5988e24 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-7.c @@ -0,0 +1,12 @@ +/* + * { dg-do compile } + * { dg-options "-std=c23" } + */ + +// recursive declarations + +extern struct bar { struct bar* p; int x; } b; +extern struct bar { struct bar* p; int x; } b; + +struct foo { struct foo { struct foo* p; int x; }* p; int x; } a; /* { dg-error "nested" } */ + diff --git a/gcc/testsuite/gcc.dg/c23-tag-8.c b/gcc/testsuite/gcc.dg/c23-tag-8.c new file mode 100644 index 00000000000..8b3b5ef5dfe --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-tag-8.c @@ -0,0 +1,10 @@ +/* { dg-do compile } + { dg-options "-std=c23" } */ + +void foo(void) +{ + struct bar { struct bar* next; }; + struct bar { struct bar* next; }; + struct bar { struct bar { struct bar* next; }* next; }; /* { dg-error "nested" } */ +} + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-1.c new file mode 100644 index 00000000000..3c0303d4c3f --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-1.c @@ -0,0 +1,10 @@ +/* + * { dg-do compile } + * { dg-options "-std=gnu23" } + */ + +struct r { int a; char b[]; }; +struct r { int a; char b[0]; }; /* allowed GNU extension */ +struct r { int a; char b[1]; }; /* { dg-error "redefinition of struct or union" } */ + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-2.c b/gcc/testsuite/gcc.dg/gnu23-tag-2.c new file mode 100644 index 00000000000..73725c79ebf --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-2.c @@ -0,0 +1,19 @@ +/* + * { dg-do compile } + * { dg-options "-std=gnu23" } + */ + +// conflicting attributes + +extern struct __attribute__(( transaction_safe )) foo { int x; } x; +extern struct __attribute__(( unused )) foo2 { int x; } x2; +extern struct __attribute__(( may_alias )) foo3 { int x; } x3; + +void test(void) +{ + extern struct foo { int x; } x; /* { dg-error "conflicting types" } */ + extern struct foo2 { int x; } x2; + extern struct foo3 { int x; } x3; +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-3.c b/gcc/testsuite/gcc.dg/gnu23-tag-3.c new file mode 100644 index 00000000000..8919144ef3a --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-3.c @@ -0,0 +1,28 @@ +/* + * { dg-do compile } + * { dg-options "-Wno-vla -std=gnu23" } + */ + +// arrays in structs + +void foo(int n, int m) +{ + struct f { int b; int a[n]; }; + struct f { int b; int a[n]; }; /* { dg-error "redefinition of struct or union" } */ + struct f { int b; int a[m]; }; /* { dg-error "redefinition of struct or union" } */ + struct f { int b; int a[5]; }; /* { dg-error "redefinition of struct or union" } */ + struct f { int b; int a[]; }; /* { dg-error "redefinition of struct or union" } */ + + struct g { int a[n]; int b; }; + struct g { int a[n]; int b; }; /* { dg-error "redefinition of struct or union" } */ + struct g { int a[m]; int b; }; /* { dg-error "redefinition of struct or union" } */ + struct g { int a[4]; int b; }; /* { dg-error "redefinition of struct or union" } */ + + struct h { int (*a)[n]; int b; }; + struct h { int (*a)[n]; int b; }; /* { dg-error "redefinition of struct or union" } */ + struct h { int (*a)[m]; int b; }; /* { dg-error "redefinition of struct or union" } */ + struct h { int (*a)[4]; int b; }; /* { dg-error "redefinition of struct or union" } */ + struct h { int (*a)[]; int b; }; /* { dg-error "redefinition of struct or union" } */ +} + + diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-4.c b/gcc/testsuite/gcc.dg/gnu23-tag-4.c new file mode 100644 index 00000000000..8db81c7b87d --- /dev/null +++ b/gcc/testsuite/gcc.dg/gnu23-tag-4.c @@ -0,0 +1,31 @@ +/* { dg-do compile } + * { dg-options "-std=gnu23" } */ + +// structs with variably modified types + +void bar(int n, int m) +{ + struct f { int b; int a[n]; } *x; + { struct f { int b; int a[n]; } *x2 = x; } + { struct f { int b; int a[m]; } *x2 = x; } + { struct f { int b; int a[5]; } *x2 = x; } + { struct f { int b; int a[0]; } *x2 = x; } + { struct f { int b; int a[]; } *x2 = x; } + + struct g { int a[n]; int b; } *y; + { struct g { int a[n]; int b; } *y2 = y; } + { struct g { int a[m]; int b; } *y2 = y; } + { struct g { int a[4]; int b; } *y2 = y; } + + struct h { int b; int a[5]; } *w; + { struct h { int b; int a[5]; } *w2 = w; } + { struct h { int b; int a[n]; } *w2 = w; } + { struct h { int b; int a[m]; } *w2 = w; } + + struct i { int b; int (*a)(int c[n]); } *u; + { struct i { int b; int (*a)(int c[4]); } *u2 = u; } + { struct i { int b; int (*a)(int c[]); } *u2 = u; } + { struct i { int b; int (*a)(int c[*]); } *u2 = u; } +} + + -- 2.39.2