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