r321770 - PR33503: When a qualified name in a redeclaration names a prior declaration in

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 3 15:03:54 PST 2018


Author: rsmith
Date: Wed Jan  3 15:03:54 2018
New Revision: 321770

URL: http://llvm.org/viewvc/llvm-project?rev=321770&view=rev
Log:
PR33503: When a qualified name in a redeclaration names a prior declaration in
an inline namespace, update its semantic DeclContext to match.

We would previously get the semantic DeclContext wrong (pointing to the named
scope rather than the inline namespace within it), resulting in wrong lookup
results and linkage-related problems if the inline namespace was an anonymous
namespace.

Modified:
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=321770&r1=321769&r2=321770&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Jan  3 15:03:54 2018
@@ -2929,6 +2929,48 @@ static bool hasIdenticalPassObjectSizeAt
   return std::equal(A->param_begin(), A->param_end(), B->param_begin(), AttrEq);
 }
 
+/// If necessary, adjust the semantic declaration context for a qualified
+/// declaration to name the correct inline namespace within the qualifier.
+static void adjustDeclContextForDeclaratorDecl(DeclaratorDecl *NewD,
+                                               DeclaratorDecl *OldD) {
+  // The only case where we need to update the DeclContext is when
+  // redeclaration lookup for a qualified name finds a declaration
+  // in an inline namespace within the context named by the qualifier:
+  //
+  //   inline namespace N { int f(); }
+  //   int ::f(); // Sema DC needs adjusting from :: to N::.
+  //
+  // For unqualified declarations, the semantic context *can* change
+  // along the redeclaration chain (for local extern declarations,
+  // extern "C" declarations, and friend declarations in particular).
+  if (!NewD->getQualifier())
+    return;
+
+  // NewD is probably already in the right context.
+  auto *NamedDC = NewD->getDeclContext()->getRedeclContext();
+  auto *SemaDC = OldD->getDeclContext()->getRedeclContext();
+  if (NamedDC->Equals(SemaDC))
+    return;
+
+  assert((NamedDC->InEnclosingNamespaceSetOf(SemaDC) ||
+          NewD->isInvalidDecl() || OldD->isInvalidDecl()) &&
+         "unexpected context for redeclaration");
+
+  auto *LexDC = NewD->getLexicalDeclContext();
+  auto FixSemaDC = [=](NamedDecl *D) {
+    if (!D)
+      return;
+    D->setDeclContext(SemaDC);
+    D->setLexicalDeclContext(LexDC);
+  };
+
+  FixSemaDC(NewD);
+  if (auto *FD = dyn_cast<FunctionDecl>(NewD))
+    FixSemaDC(FD->getDescribedFunctionTemplate());
+  else if (auto *VD = dyn_cast<VarDecl>(NewD))
+    FixSemaDC(VD->getDescribedVarTemplate());
+}
+
 /// 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,
@@ -3953,6 +3995,7 @@ void Sema::MergeVarDecl(VarDecl *New, Lo
   New->setPreviousDecl(Old);
   if (NewTemplate)
     NewTemplate->setPreviousDecl(OldTemplate);
+  adjustDeclContextForDeclaratorDecl(New, Old);
 
   // Inherit access appropriately.
   New->setAccess(Old->getAccess());
@@ -9252,12 +9295,13 @@ bool Sema::CheckFunctionDeclaration(Scop
 
     if (FunctionTemplateDecl *OldTemplateDecl
                                   = dyn_cast<FunctionTemplateDecl>(OldDecl)) {
-      NewFD->setPreviousDeclaration(OldTemplateDecl->getTemplatedDecl());
+      auto *OldFD = OldTemplateDecl->getTemplatedDecl();
+      NewFD->setPreviousDeclaration(OldFD);
+      adjustDeclContextForDeclaratorDecl(NewFD, OldFD);
       FunctionTemplateDecl *NewTemplateDecl
         = NewFD->getDescribedFunctionTemplate();
       assert(NewTemplateDecl && "Template/non-template mismatch");
-      if (CXXMethodDecl *Method
-            = dyn_cast<CXXMethodDecl>(NewTemplateDecl->getTemplatedDecl())) {
+      if (auto *Method = dyn_cast<CXXMethodDecl>(NewFD)) {
         Method->setAccess(OldTemplateDecl->getAccess());
         NewTemplateDecl->setAccess(OldTemplateDecl->getAccess());
       }
@@ -9270,22 +9314,22 @@ bool Sema::CheckFunctionDeclaration(Scop
         assert(OldTemplateDecl->isMemberSpecialization());
         // Explicit specializations of a member template do not inherit deleted
         // status from the parent member template that they are specializing.
-        if (OldTemplateDecl->getTemplatedDecl()->isDeleted()) {
-          FunctionDecl *const OldTemplatedDecl =
-              OldTemplateDecl->getTemplatedDecl();
+        if (OldFD->isDeleted()) {
           // FIXME: This assert will not hold in the presence of modules.
-          assert(OldTemplatedDecl->getCanonicalDecl() == OldTemplatedDecl);
+          assert(OldFD->getCanonicalDecl() == OldFD);
           // FIXME: We need an update record for this AST mutation.
-          OldTemplatedDecl->setDeletedAsWritten(false);
+          OldFD->setDeletedAsWritten(false);
         }
       }
 
     } else {
       if (shouldLinkDependentDeclWithPrevious(NewFD, OldDecl)) {
+        auto *OldFD = cast<FunctionDecl>(OldDecl);
         // This needs to happen first so that 'inline' propagates.
-        NewFD->setPreviousDeclaration(cast<FunctionDecl>(OldDecl));
+        NewFD->setPreviousDeclaration(OldFD);
+        adjustDeclContextForDeclaratorDecl(NewFD, OldFD);
         if (isa<CXXMethodDecl>(NewFD))
-          NewFD->setAccess(OldDecl->getAccess());
+          NewFD->setAccess(OldFD->getAccess());
       }
     }
   } else if (!getLangOpts().CPlusPlus && MayNeedOverloadableChecks &&

Modified: cfe/trunk/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp?rev=321770&r1=321769&r2=321770&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp (original)
+++ cfe/trunk/test/CXX/dcl.decl/dcl.meaning/p1-0x.cpp Wed Jan  3 15:03:54 2018
@@ -101,4 +101,25 @@ namespace inline_namespaces {
   template<> struct N::V<int> {};
   template struct N::V<int*>;
   template struct N::V<char>; // expected-error {{undefined}}
+
+  struct Q {};
+
+  // Perversely, inline anonymous namespaces can cause an ostensibly
+  // external-linkage declaration to acquire internal linkage when
+  // redeclared with a qualified name.
+  inline namespace {
+    struct Q {} q;
+    int f_in_inline();
+    extern int v_in_inline;
+    typedef int t_in_inline;
+  }
+  // FIXME: These "extra qualification" warnings are bogus: the qualification
+  // changes the meaning of the program.
+  int inline_namespaces::f_in_inline() { // expected-warning {{extra qualification}}
+    // Finds <anon>::Q, not inline_namespaces::Q
+    Q x = q;
+    return 0;
+  }
+  int inline_namespaces::v_in_inline = // expected-warning {{extra qualification}}
+    (Q(q), 0);
 }




More information about the cfe-commits mailing list