r359048 - C++ DR2387: a variable template declared wtih (or instantiated with) a
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 23 16:48:00 PDT 2019
Author: rsmith
Date: Tue Apr 23 16:48:00 2019
New Revision: 359048
URL: http://llvm.org/viewvc/llvm-project?rev=359048&view=rev
Log:
C++ DR2387: a variable template declared wtih (or instantiated with) a
const-qualified type is not implicitly given internal linkage. But a
variable template declared 'static' is.
Added:
cfe/trunk/test/CXX/drs/dr23xx.cpp
Modified:
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/AST/Decl.cpp
cfe/trunk/lib/Sema/SemaTemplate.cpp
cfe/trunk/test/CXX/drs/dr0xx.cpp
cfe/trunk/test/CXX/drs/dr17xx.cpp
cfe/trunk/test/CXX/module/module.interface/p3.cpp
cfe/trunk/test/CXX/module/module.interface/p5.cpp
cfe/trunk/test/CodeGenCXX/cxx1y-variable-template-linkage.cpp
cfe/trunk/test/SemaCXX/PR10177.cpp
cfe/trunk/test/SemaCXX/warn-unused-filescoped.cpp
cfe/trunk/test/SemaCXX/warn-unused-variables.cpp
cfe/trunk/www/cxx_dr_status.html
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Apr 23 16:48:00 2019
@@ -4384,6 +4384,8 @@ def err_explicit_instantiation_of_typede
"explicit instantiation of typedef %0">;
def err_explicit_instantiation_storage_class : Error<
"explicit instantiation cannot have a storage class">;
+def err_explicit_instantiation_internal_linkage : Error<
+ "explicit instantiation declaration of %0 with internal linkage">;
def err_explicit_instantiation_not_known : Error<
"explicit instantiation of %0 does not refer to a function template, "
"variable template, member function, member class, or static data member">;
Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Tue Apr 23 16:48:00 2019
@@ -610,6 +610,18 @@ static LinkageInfo getExternalLinkageFor
return LinkageInfo::external();
}
+static StorageClass getStorageClass(const Decl *D) {
+ if (auto *TD = dyn_cast<TemplateDecl>(D))
+ D = TD->getTemplatedDecl();
+ if (D) {
+ if (auto *VD = dyn_cast<VarDecl>(D))
+ return VD->getStorageClass();
+ if (auto *FD = dyn_cast<FunctionDecl>(D))
+ return FD->getStorageClass();
+ }
+ return SC_None;
+}
+
LinkageInfo
LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
LVComputationKind computation,
@@ -621,24 +633,28 @@ LinkageComputer::getLVForNamespaceScopeD
// C++ [basic.link]p3:
// A name having namespace scope (3.3.6) has internal linkage if it
// is the name of
- // - an object, reference, function or function template that is
- // explicitly declared static; or,
- // (This bullet corresponds to C99 6.2.2p3.)
+
+ if (getStorageClass(D->getCanonicalDecl()) == SC_Static) {
+ // - a variable, variable template, function, or function template
+ // that is explicitly declared static; or
+ // (This bullet corresponds to C99 6.2.2p3.)
+ return getInternalLinkageFor(D);
+ }
+
if (const auto *Var = dyn_cast<VarDecl>(D)) {
- // Explicitly declared static.
- if (Var->getStorageClass() == SC_Static)
- return getInternalLinkageFor(Var);
-
- // - a non-inline, non-volatile object or reference that is explicitly
- // declared const or constexpr and neither explicitly declared extern
- // nor previously declared to have external linkage; or (there is no
- // equivalent in C99)
- // The C++ modules TS adds "non-exported" to this list.
+ // - a non-template variable of non-volatile const-qualified type, unless
+ // - it is explicitly declared extern, or
+ // - it is inline or exported, or
+ // - it was previously declared and the prior declaration did not have
+ // internal linkage
+ // (There is no equivalent in C99.)
if (Context.getLangOpts().CPlusPlus &&
Var->getType().isConstQualified() &&
!Var->getType().isVolatileQualified() &&
!Var->isInline() &&
- !isExportedFromModuleInterfaceUnit(Var)) {
+ !isExportedFromModuleInterfaceUnit(Var) &&
+ !isa<VarTemplateSpecializationDecl>(Var) &&
+ !Var->getDescribedVarTemplate()) {
const VarDecl *PrevVar = Var->getPreviousDecl();
if (PrevVar)
return getLVForDecl(PrevVar, computation);
@@ -658,14 +674,6 @@ LinkageComputer::getLVForNamespaceScopeD
if (PrevVar->getStorageClass() == SC_Static)
return getInternalLinkageFor(Var);
}
- } else if (const FunctionDecl *Function = D->getAsFunction()) {
- // C++ [temp]p4:
- // A non-member function template can have internal linkage; any
- // other template name shall have external linkage.
-
- // Explicitly declared static.
- if (Function->getCanonicalDecl()->getStorageClass() == SC_Static)
- return getInternalLinkageFor(Function);
} else if (const auto *IFD = dyn_cast<IndirectFieldDecl>(D)) {
// - a data member of an anonymous union.
const VarDecl *VD = IFD->getVarDecl();
@@ -674,6 +682,8 @@ LinkageComputer::getLVForNamespaceScopeD
}
assert(!isa<FieldDecl>(D) && "Didn't expect a FieldDecl!");
+ // FIXME: This gives internal linkage to names that should have no linkage
+ // (those not covered by [basic.link]p6).
if (D->isInAnonymousNamespace()) {
const auto *Var = dyn_cast<VarDecl>(D);
const auto *Func = dyn_cast<FunctionDecl>(D);
@@ -733,10 +743,20 @@ LinkageComputer::getLVForNamespaceScopeD
// C++ [basic.link]p4:
- // A name having namespace scope has external linkage if it is the
- // name of
+ // A name having namespace scope that has not been given internal linkage
+ // above and that is the name of
+ // [...bullets...]
+ // has its linkage determined as follows:
+ // - if the enclosing namespace has internal linkage, the name has
+ // internal linkage; [handled above]
+ // - otherwise, if the declaration of the name is attached to a named
+ // module and is not exported, the name has module linkage;
+ // - otherwise, the name has external linkage.
+ // LV is currently set up to handle the last two bullets.
//
- // - an object or reference, unless it has internal linkage; or
+ // The bullets are:
+
+ // - a variable; or
if (const auto *Var = dyn_cast<VarDecl>(D)) {
// GCC applies the following optimization to variables and static
// data members, but not to functions:
@@ -782,7 +802,7 @@ LinkageComputer::getLVForNamespaceScopeD
mergeTemplateLV(LV, spec, computation);
}
- // - a function, unless it has internal linkage; or
+ // - a function; or
} else if (const auto *Function = dyn_cast<FunctionDecl>(D)) {
// In theory, we can modify the function's LV by the LV of its
// type unless it has C linkage (see comment above about variables
@@ -836,7 +856,8 @@ LinkageComputer::getLVForNamespaceScopeD
mergeTemplateLV(LV, spec, computation);
}
- // - an enumerator belonging to an enumeration with external linkage;
+ // FIXME: This is not part of the C++ standard any more.
+ // - an enumerator belonging to an enumeration with external linkage; or
} else if (isa<EnumConstantDecl>(D)) {
LinkageInfo EnumLV = getLVForDecl(cast<NamedDecl>(D->getDeclContext()),
computation);
@@ -844,16 +865,16 @@ LinkageComputer::getLVForNamespaceScopeD
return LinkageInfo::none();
LV.merge(EnumLV);
- // - a template, unless it is a function template that has
- // internal linkage (Clause 14);
+ // - a template
} else if (const auto *temp = dyn_cast<TemplateDecl>(D)) {
bool considerVisibility = !hasExplicitVisibilityAlready(computation);
LinkageInfo tempLV =
getLVForTemplateParameterList(temp->getTemplateParameters(), computation);
LV.mergeMaybeWithVisibility(tempLV, considerVisibility);
- // - a namespace (7.3), unless it is declared within an unnamed
- // namespace.
+ // An unnamed namespace or a namespace declared directly or indirectly
+ // within an unnamed namespace has internal linkage. All other namespaces
+ // have external linkage.
//
// We handled names in anonymous namespaces above.
} else if (isa<NamespaceDecl>(D)) {
Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Tue Apr 23 16:48:00 2019
@@ -8619,6 +8619,29 @@ static bool CheckExplicitInstantiationSc
return false;
}
+/// Common checks for whether an explicit instantiation of \p D is valid.
+static bool CheckExplicitInstantiation(Sema &S, NamedDecl *D,
+ SourceLocation InstLoc,
+ bool WasQualifiedName,
+ TemplateSpecializationKind TSK) {
+ // C++ [temp.explicit]p13:
+ // An explicit instantiation declaration shall not name a specialization of
+ // a template with internal linkage.
+ if (TSK == TSK_ExplicitInstantiationDeclaration &&
+ D->getFormalLinkage() == InternalLinkage) {
+ S.Diag(InstLoc, diag::err_explicit_instantiation_internal_linkage) << D;
+ return true;
+ }
+
+ // C++11 [temp.explicit]p3: [DR 275]
+ // An explicit instantiation shall appear in an enclosing namespace of its
+ // template.
+ if (CheckExplicitInstantiationScope(S, D, InstLoc, WasQualifiedName))
+ return true;
+
+ return false;
+}
+
/// Determine whether the given scope specifier has a template-id in it.
static bool ScopeSpecifierHasTemplateId(const CXXScopeSpec &SS) {
if (!SS.isSet())
@@ -8770,13 +8793,8 @@ DeclResult Sema::ActOnExplicitInstantiat
TemplateSpecializationKind PrevDecl_TSK
= PrevDecl ? PrevDecl->getTemplateSpecializationKind() : TSK_Undeclared;
- // C++0x [temp.explicit]p2:
- // [...] An explicit instantiation shall appear in an enclosing
- // namespace of its template. [...]
- //
- // This is C++ DR 275.
- if (CheckExplicitInstantiationScope(*this, ClassTemplate, TemplateNameLoc,
- SS.isSet()))
+ if (CheckExplicitInstantiation(*this, ClassTemplate, TemplateNameLoc,
+ SS.isSet(), TSK))
return true;
ClassTemplateSpecializationDecl *Specialization = nullptr;
@@ -8999,12 +9017,7 @@ Sema::ActOnExplicitInstantiation(Scope *
= ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
: TSK_ExplicitInstantiationDeclaration;
- // C++0x [temp.explicit]p2:
- // [...] An explicit instantiation shall appear in an enclosing
- // namespace of its template. [...]
- //
- // This is C++ DR 275.
- CheckExplicitInstantiationScope(*this, Record, NameLoc, true);
+ CheckExplicitInstantiation(*this, Record, NameLoc, true, TSK);
// Verify that it is okay to explicitly instantiate here.
CXXRecordDecl *PrevDecl
@@ -9235,8 +9248,7 @@ DeclResult Sema::ActOnExplicitInstantiat
diag::ext_explicit_instantiation_without_qualified_id)
<< Prev << D.getCXXScopeSpec().getRange();
- // Check the scope of this explicit instantiation.
- CheckExplicitInstantiationScope(*this, Prev, D.getIdentifierLoc(), true);
+ CheckExplicitInstantiation(*this, Prev, D.getIdentifierLoc(), true, TSK);
// Verify that it is okay to explicitly instantiate here.
TemplateSpecializationKind PrevTSK = Prev->getTemplateSpecializationKind();
@@ -9444,11 +9456,11 @@ DeclResult Sema::ActOnExplicitInstantiat
diag::ext_explicit_instantiation_without_qualified_id)
<< Specialization << D.getCXXScopeSpec().getRange();
- CheckExplicitInstantiationScope(*this,
- FunTmpl? (NamedDecl *)FunTmpl
- : Specialization->getInstantiatedFromMemberFunction(),
- D.getIdentifierLoc(),
- D.getCXXScopeSpec().isSet());
+ CheckExplicitInstantiation(
+ *this,
+ FunTmpl ? (NamedDecl *)FunTmpl
+ : Specialization->getInstantiatedFromMemberFunction(),
+ D.getIdentifierLoc(), D.getCXXScopeSpec().isSet(), TSK);
// FIXME: Create some kind of ExplicitInstantiationDecl here.
return (Decl*) nullptr;
Modified: cfe/trunk/test/CXX/drs/dr0xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr0xx.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr0xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr0xx.cpp Tue Apr 23 16:48:00 2019
@@ -869,18 +869,17 @@ namespace dr68 { // dr68: yes
}
namespace dr69 { // dr69: yes
- template<typename T> static void f() {}
+ template<typename T> static void f() {} // #dr69-f
// FIXME: Should we warn here?
inline void g() { f<int>(); }
- // FIXME: This should be rejected, per [temp.explicit]p11.
- extern template void f<char>();
+ extern template void f<char>(); // expected-error {{explicit instantiation declaration of 'f' with internal linkage}}
#if __cplusplus < 201103L
// expected-error at -2 {{C++11 extension}}
#endif
template<void(*)()> struct Q {};
Q<&f<int> > q;
#if __cplusplus < 201103L
- // expected-error at -2 {{internal linkage}} expected-note at -11 {{here}}
+ // expected-error at -2 {{internal linkage}} expected-note@#dr69-f {{here}}
#endif
}
Modified: cfe/trunk/test/CXX/drs/dr17xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr17xx.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr17xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr17xx.cpp Tue Apr 23 16:48:00 2019
@@ -77,7 +77,7 @@ namespace dr1758 { // dr1758: 3.7
#endif
}
-namespace dr1722 { // dr1722: 9.0
+namespace dr1722 { // dr1722: 9
#if __cplusplus >= 201103L
void f() {
const auto lambda = [](int x) { return x + 1; };
Added: cfe/trunk/test/CXX/drs/dr23xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr23xx.cpp?rev=359048&view=auto
==============================================================================
--- cfe/trunk/test/CXX/drs/dr23xx.cpp (added)
+++ cfe/trunk/test/CXX/drs/dr23xx.cpp Tue Apr 23 16:48:00 2019
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+
+#if __cplusplus <= 201103L
+// expected-no-diagnostics
+#endif
+
+namespace dr2387 { // dr2387: 9
+#if __cplusplus >= 201402L
+ template<int> int a = 0;
+ extern template int a<0>; // ok
+
+ template<int> static int b = 0;
+ extern template int b<0>; // expected-error {{internal linkage}}
+
+ template<int> const int c = 0;
+ extern template const int c<0>; // ok, has external linkage despite 'const'
+
+ template<typename T> T d = 0;
+ extern template int d<int>;
+ extern template const int d<const int>;
+#endif
+}
Modified: cfe/trunk/test/CXX/module/module.interface/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/module/module.interface/p3.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/test/CXX/module/module.interface/p3.cpp (original)
+++ cfe/trunk/test/CXX/module/module.interface/p3.cpp Tue Apr 23 16:48:00 2019
@@ -48,7 +48,7 @@ export namespace { int c; } // expected-
namespace { // expected-note {{here}}
export int d; // expected-error {{export declaration appears within anonymous namespace}}
}
-export template<typename> static int e; // FIXME
+export template<typename> static int e; // expected-error {{declaration of 'e' with internal linkage cannot be exported}}
export template<typename> static int f(); // expected-error {{declaration of 'f' with internal linkage cannot be exported}}
export const int k = 5;
export static union { int n; }; // expected-error {{declaration of 'n' with internal linkage cannot be exported}}
Modified: cfe/trunk/test/CXX/module/module.interface/p5.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/module/module.interface/p5.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/test/CXX/module/module.interface/p5.cpp (original)
+++ cfe/trunk/test/CXX/module/module.interface/p5.cpp Tue Apr 23 16:48:00 2019
@@ -14,7 +14,7 @@ static union { int sg1, sg2; }; // expec
namespace NS {}
template<typename> int ta;
-template<typename> static int sta;
+template<typename> static int sta; // expected-note {{target}}
template<typename> void tb();
template<typename> static void stb(); // expected-note {{target}}
template<typename> struct tc {};
@@ -44,7 +44,7 @@ namespace UnnamedNS {
}
}
-export { // expected-note 18{{here}}
+export { // expected-note 19{{here}}
using ::a;
using ::sa; // expected-error {{using declaration referring to 'sa' with internal linkage}}
using ::b;
@@ -56,7 +56,7 @@ export { // expected-note 18{{here}}
using ::sg1; // expected-error {{using declaration referring to 'sg1' with internal linkage}}
using ::ta;
- using ::sta; // FIXME {{using declaration referring to 'sta' with internal linkage}}
+ using ::sta; // expected-error {{using declaration referring to 'sta' with internal linkage}}
using ::tb;
using ::stb; // expected-error {{using declaration referring to 'stb' with internal linkage}}
using ::tc;
Modified: cfe/trunk/test/CodeGenCXX/cxx1y-variable-template-linkage.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx1y-variable-template-linkage.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/cxx1y-variable-template-linkage.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/cxx1y-variable-template-linkage.cpp Tue Apr 23 16:48:00 2019
@@ -6,21 +6,61 @@
// should be 'internal global' and not 'linkonce_odr global'.
template <typename T> int x = 42;
-
+// CHECK-DAG: @_Z1xIiE = linkonce_odr global
// CHECK-DAG: @_Z1xIZL3foovE3FooE = internal global
+// 'static' affects the linkage of the global
+template <typename T> static int y = 42;
+// CHECK-DAG: @_ZL1yIiE = internal global
+// CHECK-DAG: @_ZL1yIZL3foovE3FooE = internal global
+
+// 'const' does not
+template <typename T> const int z = 42;
+// CHECK-DAG: @_Z1zIiE = linkonce_odr constant
+// CHECK-DAG: @_Z1zIZL3foovE3FooE = internal constant
+
+template <typename T> T t = 42;
+// CHECK-DAG: @_Z1tIiE = linkonce_odr global
+// CHECK-DAG: @_Z1tIKiE = linkonce_odr constant
+
+int mode;
+
// CHECK-DAG: define internal dereferenceable(4) i32* @_ZL3foov(
-static int &foo() {
+static const int &foo() {
struct Foo { };
-
- // CHECK-DAG: ret i32* @_Z1xIZL3foovE3FooE
- return x<Foo>;
+
+ switch (mode) {
+ case 0:
+ // CHECK-DAG: @_Z1xIiE
+ return x<int>;
+ case 1:
+ // CHECK-DAG: @_Z1xIZL3foovE3FooE
+ return x<Foo>;
+ case 2:
+ // CHECK-DAG: @_ZL1yIiE
+ return y<int>;
+ case 3:
+ // CHECK-DAG: @_ZL1yIZL3foovE3FooE
+ return y<Foo>;
+ case 4:
+ // CHECK-DAG: @_Z1zIiE
+ return z<int>;
+ case 5:
+ // CHECK-DAG: @_Z1zIZL3foovE3FooE
+ return z<Foo>;
+ case 6:
+ // CHECK-DAG: @_Z1tIiE
+ return t<int>;
+ case 7:
+ // CHECK-DAG: @_Z1tIKiE
+ return t<const int>;
+ }
}
#if !__has_feature(cxx_exceptions) // File A
// CHECKA-DAG: define dereferenceable(4) i32* @_Z3barv(
-int &bar() {
+const int &bar() {
// CHECKA-DAG: call dereferenceable(4) i32* @_ZL3foov()
return foo();
}
@@ -28,7 +68,7 @@ int &bar() {
#else // File B
// CHECKB-DAG: declare dereferenceable(4) i32* @_Z3barv(
-int &bar();
+const int &bar();
int main() {
// CHECKB-DAG: call dereferenceable(4) i32* @_Z3barv()
Modified: cfe/trunk/test/SemaCXX/PR10177.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/PR10177.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/PR10177.cpp (original)
+++ cfe/trunk/test/SemaCXX/PR10177.cpp Tue Apr 23 16:48:00 2019
@@ -57,11 +57,10 @@ namespace N {
}
#else
-// expected-no-diagnostics
namespace { template<typename> extern int n; }
template<typename T> int g() { return n<int>; }
-namespace { extern template int n<int>; }
+namespace { extern template int n<int>; } // expected-error {{explicit instantiation declaration of 'n<int>' with internal linkage}}
#endif
Modified: cfe/trunk/test/SemaCXX/warn-unused-filescoped.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-unused-filescoped.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-unused-filescoped.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-unused-filescoped.cpp Tue Apr 23 16:48:00 2019
@@ -207,8 +207,9 @@ static void completeRedeclChainForTempla
namespace test10 {
#if __cplusplus >= 201103L
+// FIXME: Warn on template definitions with no instantiations?
template<class T>
-constexpr T pi = T(3.14); // expected-warning {{unused}}
+constexpr T pi = T(3.14);
#endif
}
Modified: cfe/trunk/test/SemaCXX/warn-unused-variables.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-unused-variables.cpp?rev=359048&r1=359047&r2=359048&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-unused-variables.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-unused-variables.cpp Tue Apr 23 16:48:00 2019
@@ -135,7 +135,9 @@ namespace PR19305 {
template<typename T> int m = 0;
template<typename T> int m<T*> = 0;
- template<> const int m<void> = 0; // expected-warning {{unused variable}}
+ // This has external linkage, so could be referenced by a declaration in a
+ // different translation unit.
+ template<> const int m<void> = 0; // no warning
}
namespace ctor_with_cleanups {
More information about the cfe-commits
mailing list