[clang] d6b6598 - [clang] Fix implicit integer conversion for opaque enums declared in class templates (#121039)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 10 07:32:01 PST 2025
Author: thebrandre
Date: 2025-01-10T16:31:58+01:00
New Revision: d6b6598e8075a5ba0271ee06a20c5a5609c0ec37
URL: https://github.com/llvm/llvm-project/commit/d6b6598e8075a5ba0271ee06a20c5a5609c0ec37
DIFF: https://github.com/llvm/llvm-project/commit/d6b6598e8075a5ba0271ee06a20c5a5609c0ec37.diff
LOG: [clang] Fix implicit integer conversion for opaque enums declared in class templates (#121039)
This commit fixes issues with enumeration types instantiated from an
opaque-enum-declarations
(see [dcl.enum]) in class templates broke basic assumptions during
parsing of arithmetic
expressions due to absent (NULL TYPE) promotion types of instances of
EnumDecl.
To this end, we repeat the simple steps in `Sema::ActOnTag` to evaluate
the promotion type
of a fixed enumeration based on its underlying type (see C++11
[conv.prom] p4).
Note that if, instead, a full *enum-specifier* (subsequent curly braces)
is provided,
`Sema::ActOnEnumBody` is re-invoked on template instantiation anyway
overriding the
promotion type and hiding the issue. This is analog to how enumerations
declarations
outside of template declarations are handled.
Note that, in contrast to `Sema::ActOnEnumBody`, `Sema::ActOnTag` is
*not* called again
for the instantiated enumeration type.
Fixes #117960.
---------
Co-authored-by: cor3ntin <corentinjabot at gmail.com>
Added:
clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 511a28c5554bbb..440b045399d991 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -958,6 +958,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 e058afe81da589..6a2331e59477a2 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 00000000000000..7101a153c6ebb7
--- /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 <typename T>
+struct A {
+ enum E : T;
+};
+
+int b = A<int>::E{} + 0;
+}
+
+
+namespace test {
+template <typename T1, typename T2>
+struct IsSame {
+ static constexpr bool check() { return false; }
+};
+
+template <typename T>
+struct IsSame<T, T> {
+ static constexpr bool check() { return true; }
+};
+} // namespace test
+
+
+template <typename T>
+struct S1 {
+ enum E : T;
+};
+// checks if EnumDecl::PromotionType is set
+int X1 = S1<int>::E{} + 0;
+int Y1 = S1<unsigned>::E{} + 0;
+static_assert(test::IsSame<decltype(S1<int>::E{}+0), int>::check(), "");
+static_assert(test::IsSame<decltype(S1<unsigned>::E{}+0), unsigned>::check(), "");
+char Z1 = S1<unsigned>::E(-1) + 0; // expected-warning{{implicit conversion from 'unsigned int' to 'char'}}
+
+template <typename Traits>
+struct S2 {
+ enum E : typename Traits::IntegerType;
+};
+
+template <typename T>
+struct Traits {
+ typedef T IntegerType;
+};
+
+int X2 = S2<Traits<int>>::E{} + 0;
+int Y2 = S2<Traits<unsigned>>::E{} + 0;
+static_assert(test::IsSame<decltype(S2<Traits<int>>::E{}+0), int>::check(), "");
+static_assert(test::IsSame<decltype(S2<Traits<unsigned>>::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<decltype(S2<Traits<char>>::E{}+char(0)), int>::check(), "");
+
+
+template <typename T>
+struct S3 {
+ enum E : unsigned;
+};
+
+int X3 = S3<float>::E{} + 0;
+
+// fails in clang 19 and earlier (see the discussion on GitHub Issue #117960):
+static_assert(test::IsSame<decltype(S3<float>::E{}+0), unsigned>::check(), "");
+
+template <typename T>
+struct S4 {
+ enum E1 : char;
+ enum E2 : T;
+};
+
+int X4 = S4<char>::E1{} + '\0';
+int Y4 = S4<char>::E2{} + '\0';
+
+template <typename T>
+struct S5 {
+ enum class E1 : char;
+ enum class E2 : T;
+};
+
+int X5 = S5<char>::E1{} + '\0'; // expected-error{{invalid operands to binary expression}}
+int Y5 = S5<char>::E2{} + '\0'; // expected-error{{invalid operands to binary expression}}
+
+
+template <typename T>
+struct S6 {
+ enum E1 : T;
+ enum E2 : E1; // expected-error{{invalid underlying type}}
+};
+
+template struct S6<int>; // expected-note{{in instantiation of template class 'S6<int>' requested here}}
+
+
+template <typename T>
+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<int>;
+
+template <typename T>
+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<float>;
+
+template <typename T>
+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<int>;
+
+#if defined(__cplusplus) && __cplusplus >= 201402L
+template <typename T>
+struct S10 {
+ enum [[deprecated("for reasons")]] E : T; // expected-note{{explicitly marked deprecated here}}
+};
+
+int X10 = S10<int>::E{} + 0; // expected-warning{{deprecated: for reasons}}
+#endif
+
+template <typename T>
+struct S11 {};
+
+template <>
+struct S11<unsigned> {
+ enum E : unsigned;
+};
+
+unsigned X11 = S11<unsigned>::E{} + 0u;
+
+#if defined(__cplusplus) && __cplusplus >= 201402L
+template <typename T>
+struct S12 {
+ enum [[deprecated("for reasons")]] E1 : T; // expected-note{{explicitly marked deprecated here}}
+ enum [[deprecated("for reasons")]] E2 : T;
+};
+
+template <>
+struct S12<float> {
+ enum E1 : unsigned;
+ enum E2 : unsigned;
+};
+
+unsigned X12 = S12<float>::E1{} + 0u;
+unsigned Y12 = S12<float>::E2{} + 0u;
+int Z12 = S12<int>::E1{} + 0; // expected-warning{{deprecated: for reasons}}
+#endif
+
+template <typename T>
+struct S13 {
+ enum __attribute__((packed)) E { X, Y };
+};
+
+static_assert(sizeof(S13<int>::E) == 1, "");
+
+template<typename T>
+struct S14 {
+ enum E : float; // expected-error {{invalid underlying type}}
+};
+
+template<typename T>
+struct S15 {
+ enum E : T; // expected-error {{invalid underlying type}}
+};
+
+template struct S15<float>; // expected-note {{in instantiation of template class 'S15<float>' requested here}}
+
+
+
+
+template <typename T>
+int f1() {
+ enum E : T;
+ return E{} + 0;
+}
+
+int F1 = f1<int>();
+
+template <typename T>
+int f2() {
+ struct LocalClass {
+ enum E : T;
+ };
+ return typename LocalClass::E{} + 0;
+}
+
+int F2 = f2<int>();
More information about the cfe-commits
mailing list