https://github.com/thebrandre updated 
https://github.com/llvm/llvm-project/pull/121039

>From 551290d313063d1e05bc665aa44e9dac1f9ff29d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Brand?= <andre.br...@mailbox.org>
Date: Tue, 31 Dec 2024 19:54:51 +0100
Subject: [PATCH] [clang] Fix implicit integer conversion of opaque enum
 declarations in class templates

According to C++11 [conv.prom], the promotion type of an 
*opaque-enum-declaration* (see [dcl.enum])
is equal to its underlying type. This was not handled correctly for member 
enumerations in class template
specializations because the promotion type of the instantiated enum was set 
only in
`Sema::ActOnEnumBody`, which is not called if there are no curly braces are 
after the enum-base.
This fixes GitHub issue #117960.
---
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  8 ++
 ...que-enum-declaration-in-class-template.cpp | 90 +++++++++++++++++++
 2 files changed, 98 insertions(+)
 create mode 100644 
clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e058afe81da589..0889491e40805b 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1620,6 +1620,14 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl 
*D) {
   if (isDeclWithinFunction(D) ? D == Def : Def && !Enum->isScoped()) {
     SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum);
     InstantiateEnumDefinition(Enum, Def);
+  } else {
+    // 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.
+    if (D->isFixed() && !Def)
+      Enum->setPromotionType(Enum->getIntegerType());
   }
 
   return Enum;
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..724343496735b1
--- /dev/null
+++ b/clang/test/SemaCXX/opaque-enum-declaration-in-class-template.cpp
@@ -0,0 +1,90 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -verify
+// RUN: %clang_cc1 -std=c++20 -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(), "");
+
+
+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}}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to