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