Implement the rules in C++ [basic.link] and C99 6.2.2 for computing

the linkage of a declaration. Switch the lame (and completely wrong)
NamedDecl::hasLinkage() over to using the new NamedDecl::getLinkage(),
along with the "can this declaration be a template argument?" check
that started all of this.

Fixes -fsyntax-only for PR5597.

llvm-svn: 89891
This commit is contained in:
Douglas Gregor 2009-11-25 22:24:25 +00:00
parent e6929ffc21
commit f73b282bf0
6 changed files with 240 additions and 10 deletions

View File

@ -196,6 +196,26 @@ public:
/// \brief Determine whether this declaration has linkage.
bool hasLinkage() const;
/// \brief Describes the different kinds of linkage
/// (C++ [basic.link], C99 6.2.2) that an entity may have.
enum Linkage {
/// \brief No linkage, which means that the entity is unique and
/// can only be referred to from within its scope.
NoLinkage = 0,
/// \brief Internal linkage, which indicates that the entity can
/// be referred to from within the translation unit (but not other
/// translation units).
InternalLinkage,
/// \brief External linkage, which indicates that the entity can
/// be referred to from other translation units.
ExternalLinkage
};
/// \brief Determine what kind of linkage this entity has.
Linkage getLinkage() const;
/// \brief Looks through UsingDecls and ObjCCompatibleAliasDecls for
/// the underlying named decl.
NamedDecl *getUnderlyingDecl();

View File

@ -211,6 +211,203 @@ FileScopeAsmDecl *FileScopeAsmDecl::Create(ASTContext &C, DeclContext *DC,
// NamedDecl Implementation
//===----------------------------------------------------------------------===//
static NamedDecl::Linkage getLinkageForNamespaceScopeDecl(const NamedDecl *D) {
assert(D->getDeclContext()->getLookupContext()->isFileContext() &&
"Not a name having namespace scope");
ASTContext &Context = D->getASTContext();
// 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 (const VarDecl *Var = dyn_cast<VarDecl>(D)) {
// Explicitly declared static.
if (Var->getStorageClass() == VarDecl::Static)
return NamedDecl::InternalLinkage;
// - an object or reference that is explicitly declared const
// and neither explicitly declared extern nor previously
// declared to have external linkage; or
// (there is no equivalent in C99)
if (Context.getLangOptions().CPlusPlus &&
Var->getType().isConstQualified() &&
Var->getStorageClass() != VarDecl::Extern &&
Var->getStorageClass() != VarDecl::PrivateExtern) {
bool FoundExtern = false;
for (const VarDecl *PrevVar = Var->getPreviousDeclaration();
PrevVar && !FoundExtern;
PrevVar = PrevVar->getPreviousDeclaration())
if (PrevVar->getLinkage() == NamedDecl::ExternalLinkage)
FoundExtern = true;
if (!FoundExtern)
return NamedDecl::InternalLinkage;
}
} else if (isa<FunctionDecl>(D) || isa<FunctionTemplateDecl>(D)) {
const FunctionDecl *Function = 0;
if (const FunctionTemplateDecl *FunTmpl
= dyn_cast<FunctionTemplateDecl>(D))
Function = FunTmpl->getTemplatedDecl();
else
Function = cast<FunctionDecl>(D);
// Explicitly declared static.
if (Function->getStorageClass() == FunctionDecl::Static)
return NamedDecl::InternalLinkage;
} else if (const FieldDecl *Field = dyn_cast<FieldDecl>(D)) {
// - a data member of an anonymous union.
if (cast<RecordDecl>(Field->getDeclContext())->isAnonymousStructOrUnion())
return NamedDecl::InternalLinkage;
}
// C++ [basic.link]p4:
// A name having namespace scope has external linkage if it is the
// name of
//
// - an object or reference, unless it has internal linkage; or
if (const VarDecl *Var = dyn_cast<VarDecl>(D)) {
if (!Context.getLangOptions().CPlusPlus &&
(Var->getStorageClass() == VarDecl::Extern ||
Var->getStorageClass() == VarDecl::PrivateExtern)) {
// C99 6.2.2p4:
// For an identifier declared with the storage-class specifier
// extern in a scope in which a prior declaration of that
// identifier is visible, if the prior declaration specifies
// internal or external linkage, the linkage of the identifier
// at the later declaration is the same as the linkage
// specified at the prior declaration. If no prior declaration
// is visible, or if the prior declaration specifies no
// linkage, then the identifier has external linkage.
if (const VarDecl *PrevVar = Var->getPreviousDeclaration()) {
if (NamedDecl::Linkage L = PrevVar->getLinkage())
return L;
}
}
// C99 6.2.2p5:
// If the declaration of an identifier for an object has file
// scope and no storage-class specifier, its linkage is
// external.
return NamedDecl::ExternalLinkage;
}
// - a function, unless it has internal linkage; or
if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
// C99 6.2.2p5:
// If the declaration of an identifier for a function has no
// storage-class specifier, its linkage is determined exactly
// as if it were declared with the storage-class specifier
// extern.
if (!Context.getLangOptions().CPlusPlus &&
(Function->getStorageClass() == FunctionDecl::Extern ||
Function->getStorageClass() == FunctionDecl::PrivateExtern ||
Function->getStorageClass() == FunctionDecl::None)) {
// C99 6.2.2p4:
// For an identifier declared with the storage-class specifier
// extern in a scope in which a prior declaration of that
// identifier is visible, if the prior declaration specifies
// internal or external linkage, the linkage of the identifier
// at the later declaration is the same as the linkage
// specified at the prior declaration. If no prior declaration
// is visible, or if the prior declaration specifies no
// linkage, then the identifier has external linkage.
if (const FunctionDecl *PrevFunc = Function->getPreviousDeclaration()) {
if (NamedDecl::Linkage L = PrevFunc->getLinkage())
return L;
}
}
return NamedDecl::ExternalLinkage;
}
// - a named class (Clause 9), or an unnamed class defined in a
// typedef declaration in which the class has the typedef name
// for linkage purposes (7.1.3); or
// - a named enumeration (7.2), or an unnamed enumeration
// defined in a typedef declaration in which the enumeration
// has the typedef name for linkage purposes (7.1.3); or
if (const TagDecl *Tag = dyn_cast<TagDecl>(D))
if (Tag->getDeclName() || Tag->getTypedefForAnonDecl())
return NamedDecl::ExternalLinkage;
// - an enumerator belonging to an enumeration with external linkage;
if (isa<EnumConstantDecl>(D))
if (cast<NamedDecl>(D->getDeclContext())->getLinkage()
== NamedDecl::ExternalLinkage)
return NamedDecl::ExternalLinkage;
// - a template, unless it is a function template that has
// internal linkage (Clause 14);
if (isa<TemplateDecl>(D))
return NamedDecl::ExternalLinkage;
// - a namespace (7.3), unless it is declared within an unnamed
// namespace.
if (isa<NamespaceDecl>(D) && !D->isInAnonymousNamespace())
return NamedDecl::ExternalLinkage;
return NamedDecl::NoLinkage;
}
NamedDecl::Linkage NamedDecl::getLinkage() const {
// Handle linkage for namespace-scope names.
if (getDeclContext()->getLookupContext()->isFileContext())
if (Linkage L = getLinkageForNamespaceScopeDecl(this))
return L;
// C++ [basic.link]p5:
// In addition, a member function, static data member, a named
// class or enumeration of class scope, or an unnamed class or
// enumeration defined in a class-scope typedef declaration such
// that the class or enumeration has the typedef name for linkage
// purposes (7.1.3), has external linkage if the name of the class
// has external linkage.
if (getDeclContext()->isRecord() &&
(isa<CXXMethodDecl>(this) || isa<VarDecl>(this) ||
(isa<TagDecl>(this) &&
(getDeclName() || cast<TagDecl>(this)->getTypedefForAnonDecl()))) &&
cast<RecordDecl>(getDeclContext())->getLinkage() == ExternalLinkage)
return ExternalLinkage;
// C++ [basic.link]p6:
// The name of a function declared in block scope and the name of
// an object declared by a block scope extern declaration have
// linkage. If there is a visible declaration of an entity with
// linkage having the same name and type, ignoring entities
// declared outside the innermost enclosing namespace scope, the
// block scope declaration declares that same entity and receives
// the linkage of the previous declaration. If there is more than
// one such matching entity, the program is ill-formed. Otherwise,
// if no matching entity is found, the block scope entity receives
// external linkage.
if (getLexicalDeclContext()->isFunctionOrMethod()) {
if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(this)) {
if (Function->getPreviousDeclaration())
if (Linkage L = Function->getPreviousDeclaration()->getLinkage())
return L;
return ExternalLinkage;
}
if (const VarDecl *Var = dyn_cast<VarDecl>(this))
if (Var->getStorageClass() == VarDecl::Extern ||
Var->getStorageClass() == VarDecl::PrivateExtern) {
if (Var->getPreviousDeclaration())
if (Linkage L = Var->getPreviousDeclaration()->getLinkage())
return L;
return ExternalLinkage;
}
}
// C++ [basic.link]p6:
// Names not covered by these rules have no linkage.
return NoLinkage;
}
std::string NamedDecl::getQualifiedNameAsString() const {
return getQualifiedNameAsString(getASTContext().getLangOptions());
}
@ -300,13 +497,7 @@ bool NamedDecl::declarationReplaces(NamedDecl *OldD) const {
}
bool NamedDecl::hasLinkage() const {
if (const VarDecl *VD = dyn_cast<VarDecl>(this))
return VD->hasExternalStorage() || VD->isFileVarDecl();
if (isa<FunctionDecl>(this) && !isa<CXXMethodDecl>(this))
return true;
return false;
return getLinkage() != NoLinkage;
}
NamedDecl *NamedDecl::getUnderlyingDecl() {

View File

@ -2365,7 +2365,7 @@ bool Sema::CheckTemplateArgumentAddressOfObjectOrFunction(Expr *Arg,
// Functions must have external linkage.
if (FunctionDecl *Func = dyn_cast<FunctionDecl>(DRE->getDecl())) {
if (Func->getStorageClass() == FunctionDecl::Static) {
if (Func->getLinkage() != NamedDecl::ExternalLinkage) {
Diag(Arg->getSourceRange().getBegin(),
diag::err_template_arg_function_not_extern)
<< Func << Arg->getSourceRange();
@ -2380,7 +2380,7 @@ bool Sema::CheckTemplateArgumentAddressOfObjectOrFunction(Expr *Arg,
}
if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
if (!Var->hasGlobalStorage()) {
if (Var->getLinkage() != NamedDecl::ExternalLinkage) {
Diag(Arg->getSourceRange().getBegin(),
diag::err_template_arg_object_not_extern)
<< Var << Arg->getSourceRange();

View File

@ -0,0 +1,11 @@
// RUN: clang-cc -fsyntax-only -verify %s
// FIXME: This test is woefully incomplete.
namespace N { } // expected-note{{here}}
// First bullet: two names with external linkage that refer to
// different kinds of entities.
void f() {
int N(); // expected-error{{redefinition}}
}

View File

@ -87,7 +87,6 @@ void f3() {
N::x = 0; // expected-error {{expected a class or namespace}}
{ int A; A::ax = 0; }
{ typedef int A; A::ax = 0; } // expected-error{{expected a class or namespace}}
{ int A(); A::ax = 0; }
{ typedef A::C A; A::ax = 0; } // expected-error {{no member named 'ax'}}
{ typedef A::C A; A::cx = 0; }
}

View File

@ -151,3 +151,12 @@ namespace ns {
Baz<static_cast<ns::E>(0)> b2; // This neither.
}
// PR5597
template<int (*)(float)> struct X0 { };
struct X1 {
static int pfunc(float);
};
void test_X0_X1() {
X0<X1::pfunc> x01;
}