| Issue |
171438
|
| Summary |
[Clang] `__builtin_common_type` is overly permissive
|
| Labels |
clang:frontend
|
| Assignees |
|
| Reporter |
frederick-vs-ja
|
Given the following type
```C++
struct pinned {
pinned(int) {}
pinned(pinned&&) = delete;
pinned& operator=(pinned&&) = delete;
};
```
`std::common_type_t<pinned, int>` shouldn't exist because none of `true ? std::declval<pinned>() : std::declval<int>()` and `true ? std::declval<const pinned&>() : std::declval<const int&>()` is well-formed ([[meta.trans.other]/4](https://eel.is/c++draft/meta.trans.other#4)).
However, the implementation strategy using `__builtin_common_type` gives `pinned`. [Godbolt link](https://godbolt.org/z/9z3j45P94).
[More expanded example](https://godbolt.org/z/oY1e4qnco):
<details><summary>Details</summary>
<p>
```C++
#include <type_traits>
#include <utility>
template <class T>
struct type_identity {
using type = T;
};
struct empty_class {};
template <class... Args>
struct builtin_common_type;
template <class... Args>
using builtin_common_type_t = typename builtin_common_type<Args...>::type;
template <class... Args>
struct builtin_common_type : __builtin_common_type<builtin_common_type_t, type_identity, empty_class, Args...> {};
template <class Tp, class Up>
using cond_result_t = decltype(false ? std::declval<Tp>() : std::declval<Up>());
template <class Tp, class Up, class = void>
struct common_type3 {};
template <class Tp, class Up>
struct common_type3<Tp, Up, std::void_t<cond_result_t<const Tp&, const Up&>>> {
using type = std::remove_cvref_t<cond_result_t<const Tp&, const Up&>>;
};
template <class Tp, class Up, class = void>
struct common_type2_imp : common_type3<Tp, Up> {};
// sub-bullet 3 - "if decay_t<decltype(false ? declval<D1>() : declval<D2>())> ..."
template <class Tp, class Up>
struct common_type2_imp<Tp, Up, std::void_t<decltype(true ? std::declval<Tp>() : std::declval<Up>())> > {
using type = std::decay_t<decltype(true ? std::declval<Tp>() : std::declval<Up>())>;
};
template <class, class = void>
struct common_type_impl {};
template <class... Tp>
struct common_types_tag;
template <class... Tp>
struct common_type;
template <class Tp, class Up>
struct common_type_impl<common_types_tag<Tp, Up>, std::void_t<typename common_type<Tp, Up>::type> > {
typedef typename common_type<Tp, Up>::type type;
};
template <class Tp, class Up, class _Vp, class... Rest>
struct common_type_impl<common_types_tag<Tp, Up, _Vp, Rest...>, std::void_t<typename common_type<Tp, Up>::type>>
: common_type_impl<common_types_tag<typename common_type<Tp, Up>::type, _Vp, Rest...> > {};
// bullet 1 - sizeof...(Tp) == 0
template <>
struct common_type<> {};
// bullet 2 - sizeof...(Tp) == 1
template <class Tp>
struct common_type<Tp> : public common_type<Tp, Tp> {};
// bullet 3 - sizeof...(Tp) == 2
// sub-bullet 1 - "If is_same_v<T1, D1> is false or ..."
template <class Tp, class Up>
struct common_type<Tp, Up>
: std::conditional_t<std::is_same_v<Tp, std::decay_t<Tp>> && std::is_same_v<Up, std::decay_t<Up>>,
common_type2_imp<Tp, Up>,
common_type<std::decay_t<Tp>, std::decay_t<Up>>> {};
// bullet 4 - sizeof...(Tp) > 2
template <class Tp, class Up, class _Vp, class... Rest>
struct common_type<Tp, Up, _Vp, Rest...> : common_type_impl<common_types_tag<Tp, Up, _Vp, Rest...>> {};
template <class... Tp>
using common_type_t = typename common_type<Tp...>::type;
struct pinned {
pinned(int) {}
pinned(pinned&&) = delete;
pinned& operator=(pinned&&) = delete;
};
template<class... Ts>
concept has_common_type = requires { typename ::common_type_t<Ts...>; };
static_assert(std::is_same_v<::builtin_common_type_t<pinned, int>, pinned>);
static_assert(!has_common_type<pinned, int>);
```
</p>
</details>
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs