[cfe-commits] [PATCH] Assert that redeclarations have the same linkage.

Rafael Ávila de Espíndola rafael.espindola at gmail.com
Tue Dec 25 22:38:07 PST 2012


It is somewhat hard to test linkage, so I decided to try to add an assert. It
found two interesting cases (see tests) and one design question: How semantic
is the declaration context?

For example, in

extern "C" {
  namespace {
   extern int x2;
  }
}
namespace {
  int x2;
}

Should Var->getDeclContext()->isExternCContext() return true for the second
decl? The patch fixes the linkage computation, but I think changing the
context would also work.

With this patch we assert in

namespace {
  int x2;
}
extern "C" {
  namespace {
   extern int x2;
  }
}

I will fix it before committing, but would like to first know if I should
change the declaration context or not.
---
 lib/AST/Decl.cpp          | 37 ++++++++++++++++++++++++++++++++++---
 test/SemaCXX/linkage2.cpp | 25 +++++++++++++++++++++++++
 2 files changed, 59 insertions(+), 3 deletions(-)
 create mode 100644 test/SemaCXX/linkage2.cpp

diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 85ab002..f816852 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -202,6 +202,16 @@ static bool useInlineVisibilityHidden(const NamedDecl *D) {
     FD->hasBody(Def) && Def->isInlined() && !Def->hasAttr<GNUInlineAttr>();
 }
 
+static bool isInExternC(const NamedDecl &ND) {
+  for (NamedDecl::redecl_iterator I = ND.redecls_begin(), E = ND.redecls_end();
+       I != E; ++I) {
+    const NamedDecl *D = cast<NamedDecl>(*I);
+    if (D->getDeclContext()->isExternCContext())
+      return true;
+  }
+  return false;
+}
+
 static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
                                               bool OnlyTemplate) {
   assert(D->getDeclContext()->getRedeclContext()->isFileContext() &&
@@ -268,8 +278,8 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D,
   if (D->isInAnonymousNamespace()) {
     const VarDecl *Var = dyn_cast<VarDecl>(D);
     const FunctionDecl *Func = dyn_cast<FunctionDecl>(D);
-    if ((!Var || !Var->getDeclContext()->isExternCContext()) &&
-        (!Func || !Func->getDeclContext()->isExternCContext()))
+    if ((!Var || !isInExternC(*Var)) &&
+        (!Func || !isInExternC(*Func)))
       return LinkageInfo::uniqueExternal();
   }
 
@@ -634,6 +644,27 @@ LinkageInfo NamedDecl::getLinkageAndVisibility() const {
   CachedLinkage = LV.linkage();
   CacheValidAndVisibility = LV.visibility() + 1;
   CachedVisibilityExplicit = LV.visibilityExplicit();
+
+#ifndef NDEBUG
+  // In C (because of gnu inline) and in c++ with microsoft extensions an
+  // static can follow an extern, so we can have two decls with different
+  // linkages.
+  // HACK: There is something wrong with modules and the following loop
+  // runs forever in some tests.
+  const LangOptions &Opts = getASTContext().getLangOpts();
+  if (!Opts.CPlusPlus || Opts.MicrosoftExt || Opts.Modules)
+    return LV;
+
+  for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I) {
+    NamedDecl *D = cast<NamedDecl>(*I);
+    if (D == this)
+      continue;
+    if (D->CacheValidAndVisibility == 0)
+      continue;
+    assert(D->CachedLinkage == CachedLinkage);
+  }
+#endif
+
   return LV;
 }
 
@@ -772,7 +803,7 @@ static LinkageInfo computeLVForDecl(const NamedDecl *D, bool OnlyTemplate) {
   //   one such matching entity, the program is ill-formed. Otherwise,
   //   if no matching entity is found, the block scope entity receives
   //   external linkage.
-  if (D->getLexicalDeclContext()->isFunctionOrMethod()) {
+  if (D->getDeclContext()->isFunctionOrMethod()) {
     if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
       if (Function->isInAnonymousNamespace() &&
           !Function->getDeclContext()->isExternCContext())
diff --git a/test/SemaCXX/linkage2.cpp b/test/SemaCXX/linkage2.cpp
new file mode 100644
index 0000000..67bf39b
--- /dev/null
+++ b/test/SemaCXX/linkage2.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// expected-no-diagnostics
+
+namespace test1 {
+  void dummy() {
+    void Bar();
+    class A {
+      friend void Bar();
+    };
+  }
+}
+
+namespace test2 {
+  extern "C" {
+    namespace {
+      extern int x2;
+      void f2();
+    }
+  }
+  namespace {
+    int x2;
+    void f2() {}
+  }
+}
-- 
1.7.11.7




More information about the cfe-commits mailing list