Author: rsmith Date: Thu Apr 25 18:51:08 2019 New Revision: 359260 URL: http://llvm.org/viewvc/llvm-project?rev=359260&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.
This reinstates part of r359048, reverted in r359076. Added: cfe/trunk/test/CXX/drs/dr23xx.cpp Modified: cfe/trunk/lib/AST/Decl.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/warn-unused-filescoped.cpp cfe/trunk/test/SemaCXX/warn-unused-variables.cpp Modified: cfe/trunk/lib/AST/Decl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=359260&r1=359259&r2=359260&view=diff ============================================================================== --- cfe/trunk/lib/AST/Decl.cpp (original) +++ cfe/trunk/lib/AST/Decl.cpp Thu Apr 25 18:51:08 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)) { Added: cfe/trunk/test/CXX/drs/dr23xx.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr23xx.cpp?rev=359260&view=auto ============================================================================== --- cfe/trunk/test/CXX/drs/dr23xx.cpp (added) +++ cfe/trunk/test/CXX/drs/dr23xx.cpp Thu Apr 25 18:51:08 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=359260&r1=359259&r2=359260&view=diff ============================================================================== --- cfe/trunk/test/CXX/module/module.interface/p3.cpp (original) +++ cfe/trunk/test/CXX/module/module.interface/p3.cpp Thu Apr 25 18:51:08 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=359260&r1=359259&r2=359260&view=diff ============================================================================== --- cfe/trunk/test/CXX/module/module.interface/p5.cpp (original) +++ cfe/trunk/test/CXX/module/module.interface/p5.cpp Thu Apr 25 18:51:08 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=359260&r1=359259&r2=359260&view=diff ============================================================================== --- cfe/trunk/test/CodeGenCXX/cxx1y-variable-template-linkage.cpp (original) +++ cfe/trunk/test/CodeGenCXX/cxx1y-variable-template-linkage.cpp Thu Apr 25 18:51:08 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/warn-unused-filescoped.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-unused-filescoped.cpp?rev=359260&r1=359259&r2=359260&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/warn-unused-filescoped.cpp (original) +++ cfe/trunk/test/SemaCXX/warn-unused-filescoped.cpp Thu Apr 25 18:51:08 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=359260&r1=359259&r2=359260&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/warn-unused-variables.cpp (original) +++ cfe/trunk/test/SemaCXX/warn-unused-variables.cpp Thu Apr 25 18:51:08 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 { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits