r175117 - Add a getLanguageLinkage method to VarDecls and FunctionDecls. Use it to fix

Rafael Espindola rafael.espindola at gmail.com
Wed Feb 13 17:18:38 PST 2013


Author: rafael
Date: Wed Feb 13 19:18:37 2013
New Revision: 175117

URL: http://llvm.org/viewvc/llvm-project?rev=175117&view=rev
Log:
Add a getLanguageLinkage method to VarDecls and FunctionDecls. Use it to fix
some cases where functions with no language linkage were being treated as having
C language linkage. In particular, don't warn in

extern "C" {
  static NonPod foo();
}

Since getLanguageLinkage checks the language linkage, the linkage computation
cannot use the language linkage. Break the loop by checking just the context
in the linkage computation.

Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/DeclBase.h
    cfe/trunk/include/clang/Basic/Linkage.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/AST/DeclBase.cpp
    cfe/trunk/lib/AST/ItaniumMangle.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/CodeGenCXX/c-linkage.cpp
    cfe/trunk/test/SemaCXX/function-extern-c.cpp
    cfe/trunk/test/SemaCXX/linkage2.cpp

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Wed Feb 13 19:18:37 2013
@@ -898,11 +898,12 @@ public:
   /// external, C linkage.
   bool isExternC() const;
 
-  /// 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;
+  /// Compute the language linkage.
+  LanguageLinkage getLanguageLinkage() const;
+
+  bool hasCLanguageLinkage() const {
+    return getLanguageLinkage() == CLanguageLinkage;
+  }
 
   /// isLocalVarDecl - Returns true for local variable declarations
   /// other than parameters.  Note that this includes static variables
@@ -1790,11 +1791,12 @@ public:
   /// external, C linkage.
   bool isExternC() const;
 
-  /// 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;
+  /// Compute the language linkage.
+  LanguageLinkage getLanguageLinkage() const;
+
+  bool hasCLanguageLinkage() const {
+    return getLanguageLinkage() == CLanguageLinkage;
+  }
 
   /// \brief Determines whether this is a global function.
   bool isGlobal() const;

Modified: cfe/trunk/include/clang/AST/DeclBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclBase.h?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclBase.h (original)
+++ cfe/trunk/include/clang/AST/DeclBase.h Wed Feb 13 19:18:37 2013
@@ -1090,6 +1090,10 @@ public:
   /// a C++ extern "C" linkage spec.
   bool isExternCContext() const;
 
+  /// \brief Determines whether this context is, or is nested within,
+  /// a C++ extern "C++" linkage spec.
+  bool isExternCXXContext() const;
+
   /// \brief Determine whether this declaration context is equivalent
   /// to the declaration context DC.
   bool Equals(const DeclContext *DC) const {

Modified: cfe/trunk/include/clang/Basic/Linkage.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Linkage.h?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Linkage.h (original)
+++ cfe/trunk/include/clang/Basic/Linkage.h Wed Feb 13 19:18:37 2013
@@ -42,6 +42,14 @@ enum Linkage {
   ExternalLinkage
 };
 
+/// \brief Describes the different kinds of language linkage
+/// (C++ [dcl.link]) that an entity may have.
+enum LanguageLinkage {
+  CLanguageLinkage,
+  CXXLanguageLinkage,
+  NoLanguageLinkage
+};
+
 /// \brief A more specific kind of linkage than enum Linkage.
 ///
 /// This is relevant to CodeGen and AST file reading.

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Wed Feb 13 19:18:37 2013
@@ -196,6 +196,12 @@ static bool useInlineVisibilityHidden(co
     FD->hasBody(Def) && Def->isInlined() && !Def->hasAttr<GNUInlineAttr>();
 }
 
+template<typename T>
+bool isInExternCContext(T *D) {
+  const T *First = D->getFirstDeclaration();
+  return First->getDeclContext()->isExternCContext();
+}
+
 static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
                                               bool OnlyTemplate) {
   assert(D->getDeclContext()->getRedeclContext()->isFileContext() &&
@@ -262,8 +268,8 @@ static LinkageInfo getLVForNamespaceScop
   if (D->isInAnonymousNamespace()) {
     const VarDecl *Var = dyn_cast<VarDecl>(D);
     const FunctionDecl *Func = dyn_cast<FunctionDecl>(D);
-    if ((!Var || !Var->hasCLanguageLinkage()) &&
-        (!Func || !Func->hasCLanguageLinkage()))
+    if ((!Var || !isInExternCContext(Var)) &&
+        (!Func || !isInExternCContext(Func)))
       return LinkageInfo::uniqueExternal();
   }
 
@@ -1210,29 +1216,36 @@ SourceRange VarDecl::getSourceRange() co
 }
 
 template<typename T>
-static bool hasCLanguageLinkageTemplate(const T &D) {
+static LanguageLinkage getLanguageLinkageTemplate(const T &D) {
   // 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 1: All function types, function names with external linkage, and
+  // variable names with external linkage have a language linkage.
+  if (!isExternalLinkage(D.getLinkage()))
+    return NoLanguageLinkage;
 
   // 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();
+  if (First->getDeclContext()->isExternCContext())
+    return CLanguageLinkage;
+  return CXXLanguageLinkage;
 }
 
-bool VarDecl::hasCLanguageLinkage() const {
-  return hasCLanguageLinkageTemplate(*this);
+LanguageLinkage VarDecl::getLanguageLinkage() const {
+  return getLanguageLinkageTemplate(*this);
 }
 
 bool VarDecl::isExternC() const {
@@ -1757,14 +1770,14 @@ bool FunctionDecl::isReservedGlobalPlace
   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 CLanguageLinkage;
 
-  return hasCLanguageLinkageTemplate(*this);
+  return getLanguageLinkageTemplate(*this);
 }
 
 bool FunctionDecl::isExternC() const {

Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Wed Feb 13 19:18:37 2013
@@ -806,6 +806,17 @@ bool DeclContext::isExternCContext() con
   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);

Modified: cfe/trunk/lib/AST/ItaniumMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ItaniumMangle.cpp?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ItaniumMangle.cpp (original)
+++ cfe/trunk/lib/AST/ItaniumMangle.cpp Wed Feb 13 19:18:37 2013
@@ -356,17 +356,6 @@ private:
 
 }
 
-static bool isInCLinkageSpecification(const Decl *D) {
-  D = D->getCanonicalDecl();
-  for (const DeclContext *DC = getEffectiveDeclContext(D);
-       !DC->isTranslationUnit(); DC = getEffectiveParentContext(DC)) {
-    if (const LinkageSpecDecl *Linkage = dyn_cast<LinkageSpecDecl>(DC))
-      return Linkage->getLanguage() == LinkageSpecDecl::lang_c;
-  }
-
-  return false;
-}
-
 bool ItaniumMangleContext::shouldMangleDeclName(const NamedDecl *D) {
   // In C, functions with no attributes never need to be mangled. Fastpath them.
   if (!getASTContext().getLangOpts().CPlusPlus && !D->hasAttrs())
@@ -405,8 +394,12 @@ bool ItaniumMangleContext::shouldMangleD
     return true;
 
   // C functions and "main" are not mangled.
-  if ((FD && FD->isMain()) || isInCLinkageSpecification(D))
-    return false;
+  if (FD)
+    return !FD->isMain() && !FD->hasCLanguageLinkage();
+
+  // C variables are not mangled.
+  if (const VarDecl *VD = dyn_cast<VarDecl>(D))
+    return !VD->hasCLanguageLinkage();
 
   return true;
 }

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Feb 13 19:18:37 2013
@@ -2065,6 +2065,22 @@ static bool isABIDefaultCC(Sema &S, Call
   return ABIDefaultCC == CC;
 }
 
+template<typename T>
+bool haveIncompatibleLanguageLinkages(const T *Old, const T *New) {
+  const DeclContext *DC = Old->getDeclContext();
+  if (DC->isRecord())
+    return false;
+
+  LanguageLinkage OldLinkage = Old->getLanguageLinkage();
+  if (OldLinkage == CXXLanguageLinkage &&
+      New->getDeclContext()->isExternCContext())
+    return true;
+  if (OldLinkage == CLanguageLinkage &&
+      New->getDeclContext()->isExternCXXContext())
+    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,
@@ -2366,7 +2382,7 @@ bool Sema::MergeFunctionDecl(FunctionDec
       assert(OldQTypeForComparison.isCanonical());
     }
 
-    if (!Old->hasCLanguageLinkage() && New->hasCLanguageLinkage()) {
+    if (haveIncompatibleLanguageLinkages(Old, New)) {
       Diag(New->getLocation(), diag::err_different_language_linkage) << New;
       Diag(Old->getLocation(), PrevDiag);
       return true;
@@ -2756,7 +2772,7 @@ void Sema::MergeVarDecl(VarDecl *New, Lo
     return;
   }
 
-  if (!Old->hasCLanguageLinkage() && New->hasCLanguageLinkage()) {
+  if (haveIncompatibleLanguageLinkages(Old, New)) {
     Diag(New->getLocation(), diag::err_different_language_linkage) << New;
     Diag(Old->getLocation(), diag::note_previous_definition);
     New->setInvalidDecl();

Modified: cfe/trunk/test/CodeGenCXX/c-linkage.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/c-linkage.cpp?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/c-linkage.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/c-linkage.cpp Wed Feb 13 19:18:37 2013
@@ -11,3 +11,16 @@ extern "C" {
 }
 
 // CHECK: define void @_ZN1N1X1fEv
+
+extern "C" {
+  static void test2_f() {
+  }
+  // CHECK: define internal void @_Z7test2_fv
+  static void test2_f(int x) {
+  }
+  // CHECK: define internal void @_Z7test2_fi
+  void test2_use() {
+    test2_f();
+    test2_f(42);
+  }
+}

Modified: cfe/trunk/test/SemaCXX/function-extern-c.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/function-extern-c.cpp?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/function-extern-c.cpp (original)
+++ cfe/trunk/test/SemaCXX/function-extern-c.cpp Wed Feb 13 19:18:37 2013
@@ -51,3 +51,13 @@ namespace test2 {
   };
   A f(void);  // expected-warning {{'f' has C-linkage specified, but returns user-defined type 'test2::A' which is incompatible with C}}
 }
+
+namespace test3 {
+  struct A {
+    A(const A&);
+  };
+  extern "C" {
+    // Don't warn for static functions.
+    static A f(void);
+  }
+}

Modified: cfe/trunk/test/SemaCXX/linkage2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/linkage2.cpp?rev=175117&r1=175116&r2=175117&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/linkage2.cpp (original)
+++ cfe/trunk/test/SemaCXX/linkage2.cpp Wed Feb 13 19:18:37 2013
@@ -2,20 +2,22 @@
 
 namespace test1 {
   int x; // expected-note {{previous definition is here}}
-  static int y; // expected-note {{previous definition is here}}
+  static int y;
   void f() {} // expected-note {{previous definition is here}}
 
   extern "C" {
     extern int x; // expected-error {{declaration of 'x' has a different language linkage}}
-    extern int y; // expected-error {{declaration of 'y' has a different language linkage}}
+    extern int y; // OK, has internal linkage, so no language linkage.
     void f(); // expected-error {{declaration of 'f' has a different language linkage}}
   }
 }
 
+// This is OK. Both test2_f don't have language linkage since they have
+// internal linkage.
 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 +73,36 @@ namespace test6 {
     shared_future<int&> f1 = get_future<int&>();
   }
 }
+
+// This is OK. The variables have internal linkage and therefore no language
+// linkage.
+extern "C" {
+  static int test7_x;
+}
+extern "C++" {
+  extern int test7_x;
+}
+extern "C++" {
+  static int test7_y;
+}
+extern "C" {
+  extern int test7_y;
+}
+extern "C" { typedef int test7_F(); static test7_F test7_f; }
+extern "C++" { extern test7_F test7_f; }
+
+// FIXME: This should be invalid. The function has no language linkage, but
+// the function type has, so this is redeclaring the function with a different
+// type.
+extern "C++" {
+  static void test8_f();
+}
+extern "C" {
+  extern void test8_f();
+}
+extern "C" {
+  static void test8_g();
+}
+extern "C++" {
+  extern void test8_g();
+}





More information about the cfe-commits mailing list