[Clang] `__builtin_common_type` is overly permissive
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Given the following type
```C++
struct pinned {
pinned(int) {}
pinned(pinned&&) = delete;
pinned& operator=(pinned&&) = delete;
};
```
`std::common_type_t` shouldn't exist because none of `true ? std::declval() : std::declval()` and `true ? std::declval() : std::declval()` 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
```C++
#include
#include
template
struct type_identity {
using type = T;
};
struct empty_class {};
template
struct builtin_common_type;
template
using builtin_common_type_t = typename builtin_common_type::type;
template
struct builtin_common_type : __builtin_common_type {};
template
using cond_result_t = decltype(false ? std::declval() : std::declval());
template
struct common_type3 {};
template
struct common_type3>> {
using type = std::remove_cvref_t>;
};
template
struct common_type2_imp : common_type3 {};
// sub-bullet 3 - "if decay_t() : declval())> ..."
template
struct common_type2_imp() : std::declval())> > {
using type = std::decay_t() : std::declval())>;
};
template
struct common_type_impl {};
template
struct common_types_tag;
template
struct common_type;
template
struct common_type_impl, std::void_t::type> > {
typedef typename common_type::type type;
};
template
struct common_type_impl, std::void_t::type>>
: common_type_impl::type, _Vp, Rest...> > {};
// bullet 1 - sizeof...(Tp) == 0
template <>
struct common_type<> {};
// bullet 2 - sizeof...(Tp) == 1
template
struct common_type : public common_type {};
// bullet 3 - sizeof...(Tp) == 2
// sub-bullet 1 - "If is_same_v is false or ..."
template
struct common_type
: std::conditional_t> && std::is_same_v>,
common_type2_imp,
common_type, std::decay_t>> {};
// bullet 4 - sizeof...(Tp) > 2
template
struct common_type : common_type_impl> {};
template
using common_type_t = typename common_type::type;
struct pinned {
pinned(int) {}
pinned(pinned&&) = delete;
pinned& operator=(pinned&&) = delete;
};
template
concept has_common_type = requires { typename ::common_type_t; };
static_assert(std::is_same_v<::builtin_common_type_t, pinned>);
static_assert(!has_common_type);
```
Contributor guide
Research direction
Start by reproducing the `__builtin_common_type` behavior with the pinned and int examples in the issue, including the expanded `common_type` implementation. Read the Clang entry point for `__builtin_common_type` and compare its result with the conditional-expression requirements in `std::common_type`. Done means the builtin no longer accepts this invalid combination and a regression test covers the reproducer.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100