blob: 7101a153c6ebb7d1119cff29306e384f7c540246 [file] [log] [blame]
// 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>();