Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

Commit

Permalink
C++ DR2387: a variable template declared wtih (or instantiated with) a
Browse files Browse the repository at this point in the history
const-qualified type is not implicitly given internal linkage. But a
variable template declared 'static' is.

This reinstates part of r359048, reverted in r359076.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@359260 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
zygoloid committed Apr 26, 2019
1 parent 6035bca commit 4241f67
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 43 deletions.
81 changes: 51 additions & 30 deletions lib/AST/Decl.cpp
Expand Up @@ -610,6 +610,18 @@ static LinkageInfo getExternalLinkageFor(const NamedDecl *D) {
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,
Expand All @@ -621,24 +633,28 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
// 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);
Expand All @@ -658,14 +674,6 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
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();
Expand All @@ -674,6 +682,8 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
}
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);
Expand Down Expand Up @@ -733,10 +743,20 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,

// 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:
Expand Down Expand Up @@ -782,7 +802,7 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
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
Expand Down Expand Up @@ -836,24 +856,25 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
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);
if (!isExternalFormalLinkage(EnumLV.getLinkage()))
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)) {
Expand Down
26 changes: 26 additions & 0 deletions test/CXX/drs/dr23xx.cpp
@@ -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
}
2 changes: 1 addition & 1 deletion test/CXX/module/module.interface/p3.cpp
Expand Up @@ -48,7 +48,7 @@ export namespace { int c; } // expected-error {{declaration of 'c' with internal
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}}
6 changes: 3 additions & 3 deletions test/CXX/module/module.interface/p5.cpp
Expand Up @@ -14,7 +14,7 @@ static union { int sg1, sg2; }; // expected-note {{target}}
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 {};
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
54 changes: 47 additions & 7 deletions test/CodeGenCXX/cxx1y-variable-template-linkage.cpp
Expand Up @@ -6,29 +6,69 @@
// 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();
}

#else // File B

// CHECKB-DAG: declare dereferenceable(4) i32* @_Z3barv(
int &bar();
const int &bar();

int main() {
// CHECKB-DAG: call dereferenceable(4) i32* @_Z3barv()
Expand Down
3 changes: 2 additions & 1 deletion test/SemaCXX/warn-unused-filescoped.cpp
Expand Up @@ -207,8 +207,9 @@ static void completeRedeclChainForTemplateSpecialization() { } // expected-warni

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
}

Expand Down
4 changes: 3 additions & 1 deletion test/SemaCXX/warn-unused-variables.cpp
Expand Up @@ -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 {
Expand Down

0 comments on commit 4241f67

Please sign in to comment.