mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-24 23:16:05 +00:00

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.
184 lines
4.5 KiB
C++
184 lines
4.5 KiB
C++
// No PCH:
|
|
// RUN: %clang_cc1 -pedantic -std=c++1y -include %s -include %s -verify %s -DNONPCH
|
|
// RUN: %clang_cc1 -pedantic -std=c++1y -include %s -include %s -verify %s -DNONPCH -DERROR
|
|
//
|
|
// With PCH:
|
|
// RUN: %clang_cc1 -pedantic -std=c++1y -emit-pch %s -o %t.a -DHEADER1
|
|
// RUN: %clang_cc1 -pedantic -std=c++1y -include-pch %t.a -emit-pch %s -o %t.b -DHEADER2
|
|
// RUN: %clang_cc1 -pedantic -std=c++1y -include-pch %t.b -verify %s -DHEADERUSE
|
|
|
|
// RUN: %clang_cc1 -pedantic -std=c++1y -emit-pch -fpch-instantiate-templates %s -o %t.a -DHEADER1
|
|
// RUN: %clang_cc1 -pedantic -std=c++1y -include-pch %t.a -emit-pch -fpch-instantiate-templates %s -o %t.b -DHEADER2
|
|
// RUN: %clang_cc1 -pedantic -std=c++1y -include-pch %t.b -verify %s -DHEADERUSE
|
|
|
|
#ifndef ERROR
|
|
// expected-no-diagnostics
|
|
#endif
|
|
|
|
#ifdef NONPCH
|
|
#if !defined(HEADER1)
|
|
#define HEADER1
|
|
#undef HEADER2
|
|
#undef HEADERUSE
|
|
#elif !defined(HEADER2)
|
|
#define HEADER2
|
|
#undef HEADERUSE
|
|
#else
|
|
#define HEADERUSE
|
|
#undef HEADER1
|
|
#undef HEADER2
|
|
#endif
|
|
#endif
|
|
|
|
|
|
// *** HEADER1: First header file
|
|
#if defined(HEADER1) && !defined(HEADER2) && !defined(HEADERUSE)
|
|
|
|
template<typename T> T var0a = T();
|
|
template<typename T> extern T var0b;
|
|
|
|
namespace join {
|
|
template<typename T> T va = T(100);
|
|
template<typename T> extern T vb;
|
|
|
|
namespace diff_types {
|
|
#ifdef ERROR
|
|
template<typename T> extern float err0;
|
|
template<typename T> extern T err1;
|
|
#endif
|
|
template<typename T> extern T def;
|
|
}
|
|
|
|
}
|
|
|
|
namespace spec {
|
|
template<typename T> constexpr T va = T(10);
|
|
template<> constexpr float va<float> = 1.5;
|
|
template constexpr int va<int>;
|
|
|
|
template<typename T> T vb = T();
|
|
template<> constexpr float vb<float> = 1.5;
|
|
|
|
template<typename T> T vc = T();
|
|
|
|
template<typename T> constexpr T vd = T(10);
|
|
template<typename T> T* vd<T*> = new T();
|
|
}
|
|
|
|
namespace spec_join1 {
|
|
template<typename T> T va = T(10);
|
|
#ifdef ERROR
|
|
template<> float va<float>; // expected-note {{previous definition is here}}
|
|
#endif
|
|
extern template int va<int>;
|
|
|
|
template<typename T> T vb = T(10);
|
|
#ifdef ERROR
|
|
template<> float vb<float>; // expected-note {{previous definition is here}}
|
|
#endif
|
|
|
|
template<typename T> T vc = T(10);
|
|
|
|
template<typename T> T vd = T(10);
|
|
template<typename T> extern T* vd<T*>;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
// *** HEADER2: Second header file -- including HEADER1
|
|
#if defined(HEADER2) && !defined(HEADERUSE)
|
|
|
|
namespace join {
|
|
template<typename T> extern T va;
|
|
template<> constexpr float va<float> = 2.5;
|
|
|
|
template<typename T> T vb = T(100);
|
|
|
|
namespace diff_types {
|
|
#ifdef ERROR
|
|
template<typename T> extern T err0; // expected-error {{redeclaration of 'err0' with a different type: 'T' vs 'float'}} // expected-note@46 {{previous declaration is here}}
|
|
template<typename T> extern float err1; // expected-error {{redeclaration of 'err1' with a different type: 'float' vs 'T'}} // expected-note@47 {{previous declaration is here}}
|
|
#endif
|
|
template<typename T> extern T def;
|
|
}
|
|
}
|
|
|
|
namespace spec_join1 {
|
|
template<typename T> extern T va;
|
|
#ifdef ERROR
|
|
template<> float va<float> = 1.5; // expected-error {{redefinition of 'va<float>'}}
|
|
#endif
|
|
extern template int va<int>;
|
|
|
|
#ifdef ERROR
|
|
template<> float vb<float> = 1.5; // expected-error {{redefinition of 'vb<float>'}}
|
|
#endif
|
|
template int vb<int>;
|
|
|
|
template<> float vc<float> = 1.5;
|
|
template int vc<int>;
|
|
|
|
template<typename T> extern T vd;
|
|
template<typename T> T* vd<T*> = new T();
|
|
}
|
|
|
|
#endif
|
|
|
|
// *** HEADERUSE: File using both header files -- including HEADER2
|
|
#ifdef HEADERUSE
|
|
|
|
template int var0a<int>;
|
|
float fvara = var0a<float>;
|
|
|
|
template<typename T> extern T var0a;
|
|
|
|
template<typename T> T var0b = T();
|
|
template int var0b<int>;
|
|
float fvarb = var0b<float>;
|
|
|
|
namespace join {
|
|
template const int va<const int>;
|
|
template<> const int va<int> = 50;
|
|
static_assert(va<float> == 2.5, "");
|
|
static_assert(va<int> == 50, "");
|
|
|
|
template<> constexpr float vb<float> = 2.5;
|
|
template const int vb<const int>;
|
|
static_assert(vb<float> == 2.5, "");
|
|
static_assert(vb<const int> == 100, "");
|
|
|
|
namespace diff_types {
|
|
template<typename T> T def = T();
|
|
}
|
|
|
|
}
|
|
|
|
namespace spec {
|
|
static_assert(va<float> == 1.5, "");
|
|
static_assert(va<int> == 10, "");
|
|
|
|
template<typename T> T* vb<T*> = new T();
|
|
int* intpb = vb<int*>;
|
|
static_assert(vb<float> == 1.5, "");
|
|
|
|
template<typename T> T* vc<T*> = new T();
|
|
template<> constexpr float vc<float> = 1.5;
|
|
int* intpc = vc<int*>;
|
|
static_assert(vc<float> == 1.5, "");
|
|
|
|
char* intpd = vd<char*>;
|
|
}
|
|
|
|
namespace spec_join1 {
|
|
template int va<int>;
|
|
int a = va<int>;
|
|
|
|
template<typename T> extern T vb;
|
|
int b = vb<int>;
|
|
|
|
int* intpb = vd<int*>;
|
|
}
|
|
|
|
#endif
|