diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 93915e5db7d13..8ca9d0c52825a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -938,6 +938,9 @@ Miscellaneous Clang Crashes Fixed - Fixed internal assertion firing when a declaration in the implicit global module is found through ADL. (GH#109879) +- Fixed a crash when an unscoped enumeration declared by an opaque-enum-declaration within a class template + with a dependent underlying type is subject to integral promotion. (#GH117960) + OpenACC Specific Changes ------------------------ diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index e058afe81da58..6a2331e59477a 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1571,6 +1571,19 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { Enum->setIntegerType(SemaRef.Context.IntTy); else Enum->setIntegerTypeSourceInfo(NewTI); + + // C++23 [conv.prom]p4 + // if integral promotion can be applied to its underlying type, a prvalue + // of an unscoped enumeration type whose underlying type is fixed can also + // be converted to a prvalue of the promoted underlying type. + // + // FIXME: that logic is already implemented in ActOnEnumBody, factor out + // into (Re)BuildEnumBody. + QualType UnderlyingType = Enum->getIntegerType(); + Enum->setPromotionType( + SemaRef.Context.isPromotableIntegerType(UnderlyingType) + ? SemaRef.Context.getPromotedIntegerType(UnderlyingType) + : UnderlyingType); } else { assert(!D->getIntegerType()->isDependentType() && "Dependent type without type source info"); diff --git a/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp b/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp new file mode 100644 index 0000000000000..7101a153c6ebb --- /dev/null +++ b/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp @@ -0,0 +1,214 @@ +// RUN: %clang_cc1 -std=c++11 -Wredeclared-class-member -Wconstant-conversion -Wdeprecated-declarations -Wc++11-narrowing -fsyntax-only %s -verify +// RUN: %clang_cc1 -std=c++14 -Wredeclared-class-member -Wconstant-conversion -Wdeprecated-declarations -Wc++11-narrowing -fsyntax-only %s -verify +// RUN: %clang_cc1 -std=c++20 -Wredeclared-class-member -Wconstant-conversion -Wdeprecated-declarations -Wc++11-narrowing -fsyntax-only %s -verify + +// Test that opaque-enum-declarations are handled correctly w.r.t integral promotions. +// The key sections in the C++11 standard are: +// C++11 [dcl.enum]p3: An enumeration declared by an opaque-enum-declaration +// has a fixed underlying type and is a complete type. +// C++11 [conv.prom]: A prvalue of an unscoped enumeration type whose underlying type +// is fixed ([dcl.enum]) can be converted to a prvalue of its underlying type. + +// This program causes clang 19 and earlier to crash because +// EnumDecl::PromotionType has not been set on the instantiated enum. +// See GitHub Issue #117960. +namespace Issue117960 { +template +struct A { + enum E : T; +}; + +int b = A::E{} + 0; +} + + +namespace test { +template +struct IsSame { + static constexpr bool check() { return false; } +}; + +template +struct IsSame { + static constexpr bool check() { return true; } +}; +} // namespace test + + +template +struct S1 { + enum E : T; +}; +// checks if EnumDecl::PromotionType is set +int X1 = S1::E{} + 0; +int Y1 = S1::E{} + 0; +static_assert(test::IsSame::E{}+0), int>::check(), ""); +static_assert(test::IsSame::E{}+0), unsigned>::check(), ""); +char Z1 = S1::E(-1) + 0; // expected-warning{{implicit conversion from 'unsigned int' to 'char'}} + +template +struct S2 { + enum E : typename Traits::IntegerType; +}; + +template +struct Traits { + typedef T IntegerType; +}; + +int X2 = S2>::E{} + 0; +int Y2 = S2>::E{} + 0; +static_assert(test::IsSame>::E{}+0), int>::check(), ""); +static_assert(test::IsSame>::E{}+0), unsigned>::check(), ""); +// C++11 [conv.prom]p4: +// A prvalue of an unscoped enumeration type whose underlying type is fixed can be converted to a +// prvalue of its underlying type. Moreover, if integral promotion can be applied to its underlying type, a +// prvalue of an unscoped enumeration type whose underlying type is fixed can also be converted to a prvalue +// of the promoted underlying type. +static_assert(test::IsSame>::E{}+char(0)), int>::check(), ""); + + +template +struct S3 { + enum E : unsigned; +}; + +int X3 = S3::E{} + 0; + +// fails in clang 19 and earlier (see the discussion on GitHub Issue #117960): +static_assert(test::IsSame::E{}+0), unsigned>::check(), ""); + +template +struct S4 { + enum E1 : char; + enum E2 : T; +}; + +int X4 = S4::E1{} + '\0'; +int Y4 = S4::E2{} + '\0'; + +template +struct S5 { + enum class E1 : char; + enum class E2 : T; +}; + +int X5 = S5::E1{} + '\0'; // expected-error{{invalid operands to binary expression}} +int Y5 = S5::E2{} + '\0'; // expected-error{{invalid operands to binary expression}} + + +template +struct S6 { + enum E1 : T; + enum E2 : E1; // expected-error{{invalid underlying type}} +}; + +template struct S6; // expected-note{{in instantiation of template class 'S6' requested here}} + + +template +struct S7 { + enum E : T; + enum E : T { X, Y, Z }; // expected-note{{previous declaration is here}} + enum E : T; // expected-warning{{class member cannot be redeclared}} +}; + +template struct S7; + +template +struct S8 { + enum E : char; + enum E : char { X, Y, Z }; // expected-note{{previous declaration is here}} + enum E : char; // expected-warning{{class member cannot be redeclared}} +}; + +template struct S8; + +template +struct S9 { + enum class E1 : T; + enum class E1 : T { X, Y, Z }; // expected-note{{previous declaration is here}} + enum class E1 : T; // expected-warning{{class member cannot be redeclared}} + enum class E2 : char; + enum class E2 : char { X, Y, Z }; // expected-note{{previous declaration is here}} + enum class E2 : char; // expected-warning{{class member cannot be redeclared}} +}; + +template struct S9; + +#if defined(__cplusplus) && __cplusplus >= 201402L +template +struct S10 { + enum [[deprecated("for reasons")]] E : T; // expected-note{{explicitly marked deprecated here}} +}; + +int X10 = S10::E{} + 0; // expected-warning{{deprecated: for reasons}} +#endif + +template +struct S11 {}; + +template <> +struct S11 { + enum E : unsigned; +}; + +unsigned X11 = S11::E{} + 0u; + +#if defined(__cplusplus) && __cplusplus >= 201402L +template +struct S12 { + enum [[deprecated("for reasons")]] E1 : T; // expected-note{{explicitly marked deprecated here}} + enum [[deprecated("for reasons")]] E2 : T; +}; + +template <> +struct S12 { + enum E1 : unsigned; + enum E2 : unsigned; +}; + +unsigned X12 = S12::E1{} + 0u; +unsigned Y12 = S12::E2{} + 0u; +int Z12 = S12::E1{} + 0; // expected-warning{{deprecated: for reasons}} +#endif + +template +struct S13 { + enum __attribute__((packed)) E { X, Y }; +}; + +static_assert(sizeof(S13::E) == 1, ""); + +template +struct S14 { + enum E : float; // expected-error {{invalid underlying type}} +}; + +template +struct S15 { + enum E : T; // expected-error {{invalid underlying type}} +}; + +template struct S15; // expected-note {{in instantiation of template class 'S15' requested here}} + + + + +template +int f1() { + enum E : T; + return E{} + 0; +} + +int F1 = f1(); + +template +int f2() { + struct LocalClass { + enum E : T; + }; + return typename LocalClass::E{} + 0; +} + +int F2 = f2();