Issue 177233
Summary clang accepts template template paramater code that all other compilers reject
Labels clang
Assignees
Reporter ribbon-otter
    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 <utility>

// Create our sample types
template<class... Td> struct A {}; 
template<class... Td> struct B {}; 

// Stand in for std::vector to reduce dependencies
template<class T> struct alloc {}; 
template< class T, class Allocator = alloc<T> > 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<class T> 
constexpr T
change_type_deeply_f(T a);

//recursive case
template<template<class...> class T, class... Td>
constexpr T<decltype(change_type_deeply_f(std::declval<Td>()))...>
change_type_deeply_f(T<Td...> a);

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

// case that actually does the modification of turning A into B
template<class... Td>
constexpr B<decltype(change_type_deeply_f(std::declval<Td>()))...>
change_type_deeply_f(A<Td...> a);
	 
///then define them

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

//recursive case
template<template<class...> class T, class... Td>
constexpr T<decltype(change_type_deeply_f(std::declval<Td>()))...>
change_type_deeply_f(T<Td...> a){return {};};

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

// case that actually does the modification of turning A into B
template<class... Td>
constexpr B<decltype(change_type_deeply_f(std::declval<Td>()))...>
change_type_deeply_f(A<Td...> a){return {};};
	 

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

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

using unchanged = vector<A<int, bool>>;
using expected = vector<B<int, bool>>;
using result = change_type_deeply<unchanged>;

static_assert(std::is_same_v<result, expected>);

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

example of clang version it works on: 21.1.6
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to