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

Reply via email to