
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}}
|