From 433a13d0cdb1c48b828fe5bfb6a835c58e1e758b Mon Sep 17 00:00:00 2001
From: Richard Smith <richard-llvm@metafoo.co.uk>
Date: Wed, 10 Jul 2013 22:04:13 +0000
Subject: [PATCH] If we friend a declaration twice, that should not make it
 visible to name lookup in the surrounding context. Slightly rework how we
 handle friend declarations to inherit the visibility of the prior
 declaration, rather than setting a friend declaration to be visible whenever
 there was a prior declaration.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@186040 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/clang/AST/Decl.h                    |  6 ++++++
 include/clang/AST/DeclBase.h                | 13 ++++++++++---
 lib/Sema/SemaDecl.cpp                       | 12 ++++--------
 lib/Sema/SemaLookup.cpp                     | 11 +++++++++--
 lib/Sema/SemaTemplate.cpp                   |  3 +--
 lib/Sema/SemaTemplateInstantiateDecl.cpp    | 20 +++++++-------------
 test/CXX/temp/temp.decls/temp.friend/p4.cpp | 17 +++++++++++++++++
 test/SemaCXX/friend.cpp                     | 12 ++++++++++++
 8 files changed, 66 insertions(+), 28 deletions(-)

diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index e90f454db30..2b2bf1a1efc 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -3402,6 +3402,12 @@ void Redeclarable<decl_type>::setPreviousDeclaration(decl_type *PrevDecl) {
   First->RedeclLink = LatestDeclLink(static_cast<decl_type*>(this));
   assert(!isa<NamedDecl>(static_cast<decl_type*>(this)) ||
          cast<NamedDecl>(static_cast<decl_type*>(this))->isLinkageValid());
+
+  // If the declaration was previously visible, a redeclaration of it remains
+  // visible even if it wouldn't be visible by itself.
+  static_cast<decl_type*>(this)->IdentifierNamespace |=
+    First->getIdentifierNamespace() &
+    (Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Type);
 }
 
 // Inline function definitions.
diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h
index 4f289d315ad..157506d2a60 100644
--- a/include/clang/AST/DeclBase.h
+++ b/include/clang/AST/DeclBase.h
@@ -295,6 +295,8 @@ protected:
   friend class ASTReader;
   friend class LinkageComputer;
 
+  template<typename decl_type> friend class Redeclarable;
+
 private:
   void CheckAccessDeclContext() const;
 
@@ -824,7 +826,7 @@ public:
   /// class, but in the semantic context of the actual entity.  This property
   /// applies only to a specific decl object;  other redeclarations of the
   /// same entity may not (and probably don't) share this property.
-  void setObjectOfFriendDecl(bool PreviouslyDeclared) {
+  void setObjectOfFriendDecl(bool PerformFriendInjection = false) {
     unsigned OldNS = IdentifierNamespace;
     assert((OldNS & (IDNS_Tag | IDNS_Ordinary |
                      IDNS_TagFriend | IDNS_OrdinaryFriend)) &&
@@ -833,15 +835,20 @@ public:
                        IDNS_TagFriend | IDNS_OrdinaryFriend)) &&
            "namespace includes other than ordinary or tag");
 
+    Decl *Prev = getPreviousDecl();
     IdentifierNamespace = 0;
     if (OldNS & (IDNS_Tag | IDNS_TagFriend)) {
       IdentifierNamespace |= IDNS_TagFriend;
-      if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Tag | IDNS_Type;
+      if (PerformFriendInjection || 
+          (Prev && Prev->getIdentifierNamespace() & IDNS_Tag))
+        IdentifierNamespace |= IDNS_Tag | IDNS_Type;
     }
 
     if (OldNS & (IDNS_Ordinary | IDNS_OrdinaryFriend)) {
       IdentifierNamespace |= IDNS_OrdinaryFriend;
-      if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Ordinary;
+      if (PerformFriendInjection ||
+          (Prev && Prev->getIdentifierNamespace() & IDNS_Ordinary))
+        IdentifierNamespace |= IDNS_Ordinary;
     }
   }
 
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 5e535da19a1..fd4ddcf55e6 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -6328,12 +6328,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
     }
 
     if (isFriend) {
-      // For now, claim that the objects have no previous declaration.
       if (FunctionTemplate) {
-        FunctionTemplate->setObjectOfFriendDecl(false);
+        FunctionTemplate->setObjectOfFriendDecl();
         FunctionTemplate->setAccess(AS_public);
       }
-      NewFD->setObjectOfFriendDecl(false);
+      NewFD->setObjectOfFriendDecl();
       NewFD->setAccess(AS_public);
     }
 
@@ -6652,8 +6651,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
 
       NewFD->setAccess(Access);
       if (FunctionTemplate) FunctionTemplate->setAccess(Access);
-
-      PrincipalDecl->setObjectOfFriendDecl(true);
     }
 
     if (NewFD->isOverloadedOperator() && !DC->isRecord() &&
@@ -10384,9 +10381,8 @@ CreateNewDecl:
   // declaration so we always pass true to setObjectOfFriendDecl to make
   // the tag name visible.
   if (TUK == TUK_Friend)
-    New->setObjectOfFriendDecl(/* PreviouslyDeclared = */ !Previous.empty() ||
-                               (!FriendSawTagOutsideEnclosingNamespace &&
-                                getLangOpts().MicrosoftExt));
+    New->setObjectOfFriendDecl(!FriendSawTagOutsideEnclosingNamespace &&
+                               getLangOpts().MicrosoftExt);
 
   // Set the access specifier.
   if (!Invalid && SearchDC->isRecord())
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index 8d954ca994c..0667b8adc04 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -2757,8 +2757,15 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, bool Operator,
       // If the only declaration here is an ordinary friend, consider
       // it only if it was declared in an associated classes.
       if (D->getIdentifierNamespace() == Decl::IDNS_OrdinaryFriend) {
-        DeclContext *LexDC = D->getLexicalDeclContext();
-        if (!AssociatedClasses.count(cast<CXXRecordDecl>(LexDC)))
+        bool DeclaredInAssociatedClass = false;
+        for (Decl *DI = D; DI; DI = D->getPreviousDecl()) {
+          DeclContext *LexDC = DI->getLexicalDeclContext();
+          if (AssociatedClasses.count(cast<CXXRecordDecl>(LexDC))) {
+            DeclaredInAssociatedClass = true;
+            break;
+          }
+        }
+        if (!DeclaredInAssociatedClass)
           continue;
       }
 
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index e6006b6bf97..6f1ab19f10e 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -1120,8 +1120,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
       NewClass->setAccess(PrevClassTemplate->getAccess());
     }
 
-    NewTemplate->setObjectOfFriendDecl(/* PreviouslyDeclared = */
-                                       PrevClassTemplate != NULL);
+    NewTemplate->setObjectOfFriendDecl();
 
     // Friend templates are visible in fairly strange ways.
     if (!CurContext->isDependentContext()) {
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index b5a80d3f6a0..14185c37ad1 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -960,7 +960,7 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
     else
       Inst->setAccess(D->getAccess());
 
-    Inst->setObjectOfFriendDecl(PrevClassTemplate != 0);
+    Inst->setObjectOfFriendDecl();
     // TODO: do we want to track the instantiation progeny of this
     // friend target decl?
   } else {
@@ -1110,8 +1110,8 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
 
   // If the original function was part of a friend declaration,
   // inherit its namespace state.
-  if (Decl::FriendObjectKind FOK = D->getFriendObjectKind())
-    Record->setObjectOfFriendDecl(FOK == Decl::FOK_Declared);
+  if (D->getFriendObjectKind())
+    Record->setObjectOfFriendDecl();
 
   // Make sure that anonymous structs and unions are recorded.
   if (D->isAnonymousStructOrUnion()) {
@@ -1315,7 +1315,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
     assert(isFriend && "non-friend has dependent specialization info?");
 
     // This needs to be set now for future sanity.
-    Function->setObjectOfFriendDecl(/*HasPrevious*/ true);
+    Function->setObjectOfFriendDecl();
 
     // Instantiate the explicit template arguments.
     TemplateArgumentListInfo ExplicitArgs(Info->getLAngleLoc(),
@@ -1365,13 +1365,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
   // If the original function was part of a friend declaration,
   // inherit its namespace state and add it to the owner.
   if (isFriend) {
-    NamedDecl *PrevDecl;
-    if (TemplateParams)
-      PrevDecl = FunctionTemplate->getPreviousDecl();
-    else
-      PrevDecl = Function->getPreviousDecl();
-
-    PrincipalDecl->setObjectOfFriendDecl(PrevDecl != 0);
+    PrincipalDecl->setObjectOfFriendDecl();
     DC->makeDeclVisibleInContext(PrincipalDecl);
 
     bool queuedInstantiation = false;
@@ -1639,7 +1633,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
                                                     TemplateParams, Method);
     if (isFriend) {
       FunctionTemplate->setLexicalDeclContext(Owner);
-      FunctionTemplate->setObjectOfFriendDecl(true);
+      FunctionTemplate->setObjectOfFriendDecl();
     } else if (D->isOutOfLine())
       FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext());
     Method->setDescribedFunctionTemplate(FunctionTemplate);
@@ -1666,7 +1660,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
                                             TempParamLists.data());
 
     Method->setLexicalDeclContext(Owner);
-    Method->setObjectOfFriendDecl(true);
+    Method->setObjectOfFriendDecl();
   } else if (D->isOutOfLine())
     Method->setLexicalDeclContext(D->getLexicalDeclContext());
 
diff --git a/test/CXX/temp/temp.decls/temp.friend/p4.cpp b/test/CXX/temp/temp.decls/temp.friend/p4.cpp
index e036cef32b2..8571a141201 100644
--- a/test/CXX/temp/temp.decls/temp.friend/p4.cpp
+++ b/test/CXX/temp/temp.decls/temp.friend/p4.cpp
@@ -26,3 +26,20 @@ void g() {
   X2<float> xf; 
   f(xf);
 }
+
+template<typename T>
+struct X3 {
+  operator int();
+
+  friend void h(int x);
+};
+
+int array2[sizeof(X3<int>)]; 
+int array3[sizeof(X3<float>)];
+
+void i() {
+  X3<int> xi;
+  h(xi);
+  X3<float> xf; 
+  h(xf);
+}
diff --git a/test/SemaCXX/friend.cpp b/test/SemaCXX/friend.cpp
index ce2f34ff6b9..60eb03da34f 100644
--- a/test/SemaCXX/friend.cpp
+++ b/test/SemaCXX/friend.cpp
@@ -163,3 +163,15 @@ namespace test9 {
     friend void C::f(int, int, int) {}  // expected-error {{no function named 'f' with type 'void (int, int, int)' was found in the specified scope}}
   };
 }
+
+namespace test10 {
+  struct A {
+    friend void f10();
+  };
+  struct B {
+    friend void f10();
+  };
+  void g() {
+    f10(); // expected-error {{undeclared identifier}}
+  }
+}
-- 
GitLab