According to [temp.expl.spec] p2:
> The declaration in an _explicit-specialization_ shall not be an
_export-declaration_. An explicit specialization shall not use a
_storage-class-specifier_ other than `thread_local`.
Clang partially implements this, but a number of issues exist:
1. We don't diagnose class scope explicit specializations of variable
templates with _storage-class-specifiers_, e.g.
```
struct A
{
template<typename T>
static constexpr int x = 0;
template<>
static constexpr int x<void> = 1; // ill-formed, but clang accepts
};
````
2. We incorrectly reject class scope explicit specializations of
variable templates when `static` is not used, e.g.
```
struct A
{
template<typename T>
static constexpr int x = 0;
template<>
constexpr int x<void> = 1; // error: non-static data member cannot be
constexpr; did you intend to make it static?
};
````
3. We don't diagnose dependent class scope explicit specializations of
function templates with storage class specifiers, e.g.
```
template<typename T>
struct A
{
template<typename U>
static void f();
template<>
static void f<int>(); // ill-formed, but clang accepts
};
````
This patch addresses these issues as follows:
- # 1 is fixed by issuing a diagnostic when an explicit
specialization of a variable template has storage class specifier
- # 2 is fixed by considering any non-function declaration with any
template parameter lists at class scope to be a static data member. This
also allows for better error recovery (it's more likely the user
intended to declare a variable template than a "field template").
- # 3 is fixed by checking whether a function template explicit
specialization has a storage class specifier even when the primary
template is not yet known.
One thing to note is that it would be far simpler to diagnose this when
parsing the _decl-specifier-seq_, but such an implementation would
necessitate a refactor of `ParsedTemplateInfo` which I believe to be
outside the scope of this patch.
35 lines
1.2 KiB
C++
35 lines
1.2 KiB
C++
// RUN: rm -rf %t
|
|
// RUN: %clang_cc1 -x c++ -I %S/Inputs/redecl-templates %s -verify -std=c++14
|
|
// RUN: %clang_cc1 -x c++ -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/redecl-templates %s -verify -std=c++14
|
|
|
|
template<int N> struct A {};
|
|
template<int N> using X = A<N>;
|
|
|
|
template<int N> constexpr void f() {}
|
|
template<int N> constexpr void g() { f<N>(); }
|
|
|
|
template<int N> extern int v;
|
|
template<int N> int &w = v<N>;
|
|
|
|
#include "a.h"
|
|
|
|
// Be careful not to mention A here, that'll import the decls from "a.h".
|
|
int g(X<1> *);
|
|
X<1> *p = 0;
|
|
|
|
// This will implicitly instantiate A<1> if we haven't imported the explicit
|
|
// specialization declaration from "a.h".
|
|
int k = g(p);
|
|
// Likewise for f and v.
|
|
void h() { g<1>(); }
|
|
int &x = w<1>;
|
|
|
|
// This is OK: we declared the explicit specialization before we triggered
|
|
// instantiation of this specialization.
|
|
template<> struct A<1> {};
|
|
template<> constexpr void f<1>() {}
|
|
// Variable template explicit specializations are always definitions unless they
|
|
// are static data members declared without an initializer.
|
|
template<> int v<1>; // expected-error {{redefinition of 'v<1>'}}
|
|
// expected-note@Inputs/redecl-templates/a.h:8 {{previous definition is here}}
|