[cfe-commits] [PATCH] Unify isExternC and hasCLanguageLinkage.
Rafael Ávila de Espíndola
rafael.espindola at gmail.com
Fri Jan 18 08:00:44 PST 2013
This patch defines a new getLanguageLinkage that determines if a
decl has C, C++ or no language linkage. It then uses this
function to unify isExternC and hasCLanguageLinkage, keeping the
original isExternC name.
This makes us accept
extern "C" {
static void test2_f() {
}
static void test2_f(int x) {
}
}
again, which I think is correct since test2_f has no language linkage.
One case I was not sure about was
static int y;
extern "C" {
extern int y;
}
If we first decide that y has internal linkage, then it has no language
linkage and this should be fine. For now I left the code rejecting it.
With this patch we now also reject
extern "C" {
void test7_f();
}
extern "C++" {
void test7_f();
}
---
include/clang/AST/Decl.h | 20 +++++------
include/clang/AST/DeclBase.h | 1 +
include/clang/Basic/Linkage.h | 6 ++++
lib/AST/Decl.cpp | 81 +++++++++++++++++--------------------------
lib/AST/DeclBase.cpp | 11 ++++++
lib/Sema/SemaDecl.cpp | 21 ++++++++---
lib/Sema/SemaOverload.cpp | 2 +-
test/SemaCXX/linkage2.cpp | 11 ++++--
8 files changed, 85 insertions(+), 68 deletions(-)
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 3cca66f..fdfdf78 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -896,13 +896,11 @@ public:
/// \brief Determines whether this variable is a variable with
/// external, C linkage.
- bool isExternC() const;
+ bool isExternC() const {
+ return getLanguageLinkage() == CLanguageLinkage;
+ }
- /// Checks if this variable has C language linkage. Note that this is not the
- /// same as isExternC since decls with non external linkage can have C
- /// language linkage. They can also have C language linkage when they are not
- /// declared in an extern C context, but a previous decl is.
- bool hasCLanguageLinkage() const;
+ LanguageLinkage getLanguageLinkage() const;
/// isLocalVarDecl - Returns true for local variable declarations
/// other than parameters. Note that this includes static variables
@@ -1788,13 +1786,11 @@ public:
/// \brief Determines whether this function is a function with
/// external, C linkage.
- bool isExternC() const;
+ bool isExternC() const {
+ return getLanguageLinkage() == CLanguageLinkage;
+ }
- /// Checks if this function has C language linkage. Note that this is not the
- /// same as isExternC since decls with non external linkage can have C
- /// language linkage. They can also have C language linkage when they are not
- /// declared in an extern C context, but a previous decl is.
- bool hasCLanguageLinkage() const;
+ LanguageLinkage getLanguageLinkage() const;
/// \brief Determines whether this is a global function.
bool isGlobal() const;
diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h
index bf588d7..8a3c73a 100644
--- a/include/clang/AST/DeclBase.h
+++ b/include/clang/AST/DeclBase.h
@@ -1076,6 +1076,7 @@ public:
/// \brief Determines whether this context is, or is nested within,
/// a C++ extern "C" linkage spec.
bool isExternCContext() const;
+ bool isExternCXXContext() const;
/// \brief Determine whether this declaration context is equivalent
/// to the declaration context DC.
diff --git a/include/clang/Basic/Linkage.h b/include/clang/Basic/Linkage.h
index 6bc1f5d..b09ccc9 100644
--- a/include/clang/Basic/Linkage.h
+++ b/include/clang/Basic/Linkage.h
@@ -42,6 +42,12 @@ enum Linkage {
ExternalLinkage
};
+enum LanguageLinkage {
+ NoLanguageLinkage,
+ CLanguageLinkage,
+ CXXLanguageLinkage
+};
+
/// \brief A more specific kind of linkage than enum Linkage.
///
/// This is relevant to CodeGen and AST file reading.
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index c053b6d..0145076 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -196,6 +196,19 @@ static bool useInlineVisibilityHidden(const NamedDecl *D) {
FD->hasBody(Def) && Def->isInlined() && !Def->hasAttr<GNUInlineAttr>();
}
+static bool isInExternCContext(const NamedDecl *D) {
+ const NamedDecl *First = NULL;
+ if (const VarDecl *Var = dyn_cast<VarDecl>(D))
+ First = Var->getFirstDeclaration();
+ else if (const FunctionDecl *Func = dyn_cast<FunctionDecl>(D))
+ First = Func->getFirstDeclaration();
+
+ if (!First)
+ return false;
+
+ return First->getDeclContext()->isExternCContext();
+}
+
static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
bool OnlyTemplate) {
assert(D->getDeclContext()->getRedeclContext()->isFileContext() &&
@@ -259,13 +272,8 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
return LinkageInfo::internal();
}
- if (D->isInAnonymousNamespace()) {
- const VarDecl *Var = dyn_cast<VarDecl>(D);
- const FunctionDecl *Func = dyn_cast<FunctionDecl>(D);
- if ((!Var || !Var->hasCLanguageLinkage()) &&
- (!Func || !Func->hasCLanguageLinkage()))
- return LinkageInfo::uniqueExternal();
- }
+ if (D->isInAnonymousNamespace() && !isInExternCContext(D))
+ return LinkageInfo::uniqueExternal();
// Set up the defaults.
@@ -1213,43 +1221,36 @@ SourceRange VarDecl::getSourceRange() const {
}
template<typename T>
-static bool hasCLanguageLinkageTemplate(const T &D) {
+static LanguageLinkage getLanguageLinkageTemplate(const T &D) {
+ // All function types, function names with external linkage, and variable
+ // names with external linkage have a language linkage.
+ if (D.getLinkage() != ExternalLinkage)
+ return NoLanguageLinkage;
+
// Language linkage is a C++ concept, but saying that everything in C has
// C language linkage fits the implementation nicely.
ASTContext &Context = D.getASTContext();
if (!Context.getLangOpts().CPlusPlus)
- return true;
+ return CLanguageLinkage;
// dcl.link 4: A C language linkage is ignored in determining the language
// linkage of the names of class members and the function type of class member
// functions.
const DeclContext *DC = D.getDeclContext();
if (DC->isRecord())
- return false;
+ return CXXLanguageLinkage;
// If the first decl is in an extern "C" context, any other redeclaration
// will have C language linkage. If the first one is not in an extern "C"
// context, we would have reported an error for any other decl being in one.
const T *First = D.getFirstDeclaration();
- return First->getDeclContext()->isExternCContext();
-}
-
-bool VarDecl::hasCLanguageLinkage() const {
- return hasCLanguageLinkageTemplate(*this);
+ if (First->getDeclContext()->isExternCContext())
+ return CLanguageLinkage;
+ return CXXLanguageLinkage;
}
-bool VarDecl::isExternC() const {
- if (getLinkage() != ExternalLinkage)
- return false;
-
- const DeclContext *DC = getDeclContext();
- if (DC->isRecord())
- return false;
-
- ASTContext &Context = getASTContext();
- if (!Context.getLangOpts().CPlusPlus)
- return true;
- return DC->isExternCContext();
+LanguageLinkage VarDecl::getLanguageLinkage() const {
+ return getLanguageLinkageTemplate(*this);
}
VarDecl *VarDecl::getCanonicalDecl() {
@@ -1760,32 +1761,14 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const {
return (proto->getArgType(1).getCanonicalType() == Context.VoidPtrTy);
}
-bool FunctionDecl::hasCLanguageLinkage() const {
+LanguageLinkage FunctionDecl::getLanguageLinkage() const {
// Users expect to be able to write
// extern "C" void *__builtin_alloca (size_t);
// so consider builtins as having C language linkage.
if (getBuiltinID())
- return true;
-
- return hasCLanguageLinkageTemplate(*this);
-}
-
-bool FunctionDecl::isExternC() const {
- if (getLinkage() != ExternalLinkage)
- return false;
-
- if (getAttr<OverloadableAttr>())
- return false;
-
- const DeclContext *DC = getDeclContext();
- if (DC->isRecord())
- return false;
-
- ASTContext &Context = getASTContext();
- if (!Context.getLangOpts().CPlusPlus)
- return true;
+ return CLanguageLinkage;
- return isMain() || DC->isExternCContext();
+ return getLanguageLinkageTemplate(*this);
}
bool FunctionDecl::isGlobal() const {
@@ -2483,7 +2466,7 @@ unsigned FunctionDecl::getMemoryFunctionKind() const {
return Builtin::BIstrlen;
default:
- if (hasCLanguageLinkage()) {
+ if (isExternC()) {
if (FnInfo->isStr("memset"))
return Builtin::BImemset;
else if (FnInfo->isStr("memcpy"))
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index 27a91cb..261c1cb 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -802,6 +802,17 @@ bool DeclContext::isExternCContext() const {
return false;
}
+bool DeclContext::isExternCXXContext() const {
+ const DeclContext *DC = this;
+ while (DC->DeclKind != Decl::TranslationUnit) {
+ if (DC->DeclKind == Decl::LinkageSpec)
+ return cast<LinkageSpecDecl>(DC)->getLanguage()
+ == LinkageSpecDecl::lang_cxx;
+ DC = DC->getParent();
+ }
+ return false;
+}
+
bool DeclContext::Encloses(const DeclContext *DC) const {
if (getPrimaryContext() != this)
return getPrimaryContext()->Encloses(DC);
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 9cf5e73..80be3ca 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2031,6 +2031,19 @@ static bool isABIDefaultCC(Sema &S, CallingConv CC, FunctionDecl *D) {
return ABIDefaultCC == CC;
}
+template <typename T>
+static bool haveConflictingLanguageLinkage(T &Old, T &New) {
+ LanguageLinkage OldL = Old.getLanguageLinkage();
+ if ((OldL == CLanguageLinkage || OldL == NoLanguageLinkage) &&
+ New.getDeclContext()->isExternCXXContext())
+ return true;
+ if ((OldL == CXXLanguageLinkage || OldL == NoLanguageLinkage) &&
+ !New.getDeclContext()->isRecord() &&
+ New.getDeclContext()->isExternCContext())
+ return true;
+ return false;
+}
+
/// MergeFunctionDecl - We just parsed a function 'New' from
/// declarator D which has the same name and scope as a previous
/// declaration 'Old'. Figure out how to resolve this situation,
@@ -2303,7 +2316,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) {
assert(OldQTypeForComparison.isCanonical());
}
- if (!Old->hasCLanguageLinkage() && New->hasCLanguageLinkage()) {
+ if (haveConflictingLanguageLinkage(*Old, *New)) {
Diag(New->getLocation(), diag::err_different_language_linkage) << New;
Diag(Old->getLocation(), PrevDiag);
return true;
@@ -2693,7 +2706,7 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
return;
}
- if (!Old->hasCLanguageLinkage() && New->hasCLanguageLinkage()) {
+ if (haveConflictingLanguageLinkage(*Old, *New)) {
Diag(New->getLocation(), diag::err_different_language_linkage) << New;
Diag(Old->getLocation(), diag::note_previous_definition);
New->setInvalidDecl();
@@ -4736,7 +4749,7 @@ void Sema::CheckShadow(Scope *S, VarDecl *D) {
template<typename T>
static bool mayConflictWithNonVisibleExternC(const T *ND) {
VarDecl::StorageClass SC = ND->getStorageClass();
- if (ND->hasCLanguageLinkage() && (SC == SC_Extern || SC == SC_PrivateExtern))
+ if (ND->isExternC() && (SC == SC_Extern || SC == SC_PrivateExtern))
return true;
return ND->getDeclContext()->isTranslationUnit();
}
@@ -6400,7 +6413,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
// If this function is declared as being extern "C", then check to see if
// the function returns a UDT (class, struct, or union type) that is not C
// compatible, and if it does, warn the user.
- if (NewFD->hasCLanguageLinkage()) {
+ if (NewFD->isExternC()) {
QualType R = NewFD->getResultType();
if (R->isIncompleteType() && !R->isVoidType())
Diag(NewFD->getLocation(), diag::warn_return_value_udt_incomplete)
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index d3d027b..eebb2f2 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -933,7 +933,7 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old,
static bool canBeOverloaded(const FunctionDecl &D) {
if (D.getAttr<OverloadableAttr>())
return true;
- if (D.hasCLanguageLinkage())
+ if (D.isExternC())
return false;
// Main cannot be overloaded (basic.start.main).
diff --git a/test/SemaCXX/linkage2.cpp b/test/SemaCXX/linkage2.cpp
index 0adc7ed..0e880c9 100644
--- a/test/SemaCXX/linkage2.cpp
+++ b/test/SemaCXX/linkage2.cpp
@@ -13,9 +13,9 @@ namespace test1 {
}
extern "C" {
- static void test2_f() { // expected-note {{previous definition is here}}
+ static void test2_f() {
}
- static void test2_f(int x) { // expected-error {{conflicting types for 'test2_f'}}
+ static void test2_f(int x) {
}
}
@@ -71,3 +71,10 @@ namespace test6 {
shared_future<int&> f1 = get_future<int&>();
}
}
+
+extern "C" {
+ void test7_f(); // expected-note {{previous declaration is here}}
+}
+extern "C++" {
+ void test7_f(); // expected-error {{declaration of 'test7_f' has a different language linkage}}
+}
--
1.7.11.7
More information about the cfe-commits
mailing list