[clang][rejects-valid][20 regression] substitution failure when deducing a template template parameter from a class template with a constant template parameter of dependent type
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
https://godbolt.org/z/MezP54Kn8
```cpp
template
struct my_constant {};
template class Constant>
void f(Constant);
using go = decltype(f(my_constant()));
```
```
:7:21: error: no matching function for call to 'f'
7 | using go = decltype(f(my_constant()));
| ^
:5:6: note: candidate template ignored: substitution failure: conflicting deduction 'U' against 'int' for parameter
1 | template
| ~
2 | struct my_constant {};
3 |
4 | template class Constant>
5 | void f(Constant);
| ^
```
Note, U is unnecessary, it just makes the error message more clear-- rather makes what it is trying to convey more clear.
Also occurs for class template partial specializations.
https://godbolt.org/z/hM86rnoov
```cpp
template
struct my_constant {};
template struct S;
template class Constant>
struct S> {};
template struct S>;
```
If you change V to not be dependent...
```cpp
template
struct my_constant {};
```
...both cases work fine.
https://godbolt.org/z/znoToTPMx
```cpp
template
struct my_constant {};
template class Constant>
void f(Constant);
using go = decltype(f(my_constant()));
```
https://godbolt.org/z/Ks9xcPT5a
```cpp
template
struct my_constant {};
template struct S;
template class Constant>
struct S> {};
template struct S>;
```
This obviously does not suffice as a workaround, however, changing the constant template parameter of Constant to be dependent on its type template parameter also prevents the issue and is a valid workaround.
https://godbolt.org/z/jaEb15PrY
```cpp
template
struct my_constant {};
template class Constant>
void f(Constant);
using go = decltype(f(my_constant()));
template struct S;
template class Constant>
struct S> {};
template struct S>;
```
Interestingly, the reverse does not cause problems for Clang.
https://godbolt.org/z/r8oE9sbbb
```cpp
template
struct my_constant {};
template class Constant>
void f(Constant);
using go = decltype(f(my_constant()));
template struct S;
template class Constant>
struct S> {};
template struct S>;
```
It honestly isn't obvious if the topmost case is-- or if it should be-- allowed by the standard. I think I would argue it should be, but I could see arguments to the contrary too, especially when passing a template as an explicit template argument.
https://godbolt.org/z/avc98vGsx
```cpp
template
struct my_constant {};
template class Constant>
void f();
using go = decltype(f());
```
(This is another manifestation of the same bug as well, but it is more dubious whether the code is correct or not.)
While investigating this, I was surprised to learn C++17 made a breaking change to deduction.
https://eel.is/c++draft/diff.cpp14.temp#1
I thought this might be related to this bug, especially with an interesting reproducer I came across while reducing it, note the differences in error messages between C++14 and C++17.
https://godbolt.org/z/1bq7b4jaY
```cpp
template
struct my_constant {};
template class Constant>
void f(Constant);
using go = decltype(f(my_constant()));
```
I don't think the bug is actually related to that change though, but it does seem to elude to what is actually going on. It seems like U is being deduced through V's type, as if 'Constant' had the same form of template-parameter-list as my_constant does. Or perhaps I'm overthinking it, as the type of U does appear to actually match. It deduces to int both through my_constant's type template parameter, and even if it shouldn't be, to int through deduction from the constant template parameter.
I've spent too long messing around with this, I have some other cases I found while reducing this one that I'm not so sure about. After some more research those might warrant other bug reports. As well as a few to other projects as well.
Contributor guide
Research direction
Start with the primary Compiler Explorer reproducer at https://godbolt.org/z/MezP54Kn8 and compare the rejected deduction with the working variants linked in the report. Trace template-template-parameter deduction and compare behavior across C++14 and C++17, including the class-template partial-specialization case. Done means the valid dependent non-type parameter cases compile without regressing the documented counterexamples.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100