Skip to content

Commit d2361e4

Browse files
authored
[C23] More improved type compatibility for enumerations (#150946)
The structural equivalence checker was not paying attention to whether enumerations had compatible fixed underlying types or not. Fixes #150594
1 parent 4a44a85 commit d2361e4

File tree

6 files changed

+132
-0
lines changed

6 files changed

+132
-0
lines changed

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,14 @@ def note_odr_number_of_bases : Note<
511511
"class has %0 base %plural{1:class|:classes}0">;
512512
def note_odr_enumerator : Note<"enumerator %0 with value %1 here">;
513513
def note_odr_missing_enumerator : Note<"no corresponding enumerator here">;
514+
def note_odr_incompatible_fixed_underlying_type : Note<
515+
"enumeration %0 declared with incompatible fixed underlying types (%1 vs. "
516+
"%2)">;
517+
def note_odr_fixed_underlying_type : Note<
518+
"enumeration %0 has fixed underlying type here">;
519+
def note_odr_missing_fixed_underlying_type : Note<
520+
"enumeration %0 missing fixed underlying type here">;
521+
514522
def err_odr_field_type_inconsistent : Error<
515523
"field %0 declared with incompatible types in different "
516524
"translation units (%1 vs. %2)">;

clang/lib/AST/ASTStructuralEquivalence.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,48 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
20932093
!CheckStructurallyEquivalentAttributes(Context, D1, D2))
20942094
return false;
20952095

2096+
// In C23, if one enumeration has a fixed underlying type, the other shall
2097+
// have a compatible fixed underlying type (6.2.7).
2098+
if (Context.LangOpts.C23) {
2099+
if (D1->isFixed() != D2->isFixed()) {
2100+
if (Context.Complain) {
2101+
Context.Diag2(D2->getLocation(),
2102+
Context.getApplicableDiagnostic(
2103+
diag::err_odr_tag_type_inconsistent))
2104+
<< Context.ToCtx.getTypeDeclType(D2)
2105+
<< (&Context.FromCtx != &Context.ToCtx);
2106+
Context.Diag1(D1->getLocation(),
2107+
D1->isFixed()
2108+
? diag::note_odr_fixed_underlying_type
2109+
: diag::note_odr_missing_fixed_underlying_type)
2110+
<< D1;
2111+
Context.Diag2(D2->getLocation(),
2112+
D2->isFixed()
2113+
? diag::note_odr_fixed_underlying_type
2114+
: diag::note_odr_missing_fixed_underlying_type)
2115+
<< D2;
2116+
}
2117+
return false;
2118+
}
2119+
if (D1->isFixed()) {
2120+
assert(D2->isFixed() && "enums expected to have fixed underlying types");
2121+
if (!IsStructurallyEquivalent(Context, D1->getIntegerType(),
2122+
D2->getIntegerType())) {
2123+
if (Context.Complain) {
2124+
Context.Diag2(D2->getLocation(),
2125+
Context.getApplicableDiagnostic(
2126+
diag::err_odr_tag_type_inconsistent))
2127+
<< Context.ToCtx.getTypeDeclType(D2)
2128+
<< (&Context.FromCtx != &Context.ToCtx);
2129+
Context.Diag2(D2->getLocation(),
2130+
diag::note_odr_incompatible_fixed_underlying_type)
2131+
<< D2 << D2->getIntegerType() << D1->getIntegerType();
2132+
}
2133+
return false;
2134+
}
2135+
}
2136+
}
2137+
20962138
llvm::SmallVector<const EnumConstantDecl *, 8> D1Enums, D2Enums;
20972139
auto CopyEnumerators =
20982140
[](auto &&Range, llvm::SmallVectorImpl<const EnumConstantDecl *> &Cont) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// [C23] missing underlying types
2+
enum E1 : int {
3+
E1Enumerator1
4+
};
5+
6+
enum E2 {
7+
E2Enumerator1
8+
};
9+
10+
// [C23] Incompatible underlying types
11+
enum E3 : long {
12+
E3Enumerator1
13+
};
14+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// [C23] missing underlying types
2+
enum E1 {
3+
E1Enumerator1
4+
};
5+
6+
enum E2 : int {
7+
E2Enumerator1
8+
};
9+
10+
// [C23] Incompatible underlying types
11+
enum E3 : short {
12+
E3Enumerator1
13+
};
14+

clang/test/ASTMerge/enum/test2.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cc1 -std=c23 -emit-pch -o %t.1.ast %S/Inputs/enum3.c
2+
// RUN: %clang_cc1 -std=c23 -emit-pch -o %t.2.ast %S/Inputs/enum4.c
3+
// RUN: not %clang_cc1 -std=c23 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s
4+
5+
// FIXME: this error should not happen!
6+
// CHECK: error: type 'struct __NSConstantString_tag' has an attribute which currently causes the types to be treated as though they are incompatible
7+
// CHECK: enum3.c:2:6: warning: type 'enum E1' has incompatible definitions in different translation units
8+
// CHECK: enum4.c:2:6: note: enumeration 'E1' missing fixed underlying type here
9+
// CHECK: enum3.c:2:6: note: enumeration 'E1' has fixed underlying type here
10+
// CHECK: enum3.c:6:6: warning: type 'enum E2' has incompatible definitions in different translation units
11+
// CHECK: enum4.c:6:6: note: enumeration 'E2' has fixed underlying type here
12+
// CHECK: enum3.c:6:6: note: enumeration 'E2' missing fixed underlying type here
13+
// CHECK: enum3.c:11:6: warning: type 'enum E3' has incompatible definitions in different translation units
14+
// CHECK: enum3.c:11:6: note: enumeration 'E3' declared with incompatible fixed underlying types ('long' vs. 'short')
15+
// CHECK: 3 warnings and 1 error generated
16+

clang/test/C/C23/n3037.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,44 @@ _Static_assert(0 == _Generic(inner_anon_tagged.untagged, struct { int i; } : 1,
402402
enum { E_Untagged1 } nontag_enum; // both-note {{previous definition is here}}
403403
_Static_assert(0 == _Generic(nontag_enum, enum { E_Untagged1 } : 1, default : 0)); // both-error {{redefinition of enumerator 'E_Untagged1'}}
404404

405+
// Test that enumerations with mixed underlying types are properly handled.
406+
enum GH150594_E1 : int { GH150594_Val1 };
407+
enum GH150594_E2 : int { GH150594_Val2 };
408+
enum GH150594_E3 { GH150594_Val3 };
409+
enum GH150594_E4 : int { GH150594_Val4 };
410+
void GH150594(void) {
411+
extern enum GH150594_E1 Fn1(void); // both-note {{previous declaration is here}}
412+
extern enum GH150594_E2 Fn2(void); // c17-note {{previous declaration is here}}
413+
extern enum GH150594_E3 Fn3(void); // both-note {{previous declaration is here}}
414+
extern enum GH150594_E4 Fn4(void); // both-note {{previous declaration is here}}
415+
enum GH150594_E1 { GH150594_Val1 };
416+
enum GH150594_E2 : int { GH150594_Val2 };
417+
enum GH150594_E3 : int { GH150594_Val3 };
418+
enum GH150594_E4 : short { GH150594_Val4 };
419+
extern enum GH150594_E1 Fn1(void); // both-error {{conflicting types for 'Fn1'}}
420+
extern enum GH150594_E2 Fn2(void); // c17-error {{conflicting types for 'Fn2'}}
421+
extern enum GH150594_E3 Fn3(void); // both-error {{conflicting types for 'Fn3'}}
422+
extern enum GH150594_E4 Fn4(void); // both-error {{conflicting types for 'Fn4'}}
423+
424+
// Show that two declarations in the same scope give expected diagnostics.
425+
enum E1 { e1 }; // both-note {{previous declaration is here}}
426+
enum E1 : int { e1 }; // both-error {{enumeration previously declared with nonfixed underlying type}}
427+
428+
enum E2 : int { e2 }; // both-note {{previous declaration is here}}
429+
enum E2 { e2 }; // both-error {{enumeration previously declared with fixed underlying type}}
430+
431+
enum E3 : int { e3 }; // both-note {{previous declaration is here}}
432+
enum E3 : short { e3 }; // both-error {{enumeration redeclared with different underlying type 'short' (was 'int')}}
433+
434+
typedef short foo;
435+
enum E4 : foo { e4 }; // c17-note 2 {{previous definition is here}}
436+
enum E4 : short { e4 }; // c17-error {{redefinition of 'E4'}} \
437+
c17-error {{redefinition of enumerator 'e4'}}
438+
439+
enum E5 : foo { e5 }; // both-note {{previous declaration is here}}
440+
enum E5 : int { e5 }; // both-error {{enumeration redeclared with different underlying type 'int' (was 'foo' (aka 'short'))}}
441+
}
442+
405443
// Test that enumerations are compatible with their underlying type, but still
406444
// diagnose when "same type" is required rather than merely "compatible type".
407445
enum E1 : int { e1 }; // Fixed underlying type

0 commit comments

Comments
 (0)