llvm / llvm/llvm-project

Explicit specializations of specializations of templated members

Open
#198,089 2 comments 0 reactions 0 assignees View on GitHub
c++17 clang:frontend diverges-from:edg diverges-from:msvc regression:19
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Consider the following example, reduced from our [CWG727](https://cplusplus.github.io/CWG/issues/727.html) test (https://godbolt.org/z/b3ej5qWhK):
```cpp
template struct B {
template static const int u = 1; // #1
template<> const int u<0> = 2; // #2, rejected by GCC and Clang 18
};

template<> template const int B<0>::u = 3; // #3, rejected by GCC

static_assert(B<0>().u<0> == 1, "not 1"); // passes in GCC
static_assert(B<0>().u<0> == 2, "not 2"); // passes in Clang 19+ and EDG
static_assert(B<0>().u<0> == 3, "not 3"); // passes in Clang 18 and MSVC
```

Consider a similar example, which adds a specialization of `#3` (https://godbolt.org/z/EbYx55zTG):
```cpp
template struct B {
template static const int u = 1; // #1
template<> const int u<0> = 2; // #2, rejected by GCC
};

template<> template const int B<0>::u = 3; // #3, rejected by GCC
template<> template<> const int B<0>::u<0> = 4; // #4, rejected by Clang 19+

static_assert(B<0>().u<0> == 2, "not 2"); // passes in Clang 19+
static_assert(B<0>().u<0> == 4, "not 4"); // passes in Clang 18, GCC, EDG, and MSVC
```

While Clang 18 was incorrect to reject `#2` in both example in the light of [CWG727](https://cplusplus.github.io/CWG/issues/727.html), I believe it was correctly selecting `#3` and `#4` for static asserts in the first and the second example, respectively.

That is because `#2` is not a specialization of `#3`, i.e. `#3` "hides" `#2`. This is supported by [N4090](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4090.pdf) (prose on page 3, case `a2` in the example after it), which claims to be capturing what Core was converging on. That said, I wasn't able to find minutes of either CWG727 or N4090, so it's an open question how much Core agreed with the paper.

Additionally, I don't believe we are correct to reject `#4` (with a `static data member 'u' already has an initializer` diagnostic) for the same reason we shouldn't be rejecting `#2` — it's a specialization, so it doesn't conflict with the definition of the primary template.

I believe that at least the latter part of this is caused by #93873, so CC @sdkrystian.

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.