llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.32k stars 12.12k forks source link

Deduction guide fails for CTAD for template aliases #111216

Open agraham-roku opened 1 month ago

agraham-roku commented 1 month ago

Here's a case where CTAD for template aliases feature isn't working. Minimal code is copied below, but here also are godbolt links:

template <typename Rep, typename Period> struct duration { constexpr duration(Rep r) : v{r} {}

template <typename R2, typename P2>
constexpr duration(duration<R2, P2> const& d)
        : v{std::chrono::duration_cast<decltype(v)>(d.v)} {}

std::chrono::duration<Rep, Period> v;

};

template <typename Rep, typename Period = std::ratio<1>> using seconds = duration<Rep, Period>;

template using milliseconds = seconds<Rep, std::chrono::milliseconds::period>;

template <typename Rep, typename Period, typename Period2> duration(duration<Rep, Period>) -> duration<Rep, Period2>;

constexpr auto d1 = seconds{1}; constexpr auto d2 = milliseconds{d1}; static_assert(d2.v.count() == 1000);

int main() {}

The clang error is:

:21:1: error: deduction guide template contains a template parameter that cannot be deduced 21 | duration(duration<Rep, Period>) -> duration<Rep, Period2>; | ^ :20:51: note: non-deducible template parameter 'Period2' 20 | template <typename Rep, typename Period, typename Period2> | ^ 1 error generated. Compiler returned: 1

llvmbot commented 1 month ago

@llvm/issue-subscribers-clang-frontend

Author: Aaron Graham (agraham-roku)

Here's a case where CTAD for template aliases feature isn't working. Minimal code is copied below, but here also are godbolt links: * https://godbolt.org/z/6jdo4qGG9 clang-trunk doesn't work * https://godbolt.org/z/3KoG4PfWT GCC supports this, as far back as 12.1 ``` #include <chrono> template <typename Rep, typename Period> struct duration { constexpr duration(Rep r) : v{r} {} template <typename R2, typename P2> constexpr duration(duration<R2, P2> const& d) : v{std::chrono::duration_cast<decltype(v)>(d.v)} {} std::chrono::duration<Rep, Period> v; }; template <typename Rep, typename Period = std::ratio<1>> using seconds = duration<Rep, Period>; template <typename Rep> using milliseconds = seconds<Rep, std::chrono::milliseconds::period>; template <typename Rep, typename Period, typename Period2> duration(duration<Rep, Period>) -> duration<Rep, Period2>; constexpr auto d1 = seconds{1}; constexpr auto d2 = milliseconds{d1}; static_assert(d2.v.count() == 1000); int main() {} ``` The clang error is: ``` <source>:21:1: error: deduction guide template contains a template parameter that cannot be deduced 21 | duration(duration<Rep, Period>) -> duration<Rep, Period2>; | ^ <source>:20:51: note: non-deducible template parameter 'Period2' 20 | template <typename Rep, typename Period, typename Period2> | ^ 1 error generated. Compiler returned: 1 ```
AndreyG commented 1 month ago

It doesn't look like a bug, just the additional diagnostic (not mandated by the standard) which is implemented in Clang, but not implemented in GCC and MSVC (https://godbolt.org/z/s7YrfPPsh).

template <typename Period>
struct duration {};

template <typename Period, typename Period2>
duration(duration<Period>) -> duration<Period2>;

It's true that Period2 cannot be deduced from the such deduction guide signature.

pepsiman commented 1 month ago

This is a bug - over.match.class.deduct says

For each function or function template f in the guides of the template named by the simple-template-id of the defining-type-id, the template arguments of the return type of f are deduced from the defining-type-id of A according to the process in [temp.deduct.type] with the exception that deduction does not fail if not all template arguments are deduced.

mrussoLuxoft commented 1 week ago

hello,

let me contribute,

independently found the error with this sample code:

#include <type_traits>

class C{
public:
    operator int() {return 0;};
};

template<typename T1, class T2 = int>
class C2{
public:
    C2(T1 t1, T2 t2){};
};

template<class T>
using TT2 = C2<T>;

TT2 x(1,2); // ok: C2<int,int>
TT2 y(3,'4'); // rejected by clang 19.1.0, but accepted by gcc 14.2 and MVSC 19.latest
TT2 z(5,C()); // rejected by clang 19.1.0, but accepted by gcc 14.2 and MVSC 19.latest

static_assert(std::is_same_v<decltype(x),decltype(y)>); // ok for gcc and MVSC
static_assert(std::is_same_v<decltype(y),decltype(z)>); // ok for gcc and MVSC

y is refused with the following error, and similarly z with C instead of char:

error: no viable constructor or deduction guide for deduction of template arguments of 'TT2'
      | TT2 y(3,'4');
      |     ^
note: candidate template ignored: constraints not satisfied [with T = int, T2 = char]
      | using TT2 = C2<T>;
      | ^
note: cannot deduce template arguments for 'TT2' from 'C2<int, char>'
note: implicit deduction guide declared as 'template <class T, class T2 = int> requires __is_deducible(TT2, C2<T, T2>) TT2(T t1, T2 t2) -> C2<T, T2>'
note: candidate function template not viable: requires 1 argument, but 2 were provided
      | class C2{
      |       ~~
      | public:
      |     C2(T1 t1, T2 t2){};
      | };
      | 
      | template<class T>
      | using TT2 = C2<T>;
      | ^
note: implicit deduction guide declared as 'template <class T, class T2 = int> requires __is_deducible(TT2, C2<T, T2>) TT2(C2<T, T2>) -> C2<T, T2>'