llvm / llvm/llvm-project

[Clang] `__builtin_common_type` is overly permissive

Open
#171,438 2 comments 0 reactions 0 assignees View on GitHub
clang:frontend
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.