llvm / llvm/llvm-project

clang accepts template template paramater code that all other compilers reject

Open
#177,233 4 comments 0 reactions 1 assignee Claimed by @mizvekov View on GitHub
clang:frontend diverges-from:gcc diverges-from:msvc
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

I wrote some code which recuses over template template parameters, but explicitly spelling out one case causes all compilers but Clang to reject it. This makes me think that clang is likely the one with the bug. Though given that my code works with clang, I might hope otherwise.

link to godbolt conformance view: https://godbolt.org/z/zK5s4bfez

Without the explicitly spelled out case, [all compilers except MSVC accept the code.](https://godbolt.org/z/b8o8Ex7EM)

While the godbolt link is likely the easiest way to see it replicate, below is the short reproducer to make this bug report self-contained.
```c++
#include

// Create our sample types
template struct A {};
template struct B {};

// Stand in for std::vector to reduce dependencies
template struct alloc {};
template< class T, class Allocator = alloc > class vector {};

//high level overview
//we want to recursively iterate over the template arguments
//and change all the class A to class B.

/// === forward declare ===

//base case
template
constexpr T
change_type_deeply_f(T a);

//recursive case
template class T, class... Td>
constexpr T()))...>
change_type_deeply_f(T a);

//we define the 2 argument template case explicitly
//adding this case causes g++ to reject it but not clang++
template class T, class T1, class T2>
constexpr T<
decltype(change_type_deeply_f(std::declval())),
decltype(change_type_deeply_f(std::declval()))
>
change_type_deeply_f(T a);

// case that actually does the modification of turning A into B
template
constexpr B()))...>
change_type_deeply_f(A a);

///then define them

//base case
template
constexpr T
change_type_deeply_f(T a){return {};};

//recursive case
template class T, class... Td>
constexpr T()))...>
change_type_deeply_f(T a){return {};};

//we define the 2 argument template case explicitly
template class T, class T1, class T2>
constexpr T<
decltype(change_type_deeply_f(std::declval())),
decltype(change_type_deeply_f(std::declval()))
>
change_type_deeply_f(T a){return {};};

// case that actually does the modification of turning A into B
template
constexpr B()))...>
change_type_deeply_f(A a){return {};};

// move from the world of values to the world of types
template using change_type_deeply =
decltype(change_type_deeply_f(std::declval()));

// === now we test it ===

using unchanged = vector>;
using expected = vector>;
using result = change_type_deeply;

static_assert(std::is_same_v);

//silence linker error on compiling
int main() { return 0; }
```

example of clang version it works on: 21.1.6

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.