[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