sepavloff created this revision.
sepavloff added a subscriber: cfe-commits.

Function specializations used in friend declarations in class templates, like:
```
    template<typename T> class C1 {
        friend void func<>(int);
```
previously were processed incorrectly: class instantiation made them ordinary
functions and they were not placed into redeclaration chains correctly. This
change repairs specialization treatment.

This change fixes PR12994.

https://reviews.llvm.org/D23016

Files:
  lib/AST/DeclTemplate.cpp
  lib/Sema/SemaAccess.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/friend-spec.cpp

Index: test/SemaCXX/friend-spec.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/friend-spec.cpp
@@ -0,0 +1,96 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -DCHECK_DIAGS %s
+// RUN: %clang_cc1 -triple i386-pc-linux-gnu -emit-llvm %s
+
+// Tests for befriended function specializations
+
+
+#ifdef CHECK_DIAGS
+
+namespace test1 {
+  template<typename T>
+  void func_01(T);
+
+  template<typename T>
+  class C1 {
+    static void private_func();
+    friend void func_01<>(int);
+  };
+
+  template<typename T>
+  void func_01(T) {
+    C1<T>::private_func();
+  }
+
+  template void func_01<>(int);
+}
+
+namespace test2 {
+  template<typename T>
+  void func_01(T);
+
+  template<typename T>
+  class C1 {
+    static void private_func();  // expected-note{{implicitly declared private here}}
+    friend void func_01<>(int);
+  };
+
+  template<typename T>
+  void func_01(T) {
+    C1<T>::private_func();  // expected-error{{'private_func' is a private member of 'test2::C1<int *>'}}
+  }
+
+  template void func_01<>(int*);  // expected-note{{in instantiation of function template specialization 'test2::func_01<int *>' requested here}}
+}
+
+namespace test3 {
+  template<typename T>
+  void func_01(T, int);
+  template<typename T>
+  void func_01(T, char);
+
+  template<typename T>
+  class C1 {
+    static void private_func();  // expected-note{{implicitly declared private here}}
+    friend void func_01<>(int, T);
+  };
+
+  template<typename T>
+  void func_01(T, int) {
+    C1<T>::private_func();
+  }
+
+  template<typename T>
+  void func_01(T, char) {
+    C1<T>::private_func();  // expected-error{{'private_func' is a private member of 'test3::C1<int>'}}
+  }
+
+  template void func_01<>(int, int);
+  template void func_01<>(int, char);  // expected-note{{in instantiation of function template specialization 'test3::func_01<int>' requested here}}
+}
+
+#endif
+
+namespace pr12994 {
+  template<class cls>
+  void PrintThis(cls &obj);
+
+  class Xxx;
+
+  template<class A>
+  class Test {
+    friend void PrintThis<>(Xxx&);
+    void printme() {}
+  };
+
+  class Aimpl {};
+
+  class Xxx : public Test<Aimpl> {};
+
+  template<class cls>
+  void PrintThis(cls &obj) {
+    obj.printme();
+  }
+
+  template void PrintThis<Xxx>(Xxx&);
+}
+
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1617,13 +1617,21 @@
                                                              Innermost),
                                                 /*InsertPos=*/nullptr);
   } else if (isFriend) {
-    // Note, we need this connection even if the friend doesn't have a body.
-    // Its body may exist but not have been attached yet due to deferred
-    // parsing.
-    // FIXME: It might be cleaner to set this when attaching the body to the
-    // friend function declaration, however that would require finding all the
-    // instantiations and modifying them.
-    Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
+    if (const FunctionTemplateSpecializationInfo *FTSI =
+                                           D->getTemplateSpecializationInfo()) {
+      Function->setFunctionTemplateSpecialization(FTSI->getTemplate(),
+           TemplateArgumentList::CreateCopy(SemaRef.Context,
+                                            FTSI->TemplateArguments->asArray()),
+                                                  /*InsertPos=*/nullptr);
+    } else {
+      // Note, we need this connection even if the friend doesn't have a body.
+      // Its body may exist but not have been attached yet due to deferred
+      // parsing.
+      // FIXME: It might be cleaner to set this when attaching the body to the
+      // friend function declaration, however that would require finding all the
+      // instantiations and modifying them.
+      Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
+    }
   }
 
   if (InitFunctionInstantiation(Function, D))
@@ -1668,6 +1676,13 @@
 
     isExplicitSpecialization = true;
 
+  } else  if (const FunctionTemplateSpecializationInfo *FTSI =
+                                           D->getTemplateSpecializationInfo()) {
+    void *InsertPos = nullptr;
+    FunctionDecl *Prev = FTSI->getTemplate()->findSpecialization(
+                                 FTSI->TemplateArguments->asArray(), InsertPos);
+    if (Prev)
+      Function->setPreviousDeclaration(Prev);
   } else if (TemplateParams || !FunctionTemplate) {
     // Look only into the namespace where the friend would be declared to
     // find a previous declaration. This is the innermost enclosing namespace,
Index: lib/Sema/SemaAccess.cpp
===================================================================
--- lib/Sema/SemaAccess.cpp
+++ lib/Sema/SemaAccess.cpp
@@ -492,10 +492,11 @@
                                   const EffectiveContext &EC,
                                   FunctionDecl *Friend) {
   AccessResult OnFailure = AR_inaccessible;
+  Friend = Friend->getCanonicalDecl();
 
   for (SmallVectorImpl<FunctionDecl*>::const_iterator
          I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
-    if (Friend == *I)
+    if (Friend == (*I)->getCanonicalDecl())
       return AR_accessible;
 
     if (EC.isDependent() && MightInstantiateTo(S, *I, Friend))
Index: lib/AST/DeclTemplate.cpp
===================================================================
--- lib/AST/DeclTemplate.cpp
+++ lib/AST/DeclTemplate.cpp
@@ -167,7 +167,20 @@
   llvm::FoldingSetNodeID ID;
   EntryType::Profile(ID,Args, getASTContext());
   EntryType *Entry = Specs.FindNodeOrInsertPos(ID, InsertPos);
-  return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr;
+  if (!Entry)
+    return nullptr;
+  auto *SpecDecl = SETraits::getDecl(Entry)->getMostRecentDecl();
+  // If we have specializations used in friend declarations and at file level,
+  // the latter are preferred.
+  if (SpecDecl->getFriendObjectKind() != FOK_None) {
+    for (auto *D : SpecDecl->redecls()) {
+      if (SpecDecl->getFriendObjectKind() == FOK_None) {
+        SpecDecl = static_cast<typename SpecEntryTraits<EntryType>::DeclType *>(D);
+        break;
+      }
+    }
+  }
+  return SpecDecl;
 }
 
 template<class Derived, class EntryType>
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to