| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // <type_traits> |
| // |
| // UNSUPPORTED: c++03, c++11, c++14, c++17 |
| // |
| // __is_always_bitcastable<_From, _To> |
| |
| #include "test_macros.h" |
| TEST_CLANG_DIAGNOSTIC_IGNORED("-Wprivate-header") |
| #include <__type_traits/is_always_bitcastable.h> |
| |
| #include <climits> |
| #include <cstdint> |
| #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
| #include <cwchar> |
| #endif |
| #include "type_algorithms.h" |
| |
| // To test pointers to functions. |
| void Func1() {} |
| using FuncPtr1 = decltype(&Func1); |
| int Func2() { return 0; } |
| using FuncPtr2 = decltype(&Func2); |
| |
| template <bool Expected, class T, class U> |
| constexpr void check_one() { |
| static_assert(std::__is_always_bitcastable<T, U>::value == Expected); |
| } |
| |
| template <bool Expected, class T, class U> |
| constexpr void check_with_volatile() { |
| check_one<Expected, T, U>(); |
| check_one<Expected, volatile T, U>(); |
| check_one<Expected, T, volatile U>(); |
| check_one<Expected, volatile T, volatile U>(); |
| } |
| |
| template <bool Expected, class T, class U> |
| constexpr void check_with_cv() { |
| check_with_volatile<Expected, T, U>(); |
| check_with_volatile<Expected, const T, U>(); |
| check_with_volatile<Expected, T, const U>(); |
| check_with_volatile<Expected, const T, const U>(); |
| } |
| |
| template <bool Expected, class Types1, class Types2 = Types1> |
| constexpr void check() { |
| types::for_each(Types1{}, []<class T>() { |
| types::for_each(Types2{}, []<class U>() { |
| check_with_cv<Expected, T, U>(); |
| }); |
| }); |
| } |
| |
| template <bool Expected, class Types1, class Types2> |
| constexpr void check_both_ways() { |
| check<Expected, Types1, Types2>(); |
| check<Expected, Types2, Types1>(); |
| } |
| |
| constexpr void test() { |
| // Arithmetic types. |
| { |
| // Bit-castable arithmetic types. |
| |
| // 8-bit types. |
| using integral_8 = types::type_list<char8_t, std::int8_t, std::uint8_t>; |
| using chars = types::type_list<char, unsigned char, signed char>; |
| #if CHAR_BIT == 8 |
| check<true, types::concatenate_t<integral_8, chars>>(); |
| #else |
| check<true, integral_8>(); |
| check<true, chars>(); |
| #endif |
| |
| // 16-bit types. |
| using integral_16 = types::type_list<char16_t, std::int16_t, std::uint16_t>; |
| #if !defined(TEST_HAS_NO_WIDE_CHARACTERS) && __WCHAR_WIDTH__ == 16 |
| check<true, types::concatenate_t<integral_16, types::type_list<wchar_t>>>(); |
| #else |
| check<true, integral_16>(); |
| #endif |
| |
| // 32-bit types. |
| using integral_32 = types::type_list<char32_t, std::int32_t, std::uint32_t>; |
| #if !defined(TEST_HAS_NO_WIDE_CHARACTERS) && __WCHAR_WIDTH__ == 32 |
| check<true, types::concatenate_t<integral_32, types::type_list<wchar_t>>>(); |
| #else |
| check<true, integral_32>(); |
| #endif |
| |
| // 64-bit types. |
| using integral_64 = types::type_list<std::int64_t, std::uint64_t>; |
| check<true, integral_64>(); |
| |
| // 128-bit types. |
| #ifndef TEST_HAS_NO_INT128 |
| check<true, types::type_list<__int128_t, __uint128_t>>(); |
| #endif |
| |
| // Bool. |
| check<true, types::type_list<bool>, types::concatenate_t<types::type_list<bool>, integral_8>>(); |
| |
| // Non-bit-castable arithmetic types. |
| |
| // Floating-point. |
| check_both_ways<false, types::floating_point_types, types::integral_types>(); |
| check_both_ways<false, types::type_list<float>, types::type_list<double, long double>>(); |
| check_both_ways<false, types::type_list<double>, types::type_list<float, long double>>(); |
| check_both_ways<false, types::type_list<long double>, types::type_list<float, double>>(); |
| |
| // Different sizes. |
| check_both_ways<false, integral_8, types::concatenate_t<integral_16, integral_32, integral_64>>(); |
| check_both_ways<false, integral_16, types::concatenate_t<integral_8, integral_32, integral_64>>(); |
| check_both_ways<false, integral_32, types::concatenate_t<integral_8, integral_16, integral_64>>(); |
| check_both_ways<false, integral_64, types::concatenate_t<integral_8, integral_16, integral_32>>(); |
| |
| // Different representations -- can convert from bool to other integral types, but not vice versa. |
| check<true, types::type_list<bool>, integral_8>(); |
| using larger_than_bool = types::concatenate_t< |
| integral_16, |
| integral_32, |
| integral_64, |
| types::floating_point_types>; |
| check<false, types::type_list<bool>, larger_than_bool>(); |
| check<false, types::concatenate_t<integral_8, larger_than_bool>, types::type_list<bool>>(); |
| |
| // Different representations -- floating point vs. integral. |
| check_both_ways<false, types::floating_point_types, types::integral_types>(); |
| } |
| |
| // Enumerations. |
| { |
| enum E1 { Value1 }; |
| enum E2 { Value2 }; |
| check<true, types::type_list<E1>>(); |
| check_both_ways<false, types::type_list<E1>, types::type_list<E2>>(); |
| |
| enum class ScopedE1 { Value1 }; |
| enum class ScopedE2 { Value1 }; |
| check<true, types::type_list<ScopedE1>>(); |
| check_both_ways<false, types::type_list<ScopedE1>, types::type_list<ScopedE2>>(); |
| } |
| |
| // Pointers. |
| { |
| check<true, types::type_list<int*>>(); |
| check_both_ways<false, types::type_list<int*>, types::type_list<const int*, long*, void*>>(); |
| |
| check<true, types::type_list<FuncPtr1>>(); |
| check_both_ways<false, types::type_list<FuncPtr1>, types::type_list<FuncPtr2>>(); |
| } |
| |
| // Pointers to members. |
| { |
| struct S { |
| int mem_obj1 = 0; |
| long mem_obj2 = 0; |
| void MemFunc1() {} |
| int MemFunc2() { return 0; } |
| }; |
| using MemObjPtr1 = decltype(&S::mem_obj1); |
| using MemObjPtr2 = decltype(&S::mem_obj2); |
| using MemFuncPtr1 = decltype(&S::MemFunc1); |
| using MemFuncPtr2 = decltype(&S::MemFunc2); |
| |
| check<true, types::type_list<MemObjPtr1>>(); |
| check<true, types::type_list<MemFuncPtr1>>(); |
| check_both_ways<false, types::type_list<MemObjPtr1>, types::type_list<MemObjPtr2>>(); |
| check_both_ways<false, types::type_list<MemFuncPtr1>, types::type_list<MemFuncPtr2>>(); |
| } |
| |
| // Trivial classes. |
| { |
| struct S1 {}; |
| check<true, types::type_list<S1>>(); |
| |
| struct S2 {}; |
| check_both_ways<false, types::type_list<S1>, types::type_list<S2>>(); |
| |
| // Having a `volatile` member doesn't prevent a class type from being considered trivially copyable. This is |
| // unfortunate behavior but it is consistent with the Standard. |
| struct VolatileMembersS { |
| volatile int x; |
| }; |
| check<true, types::type_list<VolatileMembersS>>(); |
| } |
| |
| // Trivial unions. |
| { |
| union U1 {}; |
| check<true, types::type_list<U1>>(); |
| |
| union U2 {}; |
| check_both_ways<false, types::type_list<U1>, types::type_list<U2>>(); |
| |
| union VolatileMembersU { |
| volatile int x; |
| }; |
| check<true, types::type_list<VolatileMembersU>>(); |
| } |
| |
| // References are not objects, and thus are not bit-castable. |
| { |
| check_both_ways<false, types::type_list<int&>, types::type_list<int&>>(); |
| } |
| |
| // Arrays. |
| { |
| check<true, types::type_list<int[8]>>(); |
| } |
| } |