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.dg/pr112488-2.c: Remove warning.
This commit is contained in:
Martin Uecker 2023-08-15 14:58:32 +02:00
parent 514ea1df44
commit 23fee88f84
17 changed files with 454 additions and 17 deletions

View file

@ -2037,6 +2037,28 @@ locate_old_decl (tree decl)
decl, TREE_TYPE (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. /* Subroutine of duplicate_decls. Compare NEWDECL to OLDDECL.
Returns true if the caller should proceed to merge the two, false Returns true if the caller should proceed to merge the two, false
if OLDDECL should simply be discarded. As a side effect, issues 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 (TREE_CODE (decl) != UNION_TYPE || b->id != NULL_TREE)
{ {
if (b->id) if (b->id)
{
/* The %s will be one of 'struct', 'union', or 'enum'. */ /* The %s will be one of 'struct', 'union', or 'enum'. */
if (!flag_isoc23)
warning_at (b->locus, 0, warning_at (b->locus, 0,
"%<%s %E%> declared inside parameter list" "%<%s %E%> declared inside parameter list"
" will not be visible outside of this definition or" " will not be visible outside of this definition or"
" declaration", keyword, b->id); " declaration", keyword, b->id);
}
else else
/* The %s will be one of 'struct', 'union', or 'enum'. */ /* The %s will be one of 'struct', 'union', or 'enum'. */
warning_at (b->locus, 0, 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. */ present, only a definition in the current scope is relevant. */
ref = lookup_tag (code, name, has_enum_type_specifier, &refloc); 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. /* If this is the right type of tag, return what we found.
(This reference will be shadowed by shadow_tag later if appropriate.) (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 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) if (name != NULL_TREE)
ref = lookup_tag (code, name, true, &refloc); 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 (ref && TREE_CODE (ref) == code)
{ {
if (TYPE_STUB_DECL (ref)) if (TYPE_STUB_DECL (ref))
@ -9581,6 +9624,25 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
warning_at (loc, 0, "union cannot be made transparent"); 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)); tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x)) for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
{ {

View file

@ -757,6 +757,7 @@ extern tree c_objc_common_truthvalue_conversion (location_t, tree);
extern tree require_complete_type (location_t, tree); extern tree require_complete_type (location_t, tree);
extern bool same_translation_unit_p (const_tree, const_tree); extern bool same_translation_unit_p (const_tree, const_tree);
extern int comptypes (tree, 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_different_types (tree, tree, bool *);
extern int comptypes_check_enum_int (tree, tree, bool *); extern int comptypes_check_enum_int (tree, tree, bool *);
extern bool c_mark_addressable (tree, bool = false); extern bool c_mark_addressable (tree, bool = false);

View file

@ -1080,6 +1080,23 @@ comptypes (tree type1, tree type2)
return ret ? (data.warning_needed ? 2 : 1) : 0; 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 /* Like comptypes, but if it returns non-zero because enum and int are
compatible, it sets *ENUM_AND_INT_P to true. */ compatible, it sets *ENUM_AND_INT_P to true. */
@ -1266,12 +1283,12 @@ comptypes_internal (const_tree type1, const_tree type2,
case ENUMERAL_TYPE: case ENUMERAL_TYPE:
case RECORD_TYPE: case RECORD_TYPE:
case UNION_TYPE: case UNION_TYPE:
if (false)
{ if (!flag_isoc23)
return tagged_types_tu_compatible_p (t1, t2, data);
}
return false; return false;
return tagged_types_tu_compatible_p (t1, t2, data);
case VECTOR_TYPE: case VECTOR_TYPE:
return known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2)) return known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2))
&& comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data); && comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data);
@ -7102,7 +7119,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
/* Aggregates in different TUs might need conversion. */ /* Aggregates in different TUs might need conversion. */
if ((codel == RECORD_TYPE || codel == UNION_TYPE) if ((codel == RECORD_TYPE || codel == UNION_TYPE)
&& codel == coder && codel == coder
&& comptypes (type, rhstype)) && comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (rhstype)))
return convert_and_check (expr_loc != UNKNOWN_LOCATION return convert_and_check (expr_loc != UNKNOWN_LOCATION
? expr_loc : location, type, rhs); ? expr_loc : location, type, rhs);
@ -8457,6 +8474,13 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
conversion. */ conversion. */
inside_init = convert (type, inside_init); 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 if (require_constant
&& TREE_CODE (inside_init) == COMPOUND_LITERAL_EXPR) && TREE_CODE (inside_init) == COMPOUND_LITERAL_EXPR)
{ {
@ -10542,7 +10566,7 @@ initialize_elementwise_p (tree type, tree value)
return !VECTOR_TYPE_P (value_type); return !VECTOR_TYPE_P (value_type);
if (AGGREGATE_TYPE_P (type)) if (AGGREGATE_TYPE_P (type))
return type != TYPE_MAIN_VARIANT (value_type); return !comptypes (type, TYPE_MAIN_VARIANT (value_type));
return false; return false;
} }

View file

@ -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" } */ 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" } */ 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" } */ 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" } */ 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" } */ 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. */ /* But those are OK if the enum content is defined. */
enum e19 : short { E19 } x19; enum e19 : short { E19 } x19;
enum e20 : long { E20 } f20 (); enum e20 : long { E20 } f20 ();
typeof (enum e21 : long { E21 }) x21; 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 f22 (enum e22 : long long { E22 } p);
int f23 (enum e23 : long long { E23 } p); /* { dg-warning "will not be visible outside of this definition or declaration" } */ int f23 (enum e23 : long long { E23 } p);
struct s24 { enum e24 : int { E24 } x; }; struct s24 { enum e24 : int { E24 } x; };
/* Incompatible kinds of tags in the same scope are errors. */ /* Incompatible kinds of tags in the same scope are errors. */

View file

@ -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" } */

View file

@ -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;
}

View file

@ -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" } */
}

View file

@ -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" } */

View file

@ -0,0 +1,33 @@
/* { dg-do run { target { ! "*-*-*" } } }
* { dg-options "-std=c23 -fpermissive" }
*/
// 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();
}

View file

@ -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), "");
}

View file

@ -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" } */

View file

@ -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" } */
}

View file

@ -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" } */

View file

@ -0,0 +1,18 @@
/*
* { dg-do compile }
* { dg-options "-std=gnu23" }
*/
// conflicting attributes
extern struct [[gnu::transaction_safe]] foo { int x; } x;
extern struct [[gnu::unused]] foo2 { int x; } x2;
extern struct [[gnu::may_alias]] foo3 { int x; } x3;
void test()
{
extern struct foo { int x; } x; /* { dg-error "conflicting types" } */
extern struct foo2 { int x; } x2;
extern struct foo3 { int x; } x3;
}

View file

@ -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" } */
}

View file

@ -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; }
}

View file

@ -3,7 +3,7 @@
extern void abort(void); extern void abort(void);
int test(int *n, struct T { char a[*n], b[*n]; }*) { /* { dg-warning "declared inside parameter list" } */ int test(int *n, struct T { char a[*n], b[*n]; }*) {
return sizeof(struct T) - sizeof(struct T); return sizeof(struct T) - sizeof(struct T);
} }