blob: 113ecf74d8bef48b615fef94c0db35315be83d7b [file] [log] [blame] [edit]
// RUN: %clang_cc1 -fsyntax-only -std=c23 -pedantic -Wall -Wno-comment -verify=both,c23 %s
// RUN: %clang_cc1 -fsyntax-only -std=c17 -pedantic -Wall -Wno-comment -Wno-c23-extensions -verify=both,c17 %s
/* WG14 N3037: Clang 21
* Improved tag compatibility
*
* Identical tag types have always been compatible across TU boundaries. This
* paper made identical tag types compatible within the same TU.
*/
struct foo { int a; } p;
void baz(struct foo f); // c17-note {{passing argument to parameter 'f' here}}
void bar(void) {
struct foo { int a; } q = {};
baz(q); // c17-error {{passing 'struct foo' to parameter of incompatible type 'struct foo'}}
}
#define PRODUCT(A ,B) struct prod { A a; B b; } // expected-note 2 {{expanded from macro 'PRODUCT'}}
#define SUM(A, B) struct sum { _Bool flag; union { A a; B b; }; } // expected-note 2 {{expanded from macro 'SUM'}}
void func1(PRODUCT(int, SUM(float, double)) x); // c17-warning {{declaration of 'struct prod' will not be visible outside of this function}} \
c17-warning {{declaration of 'struct sum' will not be visible outside of this function}} \
c17-note {{passing argument to parameter 'x' here}}
void func2(PRODUCT(int, SUM(float, double)) y) { // c17-warning {{declaration of 'struct prod' will not be visible outside of this function}} \
c17-warning {{declaration of 'struct sum' will not be visible outside of this function}}
func1(y); // c17-error {{passing 'struct prod' to parameter of incompatible type 'struct prod'}}
}
struct foop { struct { int x; }; }; // c17-note {{previous definition is here}}
struct foop { struct { int x; }; }; // c17-error {{redefinition of 'foop'}}
// Test the field lookup compatibility isn't sufficient, the structure of types should be compatible.
struct AnonymousStructNotMatchingFields { // c17-note {{previous definition is here}}
struct { // c23-note {{field has name '' here}}
int x;
};
};
struct AnonymousStructNotMatchingFields { // c23-error {{type 'struct AnonymousStructNotMatchingFields' has incompatible definitions}} \
c17-error {{redefinition of 'AnonymousStructNotMatchingFields'}}
int x; // c23-note {{field has name 'x' here}}
};
union barp { int x; float y; }; // c17-note {{previous definition is here}}
union barp { int x; float y; }; // c17-error {{redefinition of 'barp'}}
typedef struct q { int x; } q_t; // c17-note 2 {{previous definition is here}}
typedef struct q { int x; } q_t; // c17-error {{redefinition of 'q'}} \
c17-error-re {{typedef redefinition with different types ('struct (unnamed struct at {{.*}})' vs 'struct q')}}
typedef struct { int x; } untagged_q_t; // both-note {{previous definition is here}}
typedef struct { int x; } untagged_q_t; // both-error {{typedef redefinition with different types}}
void func3(void) {
struct S { int x; }; // c17-note {{previous definition is here}}
struct T { struct S s; }; // c17-note {{previous definition is here}}
struct S { int x; }; // c17-error {{redefinition of 'S'}}
struct T { struct S s; }; // c17-error {{redefinition of 'T'}}
}
struct food { int (*p)[3]; }; // c23-note {{field 'p' has type 'int (*)[3]' here}} \
c17-note {{previous definition is here}}
struct food { int (*p)[]; }; // c23-error {{type 'struct food' has incompatible definitions}} \
c23-note {{field 'p' has type 'int (*)[]' here}} \
c17-error {{redefinition of 'food'}}
union bard { int x; float y; }; // c23-note {{field has name 'x' here}} \
c17-note {{previous definition is here}}
union bard { int z; float y; }; // c23-error {{type 'union bard' has incompatible definitions}} \
c23-note {{field has name 'z' here}} \
c17-error {{redefinition of 'bard'}}
union purr { int x; float y; }; // c23-note {{field has name 'x' here}} \
c17-note {{previous definition is here}}
union purr { float y; int x; }; // c23-error {{type 'union purr' has incompatible definitions}} \
c23-note {{field has name 'y' here}} \
c17-error {{redefinition of 'purr'}}
// The presence of an attribute makes two types not compatible.
struct [[gnu::packed]] attr_test { // c17-note {{previous definition is here}} \
c23-note {{attribute 'gnu::packed' here}}
int x;
};
struct attr_test { // c17-error {{redefinition of 'attr_test'}} \
c23-error {{type 'struct attr_test' has an attribute which currently causes the types to be treated as though they are incompatible}}
int x;
};
struct attr_test_2 { // c17-note {{previous definition is here}}
int x;
};
struct [[gnu::packed]] attr_test_2 { // c17-error {{redefinition of 'attr_test_2'}} \
c23-error {{type 'struct attr_test_2' has an attribute which currently causes the types to be treated as though they are incompatible}} \
c23-note {{attribute 'gnu::packed' here}}
int x;
};
// This includes the same attribute on both types.
struct [[gnu::packed]] attr_test_3 { // c17-note {{previous definition is here}} \
c23-note {{attribute 'gnu::packed' here}}
int x;
};
struct [[gnu::packed]] attr_test_3 { // c17-error {{redefinition of 'attr_test_3'}} \
c23-error {{type 'struct attr_test_3' has an attribute which currently causes the types to be treated as though they are incompatible}} \
c23-note {{attribute 'gnu::packed' here}}
int x;
};
// Everything which applies to the tag itself also applies to fields.
struct field_attr_test_1 { // c17-note {{previous definition is here}}
int x;
[[gnu::packed]] int y; // c23-note {{attribute 'gnu::packed' here}}
};
struct field_attr_test_1 { // c17-error {{redefinition of 'field_attr_test_1'}} \
c23-error {{type 'struct field_attr_test_1' has a member with an attribute which currently causes the types to be treated as though they are incompatible}}
int x;
int y;
};
struct field_attr_test_2 { // c17-note {{previous definition is here}}
[[gnu::packed]] int x; // c23-note {{attribute 'gnu::packed' here}}
int y;
};
struct field_attr_test_2 { // c17-error {{redefinition of 'field_attr_test_2'}} \
c23-error {{type 'struct field_attr_test_2' has a member with an attribute which currently causes the types to be treated as though they are incompatible}}
int x;
int y;
};
struct field_attr_test_3 { // c17-note {{previous definition is here}}
[[gnu::packed]] int x; // c23-note {{attribute 'gnu::packed' here}}
int y;
};
struct field_attr_test_3 { // c17-error {{redefinition of 'field_attr_test_3'}} \
c23-error {{type 'struct field_attr_test_3' has a member with an attribute which currently causes the types to be treated as though they are incompatible}}
int x [[gnu::packed]]; // c23-note {{attribute 'gnu::packed' here}}
int y;
};
// Show that equivalent field types are not an issue.
typedef int typedef_of_type_int;
struct equivalent_field_types { // c17-note {{previous definition is here}}
int x;
};
struct equivalent_field_types { // c17-error {{redefinition of 'equivalent_field_types'}}
typedef_of_type_int x;
};
struct quals_matter { // c17-note {{previous definition is here}}
int x; // c23-note {{field 'x' has type 'int' here}}
};
struct quals_matter { // c17-error {{redefinition of 'quals_matter'}} \
c23-error {{type 'struct quals_matter' has incompatible definitions}}
const int x; // c23-note {{field 'x' has type 'const int' here}}
};
struct qual_order_does_not_matter { // c17-note {{previous definition is here}}
const volatile int x;
};
struct qual_order_does_not_matter { // c17-error {{redefinition of 'qual_order_does_not_matter'}}
volatile const int x;
};
struct nested { // both-note {{previous definition is here}}
int x;
struct nested { // both-error {{nested redefinition of 'nested'}}
int x;
};
};
// Show that bit-field order does matter, including anonymous bit-fields.
struct bit_field_1 { // c17-note 2 {{previous definition is here}}
int a : 1;
int : 0; // c23-note {{field has name '' here}}
int b : 1;
};
struct bit_field_1 { // c17-error {{redefinition of 'bit_field_1'}}
int a : 1;
int : 0;
int b : 1;
};
struct bit_field_1 { // c17-error {{redefinition of 'bit_field_1'}} \
c23-error {{type 'struct bit_field_1' has incompatible definitions}}
int a : 1;
int b : 1; // c23-note {{field has name 'b' here}}
};
struct bit_field_2 { // c17-note {{previous definition is here}}
int a : 1;
int b : 1; // c23-note {{bit-field 'b' has bit-width 1 here}}
};
struct bit_field_2 { // c17-error {{redefinition of 'bit_field_2'}} \
c23-error {{type 'struct bit_field_2' has incompatible definitions}}
int a : 1;
int b : 2; // c23-note {{bit-field 'b' has bit-width 2 here}}
};
// Test a bit-field with an attribute.
struct bit_field_3 { // c17-note {{previous definition is here}}
int a : 1;
int b : 1;
};
struct bit_field_3 { // c17-error {{redefinition of 'bit_field_3'}} \
c23-error {{type 'struct bit_field_3' has a member with an attribute which currently causes the types to be treated as though they are incompatible}}
int a : 1;
[[deprecated]] int b : 1; // c23-note {{attribute 'deprecated' here}}
};
struct bit_field_4 { // c17-note {{previous definition is here}}
int a : 1;
int b : 1; // c23-note {{bit-field 'b' has bit-width 1 here}}
};
struct bit_field_4 { // c17-error {{redefinition of 'bit_field_4'}} \
c23-error {{type 'struct bit_field_4' has incompatible definitions}}
int a : 1;
int b; // c23-note {{field 'b' is not a bit-field}}
};
struct bit_field_5 { // c17-note {{previous definition is here}}
int a : 1;
int b; // c23-note {{field 'b' is not a bit-field}}
};
struct bit_field_5 { // c17-error {{redefinition of 'bit_field_5'}} \
c23-error {{type 'struct bit_field_5' has incompatible definitions}}
int a : 1;
int b : 1; // c23-note {{bit-field 'b' has bit-width 1 here}}
};
struct bit_field_6 { // c17-note {{previous definition is here}}
int a : 2;
};
struct bit_field_6 { // c17-error {{redefinition of 'bit_field_6'}}
int a : 1 + 1;
};
enum E { A }; // c17-note 2 {{previous definition is here}}
enum E { A }; // c17-error {{redefinition of 'E'}} \
c17-error {{redefinition of enumerator 'A'}}
enum Q { D = 1 }; // c17-note 2 {{previous definition is here}}
enum Q { D = D }; // c17-error {{redefinition of 'Q'}} \
c17-error {{redefinition of enumerator 'D'}}
// The order of the enumeration constants does not matter, only the values do.
enum X { B = 1, C = 1 + 1 }; // c17-note 3 {{previous definition is here}}
enum X { C = 2, B = 1 }; // c17-error {{redefinition of 'X'}} \
c17-error {{redefinition of enumerator 'C'}} \
c17-error {{redefinition of enumerator 'B'}}
// Different enumeration constants.
enum Y { YA = 1, YB = 2 }; // c23-note {{enumerator 'YB' with value 2 here}} \
c17-note 3 {{previous definition is here}}
enum Y { YA = 1, YB = 3 }; // c23-error {{type 'enum Y' has incompatible definitions}} \
c23-note {{enumerator 'YB' with value 3 here}} \
c17-error {{redefinition of 'Y'}} \
c17-error {{redefinition of enumerator 'YA'}} \
c17-error {{redefinition of enumerator 'YB'}}
// Different enumeration names, same named constants.
enum Z1 { ZC = 1 }; // both-note {{previous definition is here}}
enum Z2 { ZC = 1 }; // both-error {{redefinition of enumerator 'ZC'}}
// Test attributes on the enumeration and enumerators.
enum [[deprecated]] enum_attr_test_1 { // c17-note {{previous definition is here}} \
c23-note {{attribute 'deprecated' here}}
EAT1 [[deprecated]] // c17-note {{previous definition is here}} \
c23-note {{attribute 'deprecated' here}}
};
enum [[deprecated]] enum_attr_test_1 { // c17-error {{redefinition of 'enum_attr_test_1'}} \
c23-error {{type 'enum enum_attr_test_1' has an attribute which currently causes the types to be treated as though they are incompatible}} \
c23-error {{type 'enum enum_attr_test_1' has a member with an attribute which currently causes the types to be treated as though they are incompatible}} \
c23-note {{attribute 'deprecated' here}}
EAT1 [[deprecated]] // c17-error {{redefinition of enumerator 'EAT1'}} \
c23-note {{attribute 'deprecated' here}}
};
enum [[deprecated]] enum_attr_test_2 { // c17-note {{previous definition is here}} \
c23-note {{attribute 'deprecated' here}}
EAT2 // c17-note {{previous definition is here}}
};
enum enum_attr_test_2 { // c17-error {{redefinition of 'enum_attr_test_2'}} \
c23-error {{type 'enum enum_attr_test_2' has an attribute which currently causes the types to be treated as though they are incompatible}}
EAT2 // c17-error {{redefinition of enumerator 'EAT2'}}
};
enum enum_attr_test_3 { // c17-note {{previous definition is here}}
EAT3 // c17-note {{previous definition is here}}
};
enum [[deprecated]] enum_attr_test_3 { // c17-error {{redefinition of 'enum_attr_test_3'}} \
c23-error {{type 'enum enum_attr_test_3' has an attribute which currently causes the types to be treated as though they are incompatible}} \
c23-note {{attribute 'deprecated' here}}
EAT3 // c17-error {{redefinition of enumerator 'EAT3'}}
};
// You cannot declare one with a fixed underlying type and the other without a
// fixed underlying type, or a different underlying type. However, it's worth
// showing that the underlying type doesn't change the redefinition behavior.
enum fixed_test_1 : int { FT1 }; // c17-note 2 {{previous definition is here}}
enum fixed_test_1 : int { FT1 }; // c17-error {{redefinition of 'fixed_test_1'}} \
c17-error {{redefinition of enumerator 'FT1'}}
enum fixed_test_2 : int { FT2 }; // c17-note 2 {{previous definition is here}}
enum fixed_test_2 : typedef_of_type_int { FT2 }; // c17-error {{redefinition of 'fixed_test_2'}} \
c17-error {{redefinition of enumerator 'FT2'}}
// Test more bizarre situations in terms of where the type is declared. This
// has always been allowed.
struct declared_funny_1 { int x; }
declared_funny_func(struct declared_funny_1 { int x; } arg) { // c17-warning {{declaration of 'struct declared_funny_1' will not be visible outside of this function}}
return declared_funny_func((__typeof__(arg)){ 0 });
}
// However, this is new.
struct Outer {
struct Inner { // c17-note {{previous definition is here}}
int x;
} i;
enum InnerEnum { // c17-note {{previous definition is here}}
IE1 // c17-note {{previous definition is here}}
} j;
};
struct Inner { // c17-error {{redefinition of 'Inner'}}
int x;
};
enum InnerEnum { // c17-error {{redefinition of 'InnerEnum'}}
IE1 // c17-error {{redefinition of enumerator 'IE1'}}
};
void hidden(void) {
struct hidden_struct { int x; };
}
struct hidden_struct { // This is fine because the previous declaration is not visible.
int y;
int z;
};
struct array { int y; int x[]; }; // c17-note {{previous definition is here}} \
c23-note {{field 'x' has type 'int[]' here}}
struct array { int y; int x[0]; }; // c17-error {{redefinition of 'array'}} \
c23-error {{type 'struct array' has incompatible definitions}} \
c23-note {{field 'x' has type 'int[0]' here}} \
both-warning {{zero size arrays are an extension}}
// So long as the bounds are the same value, everything is fine. They do not
// have to be token equivalent.
struct array_2 { int y; int x[3]; }; // c17-note {{previous definition is here}}
struct array_2 { int y; int x[1 + 1 + 1]; }; // c17-error {{redefinition of 'array_2'}}
struct alignment { // c17-note {{previous definition is here}}
_Alignas(int) int x; // c23-note {{attribute '_Alignas' here}}
};
struct alignment { // c17-error {{redefinition of 'alignment'}} \
c23-error {{type 'struct alignment' has a member with an attribute which currently causes the types to be treated as though they are incompatible}}
int x;
};
// Both structures need to have a tag in order to be compatible within the same
// translation unit.
struct {int i;} nontag;
struct tag {int i;} tagged; // c17-note 2 {{previous definition is here}}
_Static_assert(1 == _Generic(tagged, struct tag {int i;}:1, default:0)); // c17-error {{redefinition of 'tag'}} \
c17-error {{static assertion failed}}
_Static_assert(0 == _Generic(tagged, struct {int i;}:1, default:0));
_Static_assert(0 == _Generic(nontag, struct tag {int i;}:1, default:0)); // c17-error {{redefinition of 'tag'}}
// That means these two structures are not actually compatible; see GH141724.
_Static_assert(0 == _Generic(nontag, struct {int i;}:1, default:0));
// Also test the behavior within a function (so the declaration context is not
// at the translation unit level).
void nontag_func_test(void) {
struct { int i; } test;
_Static_assert(0 == _Generic(test, struct { int i; } : 1, default : 0));
}
// Same kind of test, but this time for a declaration in the parameter list.
void nontag_param_test(struct { int i; } herp) {
_Static_assert(0 == _Generic(herp, struct { int i; } : 1, default : 0));
}
// Same kind of test, but demonstrating that these still aren't compatible.
void nontag_both_in_params(struct { int i; } Arg1, struct { int i; } Arg2) {
_Static_assert(0 == _Generic(__typeof__(Arg1), __typeof__(Arg2) : 1, default : 0)); // both-warning {{passing a type argument as the first operand to '_Generic' is a C2y extension}}
}
struct InnerUnnamedStruct {
struct {
int i;
} untagged;
} inner_unnamed_tagged;
_Static_assert(0 == _Generic(inner_unnamed_tagged.untagged, struct { int i; } : 1, default : 0));
struct InnerUnnamedStruct_same {
struct {
int i;
} untagged;
};
struct InnerUnnamedStruct_differentNaming {
struct {
int i;
} untaggedDifferent;
};
struct InnerUnnamedStruct_differentShape {
float x;
struct {
int i;
} untagged;
int y;
};
void compare_unnamed_struct_from_different_outer_type(
struct InnerUnnamedStruct sameOuterType,
struct InnerUnnamedStruct_same matchingType,
struct InnerUnnamedStruct_differentNaming differentFieldName,
struct InnerUnnamedStruct_differentShape differentType) {
inner_unnamed_tagged.untagged = sameOuterType.untagged;
inner_unnamed_tagged.untagged = matchingType.untagged; // both-error-re {{assigning to 'struct (unnamed struct at {{.*}})' from incompatible type 'struct (unnamed struct at {{.*}})'}}
inner_unnamed_tagged.untagged = differentFieldName.untaggedDifferent; // both-error-re {{assigning to 'struct (unnamed struct at {{.*}})' from incompatible type 'struct (unnamed struct at {{.*}})'}}
inner_unnamed_tagged.untagged = differentType.untagged; // both-error-re {{assigning to 'struct (unnamed struct at {{.*}})' from incompatible type 'struct (unnamed struct at {{.*}})'}}
}
// Test the same thing with enumerations (test for unions is omitted because
// unions and structures are both RecordDecl objects, whereas EnumDecl is not).
enum { E_Untagged1 } nontag_enum; // both-note {{previous definition is here}}
_Static_assert(0 == _Generic(nontag_enum, enum { E_Untagged1 } : 1, default : 0)); // both-error {{redefinition of enumerator 'E_Untagged1'}}
// Test that enumerations with mixed underlying types are properly handled.
enum GH150594_E1 : int { GH150594_Val1 };
enum GH150594_E2 : int { GH150594_Val2 };
enum GH150594_E3 { GH150594_Val3 };
enum GH150594_E4 : int { GH150594_Val4 };
void GH150594(void) {
extern enum GH150594_E1 Fn1(void); // both-note {{previous declaration is here}}
extern enum GH150594_E2 Fn2(void); // c17-note {{previous declaration is here}}
extern enum GH150594_E3 Fn3(void); // both-note {{previous declaration is here}}
extern enum GH150594_E4 Fn4(void); // both-note {{previous declaration is here}}
enum GH150594_E1 { GH150594_Val1 };
enum GH150594_E2 : int { GH150594_Val2 };
enum GH150594_E3 : int { GH150594_Val3 };
enum GH150594_E4 : short { GH150594_Val4 };
extern enum GH150594_E1 Fn1(void); // both-error {{conflicting types for 'Fn1'}}
extern enum GH150594_E2 Fn2(void); // c17-error {{conflicting types for 'Fn2'}}
extern enum GH150594_E3 Fn3(void); // both-error {{conflicting types for 'Fn3'}}
extern enum GH150594_E4 Fn4(void); // both-error {{conflicting types for 'Fn4'}}
// Show that two declarations in the same scope give expected diagnostics.
enum E1 { e1 }; // both-note {{previous declaration is here}}
enum E1 : int { e1 }; // both-error {{enumeration previously declared with nonfixed underlying type}}
enum E2 : int { e2 }; // both-note {{previous declaration is here}}
enum E2 { e2 }; // both-error {{enumeration previously declared with fixed underlying type}}
enum E3 : int { e3 }; // both-note {{previous declaration is here}}
enum E3 : short { e3 }; // both-error {{enumeration redeclared with different underlying type 'short' (was 'int')}}
typedef short foo;
enum E4 : foo { e4 }; // c17-note 2 {{previous definition is here}}
enum E4 : short { e4 }; // c17-error {{redefinition of 'E4'}} \
c17-error {{redefinition of enumerator 'e4'}}
enum E5 : foo { e5 }; // both-note {{previous declaration is here}}
enum E5 : int { e5 }; // both-error {{enumeration redeclared with different underlying type 'int' (was 'foo' (aka 'short'))}}
}
// Test that enumerations are compatible with their underlying type, but still
// diagnose when "same type" is required rather than merely "compatible type".
enum E1 : int { e1 }; // Fixed underlying type
enum E2 { e2 }; // Unfixed underlying type, defaults to int or unsigned int
struct GH149965_1 { int h; };
// This typeof trick is used to get the underlying type of the enumeration in a
// platform agnostic way.
struct GH149965_2 { __typeof__(+(enum E2){}) h; };
void gh149965(void) {
extern struct GH149965_1 x1; // c17-note {{previous declaration is here}}
extern struct GH149965_2 x2; // c17-note {{previous declaration is here}}
// Both the structure and the variable declarations are fine because only a
// compatible type is required, not the same type, because the structures are
// declared in different scopes.
struct GH149965_1 { enum E1 h; };
struct GH149965_2 { enum E2 h; };
extern struct GH149965_1 x1; // c17-error {{redeclaration of 'x1'}}
extern struct GH149965_2 x2; // c17-error {{redeclaration of 'x2'}}
// However, in the same scope, the same type is required, not just compatible
// types.
// FIXME: this should be an error in both C17 and C23 mode.
struct GH149965_3 { int h; }; // c17-note {{previous definition is here}}
struct GH149965_3 { enum E1 h; }; // c17-error {{redefinition of 'GH149965_3'}}
// For Clang, the composite type after declaration merging is the enumeration
// type rather than an integer type.
enum E1 *eptr;
[[maybe_unused]] __typeof__(x1.h) *ptr = eptr;
enum E2 *eptr2;
[[maybe_unused]] __typeof__(x2.h) *ptr2 = eptr2;
}