[clang-tools-extra] 0b67243 - [Clang] Support friend declarations with a dependent nested-name-specifier (#208345)

via cfe-commits cfe-commits at lists.llvm.org
Sun Aug 16 00:46:38 PDT 2026


Author: Oleksandr Tarasiuk
Date: 2026-08-16T10:46:32+03:00
New Revision: 0b672437c396b700fdb94bd64b176b33f084053b

URL: https://github.com/llvm/llvm-project/commit/0b672437c396b700fdb94bd64b176b33f084053b
DIFF: https://github.com/llvm/llvm-project/commit/0b672437c396b700fdb94bd64b176b33f084053b.diff

LOG: [Clang] Support friend declarations with a dependent nested-name-specifier (#208345)

Fixes https://github.com/llvm/llvm-project/issues/104057

---

This patch adds support for friend declarations with a dependent NNS

Added: 
    clang/test/AST/friend-template-decl.cpp
    clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
    clang/test/Index/load-dependent-friend-template.cpp
    clang/test/SemaTemplate/dependent-friend-constraints.cpp

Modified: 
    clang-tools-extra/clang-doc/Serialize.cpp
    clang/docs/ReleaseNotes.md
    clang/include/clang/AST/ASTNodeTraverser.h
    clang/include/clang/AST/DeclFriend.h
    clang/include/clang/AST/DeclTemplate.h
    clang/include/clang/AST/JSONNodeDumper.h
    clang/include/clang/AST/RecursiveASTVisitor.h
    clang/include/clang/AST/TextNodeDumper.h
    clang/include/clang/Basic/DeclNodes.td
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Sema/Template.h
    clang/include/clang/Sema/TemplateDeduction.h
    clang/include/clang/Serialization/ASTBitCodes.h
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/ASTStructuralEquivalence.cpp
    clang/lib/AST/DeclFriend.cpp
    clang/lib/AST/DeclPrinter.cpp
    clang/lib/AST/DeclTemplate.cpp
    clang/lib/AST/JSONNodeDumper.cpp
    clang/lib/AST/ODRHash.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/lib/Index/IndexDecl.cpp
    clang/lib/Parse/ParseDeclCXX.cpp
    clang/lib/Sema/Sema.cpp
    clang/lib/Sema/SemaAccess.cpp
    clang/lib/Sema/SemaCodeComplete.cpp
    clang/lib/Sema/SemaConcept.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaExceptionSpec.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/lib/Sema/SemaTemplateDeduction.cpp
    clang/lib/Sema/SemaTemplateDeductionGuide.cpp
    clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
    clang/lib/Serialization/ASTReaderDecl.cpp
    clang/lib/Serialization/ASTWriterDecl.cpp
    clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
    clang/test/CXX/drs/cwg18xx.cpp
    clang/test/CXX/drs/cwg19xx.cpp
    clang/test/CXX/drs/cwg28xx.cpp
    clang/test/CXX/drs/cwg6xx.cpp
    clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
    clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
    clang/test/Modules/odr_hash.cpp
    clang/test/Parser/cxx2c-variadic-friends.cpp
    clang/test/SemaCXX/cxx2c-variadic-friends.cpp
    clang/test/SemaCXX/many-template-parameter-lists.cpp
    clang/test/SemaTemplate/GH71595.cpp
    clang/test/SemaTemplate/concepts-friends.cpp
    clang/test/SemaTemplate/ctad.cpp
    clang/test/SemaTemplate/friend-template.cpp
    clang/test/SemaTemplate/nested-template.cpp
    clang/tools/libclang/CIndex.cpp
    clang/tools/libclang/CursorVisitor.h
    clang/unittests/AST/DeclTest.cpp
    clang/unittests/AST/StructuralEquivalenceTest.cpp
    clang/www/cxx_dr_status.html

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 50118e0472075..5fa23416949c4 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -1029,9 +1029,6 @@ void Serializer::parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
   llvm::SmallVector<FriendInfo, 4> LocalFriends;
 
   for (const FriendDecl *FD : D->friends()) {
-    if (FD->isUnsupportedFriend())
-      continue;
-
     FriendInfo F(InfoType::IT_friend, getUSRForDecl(FD));
     const auto *ActualDecl = FD->getFriendDecl();
     if (!ActualDecl) {

diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 70ea2e35090c6..59ebcc4444b28 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -123,6 +123,7 @@ features cannot lower the translation-unit ABI level;
 ## What's New in Clang {{env.config.release}}?
 
 ### C++ Language Changes
+- Clang now supports friend declarations with a dependent nested name specifier. (#GH104057)
 
 #### C++2d Feature Support
 

diff  --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index a8a73c5b72d33..e00c3dd537b0d 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -791,6 +791,14 @@ class ASTNodeTraverser
     }
   }
 
+  void VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+    for (const TemplateParameterList *TPL : D->getTemplateParameterLists())
+      dumpTemplateParameters(TPL);
+    if (D->getFriendKind() !=
+        FriendTemplateDecl::FriendTemplateEntityKind::Template)
+      VisitFriendDecl(D);
+  }
+
   void VisitObjCMethodDecl(const ObjCMethodDecl *D) {
     if (D->isThisDeclarationADefinition())
       dumpDeclContext(D);

diff  --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 1f8c210263677..6d2659337a9c8 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -15,18 +15,12 @@
 #define LLVM_CLANG_AST_DECLFRIEND_H
 
 #include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/ExternalASTSource.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/LLVM.h"
-#include "clang/Basic/SourceLocation.h"
-#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
-#include "llvm/Support/TrailingObjects.h"
 #include <cassert>
 #include <iterator>
 
@@ -49,9 +43,7 @@ class ASTContext;
 /// @endcode
 ///
 /// The semantic context of a friend decl is its declaring class.
-class FriendDecl final
-    : public Decl,
-      private llvm::TrailingObjects<FriendDecl, TemplateParameterList *> {
+class FriendDecl : public Decl {
   LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION();
 
 public:
@@ -61,46 +53,28 @@ class FriendDecl final
   friend class CXXRecordDecl;
   friend class CXXRecordDecl::friend_iterator;
 
+  // Location of the '...', if present.
+  SourceLocation EllipsisLoc;
+
+  SourceLocation FriendLoc;
+
+protected:
   // The declaration that's a friend of this class.
   FriendUnion Friend;
 
-  // A pointer to the next friend in the sequence.
   LazyDeclPtr NextFriend;
 
-  // Location of the 'friend' specifier.
-  SourceLocation FriendLoc;
-
-  // Location of the '...', if present.
-  SourceLocation EllipsisLoc;
-
-  /// True if this 'friend' declaration is unsupported.  Eventually we
-  /// will support every possible friend declaration, but for now we
-  /// silently ignore some and set this flag to authorize all access.
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned UnsupportedFriend : 1;
-
-  // The number of "outer" template parameter lists in non-templatic
-  // (currently unsupported) friend type declarations, such as
-  //     template <class T> friend class A<T>::B;
-  unsigned NumTPLists : 31;
-
-  FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend,
-             SourceLocation FriendL, SourceLocation EllipsisLoc,
-             ArrayRef<TemplateParameterList *> FriendTypeTPLists)
-      : Decl(Decl::Friend, DC, L), Friend(Friend), FriendLoc(FriendL),
-        EllipsisLoc(EllipsisLoc), UnsupportedFriend(false),
-        NumTPLists(FriendTypeTPLists.size()) {
-    llvm::copy(FriendTypeTPLists, getTrailingObjects());
-  }
+  FriendDecl(Kind K, DeclContext *DC, SourceLocation L, FriendUnion Friend,
+             SourceLocation FL, SourceLocation EllipsisLoc = {})
+      : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FL), Friend(Friend),
+        NextFriend() {}
 
-  FriendDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
-      : Decl(Decl::Friend, Empty), UnsupportedFriend(false),
-        NumTPLists(NumFriendTypeTPLists) {}
+  FriendDecl(Kind K, EmptyShell Empty) : Decl(K, Empty) {}
 
   FriendDecl *getNextFriend() {
-    if (!NextFriend.isOffset())
-      return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
-    return getNextFriendSlowCase();
+    if (NextFriend.isOffset())
+      return getNextFriendSlowCase();
+    return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
   }
 
   FriendDecl *getNextFriendSlowCase();
@@ -109,14 +83,11 @@ class FriendDecl final
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
   friend class ASTNodeImporter;
-  friend TrailingObjects;
 
-  static FriendDecl *
-  Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend_,
-         SourceLocation FriendL, SourceLocation EllipsisLoc = {},
-         ArrayRef<TemplateParameterList *> FriendTypeTPLists = {});
-  static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                        unsigned FriendTypeNumTPLists);
+  static FriendDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L,
+                            FriendUnion Friend, SourceLocation FriendL,
+                            SourceLocation EllipsisLoc = {});
+  static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
 
   /// If this friend declaration names an (untemplated but possibly
   /// dependent) type, return the type; otherwise return null.  This
@@ -126,72 +97,26 @@ class FriendDecl final
     return Friend.dyn_cast<TypeSourceInfo*>();
   }
 
-  unsigned getFriendTypeNumTemplateParameterLists() const {
-    return NumTPLists;
-  }
-
-  TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const {
-    return getTrailingObjects(NumTPLists)[N];
-  }
-
   /// If this friend declaration doesn't name a type, return the inner
   /// declaration.
-  NamedDecl *getFriendDecl() const {
+  virtual NamedDecl *getFriendDecl() const {
     return Friend.dyn_cast<NamedDecl *>();
   }
 
-  /// Retrieves the location of the 'friend' keyword.
-  SourceLocation getFriendLoc() const {
-    return FriendLoc;
-  }
-
   /// Retrieves the location of the '...', if present.
   SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
 
-  /// Retrieves the source range for the friend declaration.
-  SourceRange getSourceRange() const override LLVM_READONLY {
-    if (TypeSourceInfo *TInfo = getFriendType()) {
-      SourceLocation StartL = (NumTPLists == 0)
-                                  ? getFriendLoc()
-                                  : getTrailingObjects()[0]->getTemplateLoc();
-      SourceLocation EndL = isPackExpansion() ? getEllipsisLoc()
-                                              : TInfo->getTypeLoc().getEndLoc();
-      return SourceRange(StartL, EndL);
-    }
-
-    if (isPackExpansion())
-      return SourceRange(getFriendLoc(), getEllipsisLoc());
-
-    if (NamedDecl *ND = getFriendDecl()) {
-      if (const auto *FD = dyn_cast<FunctionDecl>(ND))
-        return FD->getSourceRange();
-      if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
-        return FTD->getSourceRange();
-      if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
-        return CTD->getSourceRange();
-      if (const auto *DD = dyn_cast<DeclaratorDecl>(ND)) {
-        if (DD->getOuterLocStart() != DD->getInnerLocStart())
-          return DD->getSourceRange();
-      }
-      return SourceRange(getFriendLoc(), ND->getEndLoc());
-    }
-
-    return SourceRange(getFriendLoc(), getLocation());
-  }
+  SourceLocation getFriendLoc() const { return FriendLoc; }
 
-  /// Determines if this friend kind is unsupported.
-  bool isUnsupportedFriend() const {
-    return UnsupportedFriend;
-  }
-  void setUnsupportedFriend(bool Unsupported) {
-    UnsupportedFriend = Unsupported;
-  }
+  SourceRange getSourceRange() const override LLVM_READONLY;
 
   bool isPackExpansion() const { return EllipsisLoc.isValid(); }
 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
-  static bool classofKind(Kind K) { return K == Decl::Friend; }
+  static bool classofKind(Kind K) {
+    return K >= firstFriend && K <= lastFriend;
+  }
 };
 
 /// An iterator over the friend declarations of a class.

diff  --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index ed1a517247943..f5b26b0aab9ba 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -19,6 +19,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/Redeclarable.h"
 #include "clang/AST/TemplateBase.h"
@@ -2457,76 +2458,78 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
 /// \code
 /// template \<typename T> class A {
 ///   friend class MyVector<T>; // not a friend template
-///   template \<typename U> friend class B; // not a friend template
+///   template \<typename U> friend class B; // friend class template
 ///   template \<typename U> friend class Foo<T>::Nested; // friend template
 /// };
 /// \endcode
-///
-/// \note This class is not currently in use.  All of the above
-/// will yield a FriendDecl, not a FriendTemplateDecl.
-class FriendTemplateDecl : public Decl {
-  virtual void anchor();
-
-public:
-  using FriendUnion = llvm::PointerUnion<NamedDecl *,TypeSourceInfo *>;
+class FriendTemplateDecl final
+    : public FriendDecl,
+      private llvm::TrailingObjects<FriendTemplateDecl,
+                                    TemplateParameterList *> {
+  void anchor() override;
 
 private:
-  // The number of template parameters;  always non-zero.
-  unsigned NumParams = 0;
+  unsigned NumTPLists = 0;
+  TemplateName Template;
 
-  // The parameter list.
-  TemplateParameterList **Params = nullptr;
-
-  // The declaration that's a friend of this class.
-  FriendUnion Friend;
-
-  // Location of the 'friend' specifier.
-  SourceLocation FriendLoc;
-
-  FriendTemplateDecl(DeclContext *DC, SourceLocation Loc,
-                     TemplateParameterList **Params, unsigned NumParams,
-                     FriendUnion Friend, SourceLocation FriendLoc)
-      : Decl(Decl::FriendTemplate, DC, Loc), NumParams(NumParams),
-        Params(Params), Friend(Friend), FriendLoc(FriendLoc) {}
+  FriendTemplateDecl(DeclContext *DC, SourceLocation Loc, FriendUnion Friend,
+                     SourceLocation FriendLoc, SourceLocation EllipsisLoc,
+                     ArrayRef<TemplateParameterList *> FriendTPLists,
+                     TemplateName Template = {})
+      : FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc,
+                   EllipsisLoc),
+        NumTPLists(FriendTPLists.size()), Template(Template) {
+    assert(!FriendTPLists.empty());
+    llvm::copy(FriendTPLists, getTrailingObjects());
+  }
 
-  FriendTemplateDecl(EmptyShell Empty) : Decl(Decl::FriendTemplate, Empty) {}
+  FriendTemplateDecl(EmptyShell Empty, unsigned NumFriendTPLists)
+      : FriendDecl(Decl::FriendTemplate, Empty), NumTPLists(NumFriendTPLists) {
+    assert(NumFriendTPLists != 0);
+  }
 
 public:
   friend class ASTDeclReader;
+  friend class ASTDeclWriter;
+  friend TrailingObjects;
+
+  enum class FriendTemplateEntityKind { Type, Template, Decl };
+
+  static FriendTemplateDecl *
+  Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
+         FriendUnion Friend, SourceLocation FriendLoc,
+         ArrayRef<TemplateParameterList *> FriendTPLists,
+         SourceLocation EllipsisLoc = {}, TemplateName Template = {});
 
   static FriendTemplateDecl *
   Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
-         MutableArrayRef<TemplateParameterList *> Params, FriendUnion Friend,
-         SourceLocation FriendLoc);
+         TemplateName Template, SourceLocation FriendLoc,
+         ArrayRef<TemplateParameterList *> FriendTPLists,
+         SourceLocation EllipsisLoc = {});
 
-  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
+  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                                unsigned NumFriendTPLists);
 
-  /// If this friend declaration names a templated type (or
-  /// a dependent member type of a templated type), return that
-  /// type;  otherwise return null.
-  TypeSourceInfo *getFriendType() const {
-    return Friend.dyn_cast<TypeSourceInfo*>();
-  }
+  SourceRange getSourceRange() const override LLVM_READONLY;
 
-  /// If this friend declaration names a templated function (or
-  /// a member function of a templated type), return that type;
-  /// otherwise return null.
-  NamedDecl *getFriendDecl() const {
-    return Friend.dyn_cast<NamedDecl*>();
-  }
+  TemplateName getFriendTemplateName() const { return Template; }
 
-  /// Retrieves the location of the 'friend' keyword.
-  SourceLocation getFriendLoc() const {
-    return FriendLoc;
+  FriendTemplateEntityKind getFriendKind() const {
+    if (getFriendType())
+      return FriendTemplateEntityKind::Type;
+    if (Template.isNull())
+      return FriendTemplateEntityKind::Decl;
+    return FriendTemplateEntityKind::Template;
   }
 
-  TemplateParameterList *getTemplateParameterList(unsigned i) const {
-    assert(i <= NumParams);
-    return Params[i];
+  NamedDecl *getFriendDecl() const override {
+    if (NamedDecl *ND = Friend.dyn_cast<NamedDecl *>())
+      return ND;
+    return Template.getAsTemplateDecl();
   }
 
-  unsigned getNumTemplateParameters() const {
-    return NumParams;
+  ArrayRef<TemplateParameterList *> getTemplateParameterLists() const {
+    return ArrayRef(getTrailingObjects(), NumTPLists);
   }
 
   // Implement isa/cast/dyncast/etc.

diff  --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h
index 4e8d1649bbf8b..679ce4e4815ae 100644
--- a/clang/include/clang/AST/JSONNodeDumper.h
+++ b/clang/include/clang/AST/JSONNodeDumper.h
@@ -268,6 +268,7 @@ class JSONNodeDumper
   void VisitLinkageSpecDecl(const LinkageSpecDecl *LSD);
   void VisitAccessSpecDecl(const AccessSpecDecl *ASD);
   void VisitFriendDecl(const FriendDecl *FD);
+  void VisitFriendTemplateDecl(const FriendTemplateDecl *FD);
   void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D);
 
   void VisitObjCIvarDecl(const ObjCIvarDecl *D);

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 12204dcfb5938..d435cda19fa49 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1739,17 +1739,15 @@ DEF_TRAVERSE_DECL(FriendDecl, {
 })
 
 DEF_TRAVERSE_DECL(FriendTemplateDecl, {
+  const TemplateName Template = D->getFriendTemplateName();
   if (D->getFriendType())
     TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
+  else if (!Template.isNull())
+    TRY_TO(TraverseTemplateName(Template));
   else
     TRY_TO(TraverseDecl(D->getFriendDecl()));
-  for (unsigned I = 0, E = D->getNumTemplateParameters(); I < E; ++I) {
-    TemplateParameterList *TPL = D->getTemplateParameterList(I);
-    for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = TPL->end();
-         ITPL != ETPL; ++ITPL) {
-      TRY_TO(TraverseDecl(*ITPL));
-    }
-  }
+  for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+    TRY_TO(TraverseTemplateParameterListHelper(TPL));
 })
 
 DEF_TRAVERSE_DECL(LinkageSpecDecl, {})

diff  --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 41ddd88a8326c..1eaafd49c2f06 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -399,6 +399,7 @@ class TextNodeDumper
   void VisitLinkageSpecDecl(const LinkageSpecDecl *D);
   void VisitAccessSpecDecl(const AccessSpecDecl *D);
   void VisitFriendDecl(const FriendDecl *D);
+  void VisitFriendTemplateDecl(const FriendTemplateDecl *D);
   void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D);
   void VisitObjCIvarDecl(const ObjCIvarDecl *D);
   void VisitObjCMethodDecl(const ObjCMethodDecl *D);

diff  --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 8d1dc4936cc72..114c6ae5282ef 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -99,7 +99,7 @@ def FileScopeAsm : DeclNode<Decl>;
 def TopLevelStmt : DeclNode<Decl>, DeclContext;
 def AccessSpec : DeclNode<Decl>;
 def Friend : DeclNode<Decl>;
-def FriendTemplate : DeclNode<Decl>;
+def FriendTemplate : DeclNode<Friend>;
 def StaticAssert : DeclNode<Decl>;
 def ExplicitInstantiation : DeclNode<Decl>;
 def CXXExpansionStmt : DeclNode<Decl>, DeclContext;

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index d44f69a815784..a6cc17980e2c2 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1206,7 +1206,6 @@ def Attributes : DiagGroup<"attributes", [UnknownAttributes,
 def UnknownSanitizers : DiagGroup<"unknown-sanitizers">;
 def UnnamedTypeTemplateArgs : DiagGroup<"unnamed-type-template-args",
                                         [CXX98CompatUnnamedTypeTemplateArgs]>;
-def UnsupportedFriend : DiagGroup<"unsupported-friend">;
 def UnusedArgument : DiagGroup<"unused-argument">;
 def UnusedCommandLineArgument : DiagGroup<"unused-command-line-argument">;
 def IgnoredOptimizationArgument : DiagGroup<"ignored-optimization-argument">;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b314c17ad27bd..7c1e61120b0f2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1934,16 +1934,6 @@ def err_friend_not_first_in_declaration : Error<
   "'friend' must appear first in a non-function declaration">;
 def err_using_decl_friend : Error<
   "cannot befriend target of using declaration">;
-def warn_template_qualified_friend_unsupported
-    : Warning<
-          "dependent nested name specifier %0 for friend class declaration is "
-          "not supported; turning off access control for %1">,
-      InGroup<UnsupportedFriend>;
-def warn_template_qualified_friend_ignored
-    : Warning<"dependent nested name specifier %0 for friend template "
-              "declaration is "
-              "not supported; ignoring this friend declaration">,
-      InGroup<UnsupportedFriend>;
 def ext_friend_tag_redecl_outside_namespace : ExtWarn<
   "unqualified friend declaration referring to type outside of the nearest "
   "enclosing namespace is a Microsoft extension; add a nested name specifier">,
@@ -1953,6 +1943,16 @@ def err_friend_template_decl_multiple_specifiers: Error<
   "a friend declaration that befriends a template must contain exactly one type-specifier">;
 def friend_template_decl_malformed_pack_expansion : Error<
   "friend declaration expands pack %0 that is declared it its own template parameter list">;
+def err_pack_indexing_in_friend : Error<
+  "a pack indexing specifier cannot be used in a nested name specifier of a "
+  "friend declaration">;
+def err_dependent_friend_not_member_of_template_spec : Error<
+  "%0 does not name a class template">;
+def err_dependent_friend_not_member : Error<
+  "friend declaration does not name a member of a class template specialization">;
+def err_dependent_friend_undeduced_params : Error<
+  "%select{template parameter|template parameters}0 of friend declaration "
+  "cannot be deduced from %1">;
 
 def err_invalid_base_in_interface : Error<
   "interface type cannot inherit from "
@@ -5325,11 +5325,16 @@ def note_ovl_candidate_deduced_mismatch : Note<
     "adjusted type of %select{|element of }4argument}1,2%3">;
 def note_ovl_candidate_non_deduced_mismatch : Note<
     "candidate template ignored: could not match %
diff {$ against $|types}0,1">;
+def note_friend_template_non_deduced_mismatch : Note<
+    "candidate friend template ignored: could not match "
+    "%
diff {$ against $|types}0,1">;
 // This note is needed because the above note would sometimes print two
 // 
diff erent types with the same name.  Remove this note when the above note
 // can handle that case properly.
 def note_ovl_candidate_non_deduced_mismatch_qualified : Note<
     "candidate template ignored: could not match %q0 against %q1">;
+def note_friend_template_non_deduced_mismatch_qualified : Note<
+    "candidate friend template ignored: could not match %q0 against %q1">;
 
 // Note that we don't treat templates 
diff erently for this diagnostic.
 def note_ovl_candidate_arity : Note<"candidate "

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d931e70cb2342..3cb0dcbbba83a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6402,7 +6402,15 @@ class Sema final : public SemaBase {
                                      SourceLocation NameLoc,
                                      SourceLocation EllipsisLoc,
                                      const ParsedAttributesView &Attr,
-                                     MultiTemplateParamsArg TempParamLists);
+                                     MultiTemplateParamsArg TempParamLists,
+                                     TemplateIdAnnotation *TemplateId);
+
+  bool CheckDependentFriend(SourceLocation Loc, NestedNameSpecifierLoc NNSLoc,
+                            ArrayRef<TemplateParameterList *> TPLs,
+                            bool IsInstantiation);
+
+  bool DiagnosePackIndexingInFriendNNS(SourceLocation Loc,
+                                       NestedNameSpecifierLoc NNSLoc);
 
   MSPropertyDecl *HandleMSProperty(Scope *S, RecordDecl *TagD,
                                    SourceLocation DeclStart, Declarator &D,
@@ -12735,6 +12743,18 @@ class Sema final : public SemaBase {
             return false;
           });
 
+  /// Perform [temp.friend] p5 template argument deduction for a dependent
+  /// friend declaration and a candidate class template specialization.
+  bool DeduceTemplateArguments(FriendTemplateDecl *FTD,
+                               ClassTemplateDecl *PatternCTD,
+                               ClassTemplateDecl *CandidateCTD,
+                               ArrayRef<TemplateParameterList *> TPLs,
+                               ArrayRef<TemplateArgument> PatternArgs,
+                               ArrayRef<TemplateArgument> CandidateArgs,
+                               SourceLocation Loc,
+                               TemplateSpecCandidateSet *FailedTSC,
+                               MultiLevelTemplateArgumentList &DeducedArgs);
+
   /// Perform template argument deduction from a function call
   /// (C++ [temp.deduct.call]).
   ///
@@ -12961,7 +12981,8 @@ class Sema final : public SemaBase {
                                   llvm::SmallBitVector &Used);
 
   void MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
-                                  unsigned Depth, llvm::SmallBitVector &Used);
+                                  bool OnlyDeduced, unsigned Depth,
+                                  llvm::SmallBitVector &Used);
 
   void MarkUsedTemplateParameters(ArrayRef<TemplateArgumentLoc> TemplateArgs,
                                   unsigned Depth, llvm::SmallBitVector &Used);
@@ -13816,6 +13837,11 @@ class Sema final : public SemaBase {
                             const MultiLevelTemplateArgumentList &TemplateArgs,
                             SourceLocation Loc, DeclarationName Entity);
 
+  TypeSourceInfo *
+  SubstFriendType(TypeSourceInfo *TSI,
+                  const MultiLevelTemplateArgumentList &TemplateArgs,
+                  SourceLocation Loc, DeclarationName Entity);
+
   /// A form of SubstType intended specifically for instantiating the
   /// type of a FunctionDecl.  Its purpose is solely to force the
   /// instantiation of default-argument expressions and to avoid

diff  --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index a3340d2f4a044..50e950e56c6ca 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -721,7 +721,7 @@ enum class TemplateSubstitutionKind : char {
 
     // Helper functions for instantiating methods.
     TypeSourceInfo *SubstFunctionType(FunctionDecl *D,
-                             SmallVectorImpl<ParmVarDecl *> &Params);
+                                      SmallVectorImpl<ParmVarDecl *> &Params);
     bool InitFunctionInstantiation(FunctionDecl *New, FunctionDecl *Tmpl);
     bool InitMethodInstantiation(CXXMethodDecl *New, CXXMethodDecl *Tmpl);
 
@@ -730,6 +730,10 @@ enum class TemplateSubstitutionKind : char {
     TemplateParameterList *
       SubstTemplateParams(TemplateParameterList *List);
 
+    bool SubstTemplateParameterLists(
+        ArrayRef<TemplateParameterList *> TPLs,
+        SmallVectorImpl<TemplateParameterList *> &InstTPLs);
+
     bool SubstQualifier(const DeclaratorDecl *OldDecl,
                         DeclaratorDecl *NewDecl);
     bool SubstQualifier(const TagDecl *OldDecl,
@@ -740,6 +744,8 @@ enum class TemplateSubstitutionKind : char {
         ArrayRef<TemplateArgument> Converted,
         VarTemplateSpecializationDecl *PrevDecl = nullptr);
 
+    bool InstantiateFriendPackExpansion(FriendDecl *D);
+
     Decl *InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias);
     Decl *InstantiateTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
     ClassTemplatePartialSpecializationDecl *

diff  --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h
index 39c909d73f565..dd9fe46c9bf8a 100644
--- a/clang/include/clang/Sema/TemplateDeduction.h
+++ b/clang/include/clang/Sema/TemplateDeduction.h
@@ -311,6 +311,11 @@ struct DeductionFailureInfo {
   }
 };
 
+enum class TemplateSpecCandidateSetKind {
+  Normal,
+  FriendTemplate,
+};
+
 /// TemplateSpecCandidate - This is a generalization of OverloadCandidate
 /// which keeps track of template argument deduction failure info, when
 /// handling explicit specializations (and instantiations) of templates
@@ -337,7 +342,8 @@ struct TemplateSpecCandidate {
   }
 
   /// Diagnose a template argument deduction failure.
-  void NoteDeductionFailure(Sema &S, bool ForTakingAddress);
+  void NoteDeductionFailure(Sema &S, bool ForTakingAddress,
+                            TemplateSpecCandidateSetKind CandidateSetKind);
 };
 
 /// TemplateSpecCandidateSet - A set of generalized overload candidates,
@@ -353,11 +359,16 @@ class TemplateSpecCandidateSet {
   // attribute on parameters.
   bool ForTakingAddress;
 
+  TemplateSpecCandidateSetKind CandidateSetKind;
+
   void destroyCandidates();
 
 public:
-  TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false)
-      : Loc(Loc), ForTakingAddress(ForTakingAddress) {}
+  TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false,
+                           TemplateSpecCandidateSetKind CandidateSetKind =
+                               TemplateSpecCandidateSetKind::Normal)
+      : Loc(Loc), ForTakingAddress(ForTakingAddress),
+        CandidateSetKind(CandidateSetKind) {}
   TemplateSpecCandidateSet(const TemplateSpecCandidateSet &) = delete;
   TemplateSpecCandidateSet &
   operator=(const TemplateSpecCandidateSet &) = delete;

diff  --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index b582cbdadc070..974761b236411 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2123,6 +2123,14 @@ enum CtorInitializerType {
   CTOR_INITIALIZER_INDIRECT_MEMBER
 };
 
+/// Kinds of friend payloads owned by FriendTemplateDecl.
+enum FriendTemplateDeclKind {
+  FTDK_Type = 0,
+  FTDK_Decl = 1,
+  FTDK_Template = 2,
+  FTDK_Dependent = 3,
+};
+
 /// Kinds of cleanup objects owned by ExprWithCleanups.
 enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };
 

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 01c5385ff5bb5..725ebbd6ec987 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -185,6 +185,8 @@ namespace clang {
       return Importer.importInto(To, From);
     }
 
+    Expected<FriendDecl::FriendUnion> importFriendUnion(FriendDecl *D);
+
     // Use this to import pointers of specific type.
     template <typename ImportT>
     [[nodiscard]] Error importInto(ImportT *&To, ImportT *From) {
@@ -539,6 +541,7 @@ namespace clang {
     ExpectedDecl VisitFieldDecl(FieldDecl *D);
     ExpectedDecl VisitIndirectFieldDecl(IndirectFieldDecl *D);
     ExpectedDecl VisitFriendDecl(FriendDecl *D);
+    ExpectedDecl VisitFriendTemplateDecl(FriendTemplateDecl *D);
     ExpectedDecl VisitObjCIvarDecl(ObjCIvarDecl *D);
     ExpectedDecl VisitVarDecl(VarDecl *D);
     ExpectedDecl VisitImplicitParamDecl(ImplicitParamDecl *D);
@@ -4392,19 +4395,15 @@ struct FriendCountAndPosition {
 
 static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
                                FriendDecl *FD2) {
-  if ((!FD1->getFriendType()) != (!FD2->getFriendType()))
+  if (FD1->getKind() != FD2->getKind())
     return false;
 
-  if (const TypeSourceInfo *TSI = FD1->getFriendType())
-    return Importer.IsStructurallyEquivalent(
-        TSI->getType(), FD2->getFriendType()->getType(), /*Complain=*/false);
-
   ASTImporter::NonEquivalentDeclSet NonEquivalentDecls;
   StructuralEquivalenceContext Ctx(
       Importer.getToContext().getLangOpts(), FD1->getASTContext(),
       FD2->getASTContext(), NonEquivalentDecls,
       StructuralEquivalenceKind::Default,
-      /* StrictTypeSpelling = */ false, /* Complain = */ false);
+      /*StrictTypeSpelling=*/false, /*Complain=*/false);
   return Ctx.IsEquivalent(FD1, FD2);
 }
 
@@ -4424,10 +4423,30 @@ static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
   }
 
   assert(FriendPosition && "Friend decl not found in own parent.");
-
   return {FriendCount, *FriendPosition};
 }
 
+Expected<FriendDecl::FriendUnion>
+ASTNodeImporter::importFriendUnion(FriendDecl *D) {
+  if (NamedDecl *FriendD = D->getFriendDecl()) {
+    NamedDecl *ToFriendD;
+    if (Error Err = importInto(ToFriendD, FriendD))
+      return std::move(Err);
+
+    if (FriendD->getFriendObjectKind() != Decl::FOK_None &&
+        !FriendD->isInIdentifierNamespace(Decl::IDNS_NonMemberOperator))
+      ToFriendD->setObjectOfFriendDecl(false);
+
+    return ToFriendD;
+  }
+
+  // The friend is a type, not a decl.
+  auto TSIOrErr = import(D->getFriendType());
+  if (TSIOrErr)
+    return *TSIOrErr;
+  return TSIOrErr.takeError();
+}
+
 ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   // Import the major distinguishing characteristics of a declaration.
   DeclContext *DC, *LexicalDC;
@@ -4454,32 +4473,10 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
 
   // Not found. Create it.
   // The declarations will be put into order later by ImportDeclContext.
-  FriendDecl::FriendUnion ToFU;
-  if (NamedDecl *FriendD = D->getFriendDecl()) {
-    NamedDecl *ToFriendD;
-    if (Error Err = importInto(ToFriendD, FriendD))
-      return std::move(Err);
-
-    if (FriendD->getFriendObjectKind() != Decl::FOK_None &&
-        !(FriendD->isInIdentifierNamespace(Decl::IDNS_NonMemberOperator)))
-      ToFriendD->setObjectOfFriendDecl(false);
-
-    ToFU = ToFriendD;
-  } else { // The friend is a type, not a decl.
-    if (auto TSIOrErr = import(D->getFriendType()))
-      ToFU = *TSIOrErr;
-    else
-      return TSIOrErr.takeError();
-  }
-
-  SmallVector<TemplateParameterList *, 1> ToTPLists(D->NumTPLists);
-  auto **FromTPLists = D->getTrailingObjects();
-  for (unsigned I = 0; I < D->NumTPLists; I++) {
-    if (auto ListOrErr = import(FromTPLists[I]))
-      ToTPLists[I] = *ListOrErr;
-    else
-      return ListOrErr.takeError();
-  }
+  auto ToFUOrErr = importFriendUnion(D);
+  if (!ToFUOrErr)
+    return ToFUOrErr.takeError();
+  FriendDecl::FriendUnion ToFU = *ToFUOrErr;
 
   auto LocationOrErr = import(D->getLocation());
   if (!LocationOrErr)
@@ -4494,7 +4491,7 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   FriendDecl *FrD;
   if (GetImportedOrCreateDecl(FrD, D, Importer.getToContext(), DC,
                               *LocationOrErr, ToFU, *FriendLocOrErr,
-                              *EllipsisLocOrErr, ToTPLists))
+                              *EllipsisLocOrErr))
     return FrD;
 
   FrD->setAccess(D->getAccess());
@@ -4503,6 +4500,74 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   return FrD;
 }
 
+ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+  DeclContext *DC, *LexicalDC;
+  if (Error Err = ImportDeclContext(D, DC, LexicalDC))
+    return std::move(Err);
+
+  const auto *RD = cast<CXXRecordDecl>(DC);
+  SmallVector<FriendTemplateDecl *, 2> ImportedEquivalentFriends;
+  for (FriendDecl *ImportedFriend : RD->friends()) {
+    auto *ImportedFriendTemplate = dyn_cast<FriendTemplateDecl>(ImportedFriend);
+    if (ImportedFriendTemplate &&
+        IsEquivalentFriend(Importer, D, ImportedFriendTemplate))
+      ImportedEquivalentFriends.push_back(ImportedFriendTemplate);
+  }
+
+  FriendCountAndPosition CountAndPosition =
+      getFriendCountAndPosition(Importer, D);
+  assert(ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount &&
+         "Class with non-matching friends is imported, ODR check wrong?");
+
+  if (ImportedEquivalentFriends.size() == CountAndPosition.TotalCount)
+    return Importer.MapImported(
+        D, ImportedEquivalentFriends[CountAndPosition.IndexOfDecl]);
+
+  FriendTemplateDecl::FriendUnion ToFU;
+  if (D->getFriendKind() !=
+      FriendTemplateDecl::FriendTemplateEntityKind::Template) {
+    auto ToFUOrErr = importFriendUnion(D);
+    if (!ToFUOrErr)
+      return ToFUOrErr.takeError();
+    ToFU = *ToFUOrErr;
+  }
+
+  TemplateName ToTemplate;
+  const TemplateName FromTemplate = D->getFriendTemplateName();
+  if (!FromTemplate.isNull()) {
+    if (Error Err = importInto(ToTemplate, FromTemplate))
+      return std::move(Err);
+  }
+
+  ArrayRef<TemplateParameterList *> FromTPLs = D->getTemplateParameterLists();
+  SmallVector<TemplateParameterList *, 1> ToTPLs(FromTPLs.size());
+  if (Error Err = ImportContainerChecked(FromTPLs, ToTPLs))
+    return std::move(Err);
+
+  auto LocationOrErr = import(D->getLocation());
+  if (!LocationOrErr)
+    return LocationOrErr.takeError();
+
+  auto FriendLocOrErr = import(D->getFriendLoc());
+  if (!FriendLocOrErr)
+    return FriendLocOrErr.takeError();
+
+  auto EllipsisLocOrErr = import(D->getEllipsisLoc());
+  if (!EllipsisLocOrErr)
+    return EllipsisLocOrErr.takeError();
+
+  FriendTemplateDecl *FTD;
+  if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
+                              *LocationOrErr, ToFU, *FriendLocOrErr, ToTPLs,
+                              *EllipsisLocOrErr, ToTemplate))
+    return FTD;
+
+  FTD->setAccess(D->getAccess());
+  FTD->setLexicalDeclContext(LexicalDC);
+  LexicalDC->addDeclInternal(FTD);
+  return FTD;
+}
+
 ExpectedDecl ASTNodeImporter::VisitObjCIvarDecl(ObjCIvarDecl *D) {
   // Import the major distinguishing characteristics of an ivar.
   DeclContext *DC, *LexicalDC;

diff  --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index d8bbfbe5dac72..ebf4507e6e67d 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2297,7 +2297,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
       return false;
   }
 
-  return true;
+  return IsStructurallyEquivalent(Context, Params1->getRequiresClause(),
+                                  Params2->getRequiresClause());
 }
 
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
@@ -2434,10 +2435,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
 
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
                                      FriendDecl *D1, FriendDecl *D2) {
+  if (D1->isPackExpansion() != D2->isPackExpansion())
+    return false;
+
   if ((D1->getFriendType() && D2->getFriendDecl()) ||
-      (D1->getFriendDecl() && D2->getFriendType())) {
-      return false;
-  }
+      (D1->getFriendDecl() && D2->getFriendType()))
+    return false;
   if (D1->getFriendType() && D2->getFriendType())
     return IsStructurallyEquivalent(Context,
                                     D1->getFriendType()->getType(),
@@ -2448,6 +2451,47 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
   return false;
 }
 
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+                                     FriendTemplateDecl *FTD1,
+                                     FriendTemplateDecl *FTD2) {
+  if (FTD1->isPackExpansion() != FTD2->isPackExpansion())
+    return false;
+
+  ArrayRef<TemplateParameterList *> TPL1 = FTD1->getTemplateParameterLists();
+  ArrayRef<TemplateParameterList *> TPL2 = FTD2->getTemplateParameterLists();
+  if (!llvm::equal(
+          TPL1, TPL2,
+          [&Context](TemplateParameterList *LHS, TemplateParameterList *RHS) {
+            return IsStructurallyEquivalent(Context, LHS, RHS);
+          }))
+    return false;
+
+  auto FK1 = FTD1->getFriendKind();
+  auto FK2 = FTD2->getFriendKind();
+  if (FK1 != FK2)
+    return false;
+
+  switch (FK1) {
+  case FriendTemplateDecl::FriendTemplateEntityKind::Type: {
+    const TemplateName TN1 = FTD1->getFriendTemplateName();
+    const TemplateName TN2 = FTD2->getFriendTemplateName();
+    if (TN1.isNull() != TN2.isNull())
+      return false;
+    if (!IsStructurallyEquivalent(Context, FTD1->getFriendType()->getType(),
+                                  FTD2->getFriendType()->getType()))
+      return false;
+    return TN1.isNull() || IsStructurallyEquivalent(Context, TN1, TN2);
+  }
+  case FriendTemplateDecl::FriendTemplateEntityKind::Template:
+    return IsStructurallyEquivalent(Context, FTD1->getFriendTemplateName(),
+                                    FTD2->getFriendTemplateName());
+  case FriendTemplateDecl::FriendTemplateEntityKind::Decl:
+    return IsStructurallyEquivalent(Context, static_cast<FriendDecl *>(FTD1),
+                                    static_cast<FriendDecl *>(FTD2));
+  }
+  llvm_unreachable("unknown friend template kind");
+}
+
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
                                      TypedefNameDecl *D1, TypedefNameDecl *D2) {
   if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))

diff  --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index 6bfc2eb62b284..d730b4f00fba2 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -13,28 +13,18 @@
 
 #include "clang/AST/DeclFriend.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
-#include "clang/Basic/LLVM.h"
+#include "clang/AST/ExternalASTSource.h"
 #include <cassert>
-#include <cstddef>
 
 using namespace clang;
 
 void FriendDecl::anchor() {}
 
-FriendDecl *FriendDecl::getNextFriendSlowCase() {
-  return cast_or_null<FriendDecl>(
-                           NextFriend.get(getASTContext().getExternalSource()));
-}
-
-FriendDecl *
-FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
-                   FriendUnion Friend, SourceLocation FriendL,
-                   SourceLocation EllipsisLoc,
-                   ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
+FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
+                               FriendUnion Friend, SourceLocation FriendL,
+                               SourceLocation EllipsisLoc) {
 #ifndef NDEBUG
   if (const auto *D = dyn_cast<NamedDecl *>(Friend)) {
     assert(isa<FunctionDecl>(D) ||
@@ -46,25 +36,22 @@ FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
     // to the original declaration when instantiating members.
     assert(D->getFriendObjectKind() ||
            (cast<CXXRecordDecl>(DC)->getTemplateSpecializationKind()));
-    // These template parameters are for friend types only.
-    assert(FriendTypeTPLists.empty());
   }
 #endif
 
-  std::size_t Extra =
-      FriendDecl::additionalSizeToAlloc<TemplateParameterList *>(
-          FriendTypeTPLists.size());
-  auto *FD = new (C, DC, Extra)
-      FriendDecl(DC, L, Friend, FriendL, EllipsisLoc, FriendTypeTPLists);
+  auto *FD =
+      new (C, DC) FriendDecl(Decl::Friend, DC, L, Friend, FriendL, EllipsisLoc);
   cast<CXXRecordDecl>(DC)->pushFriendDecl(FD);
   return FD;
 }
 
-FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                           unsigned FriendTypeNumTPLists) {
-  std::size_t Extra =
-      additionalSizeToAlloc<TemplateParameterList *>(FriendTypeNumTPLists);
-  return new (C, ID, Extra) FriendDecl(EmptyShell(), FriendTypeNumTPLists);
+FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
+  return new (C, ID) FriendDecl(Decl::Friend, EmptyShell());
+}
+
+FriendDecl *FriendDecl::getNextFriendSlowCase() {
+  return cast_or_null<FriendDecl>(
+      NextFriend.get(getASTContext().getExternalSource()));
 }
 
 FriendDecl *CXXRecordDecl::getFirstFriend() const {
@@ -72,3 +59,29 @@ FriendDecl *CXXRecordDecl::getFirstFriend() const {
   Decl *First = data().FirstFriend.get(Source);
   return First ? cast<FriendDecl>(First) : nullptr;
 }
+
+SourceRange FriendDecl::getSourceRange() const {
+  if (TypeSourceInfo *TInfo = getFriendType()) {
+    SourceLocation EndL =
+        isPackExpansion() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc();
+    return SourceRange(getFriendLoc(), EndL);
+  }
+
+  if (isPackExpansion())
+    return SourceRange(getFriendLoc(), getEllipsisLoc());
+
+  if (NamedDecl *ND = getFriendDecl()) {
+    if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+      return FD->getSourceRange();
+    if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+      return FTD->getSourceRange();
+    if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
+      return CTD->getSourceRange();
+    if (const auto *DD = dyn_cast<DeclaratorDecl>(ND)) {
+      if (DD->getOuterLocStart() != DD->getInnerLocStart())
+        return DD->getSourceRange();
+    }
+    return SourceRange(getFriendLoc(), ND->getEndLoc());
+  }
+  return SourceRange(getFriendLoc(), getLocation());
+}

diff  --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 4be3e977b815e..1312ffd080342 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -70,6 +70,7 @@ namespace {
     void VisitEmptyDecl(EmptyDecl *D);
     void VisitFunctionDecl(FunctionDecl *D);
     void VisitFriendDecl(FriendDecl *D);
+    void VisitFriendTemplateDecl(FriendTemplateDecl *D);
     void VisitFieldDecl(FieldDecl *D);
     void VisitVarDecl(VarDecl *D);
     void VisitLabelDecl(LabelDecl *D);
@@ -889,24 +890,17 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
 
 void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
   if (TypeSourceInfo *TSI = D->getFriendType()) {
-    unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
-    for (unsigned i = 0; i < NumTPLists; ++i)
-      printTemplateParameters(D->getFriendTypeTemplateParameterList(i));
     Out << "friend ";
     Out << TSI->getType().getAsString(Policy);
-  }
-  else if (FunctionDecl *FD =
-      dyn_cast<FunctionDecl>(D->getFriendDecl())) {
+  } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D->getFriendDecl())) {
     Out << "friend ";
     VisitFunctionDecl(FD);
-  }
-  else if (FunctionTemplateDecl *FTD =
-           dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) {
+  } else if (FunctionTemplateDecl *FTD =
+                 dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) {
     Out << "friend ";
     VisitFunctionTemplateDecl(FTD);
-  }
-  else if (ClassTemplateDecl *CTD =
-           dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) {
+  } else if (ClassTemplateDecl *CTD =
+                 dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) {
     Out << "friend ";
     VisitRedeclarableTemplateDecl(CTD);
   }
@@ -915,6 +909,27 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
     Out << "...";
 }
 
+void DeclPrinter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+  for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+    printTemplateParameters(TPL);
+
+  TemplateName TN = D->getFriendTemplateName();
+  if (D->getFriendType() || TN.isNull()) {
+    VisitFriendDecl(D);
+  } else {
+    Out << "friend ";
+    if (auto *CTD =
+            dyn_cast_if_present<ClassTemplateDecl>(TN.getAsTemplateDecl()))
+      Out << CTD->getTemplatedDecl()->getKindName() << ' ';
+    TN.print(Out, Policy,
+             Policy.SuppressScope ? TemplateName::Qualified::None
+                                  : TemplateName::Qualified::AsWritten);
+
+    if (D->isPackExpansion())
+      Out << "...";
+  }
+}
+
 void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
   prettyPrintPragmas(D);
   // FIXME: add printing of pragma attributes if required.

diff  --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 3ff6e3a3221fb..6f7ce21227338 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1236,21 +1236,52 @@ void FriendTemplateDecl::anchor() {}
 
 FriendTemplateDecl *
 FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC,
-                           SourceLocation L,
-                           MutableArrayRef<TemplateParameterList *> Params,
-                           FriendUnion Friend, SourceLocation FLoc) {
-  TemplateParameterList **TPL = nullptr;
-  if (!Params.empty()) {
-    TPL = new (Context) TemplateParameterList *[Params.size()];
-    llvm::copy(Params, TPL);
-  }
-  return new (Context, DC)
-      FriendTemplateDecl(DC, L, TPL, Params.size(), Friend, FLoc);
+                           SourceLocation Loc, FriendUnion Friend,
+                           SourceLocation FriendLoc,
+                           ArrayRef<TemplateParameterList *> FriendTPLists,
+                           SourceLocation EllipsisLoc, TemplateName Template) {
+  std::size_t Extra =
+      FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          FriendTPLists.size());
+  auto *FTD = new (Context, DC, Extra) FriendTemplateDecl(
+      DC, Loc, Friend, FriendLoc, EllipsisLoc, FriendTPLists, Template);
+  cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD);
+  return FTD;
 }
 
-FriendTemplateDecl *FriendTemplateDecl::CreateDeserialized(ASTContext &C,
-                                                           GlobalDeclID ID) {
-  return new (C, ID) FriendTemplateDecl(EmptyShell());
+FriendTemplateDecl *
+FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC,
+                           SourceLocation Loc, TemplateName Template,
+                           SourceLocation FriendLoc,
+                           ArrayRef<TemplateParameterList *> FriendTPLists,
+                           SourceLocation EllipsisLoc) {
+  auto *Friend = Template.getAsTemplateDecl();
+  assert(Friend && "friend template name must be resolved");
+  std::size_t Extra =
+      FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          FriendTPLists.size());
+  auto *FTD = new (Context, DC, Extra) FriendTemplateDecl(
+      DC, Loc, Friend, FriendLoc, EllipsisLoc, FriendTPLists, Template);
+  cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD);
+  return FTD;
+}
+
+FriendTemplateDecl *
+FriendTemplateDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                       unsigned NumFriendTPLists) {
+  std::size_t Extra =
+      FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          NumFriendTPLists);
+  return new (C, ID, Extra) FriendTemplateDecl(EmptyShell(), NumFriendTPLists);
+}
+
+SourceRange FriendTemplateDecl::getSourceRange() const {
+  SourceLocation Begin = getTemplateParameterLists().front()->getTemplateLoc();
+  SourceLocation End =
+      !Template.isNull() && !getFriendType()
+          ? (isPackExpansion() ? getEllipsisLoc() : getLocation())
+          : FriendDecl::getSourceRange().getEnd();
+  return SourceRange(Begin, End);
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index af54f15410789..b9c2272b97a4b 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1151,6 +1151,20 @@ void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) {
   attributeOnlyIfTrue("isPackExpansion", FD->isPackExpansion());
 }
 
+void JSONNodeDumper::VisitFriendTemplateDecl(const FriendTemplateDecl *FD) {
+  if (FD->getFriendKind() !=
+      FriendTemplateDecl::FriendTemplateEntityKind::Template) {
+    VisitFriendDecl(FD);
+    return;
+  }
+
+  llvm::SmallString<128> Str;
+  llvm::raw_svector_ostream OS(Str);
+  FD->getFriendTemplateName().print(OS, PrintPolicy);
+  JOS.attribute("templateName", Str);
+  attributeOnlyIfTrue("isPackExpansion", FD->isPackExpansion());
+}
+
 void JSONNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
   VisitNamedDecl(D);
   JOS.attribute("type", createQualType(D->getType()));

diff  --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..297d6ba5b7686 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -164,7 +164,9 @@ void ODRHash::AddTemplateName(TemplateName Name) {
   case TemplateName::AssumedTemplate:
   case TemplateName::SubstTemplateTemplateParm:
   case TemplateName::SubstTemplateTemplateParmPack:
+    break;
   case TemplateName::UsingTemplate:
+    AddDecl(Name.getAsUsingShadowDecl()->getTargetDecl());
     break;
   case TemplateName::DeducedTemplate:
     llvm_unreachable("Unexpected DeducedTemplate");
@@ -220,6 +222,11 @@ void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {
   for (auto *ND : TPL->asArray()) {
     AddSubDecl(ND);
   }
+
+  const Expr *RequiresClause = TPL->getRequiresClause();
+  AddBoolean(RequiresClause);
+  if (RequiresClause)
+    AddStmt(RequiresClause);
 }
 
 void ODRHash::clear() {
@@ -473,6 +480,26 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
     Hash.AddBoolean(D->isPackExpansion());
   }
 
+  void VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+    for (const TemplateParameterList *TPL : D->getTemplateParameterLists())
+      Hash.AddTemplateParameterList(TPL);
+
+    bool IsTemplateFriend =
+        D->getFriendKind() ==
+        FriendTemplateDecl::FriendTemplateEntityKind::Template;
+    Hash.AddBoolean(!IsTemplateFriend);
+    if (!IsTemplateFriend) {
+      VisitFriendDecl(D);
+      if (D->getFriendKind() ==
+              FriendTemplateDecl::FriendTemplateEntityKind::Type &&
+          !D->getFriendTemplateName().isNull())
+        Hash.AddTemplateName(D->getFriendTemplateName());
+    } else {
+      Hash.AddTemplateName(D->getFriendTemplateName());
+      Hash.AddBoolean(D->isPackExpansion());
+    }
+  }
+
   void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
     // Only care about default arguments as part of the definition.
     const bool hasDefaultArgument =
@@ -557,6 +584,7 @@ bool ODRHash::isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent) {
     case Decl::EnumConstant: // Only found in EnumDecl's.
     case Decl::Field:
     case Decl::Friend:
+    case Decl::FriendTemplate:
     case Decl::FunctionTemplate:
     case Decl::StaticAssert:
     case Decl::TypeAlias:

diff  --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 9bcc0280bbe40..d07767aab2068 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -3051,6 +3051,18 @@ void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) {
     OS << "...";
 }
 
+void TextNodeDumper::VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+  if (D->getFriendKind() !=
+      FriendTemplateDecl::FriendTemplateEntityKind::Template) {
+    VisitFriendDecl(D);
+    return;
+  }
+
+  dumpBareTemplateName(D->getFriendTemplateName());
+  if (D->isPackExpansion())
+    OS << "...";
+}
+
 void TextNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
   dumpName(D);
   dumpType(D->getType());

diff  --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp
index df875e0b40079..8b32a48483a67 100644
--- a/clang/lib/Index/IndexDecl.cpp
+++ b/clang/lib/Index/IndexDecl.cpp
@@ -763,6 +763,15 @@ class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> {
     return true;
   }
 
+  bool VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+    const NamedDecl *ND = cast<NamedDecl>(D->getDeclContext());
+    if (!D->getFriendType() && D->getFriendTemplateName().isNull())
+      ND = D->getFriendDecl();
+    for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+      indexTemplateParameters(TPL, ND);
+    return VisitFriendDecl(D);
+  }
+
   bool VisitImportDecl(const ImportDecl *D) {
     return IndexCtx.importedModule(D);
   }

diff  --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index a3617c3db49c4..0bdea354c2406 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1986,7 +1986,10 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
 
   bool Owned = false;
   SkipBodyInfo SkipBody;
-  if (TemplateId) {
+  if (TemplateId &&
+      (TUK != TagUseKind::Friend ||
+       TemplateInfo.Kind != ParsedTemplateKind::Template ||
+       TemplateId->isInvalid() || !TemplateId->Template.get().isDependent())) {
     // Explicit specialization, class template partial specialization,
     // or explicit instantiation.
     ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
@@ -2006,10 +2009,6 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
           TemplateId->TemplateNameLoc, TemplateId->LAngleLoc, TemplateArgsPtr,
           TemplateId->RAngleLoc, attrs);
 
-      // Friend template-ids are treated as references unless
-      // they have template headers, in which case they're ill-formed
-      // (FIXME: "template <class T> friend class A<T>::B<int>;").
-      // We diagnose this error in ActOnClassTemplateSpecialization.
     } else if (TUK == TagUseKind::Reference ||
                (TUK == TagUseKind::Friend &&
                 TemplateInfo.Kind == ParsedTemplateKind::NonTemplate)) {
@@ -2106,11 +2105,17 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
       SkipUntil(tok::semi, StopBeforeMatch);
     }
 
+    if (TemplateId) {
+      Name = nullptr;
+      NameLoc = TemplateId->TemplateNameLoc;
+    }
+
     TagOrTempResult = Actions.ActOnTemplatedFriendTag(
         getCurScope(), DS.getFriendSpecLoc(), TagType, StartLoc, SS, Name,
         NameLoc, EllipsisLoc, attrs,
         MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] : nullptr,
-                               TemplateParams ? TemplateParams->size() : 0));
+                               TemplateParams ? TemplateParams->size() : 0),
+        TemplateId);
   } else {
     if (TUK != TagUseKind::Declaration && TUK != TagUseKind::Definition)
       ProhibitCXX11Attributes(attrs, diag::err_attributes_not_allowed,

diff  --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 0f291900d5612..f1e328ccba426 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1178,8 +1178,9 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
   for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
                                       E = RD->friend_end();
        I != E && Complete; ++I) {
+    FriendDecl *Friend = *I;
     // Check if friend classes and methods are complete.
-    if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
+    if (TypeSourceInfo *TSI = Friend->getFriendType()) {
       // Friend classes are available as the TypeSourceInfo of the FriendDecl.
       if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl())
         Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete);
@@ -1188,7 +1189,7 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
     } else {
       // Friend functions are available through the NamedDecl of FriendDecl.
       if (const FunctionDecl *FD =
-          dyn_cast<FunctionDecl>((*I)->getFriendDecl()))
+              dyn_cast<FunctionDecl>(Friend->getFriendDecl()))
         Complete = FD->isDefined();
       else
         // This is a template friend, give up.

diff  --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 9b4f59613d33b..cc2af8feb3794 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -21,6 +21,8 @@
 #include "clang/Sema/DelayedDiagnostic.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
+#include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateDeduction.h"
 #include "llvm/ADT/ScopeExit.h"
 
 using namespace clang;
@@ -272,7 +274,163 @@ struct AccessTarget : public AccessedEntity {
   mutable const CXXRecordDecl *InstanceContext;
   const CXXRecordDecl *DeclaringClass;
 };
+} // namespace
 
+static CanQual<FunctionProtoType> GetCanonicalFunctionProto(ASTContext &Context,
+                                                            QualType Ty) {
+  return Context.getCanonicalType(Ty)->getAs<FunctionProtoType>();
+}
+
+static CanQual<FunctionProtoType>
+GetCanonicalFunctionProto(ASTContext &Context, const FunctionDecl *FD) {
+  return GetCanonicalFunctionProto(Context, FD->getType());
+}
+
+static const TemplateSpecializationType *
+GetQualifierClassTemplateSpecializationType(ASTContext &Context,
+                                            NestedNameSpecifier NNS) {
+  if (!NNS || NNS.getKind() != NestedNameSpecifier::Kind::Type)
+    return nullptr;
+
+  QualType Ty(NNS.getAsType(), 0);
+  if (const auto *ICNT = Ty->getAs<InjectedClassNameType>())
+    Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(Context);
+
+  const auto *TST = Ty->getAsNonAliasTemplateSpecializationType();
+  if (TST && isa_and_nonnull<ClassTemplateDecl>(
+                 TST->getTemplateName().getAsTemplateDecl()))
+    return TST;
+
+  return nullptr;
+}
+
+static FunctionTemplateDecl *TryGetFunctionTemplateDecl(FunctionDecl *FD) {
+  if (auto *FTD = FD->getPrimaryTemplate())
+    return FTD->getCanonicalDecl();
+
+  if (auto *FTD = FD->getDescribedFunctionTemplate())
+    return FTD->getCanonicalDecl();
+
+  if (FunctionDecl *Pattern =
+          FD->getTemplateInstantiationPattern(/*ForDefinition=*/false)) {
+    if (auto *FTD = Pattern->getDescribedFunctionTemplate())
+      return FTD->getCanonicalDecl();
+    if (auto *FTD = Pattern->getPrimaryTemplate())
+      return FTD->getCanonicalDecl();
+  }
+
+  return nullptr;
+}
+
+static ClassTemplateDecl *GetClassTemplatePattern(ClassTemplateDecl *CTD) {
+  while (ClassTemplateDecl *Pattern = CTD->getInstantiatedFromMemberTemplate())
+    CTD = Pattern;
+  return CTD;
+}
+
+static ClassTemplateDecl *GetClassTemplateDecl(CXXRecordDecl *RD) {
+  if (auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+    return Spec->getSpecializedTemplate();
+  return RD->getDescribedClassTemplate();
+}
+
+static TemplateParameterList *
+SubstTemplateParameterList(Sema &S, TemplateParameterList *TPL, DeclContext *DC,
+                           const MultiLevelTemplateArgumentList &Args) {
+  TemplateParameterList *InstTPL =
+      S.SubstTemplateParams(TPL, DC, Args,
+                            /*EvaluateConstraints=*/false);
+  if (!InstTPL || !TPL->getRequiresClause())
+    return InstTPL;
+
+  ExprResult InstRequiresClause =
+      S.SubstConstraintExprWithoutSatisfaction(TPL->getRequiresClause(), Args);
+  if (!InstRequiresClause.isUsable())
+    return nullptr;
+
+  return TemplateParameterList::Create(
+      S.Context, InstTPL->getTemplateLoc(), InstTPL->getLAngleLoc(),
+      InstTPL->asArray(), InstTPL->getRAngleLoc(), InstRequiresClause.get());
+}
+
+static AccessResult
+DeduceTemplateArguments(Sema &S, FriendTemplateDecl *FTD, DeclContext *DC,
+                        const TemplateSpecializationType *TST,
+                        ArrayRef<TemplateParameterList *> TPLs,
+                        TemplateSpecCandidateSet *FailedTSC,
+                        MultiLevelTemplateArgumentList &DeducedArgs) {
+  const auto *CandidateRD = dyn_cast<CXXRecordDecl>(DC);
+  if (!CandidateRD)
+    return AR_inaccessible;
+
+  ClassTemplateDecl *CandidateCTD = CandidateRD->getDescribedClassTemplate();
+  ArrayRef<TemplateArgument> CandidateArgs;
+  if (CandidateCTD) {
+    CandidateArgs = CandidateCTD->getInjectedTemplateArgs(S.Context);
+  } else {
+    const auto *CandidateSpec =
+        dyn_cast<ClassTemplateSpecializationDecl>(CandidateRD);
+    if (!CandidateSpec)
+      return AR_inaccessible;
+    CandidateCTD = CandidateSpec->getSpecializedTemplate();
+    CandidateArgs = CandidateSpec->getTemplateArgs().asArray();
+  }
+
+  auto *PatternCTD = dyn_cast_if_present<ClassTemplateDecl>(
+      TST->getTemplateName().getAsTemplateDecl());
+  if (!PatternCTD || !declaresSameEntity(GetClassTemplatePattern(CandidateCTD),
+                                         GetClassTemplatePattern(PatternCTD)))
+    return AR_inaccessible;
+
+  if (S.DeduceTemplateArguments(FTD, PatternCTD, CandidateCTD, TPLs,
+                                TST->template_arguments(), CandidateArgs,
+                                FTD->getLocation(), FailedTSC, DeducedArgs))
+    return AR_accessible;
+
+  return CandidateRD->isDependentContext() ? AR_dependent : AR_inaccessible;
+}
+
+class FriendTemplateMatchContext {
+  Sema &S;
+  FriendTemplateDecl *FTD;
+  Sema::InstantiatingTemplate Inst;
+  TemplateDeductionInfo Info;
+  MultiLevelTemplateArgumentList DeducedArgs;
+  Sema::SFINAETrap Trap;
+  LocalInstantiationScope InstantiationScope;
+  AccessResult Result = AR_inaccessible;
+
+public:
+  FriendTemplateMatchContext(Sema &S, FriendTemplateDecl *FTD)
+      : S(S), FTD(FTD), Inst(S, FTD->getLocation(), FTD),
+        Info(FTD->getLocation()), Trap(S, Info), InstantiationScope(S) {}
+
+  AccessResult deduce(DeclContext *DC, const TemplateSpecializationType *TST,
+                      ArrayRef<TemplateParameterList *> TPLs,
+                      TemplateSpecCandidateSet *FailedTSC) {
+    if (Inst.isInvalid())
+      return Result = AR_inaccessible;
+    return Result = DeduceTemplateArguments(S, FTD, DC, TST, TPLs, FailedTSC,
+                                            DeducedArgs);
+  }
+
+  AccessResult getAccessResult() const { return Result; }
+  MultiLevelTemplateArgumentList &getDeducedArgs() { return DeducedArgs; }
+
+  bool hasDeducedArgs() const { return Result == AR_accessible; }
+  bool hasErrorOccurred() const { return Trap.hasErrorOccurred(); }
+};
+
+static bool HasSameFunctionType(Sema &S, QualType FriendType,
+                                QualType ContextType, SourceLocation Loc) {
+  if (!S.Context.hasSameFunctionTypeIgnoringExceptionSpec(FriendType,
+                                                          ContextType))
+    return false;
+
+  const auto *FriendFPT = FriendType->castAs<FunctionProtoType>();
+  const auto *ContextFPT = ContextType->castAs<FunctionProtoType>();
+  return !S.CheckEquivalentExceptionSpec(S.PDiag(), S.PDiag(), FriendFPT, Loc,
+                                         ContextFPT, Loc);
 }
 
 /// Checks whether one class might instantiate to the other.
@@ -284,8 +442,12 @@ static bool MightInstantiateTo(const CXXRecordDecl *From,
 
   const DeclContext *FromDC = From->getDeclContext()->getPrimaryContext();
   const DeclContext *ToDC = To->getDeclContext()->getPrimaryContext();
-  if (FromDC == ToDC) return true;
-  if (FromDC->isFileContext() || ToDC->isFileContext()) return false;
+
+  if (FromDC == ToDC)
+    return true;
+
+  if (FromDC->isFileContext() || ToDC->isFileContext())
+    return false;
 
   // Be conservative.
   return true;
@@ -343,9 +505,7 @@ static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived,
   return OnFailure;
 }
 
-
-static bool MightInstantiateTo(Sema &S, DeclContext *Context,
-                               DeclContext *Friend) {
+static bool MightInstantiateTo(DeclContext *Context, DeclContext *Friend) {
   if (Friend == Context)
     return true;
 
@@ -364,7 +524,7 @@ static bool MightInstantiateTo(Sema &S, DeclContext *Context,
 
 // Asks whether the type in 'context' can ever instantiate to the type
 // in 'friend'.
-static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) {
+static bool MightInstantiateTo(CanQualType Context, CanQualType Friend) {
   if (Friend == Context)
     return true;
 
@@ -375,49 +535,66 @@ static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend)
   return true;
 }
 
-static bool MightInstantiateTo(Sema &S,
-                               FunctionDecl *Context,
-                               FunctionDecl *Friend) {
-  if (Context->getDeclName() != Friend->getDeclName())
+static bool MightInstantiateTo(CanQual<FunctionProtoType> Context,
+                               CanQual<FunctionProtoType> Friend) {
+  if (Friend.getQualifiers() != Context.getQualifiers())
+    return false;
+
+  if (Friend->getNumParams() != Context->getNumParams())
     return false;
 
-  if (!MightInstantiateTo(S,
-                          Context->getDeclContext(),
-                          Friend->getDeclContext()))
+  if (!MightInstantiateTo(Context->getReturnType(), Friend->getReturnType()))
     return false;
 
-  CanQual<FunctionProtoType> FriendTy
-    = S.Context.getCanonicalType(Friend->getType())
-         ->getAs<FunctionProtoType>();
-  CanQual<FunctionProtoType> ContextTy
-    = S.Context.getCanonicalType(Context->getType())
-         ->getAs<FunctionProtoType>();
+  for (unsigned I = 0, E = Friend->getNumParams(); I != E; ++I)
+    if (!MightInstantiateTo(Context->getParamType(I), Friend->getParamType(I)))
+      return false;
+
+  return true;
+}
+
+static bool MightInstantiateTo(ASTContext &Ctx, DeclarationName Context,
+                               DeclarationName Friend) {
+  if (Context == Friend)
+    return true;
 
-  // There isn't any way that I know of to add qualifiers
-  // during instantiation.
-  if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
+  if (Context.getNameKind() != Friend.getNameKind())
     return false;
 
-  if (FriendTy->getNumParams() != ContextTy->getNumParams())
+  switch (Context.getNameKind()) {
+  case DeclarationName::CXXConstructorName:
+  case DeclarationName::CXXDestructorName:
+  case DeclarationName::CXXConversionFunctionName:
+    return MightInstantiateTo(Ctx.getCanonicalType(Context.getCXXNameType()),
+                              Ctx.getCanonicalType(Friend.getCXXNameType()));
+
+  default:
     return false;
+  }
+}
 
-  if (!MightInstantiateTo(S, ContextTy->getReturnType(),
-                          FriendTy->getReturnType()))
+static bool MightInstantiateTo(ASTContext &Ctx, FunctionDecl *Context,
+                               FunctionDecl *Friend) {
+  if (!MightInstantiateTo(Ctx, Context->getDeclName(), Friend->getDeclName()))
     return false;
 
-  for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I)
-    if (!MightInstantiateTo(S, ContextTy->getParamType(I),
-                            FriendTy->getParamType(I)))
-      return false;
+  DeclContext *ContextDC = Context->getDeclContext();
+  DeclContext *FriendDC = Friend->getDeclContext();
 
-  return true;
+  if (!FriendDC->isDependentContext() &&
+      !MightInstantiateTo(ContextDC, FriendDC))
+    return false;
+
+  CanQual<FunctionProtoType> FriendTy = GetCanonicalFunctionProto(Ctx, Friend);
+  CanQual<FunctionProtoType> ContextTy =
+      GetCanonicalFunctionProto(Ctx, Context);
+
+  return MightInstantiateTo(ContextTy, FriendTy);
 }
 
-static bool MightInstantiateTo(Sema &S,
-                               FunctionTemplateDecl *Context,
+static bool MightInstantiateTo(ASTContext &Ctx, FunctionTemplateDecl *Context,
                                FunctionTemplateDecl *Friend) {
-  return MightInstantiateTo(S,
-                            Context->getTemplatedDecl(),
+  return MightInstantiateTo(Ctx, Context->getTemplatedDecl(),
                             Friend->getTemplatedDecl());
 }
 
@@ -478,7 +655,7 @@ static AccessResult MatchesFriend(Sema &S,
     }
 
     // It's a match.
-    if (Friend == CTD->getCanonicalDecl())
+    if (declaresSameEntity(Friend, CTD))
       return AR_accessible;
 
     // If the context isn't dependent, it can't be a dependent match.
@@ -492,8 +669,7 @@ static AccessResult MatchesFriend(Sema &S,
 
     // If the class's context can't instantiate to the friend's
     // context, it can't be a dependent match.
-    if (!MightInstantiateTo(S, CTD->getDeclContext(),
-                            Friend->getDeclContext()))
+    if (!MightInstantiateTo(CTD->getDeclContext(), Friend->getDeclContext()))
       continue;
 
     // Otherwise, it's a dependent match.
@@ -515,7 +691,7 @@ static AccessResult MatchesFriend(Sema &S,
     if (Friend == *I)
       return AR_accessible;
 
-    if (EC.isDependent() && MightInstantiateTo(S, *I, Friend))
+    if (EC.isDependent() && MightInstantiateTo(S.Context, *I, Friend))
       OnFailure = AR_dependent;
   }
 
@@ -534,21 +710,390 @@ static AccessResult MatchesFriend(Sema &S,
   for (SmallVectorImpl<FunctionDecl*>::const_iterator
          I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
 
-    FunctionTemplateDecl *FTD = (*I)->getPrimaryTemplate();
-    if (!FTD)
-      FTD = (*I)->getDescribedFunctionTemplate();
+    FunctionTemplateDecl *FTD = TryGetFunctionTemplateDecl(*I);
     if (!FTD)
       continue;
 
-    FTD = FTD->getCanonicalDecl();
-
     if (Friend == FTD)
       return AR_accessible;
 
-    if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend))
+    if (EC.isDependent() && MightInstantiateTo(S.Context, FTD, Friend))
+      OnFailure = AR_dependent;
+  }
+
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  NamedDecl *ND) {
+  ND = cast<NamedDecl>(ND->getCanonicalDecl());
+  if (ClassTemplateDecl *CTD = dyn_cast<ClassTemplateDecl>(ND))
+    return MatchesFriend(S, EC, CTD);
+
+  if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+    return MatchesFriend(S, EC, FTD);
+
+  if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(ND))
+    return MatchesFriend(S, EC, RD);
+
+  assert(isa<FunctionDecl>(ND) && "unknown friend decl kind");
+  return MatchesFriend(S, EC, cast<FunctionDecl>(ND));
+}
+
+static AccessResult MatchesFriend(Sema &S, FriendTemplateDecl *FTD,
+                                  DeclarationName FriendName,
+                                  TagTypeKind FriendTagKind,
+                                  ClassTemplateDecl *ContextCTD,
+                                  const TemplateSpecializationType *FriendTST,
+                                  ArrayRef<TemplateParameterList *> TPLs,
+                                  TemplateParameterList *MemberTPL,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  if (FriendName != ContextCTD->getDeclName())
+    return AR_inaccessible;
+
+  if ((FriendTagKind == TagTypeKind::Union) !=
+      ContextCTD->getTemplatedDecl()->isUnion())
+    return AR_inaccessible;
+
+  DeclContext *ContextDC = ContextCTD->getDeclContext();
+  AccessResult OnFailure =
+      ContextDC->isDependentContext() ? AR_dependent : AR_inaccessible;
+
+  FriendTemplateMatchContext FTMC(S, FTD);
+  AccessResult Result = FTMC.deduce(ContextDC, FriendTST, TPLs, FailedTSC);
+  if (!FTMC.hasDeducedArgs())
+    return Result;
+
+  TemplateParameterList *InstTPL = SubstTemplateParameterList(
+      S, MemberTPL, ContextDC, FTMC.getDeducedArgs());
+  if (!InstTPL || FTMC.hasErrorOccurred())
+    return OnFailure;
+
+  Sema::TemplateCompareNewDeclInfo FriendInfo(
+      ContextDC, FTD->getLexicalDeclContext(), FTD->getLocation());
+  if (S.TemplateParameterListsAreEqual(
+          FriendInfo, InstTPL, ContextCTD, ContextCTD->getTemplateParameters(),
+          /*Complain=*/false, Sema::TPL_TemplateMatch))
+    return AR_accessible;
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD,
+                                  ClassTemplateDecl *FriendCTD,
+                                  NestedNameSpecifier Qualifier,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  const auto *FriendTST =
+      GetQualifierClassTemplateSpecializationType(S.Context, Qualifier);
+  if (!FriendTST)
+    return MatchesFriend(S, EC, FriendCTD);
+
+  ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
+
+  AccessResult OnFailure = AR_inaccessible;
+  for (CXXRecordDecl *ContextRD : EC.Records) {
+    ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD);
+    if (!ContextCTD)
+      continue;
+
+    AccessResult Result =
+        MatchesFriend(S, FTD, FriendCTD->getDeclName(),
+                      FriendCTD->getTemplatedDecl()->getTagKind(), ContextCTD,
+                      FriendTST, TPLs.drop_back(), TPLs.back(), FailedTSC);
+    if (Result == AR_accessible)
+      return AR_accessible;
+    if (Result == AR_dependent)
+      OnFailure = AR_dependent;
+  }
+
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD,
+                                  TemplateName FriendTemplate,
+                                  ClassTemplateDecl *FriendCTD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  NestedNameSpecifier Qualifier = FriendTemplate.getQualifier();
+  if (FriendTemplate.getAsUsingShadowDecl())
+    Qualifier = FriendCTD->getTemplatedDecl()->getQualifier();
+  return MatchesFriend(S, EC, FTD, FriendCTD, Qualifier, FailedTSC);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD,
+                                  ClassTemplateDecl *FriendCTD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  return MatchesFriend(S, EC, FTD, FriendCTD,
+                       FriendCTD->getTemplatedDecl()->getQualifier(),
+                       FailedTSC);
+}
+
+static AccessResult MatchesFriend(Sema &S, FriendTemplateDecl *FTD,
+                                  FunctionDecl *FriendFD,
+                                  FunctionDecl *ContextFD,
+                                  const TemplateSpecializationType *FriendTST,
+                                  ArrayRef<TemplateParameterList *> TPLs,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  if (!MightInstantiateTo(S.Context, ContextFD->getDeclName(),
+                          FriendFD->getDeclName()))
+    return AR_inaccessible;
+
+  FunctionTemplateDecl *FriendTemplate =
+      FriendFD->getDescribedFunctionTemplate();
+  FunctionTemplateDecl *ContextTemplate = TryGetFunctionTemplateDecl(ContextFD);
+
+  if (FriendTemplate && !ContextTemplate)
+    return AR_inaccessible;
+
+  DeclContext *ContextDC = ContextFD->getDeclContext();
+  AccessResult OnFailure =
+      ContextDC->isDependentContext() ? AR_dependent : AR_inaccessible;
+
+  FriendTemplateMatchContext FTMC(S, FTD);
+  AccessResult Result = FTMC.deduce(ContextDC, FriendTST, TPLs, FailedTSC);
+  if (!FTMC.hasDeducedArgs())
+    return Result;
+
+  Sema::TemplateCompareNewDeclInfo FriendInfo(
+      ContextDC, FTD->getLexicalDeclContext(), FTD->getLocation());
+  if (FriendTemplate) {
+    TemplateParameterList *InstTPL =
+        SubstTemplateParameterList(S, FriendTemplate->getTemplateParameters(),
+                                   ContextDC, FTMC.getDeducedArgs());
+    if (!InstTPL || !S.TemplateParameterListsAreEqual(
+                        FriendInfo, InstTPL, ContextTemplate,
+                        ContextTemplate->getTemplateParameters(),
+                        /*Complain=*/false, Sema::TPL_TemplateMatch))
+      return OnFailure;
+
+    ContextFD = ContextTemplate->getTemplatedDecl();
+  }
+
+  Sema::ContextRAII SavedContext(S, FTD->getDeclContext());
+  QualType InstFriendType =
+      S.SubstType(FriendFD->getType(), FTMC.getDeducedArgs(),
+                  FriendFD->getLocation(), FriendFD->getDeclName());
+  SavedContext.pop();
+  if (InstFriendType.isNull() || FTMC.hasErrorOccurred())
+    return OnFailure;
+
+  if (ContextTemplate && !FriendTemplate) {
+    AccessResult OnSpecializationFailure =
+        ContextFD->isDependentContext() ? AR_dependent : OnFailure;
+    const ASTTemplateArgumentListInfo *ArgsWritten =
+        FriendFD->getTemplateSpecializationArgsAsWritten();
+    TemplateArgumentListInfo InstArgs;
+    if (ArgsWritten) {
+      InstArgs.setLAngleLoc(ArgsWritten->getLAngleLoc());
+      InstArgs.setRAngleLoc(ArgsWritten->getRAngleLoc());
+      if (S.SubstTemplateArguments(ArgsWritten->arguments(),
+                                   FTMC.getDeducedArgs(), InstArgs))
+        return OnSpecializationFailure;
+    }
+
+    FunctionDecl *ContextSpecialization = nullptr;
+    TemplateDeductionInfo FunctionInfo(FTD->getLocation());
+    if (S.DeduceTemplateArguments(
+            ContextTemplate, ArgsWritten ? &InstArgs : nullptr, InstFriendType,
+            ContextSpecialization,
+            FunctionInfo) != TemplateDeductionResult::Success ||
+        !ContextSpecialization || FTMC.hasErrorOccurred() ||
+        !declaresSameEntity(ContextSpecialization, ContextFD))
+      return OnSpecializationFailure;
+
+    ContextFD = ContextSpecialization;
+  }
+
+  if (!HasSameFunctionType(S, InstFriendType, ContextFD->getType(),
+                           FTD->getLocation()) ||
+      FTMC.hasErrorOccurred())
+    return OnFailure;
+
+  if (!FriendTemplate)
+    return AR_accessible;
+
+  AssociatedConstraint FriendRequiresClause =
+      FriendFD->getTrailingRequiresClause();
+  AssociatedConstraint ContextRequiresClause =
+      ContextFD->getTrailingRequiresClause();
+  if (FriendRequiresClause.isNull() != ContextRequiresClause.isNull())
+    return AR_inaccessible;
+
+  if (!FriendRequiresClause)
+    return AR_accessible;
+
+  ExprResult InstFriendRequiresClause =
+      S.SubstConstraintExprWithoutSatisfaction(
+          const_cast<Expr *>(FriendRequiresClause.ConstraintExpr),
+          FTMC.getDeducedArgs());
+
+  if (!InstFriendRequiresClause.isUsable())
+    return OnFailure;
+
+  if (!S.AreConstraintExpressionsEqual(
+          ContextFD, ContextRequiresClause.ConstraintExpr, FriendInfo,
+          InstFriendRequiresClause.get()))
+    return OnFailure;
+  return FTMC.hasErrorOccurred() ? AR_inaccessible : AR_accessible;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD,
+                                  FunctionDecl *FriendFD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
+      S.Context, FriendFD->getQualifier());
+  if (!FriendTST)
+    return AR_inaccessible;
+
+  ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
+
+  AccessResult OnFailure = AR_inaccessible;
+  for (FunctionDecl *ContextFD : EC.Functions) {
+    AccessResult Result =
+        MatchesFriend(S, FTD, FriendFD, ContextFD, FriendTST, TPLs, FailedTSC);
+    if (Result == AR_accessible)
+      return AR_accessible;
+
+    if (Result == AR_dependent)
       OnFailure = AR_dependent;
   }
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD, NamedDecl *Friend,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  TemplateName FriendTemplate = FTD->getFriendTemplateName();
+  if (auto *FriendCTD = dyn_cast_if_present<ClassTemplateDecl>(
+          FriendTemplate.getAsTemplateDecl()))
+    return MatchesFriend(S, EC, FTD, FriendTemplate, FriendCTD, FailedTSC);
+  if (auto *FriendCTD = dyn_cast<ClassTemplateDecl>(Friend))
+    return MatchesFriend(S, EC, FTD, FriendCTD, FailedTSC);
+  if (FunctionDecl *FriendFD = Friend->getAsFunction())
+    return MatchesFriend(S, EC, FTD, FriendFD, FailedTSC);
+  return MatchesFriend(S, EC, Friend);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD,
+                                  TypeSourceInfo *FriendTSI,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  QualType FriendType = FriendTSI->getType();
+  if (!FriendType->isDependentType())
+    return MatchesFriend(S, EC, S.Context.getCanonicalType(FriendType));
+
+  AccessResult OnFailure = AR_inaccessible;
+  if (auto FriendTSTL =
+          FriendTSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>()) {
+    const auto *FriendTST = FriendTSTL.getTypePtr();
+    const auto *FriendQTST = GetQualifierClassTemplateSpecializationType(
+        S.Context, FriendTSTL.getQualifierLoc().getNestedNameSpecifier());
+    if (!FriendQTST)
+      return OnFailure;
+
+    ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
+
+    TemplateName FriendTemplate = FriendTST->getTemplateName();
+    DeclarationName FriendName;
+    if (TemplateDecl *TD = FriendTemplate.getAsTemplateDecl())
+      FriendName = TD->getDeclName();
+    else if (DependentTemplateName *DTN =
+                 FriendTemplate.getAsDependentTemplateName())
+      FriendName = DTN->getName().getIdentifier();
+
+    TagTypeKind FriendTagKind =
+        TypeWithKeyword::getTagTypeKindForKeyword(FriendTST->getKeyword());
+
+    for (CXXRecordDecl *ContextRD : EC.Records) {
+      ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD);
+      if (!ContextCTD)
+        continue;
+
+      if (FriendName && ContextCTD->getDeclName() != FriendName)
+        continue;
+
+      if ((FriendTagKind == TagTypeKind::Union) !=
+          ContextCTD->getTemplatedDecl()->isUnion())
+        continue;
+
+      FriendTemplateMatchContext FTMC(S, FTD);
+      AccessResult Result =
+          FTMC.deduce(ContextRD->getDeclContext(), FriendQTST, TPLs, FailedTSC);
+      if (!FTMC.hasDeducedArgs()) {
+        if (Result == AR_dependent)
+          OnFailure = AR_dependent;
+        continue;
+      }
+
+      TypeSourceInfo *InstFriendTSI =
+          S.SubstFriendType(FriendTSI, FTMC.getDeducedArgs(),
+                            FTD->getLocation(), DeclarationName());
+      if (InstFriendTSI && !FTMC.hasErrorOccurred() &&
+          S.Context.hasSameType(InstFriendTSI->getType(),
+                                S.Context.getCanonicalTagType(ContextRD)))
+        return AR_accessible;
+
+      if (ContextRD->isDependentContext())
+        OnFailure = AR_dependent;
+    }
+
+    return OnFailure;
+  }
+
+  const auto *FriendDNT = FriendType->getAs<DependentNameType>();
+  if (!FriendDNT)
+    return OnFailure;
+
+  const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
+      S.Context, FriendDNT->getQualifier());
+  if (!FriendTST)
+    return OnFailure;
 
+  ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
+
+  TagTypeKind FriendTagKind =
+      TypeWithKeyword::getTagTypeKindForKeyword(FriendDNT->getKeyword());
+  for (CXXRecordDecl *ContextRD : EC.Records) {
+    if (ContextRD->getDeclName() != FriendDNT->getIdentifier())
+      continue;
+
+    if (ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD)) {
+      if (FTD->getFriendTemplateName().isNull()) {
+        if (FailedTSC) {
+          MultiLevelTemplateArgumentList DeducedArgs;
+          DeduceTemplateArguments(S, FTD, ContextCTD->getDeclContext(),
+                                  FriendTST, TPLs, FailedTSC, DeducedArgs);
+        }
+        continue;
+      }
+
+      AccessResult Result = MatchesFriend(
+          S, FTD, FriendDNT->getIdentifier(), FriendTagKind, ContextCTD,
+          FriendTST, TPLs.drop_back(), TPLs.back(), FailedTSC);
+      if (Result == AR_accessible)
+        return AR_accessible;
+      if (Result == AR_dependent)
+        OnFailure = AR_dependent;
+      continue;
+    }
+
+    if (!FTD->getFriendTemplateName().isNull())
+      continue;
+
+    if ((FriendTagKind == TagTypeKind::Union) != ContextRD->isUnion())
+      continue;
+
+    MultiLevelTemplateArgumentList DeducedArgs;
+    AccessResult Result =
+        DeduceTemplateArguments(S, FTD, ContextRD->getDeclContext(), FriendTST,
+                                TPLs, FailedTSC, DeducedArgs);
+    if (Result == AR_accessible)
+      return AR_accessible;
+    if (Result == AR_dependent)
+      OnFailure = AR_dependent;
+  }
   return OnFailure;
 }
 
@@ -557,40 +1102,47 @@ static AccessResult MatchesFriend(Sema &S,
 static AccessResult MatchesFriend(Sema &S,
                                   const EffectiveContext &EC,
                                   FriendDecl *FriendD) {
-  // Whitelist accesses if there's an invalid or unsupported friend
-  // declaration.
-  if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend())
+  // Whitelist accesses if there's an invalid friend declaration.
+  if (FriendD->isInvalidDecl())
     return AR_accessible;
 
+  if (NamedDecl *Friend = FriendD->getFriendDecl())
+    return MatchesFriend(S, EC, Friend);
+
   if (TypeSourceInfo *T = FriendD->getFriendType())
     return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified());
 
-  NamedDecl *Friend
-    = cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl());
-
-  // FIXME: declarations with dependent or templated scope.
-
-  if (isa<ClassTemplateDecl>(Friend))
-    return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
+  return AR_inaccessible;
+}
 
-  if (isa<FunctionTemplateDecl>(Friend))
-    return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  if (FTD->isInvalidDecl())
+    return AR_accessible;
 
-  if (isa<CXXRecordDecl>(Friend))
-    return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
+  if (TypeSourceInfo *TSI = FTD->getFriendType())
+    return MatchesFriend(S, EC, FTD, TSI, FailedTSC);
 
-  assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
-  return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
+  NamedDecl *Friend = FTD->getFriendDecl();
+  assert(Friend && "friend template must name a type or declaration");
+  return MatchesFriend(S, EC, FTD, Friend, FailedTSC);
 }
 
-static AccessResult GetFriendKind(Sema &S,
-                                  const EffectiveContext &EC,
-                                  const CXXRecordDecl *Class) {
+static AccessResult GetFriendKind(Sema &S, const EffectiveContext &EC,
+                                  const CXXRecordDecl *Class,
+                                  TemplateSpecCandidateSet *FailedTSC) {
   AccessResult OnFailure = AR_inaccessible;
 
   // Okay, check friends.
-  for (auto *Friend : Class->friends()) {
-    switch (MatchesFriend(S, EC, Friend)) {
+  for (FriendDecl *Friend : Class->friends()) {
+    AccessResult AR;
+    if (auto *FTD = dyn_cast<FriendTemplateDecl>(Friend))
+      AR = MatchesFriend(S, EC, FTD, FailedTSC);
+    else
+      AR = MatchesFriend(S, EC, Friend);
+
+    switch (AR) {
     case AR_accessible:
       return AR_accessible;
 
@@ -614,6 +1166,7 @@ namespace {
 struct ProtectedFriendContext {
   Sema &S;
   const EffectiveContext &EC;
+  TemplateSpecCandidateSet *FailedTSC;
   const CXXRecordDecl *NamingClass;
   bool CheckDependent;
   bool EverDependent;
@@ -623,18 +1176,19 @@ struct ProtectedFriendContext {
 
   ProtectedFriendContext(Sema &S, const EffectiveContext &EC,
                          const CXXRecordDecl *InstanceContext,
-                         const CXXRecordDecl *NamingClass)
-    : S(S), EC(EC), NamingClass(NamingClass),
-      CheckDependent(InstanceContext->isDependentContext() ||
-                     NamingClass->isDependentContext()),
-      EverDependent(false) {}
+                         const CXXRecordDecl *NamingClass,
+                         TemplateSpecCandidateSet *FailedTSC)
+      : S(S), EC(EC), FailedTSC(FailedTSC), NamingClass(NamingClass),
+        CheckDependent(InstanceContext->isDependentContext() ||
+                       NamingClass->isDependentContext()),
+        EverDependent(false) {}
 
   /// Check classes in the current path for friendship, starting at
   /// the given index.
   bool checkFriendshipAlongPath(unsigned I) {
     assert(I < CurPath.size());
     for (unsigned E = CurPath.size(); I != E; ++I) {
-      switch (GetFriendKind(S, EC, CurPath[I])) {
+      switch (GetFriendKind(S, EC, CurPath[I], FailedTSC)) {
       case AR_accessible:   return true;
       case AR_inaccessible: continue;
       case AR_dependent:    EverDependent = true; continue;
@@ -721,9 +1275,9 @@ struct ProtectedFriendContext {
 ///     because the original target might have been more accessible
 ///     because of crazy subclassing.
 /// So we don't implement that.
-static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
-                                           const CXXRecordDecl *InstanceContext,
-                                           const CXXRecordDecl *NamingClass) {
+static AccessResult GetProtectedFriendKind(
+    Sema &S, const EffectiveContext &EC, const CXXRecordDecl *InstanceContext,
+    const CXXRecordDecl *NamingClass, TemplateSpecCandidateSet *FailedTSC) {
   assert(InstanceContext == nullptr ||
          InstanceContext->getCanonicalDecl() == InstanceContext);
   assert(NamingClass->getCanonicalDecl() == NamingClass);
@@ -731,19 +1285,20 @@ static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
   // If we don't have an instance context, our constraints give us
   // that NamingClass <= P <= NamingClass, i.e. P == NamingClass.
   // This is just the usual friendship check.
-  if (!InstanceContext) return GetFriendKind(S, EC, NamingClass);
+  if (!InstanceContext)
+    return GetFriendKind(S, EC, NamingClass, FailedTSC);
 
-  ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass);
+  ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass, FailedTSC);
   if (PRC.findFriendship(InstanceContext)) return AR_accessible;
   if (PRC.EverDependent) return AR_dependent;
   return AR_inaccessible;
 }
 
-static AccessResult HasAccess(Sema &S,
-                              const EffectiveContext &EC,
+static AccessResult HasAccess(Sema &S, const EffectiveContext &EC,
                               const CXXRecordDecl *NamingClass,
                               AccessSpecifier Access,
-                              const AccessTarget &Target) {
+                              const AccessTarget &Target,
+                              TemplateSpecCandidateSet *FailedTSC) {
   assert(NamingClass->getCanonicalDecl() == NamingClass &&
          "declaration should be canonicalized before being passed here");
 
@@ -863,7 +1418,8 @@ static AccessResult HasAccess(Sema &S,
       if (!InstanceContext) return AR_dependent;
     }
 
-    switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) {
+    switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass,
+                                   FailedTSC)) {
     case AR_accessible: return AR_accessible;
     case AR_inaccessible: return OnFailure;
     case AR_dependent: return AR_dependent;
@@ -871,7 +1427,7 @@ static AccessResult HasAccess(Sema &S,
     llvm_unreachable("impossible friendship kind");
   }
 
-  switch (GetFriendKind(S, EC, NamingClass)) {
+  switch (GetFriendKind(S, EC, NamingClass, FailedTSC)) {
   case AR_accessible: return AR_accessible;
   case AR_inaccessible: return OnFailure;
   case AR_dependent: return AR_dependent;
@@ -984,7 +1540,8 @@ static CXXBasePath *FindBestPath(Sema &S,
       AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
       PathAccess = std::max(PathAccess, BaseAccess);
 
-      switch (HasAccess(S, EC, NC, PathAccess, Target)) {
+      switch (HasAccess(S, EC, NC, PathAccess, Target,
+                        /*FailedTSC=*/nullptr)) {
       case AR_inaccessible: break;
       case AR_accessible:
         PathAccess = AS_public;
@@ -1180,7 +1737,8 @@ static void DiagnoseAccessPath(Sema &S,
     accessSoFar = D->getAccess();
     const CXXRecordDecl *declaringClass = entity.getDeclaringClass();
 
-    switch (HasAccess(S, EC, declaringClass, accessSoFar, entity)) {
+    switch (HasAccess(S, EC, declaringClass, accessSoFar, entity,
+                      /*FailedTSC=*/nullptr)) {
     // If the declaration is accessible when named in its declaring
     // class, then we must be constrained by the path.
     case AR_accessible:
@@ -1223,7 +1781,8 @@ static void DiagnoseAccessPath(Sema &S,
       accessSoFar = baseAccess;
     }
 
-    switch (HasAccess(S, EC, derivingClass, accessSoFar, entity)) {
+    switch (HasAccess(S, EC, derivingClass, accessSoFar, entity,
+                      /*FailedTSC=*/nullptr)) {
     case AR_inaccessible: break;
     case AR_accessible:
       accessSoFar = AS_public;
@@ -1327,9 +1886,9 @@ static bool IsMicrosoftUsingDeclarationAccessBug(Sema& S,
 
 /// Determines whether the accessed entity is accessible.  Public members
 /// have been weeded out by this point.
-static AccessResult IsAccessible(Sema &S,
-                                 const EffectiveContext &EC,
-                                 AccessTarget &Entity) {
+static AccessResult IsAccessible(Sema &S, const EffectiveContext &EC,
+                                 AccessTarget &Entity,
+                                 TemplateSpecCandidateSet *FailedTSC) {
   // Determine the actual naming class.
   const CXXRecordDecl *NamingClass = Entity.getEffectiveNamingClass();
 
@@ -1341,7 +1900,8 @@ static AccessResult IsAccessible(Sema &S,
   // which don't require [M4] or [B4]. These are by far the most
   // common forms of privileged access.
   if (UnprivilegedAccess != AS_none) {
-    switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) {
+    switch (
+        HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity, FailedTSC)) {
     case AR_dependent:
       // This is actually an interesting policy decision.  We don't
       // *have* to delay immediately here: we can do the full access
@@ -1370,7 +1930,7 @@ static AccessResult IsAccessible(Sema &S,
     const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
 
     FinalAccess = Target->getAccess();
-    switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) {
+    switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity, FailedTSC)) {
     case AR_accessible:
       // Target is accessible at EC when named in its declaring class.
       // We can now hill-climb and simply check whether the declaring
@@ -1422,25 +1982,30 @@ static void DelayDependentAccess(Sema &S,
                               Entity.getDiag());
 }
 
-/// Checks access to an entity from the given effective context.
-static AccessResult CheckEffectiveAccess(Sema &S,
-                                         const EffectiveContext &EC,
+static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
                                          SourceLocation Loc,
-                                         AccessTarget &Entity) {
-  assert(Entity.getAccess() != AS_public && "called for public access!");
+                                         AccessTarget &Entity,
+                                         TemplateSpecCandidateSet *FailedTSC) {
+  assert((Entity.isQuiet() || FailedTSC) &&
+         "non-quiet access check requires a candidate set");
 
-  switch (IsAccessible(S, EC, Entity)) {
+  switch (IsAccessible(S, EC, Entity, FailedTSC)) {
   case AR_dependent:
     DelayDependentAccess(S, EC, Loc, Entity);
     return AR_dependent;
 
-  case AR_inaccessible:
+  case AR_inaccessible: {
     if (S.getLangOpts().MSVCCompat &&
         IsMicrosoftUsingDeclarationAccessBug(S, Loc, Entity))
       return AR_accessible;
-    if (!Entity.isQuiet())
-      DiagnoseBadAccess(S, Loc, EC, Entity);
+
+    if (Entity.isQuiet())
+      return AR_inaccessible;
+
+    DiagnoseBadAccess(S, Loc, EC, Entity);
+    FailedTSC->NoteCandidates(S, Loc);
     return AR_inaccessible;
+  }
 
   case AR_accessible:
     return AR_accessible;
@@ -1450,6 +2015,20 @@ static AccessResult CheckEffectiveAccess(Sema &S,
   llvm_unreachable("invalid access result");
 }
 
+static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
+                                         SourceLocation Loc,
+                                         AccessTarget &Entity) {
+  assert(Entity.getAccess() != AS_public && "called for public access!");
+
+  if (Entity.isQuiet())
+    return CheckEffectiveAccess(S, EC, Loc, Entity, /*FailedTSC=*/nullptr);
+
+  TemplateSpecCandidateSet FailedTSC(
+      Loc, /*ForTakingAddress=*/false,
+      TemplateSpecCandidateSetKind::FriendTemplate);
+  return CheckEffectiveAccess(S, EC, Loc, Entity, &FailedTSC);
+}
+
 static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
                                       AccessTarget &Entity) {
   // If the access path is public, it's accessible everywhere.
@@ -1944,7 +2523,8 @@ bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass,
     AccessTarget Entity(Context, AccessedEntity::Member, NamingClass,
                         DeclAccessPair::make(Target, AS_none), BaseType);
     EffectiveContext EC(CurContext);
-    return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible;
+    return ::IsAccessible(*this, EC, Entity, /*FailedTSC=*/nullptr) !=
+           ::AR_inaccessible;
   }
 
   if (ObjCIvarDecl *Ivar = dyn_cast<ObjCIvarDecl>(Target)) {

diff  --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index bd239adb0f215..3b3c9a476421d 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -4497,6 +4497,7 @@ CXCursorKind clang::getCursorKindForDecl(const Decl *D) {
   case Decl::StaticAssert:
     return CXCursor_StaticAssert;
   case Decl::Friend:
+  case Decl::FriendTemplate:
     return CXCursor_FriendDecl;
   case Decl::TranslationUnit:
     return CXCursor_TranslationUnit;

diff  --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index ce91099cd037c..606cfe7bdcf73 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2175,9 +2175,10 @@ void SubstituteParameterMappings::buildParameterMapping(
       assert(Arg && "expected a default argument");
       DefaultArgs.emplace_back(std::move(*Arg));
     }
-    SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*Depth=*/0,
-                                       OccurringIndices);
-    SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*Depth=*/0,
+    SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*OnlyDeduced=*/false,
+                                       /*Depth=*/0, OccurringIndices);
+    SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*OnlyDeduced=*/false,
+                                       /*Depth=*/0,
                                        OccurringIndicesForSubsumption);
   }
 

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d87710d3cf140..c68ac12a88e8b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10916,9 +10916,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
       if (isFriend) {
         // For friend function specializations, this is a dependent
         // specialization if its semantic context is dependent, its
-        // type is dependent, or if its template-id is dependent.
+        // qualifier is dependent, its type is dependent, or its template-id is
+        // dependent.
         isDependentSpecialization =
-            DC->isDependentContext() || NewFD->getType()->isDependentType() ||
+            DC->isDependentContext() || NewFD->getQualifier().isDependent() ||
+            NewFD->getType()->isDependentType() ||
             (HasExplicitTemplateArgs &&
              TemplateSpecializationType::
                  anyInstantiationDependentTemplateArguments(
@@ -12583,7 +12585,8 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
       // struct B { struct Y { ~Y(); }; using X = Y; };
       // template struct A<B>;
       if (NewFD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None ||
-          !Destructor->getFunctionObjectParameterType()->isDependentType()) {
+          (!Destructor->getFunctionObjectParameterType()->isDependentType() &&
+           !Destructor->getDeclName().isDependentName())) {
         CanQualType ClassType =
             Context.getCanonicalTagType(Destructor->getParent());
 

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5c425a176ff41..8d5ee07c5ad49 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18127,31 +18127,153 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
   return Decl;
 }
 
+static QualType IgnorePackIndexing(QualType T) {
+  if (const auto *PIT = dyn_cast<PackIndexingType>(T))
+    return PIT->getPattern();
+  return T;
+}
+
+static const TemplateSpecializationType *
+GetClassTemplateSpecializationType(ASTContext &Context, QualType T) {
+  T = IgnorePackIndexing(T);
+  if (const auto *ICNT = dyn_cast<InjectedClassNameType>(T))
+    T = ICNT->getDecl()->getCanonicalTemplateSpecializationType(Context);
+
+  const auto *TST = dyn_cast<TemplateSpecializationType>(T);
+  if (!TST)
+    return nullptr;
+
+  TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl();
+  if (!TD || isa<ClassTemplateDecl>(TD))
+    return TST;
+  return nullptr;
+}
+
+bool Sema::DiagnosePackIndexingInFriendNNS(SourceLocation Loc,
+                                           NestedNameSpecifierLoc NNSLoc) {
+  for (TypeLoc TL = NNSLoc.getAsTypeLoc(); TL;
+       TL = TL.getPrefix().getAsTypeLoc()) {
+    if (TL.getTypeLocClass() != TypeLoc::PackIndexing)
+      continue;
+
+    Diag(Loc, diag::err_pack_indexing_in_friend) << TL.getSourceRange();
+    return true;
+  }
+  return false;
+}
+
+static void DiagnoseDependentFriendNotMember(Sema &S, SourceLocation Loc,
+                                             NestedNameSpecifier NNS) {
+  QualType T(NNS.getAsType(), 0);
+  if (const auto *TST =
+          dyn_cast<TemplateSpecializationType>(IgnorePackIndexing(T))) {
+    if (isa_and_nonnull<TypeAliasTemplateDecl>(
+            TST->getTemplateName().getAsTemplateDecl())) {
+      S.Diag(Loc, diag::err_dependent_friend_not_member_of_template_spec)
+          << NNS;
+      return;
+    }
+  }
+
+  if (NNS.getAsRecordDecl()) {
+    S.Diag(Loc, diag::err_dependent_friend_not_member_of_template_spec) << NNS;
+  } else {
+    S.Diag(Loc, diag::err_dependent_friend_not_member);
+  }
+}
+
+bool Sema::CheckDependentFriend(SourceLocation Loc,
+                                NestedNameSpecifierLoc NNSLoc,
+                                ArrayRef<TemplateParameterList *> TPLs,
+                                bool IsInstantiation) {
+  NestedNameSpecifier NNS = NNSLoc.getNestedNameSpecifier();
+  if (!NNS.isDependent() && !IsInstantiation)
+    return false;
+
+  assert(NNS.getKind() == NestedNameSpecifier::Kind::Type &&
+         "nested-name-specifier of dependent friend must be a type");
+
+  QualType T(NNS.getAsType(), 0);
+  if (DiagnosePackIndexingInFriendNNS(Loc, NNSLoc))
+    return true;
+
+  const TemplateSpecializationType *TST =
+      GetClassTemplateSpecializationType(Context, T);
+  if (!TST) {
+    DiagnoseDependentFriendNotMember(*this, Loc, NNS);
+    return true;
+  }
+
+  if (TPLs.empty())
+    return false;
+
+  SmallVector<NamedDecl *, 4> UndeducedParameters;
+  for (TemplateParameterList *Params : TPLs) {
+    llvm::SmallBitVector UsedParameters(Params->size());
+    MarkUsedTemplateParameters(TST->template_arguments(),
+                               /*OnlyDeduced=*/true, Params->getDepth(),
+                               UsedParameters);
+
+    for (unsigned I = 0, N = UsedParameters.size(); I != N; ++I)
+      if (!UsedParameters[I])
+        UndeducedParameters.push_back(Params->getParam(I));
+  }
+
+  if (UndeducedParameters.empty())
+    return false;
+
+  Diag(Loc, diag::err_dependent_friend_undeduced_params)
+      << (UndeducedParameters.size() > 1) << QualType(TST, 0);
+
+  for (NamedDecl *Param : UndeducedParameters) {
+    if (Param->getDeclName())
+      Diag(Param->getLocation(), diag::note_non_deducible_parameter)
+          << Param->getDeclName();
+    else
+      Diag(Param->getLocation(), diag::note_non_deducible_parameter)
+          << "(anonymous)";
+  }
+
+  return true;
+}
+
 DeclResult Sema::ActOnTemplatedFriendTag(
     Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc,
     CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
     SourceLocation EllipsisLoc, const ParsedAttributesView &Attr,
-    MultiTemplateParamsArg TempParamLists) {
+    MultiTemplateParamsArg TempParamLists, TemplateIdAnnotation *TemplateId) {
   TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
 
   bool IsMemberSpecialization = false;
   bool Invalid = false;
 
-  if (TemplateParameterList *TemplateParams =
-          MatchTemplateParametersToScopeSpecifier(
-              TagLoc, NameLoc, SS, nullptr, TempParamLists, /*friend*/ true,
-              IsMemberSpecialization, Invalid)) {
+  TemplateParameterList *TemplateParams =
+      MatchTemplateParametersToScopeSpecifier(TagLoc, NameLoc, SS, TemplateId,
+                                              TempParamLists, /*friend*/ true,
+                                              IsMemberSpecialization, Invalid);
+  if (TemplateId) {
+    if (Invalid)
+      return true;
+
+    if (TemplateParams) {
+      Diag(NameLoc, diag::err_not_class_template_specialization) << 0;
+      return true;
+    }
+  }
+
+  if (TemplateParams) {
     if (TemplateParams->size() > 0) {
-      // This is a declaration of a class template.
       if (Invalid)
         return true;
 
-      return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS,
-                                Name, NameLoc, Attr, TemplateParams, AS_public,
-                                /*ModulePrivateLoc=*/SourceLocation(),
-                                FriendLoc, TempParamLists.size() - 1,
-                                TempParamLists.data(), IsMemberSpecialization)
-          .get();
+      if (SS.isEmpty() || !SS.getScopeRep().isDependent()) {
+        DeclResult Result = CheckClassTemplate(
+            S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr,
+            TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(),
+            FriendLoc, TempParamLists.size() - 1, TempParamLists.data(),
+            IsMemberSpecialization);
+        return Result.get();
+      }
     } else {
       // The "template<>" header is extraneous.
       Diag(TemplateParams->getTemplateLoc(), diag::err_template_tag_noparams)
@@ -18159,9 +18281,10 @@ DeclResult Sema::ActOnTemplatedFriendTag(
     }
   }
 
-  if (Invalid) return true;
+  if (Invalid)
+    return true;
 
-  bool isAllExplicitSpecializations =
+  bool IsAllExplicitSpecializations =
       llvm::all_of(TempParamLists, [](const TemplateParameterList *List) {
         return List->size() == 0;
       });
@@ -18172,7 +18295,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
   // about the template header and build an appropriate non-templated
   // friend.  TODO: for source fidelity, remember the headers.
   NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
-  if (isAllExplicitSpecializations) {
+  if (!TemplateId && IsAllExplicitSpecializations) {
     if (SS.isEmpty()) {
       bool Owned = false;
       bool IsDependent = false;
@@ -18189,16 +18312,15 @@ DeclResult Sema::ActOnTemplatedFriendTag(
     }
 
     TypeSourceInfo *TSI = nullptr;
-    ElaboratedTypeKeyword Keyword
-      = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
+    ElaboratedTypeKeyword Keyword =
+        TypeWithKeyword::getKeywordForTagTypeKind(Kind);
     QualType T = CheckTypenameType(Keyword, TagLoc, QualifierLoc, *Name,
                                    NameLoc, &TSI, /*DeducedTSTContext=*/true);
     if (T.isNull())
       return true;
 
-    FriendDecl *Friend =
-        FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
-                           EllipsisLoc, TempParamLists);
+    FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI,
+                                            FriendLoc, EllipsisLoc);
     Friend->setAccess(AS_public);
     CurContext->addDecl(Friend);
     return Friend;
@@ -18206,43 +18328,86 @@ DeclResult Sema::ActOnTemplatedFriendTag(
 
   assert(SS.isNotEmpty() && "valid templated tag with no SS and no direct?");
 
-  // CWG 2917: if it (= the friend-type-specifier) is a pack expansion
-  // (13.7.4 [temp.variadic]), any packs expanded by that pack expansion
-  // shall not have been introduced by the template-declaration.
+  ArrayRef<TemplateParameterList *> TPLs = TempParamLists;
+  if (TemplateParams)
+    TPLs = TPLs.drop_back();
+  if (CheckDependentFriend(TagLoc, QualifierLoc, TPLs,
+                           /*IsInstantiation=*/false))
+    return true;
+
+  TypeSourceInfo *TSI = nullptr;
+  if (TemplateId) {
+    ASTTemplateArgsPtr ParsedArgs(TemplateId->getTemplateArgs(),
+                                  TemplateId->NumArgs);
+    TypeResult ParsedType = ActOnTagTemplateIdType(
+        TagUseKind::Friend, static_cast<TypeSpecifierType>(TagSpec), TagLoc, SS,
+        TemplateId->TemplateKWLoc, TemplateId->Template, NameLoc,
+        TemplateId->LAngleLoc, ParsedArgs, TemplateId->RAngleLoc);
+    if (ParsedType.isInvalid())
+      return true;
+
+    GetTypeFromParser(ParsedType.get(), &TSI);
+  } else {
+    ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
+    QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
+    TSI = Context.CreateTypeSourceInfo(T);
+
+    DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>();
+    TL.setElaboratedKeywordLoc(TagLoc);
+    TL.setQualifierLoc(QualifierLoc);
+    TL.setNameLoc(NameLoc);
+  }
+
   SmallVector<UnexpandedParameterPack, 1> Unexpanded;
-  collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
-  unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
-  for (UnexpandedParameterPack &U : Unexpanded) {
-    if (std::optional<std::pair<unsigned, unsigned>> DI = getDepthAndIndex(U);
-        DI && DI->first >= FriendDeclDepth) {
-      auto *ND = dyn_cast<NamedDecl *>(U.first);
-      if (!ND)
-        ND = cast<const TemplateTypeParmType *>(U.first)->getDecl();
-      Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
-          << ND->getDeclName() << SourceRange(SS.getBeginLoc(), EllipsisLoc);
+  collectUnexpandedParameterPacks(TSI->getTypeLoc(), Unexpanded);
+  if (EllipsisLoc.isInvalid()) {
+    if (DiagnoseUnexpandedParameterPack(TagLoc, TSI, UPPC_FriendDeclaration))
       return true;
+  } else if (Unexpanded.empty()) {
+    Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
+        << TSI->getTypeLoc().getSourceRange();
+    return true;
+  } else {
+    // CWG 2917: a pack expanded by a friend-type-specifier cannot have been
+    // introduced by the template-declaration containing that specifier.
+    if (!TempParamLists.empty()) {
+      unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
+      for (UnexpandedParameterPack &U : Unexpanded) {
+        if (std::optional<std::pair<unsigned, unsigned>> DI =
+                getDepthAndIndex(U);
+            DI && DI->first >= FriendDeclDepth) {
+          auto *ND = dyn_cast<NamedDecl *>(U.first);
+          if (!ND)
+            ND = cast<const TemplateTypeParmType *>(U.first)->getDecl();
+          Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
+              << ND->getDeclName()
+              << SourceRange(TSI->getTypeLoc().getBeginLoc(), EllipsisLoc);
+          return true;
+        }
+      }
     }
   }
 
-  // Handle the case of a templated-scope friend class.  e.g.
-  //   template <class T> class A<T>::B;
-  // FIXME: we don't support these right now.
-  Diag(NameLoc, diag::warn_template_qualified_friend_unsupported)
-    << SS.getScopeRep() << SS.getRange() << cast<CXXRecordDecl>(CurContext);
-  ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
-  QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
-  TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T);
-  DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>();
-  TL.setElaboratedKeywordLoc(TagLoc);
-  TL.setQualifierLoc(SS.getWithLocInContext(Context));
-  TL.setNameLoc(NameLoc);
+  FriendDecl *Friend;
+  if (TempParamLists.empty())
+    Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+                                EllipsisLoc);
+  else {
+    if (CheckTemplateDeclScope(S, TempParamLists.back()))
+      return true;
+
+    TemplateName FriendTemplate;
+    if (TemplateParams)
+      FriendTemplate = Context.getDependentTemplateName(
+          {SS.getScopeRep(), Name, /*HasTemplateKeyword=*/false});
+    Friend =
+        FriendTemplateDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+                                   TempParamLists, EllipsisLoc, FriendTemplate);
+  }
 
-  FriendDecl *Friend =
-      FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
-                         EllipsisLoc, TempParamLists);
   Friend->setAccess(AS_public);
-  Friend->setUnsupportedFriend(true);
   CurContext->addDecl(Friend);
+
   return Friend;
 }
 
@@ -18343,11 +18508,14 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
   // friend a member of an arbitrary specialization of your template).
 
   Decl *D;
-  if (!TempParams.empty())
+  if (!TempParams.empty()) {
+    if (CheckTemplateDeclScope(S, TempParams.back()))
+      return nullptr;
+
     // TODO: Support variadic friend template decls?
-    D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI,
-                                   FriendLoc);
-  else
+    D = FriendTemplateDecl::Create(Context, CurContext, Loc, TSI, FriendLoc,
+                                   TempParams, EllipsisLoc);
+  } else
     D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(),
                            TSI, FriendLoc, EllipsisLoc);
 
@@ -18410,6 +18578,10 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
   DeclarationNameInfo NameInfo = GetNameForDeclarator(D);
   assert(NameInfo.getName());
 
+  if (SS.isValid() && DiagnosePackIndexingInFriendNNS(
+                          NameInfo.getLoc(), SS.getWithLocInContext(Context)))
+    return nullptr;
+
   // Check for unexpanded parameter packs.
   if (DiagnoseUnexpandedParameterPack(Loc, TInfo, UPPC_FriendDeclaration) ||
       DiagnoseUnexpandedParameterPack(NameInfo, UPPC_FriendDeclaration) ||
@@ -18424,7 +18596,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
                                Kind == NestedNameSpecifier::Kind::Namespace;
     if (IsNamespaceOrGlobal) {
       Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def)
-          << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange());
+          << SS.getScopeRep();
       SS.clear();
     }
   }
@@ -18535,11 +18707,8 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
              diag::warn_cxx98_compat_friend_is_member :
              diag::err_friend_is_member);
 
-  //   - There's a scope specifier that does not match any template
-  //     parameter lists, in which case we use some arbitrary context,
-  //     create a method or method template, and wait for instantiation.
-  //   - There's a scope specifier that does match some template
-  //     parameter lists, which we don't handle right now.
+    //   - There's a dependent scope specifier, in which case we use an
+    //     arbitrary context and wait for instantiation.
   } else {
     DC = CurContext;
     assert(isa<CXXRecordDecl>(DC) && "friend declaration not in class?");
@@ -18613,81 +18782,88 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
       PushOnScopeChains(ND, EnclosingScope, /*AddToContext=*/ false);
   }
 
-  FriendDecl *FrD = FriendDecl::Create(Context, CurContext,
-                                       D.getIdentifierLoc(), ND,
-                                       DS.getFriendSpecLoc());
-  FrD->setAccess(AS_public);
-  CurContext->addDecl(FrD);
+  warnOnReservedIdentifier(ND);
 
   if (ND->isInvalidDecl()) {
-    FrD->setInvalidDecl();
-  } else {
-    if (DC->isRecord()) CheckFriendAccess(ND);
+    FriendDecl *Friend = FriendDecl::Create(
+        Context, CurContext, D.getIdentifierLoc(), ND, DS.getFriendSpecLoc());
+    Friend->setAccess(AS_public);
+    if (!isa<FunctionTemplateDecl>(ND))
+      Friend->setInvalidDecl();
+    CurContext->addDecl(Friend);
+    return ND;
+  }
 
-    FunctionDecl *FD;
-    if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
-      FD = FTD->getTemplatedDecl();
-    else
-      FD = cast<FunctionDecl>(ND);
-
-    // C++ [class.friend]p6:
-    //   A function may be defined in a friend declaration of a class if and
-    //   only if the class is a non-local class, and the function name is
-    //   unqualified.
-    if (D.isFunctionDefinition()) {
-      // Qualified friend function definition.
-      if (SS.isNotEmpty()) {
-        // FIXME: We should only do this if the scope specifier names the
-        // innermost enclosing namespace; otherwise the fixit changes the
-        // meaning of the code.
-        SemaDiagnosticBuilder DB =
-            Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def);
-
-        DB << SS.getScopeRep();
-        if (DC->isFileContext())
-          DB << FixItHint::CreateRemoval(SS.getRange());
-
-        // Friend function defined in a local class.
-      } else if (FunctionContainingLocalClass) {
-        Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class);
-
-        // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have
-        // a template-id, the function name is not unqualified because these is
-        // no name. While the wording requires some reading in-between the
-        // lines, GCC, MSVC, and EDG all consider a friend function
-        // specialization definitions to be de facto explicit specialization
-        // and diagnose them as such.
-      } else if (isTemplateId) {
-        Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def);
-      }
-    }
+  FunctionDecl *FD = ND->getAsFunction();
+  assert(FD && "Expected a function declaration!");
 
-    // C++11 [dcl.fct.default]p4: If a friend declaration specifies a
-    // default argument expression, that declaration shall be a definition
-    // and shall be the only declaration of the function or function
-    // template in the translation unit.
-    if (functionDeclHasDefaultArgument(FD)) {
-      // We can't look at FD->getPreviousDecl() because it may not have been set
-      // if we're in a dependent context. If the function is known to be a
-      // redeclaration, we will have narrowed Previous down to the right decl.
-      if (D.isRedeclaration()) {
-        Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_redeclared);
-        Diag(Previous.getRepresentativeDecl()->getLocation(),
-             diag::note_previous_declaration);
-      } else if (!D.isFunctionDefinition())
-        Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def);
-    }
+  ArrayRef<TemplateParameterList *> TPLs = FD->getTemplateParameterLists();
+  if (!TPLs.empty() && SS.isValid() && CheckTemplateDeclScope(S, TPLs.back()))
+    return nullptr;
 
-    // Mark templated-scope function declarations as unsupported.
-    if (!FD->getTemplateParameterLists().empty() && SS.isValid()) {
-      Diag(FD->getLocation(), diag::warn_template_qualified_friend_unsupported)
-        << SS.getScopeRep() << SS.getRange()
-        << cast<CXXRecordDecl>(CurContext);
-      FrD->setUnsupportedFriend(true);
-    }
-  }
+  FriendDecl *Friend;
+  if (!TPLs.empty() && SS.isValid())
+    Friend =
+        FriendTemplateDecl::Create(Context, CurContext, D.getIdentifierLoc(),
+                                   ND, DS.getFriendSpecLoc(), TPLs);
+  else
+    Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), ND,
+                                DS.getFriendSpecLoc());
 
-  warnOnReservedIdentifier(ND);
+  Friend->setAccess(AS_public);
+  CurContext->addDecl(Friend);
+
+  if (DC->isRecord())
+    CheckFriendAccess(ND);
+
+  if (!TemplateParams.empty() && SS.isValid() &&
+      CheckDependentFriend(NameInfo.getLoc(), SS.getWithLocInContext(Context),
+                           FD->getTemplateParameterLists(),
+                           /*IsInstantiation=*/false))
+    return ND;
+
+  // C++ [class.friend]p6:
+  //   A function may be defined in a friend declaration of a class if and
+  //   only if the class is a non-local class, and the function name is
+  //   unqualified.
+  if (D.isFunctionDefinition()) {
+    // Qualified friend function definition.
+    if (SS.isNotEmpty()) {
+      SemaDiagnosticBuilder DB =
+          Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def);
+
+      DB << SS.getScopeRep();
+
+      // Friend function defined in a local class.
+    } else if (FunctionContainingLocalClass) {
+      Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class);
+
+      // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have
+      // a template-id, the function name is not unqualified because these is
+      // no name. While the wording requires some reading in-between the
+      // lines, GCC, MSVC, and EDG all consider a friend function
+      // specialization definitions to be de facto explicit specialization
+      // and diagnose them as such.
+    } else if (isTemplateId) {
+      Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def);
+    }
+  }
+
+  // C++11 [dcl.fct.default]p4: If a friend declaration specifies a
+  // default argument expression, that declaration shall be a definition
+  // and shall be the only declaration of the function or function
+  // template in the translation unit.
+  if (functionDeclHasDefaultArgument(FD)) {
+    // We can't look at FD->getPreviousDecl() because it may not have been set
+    // if we're in a dependent context. If the function is known to be a
+    // redeclaration, we will have narrowed Previous down to the right decl.
+    if (D.isRedeclaration()) {
+      Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_redeclared);
+      Diag(Previous.getRepresentativeDecl()->getLocation(),
+           diag::note_previous_declaration);
+    } else if (!D.isFunctionDefinition())
+      Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def);
+  }
 
   return ND;
 }

diff  --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index d41137ddc85d5..daa4bb5234b7b 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -655,7 +655,8 @@ static bool CheckEquivalentExceptionSpecImpl(
     return true;
   }
 
-  S.Diag(NewLoc, DiagID);
+  if (DiagID.getDiagID() != 0)
+    S.Diag(NewLoc, DiagID);
   if (NoteID.getDiagID() != 0 && OldLoc.isValid())
     S.Diag(OldLoc, NoteID);
   return true;
@@ -667,7 +668,7 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
                                         SourceLocation OldLoc,
                                         const FunctionProtoType *New,
                                         SourceLocation NewLoc) {
-  if (!getLangOpts().CXXExceptions)
+  if (!getLangOpts().CXXExceptions && !getLangOpts().CPlusPlus17)
     return false;
   return CheckEquivalentExceptionSpecImpl(*this, DiagID, NoteID, Old, OldLoc,
                                           New, NewLoc);

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 97744b6e8836b..81bc6c68f377d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12420,8 +12420,9 @@ static TemplateDecl *getDescribedTemplate(Decl *Templated) {
 /// Diagnose a failed template-argument deduction.
 static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
                                  DeductionFailureInfo &DeductionFailure,
-                                 unsigned NumArgs,
-                                 bool TakingCandidateAddress) {
+                                 unsigned NumArgs, bool TakingCandidateAddress,
+                                 TemplateSpecCandidateSetKind CandidateSetKind =
+                                     TemplateSpecCandidateSetKind::Normal) {
   TemplateParameter Param = DeductionFailure.getTemplateParameter();
   NamedDecl *ParamD;
   (ParamD = Param.dyn_cast<TemplateTypeParmDecl*>()) ||
@@ -12668,7 +12669,10 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
           //    name for types, not decls.
           // Ideally, this should folded into the diagnostic printer.
           S.Diag(Templated->getLocation(),
-                 diag::note_ovl_candidate_non_deduced_mismatch_qualified)
+                 CandidateSetKind ==
+                         TemplateSpecCandidateSetKind::FriendTemplate
+                     ? diag::note_friend_template_non_deduced_mismatch_qualified
+                     : diag::note_ovl_candidate_non_deduced_mismatch_qualified)
               << FirstTN.getAsTemplateDecl() << SecondTN.getAsTemplateDecl();
           return;
         }
@@ -12684,7 +12688,9 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
     // diagnostic that mentions 'auto' and lambda in addition to
     // (or instead of?) the canonical template type parameters.
     S.Diag(Templated->getLocation(),
-           diag::note_ovl_candidate_non_deduced_mismatch)
+           CandidateSetKind == TemplateSpecCandidateSetKind::FriendTemplate
+               ? diag::note_friend_template_non_deduced_mismatch
+               : diag::note_ovl_candidate_non_deduced_mismatch)
         << FirstTA << SecondTA;
     return;
   }
@@ -13635,10 +13641,12 @@ struct CompareTemplateSpecCandidatesForDisplay {
 /// Diagnose a template argument deduction failure.
 /// We are treating these failures as overload failures due to bad
 /// deductions.
-void TemplateSpecCandidate::NoteDeductionFailure(Sema &S,
-                                                 bool ForTakingAddress) {
+void TemplateSpecCandidate::NoteDeductionFailure(
+    Sema &S, bool ForTakingAddress,
+    TemplateSpecCandidateSetKind CandidateSetKind) {
   DiagnoseBadDeduction(S, FoundDecl, Specialization, // pattern
-                       DeductionFailure, /*NumArgs=*/0, ForTakingAddress);
+                       DeductionFailure, /*NumArgs=*/0, ForTakingAddress,
+                       CandidateSetKind);
 }
 
 void TemplateSpecCandidateSet::destroyCandidates() {
@@ -13690,7 +13698,7 @@ void TemplateSpecCandidateSet::NoteCandidates(Sema &S, SourceLocation Loc) {
 
     assert(Cand->Specialization &&
            "Non-matching built-in candidates are not added to Cands.");
-    Cand->NoteDeductionFailure(S, ForTakingAddress);
+    Cand->NoteDeductionFailure(S, ForTakingAddress, CandidateSetKind);
   }
 
   if (I != E)

diff  --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 643392833759d..dff681454ae5a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1977,14 +1977,9 @@ DeclResult Sema::CheckClassTemplate(
   if (SS.isNotEmpty() && !SS.isInvalid()) {
     SemanticContext = computeDeclContext(SS, true);
     if (!SemanticContext) {
-      // FIXME: Horrible, horrible hack! We can't currently represent this
-      // in the AST, and historically we have just ignored such friend
-      // class templates, so don't complain here.
-      Diag(NameLoc, TUK == TagUseKind::Friend
-                        ? diag::warn_template_qualified_friend_ignored
-                        : diag::err_template_qualified_declarator_no_match)
+      Diag(NameLoc, diag::err_template_qualified_declarator_no_match)
           << SS.getScopeRep() << SS.getRange();
-      return TUK != TagUseKind::Friend;
+      return true;
     }
 
     if (RequireCompleteDeclContext(SS, SemanticContext))
@@ -3053,6 +3048,8 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier(
       if (TemplateDecl *Template = Name.getAsTemplateDecl()) {
         ExpectedTemplateParams = Template->getTemplateParameters();
         NeedNonemptyTemplateHeader = true;
+      } else if (Name.getAsDependentTemplateName()) {
+        NeedNonemptyTemplateHeader = true;
       } else if (Name.getAsDeducedTemplateName()) {
         // FIXME:  We actually could/should check the template arguments here
         // against the corresponding template parameter list.
@@ -9581,6 +9578,15 @@ bool Sema::CheckDependentFunctionTemplateSpecialization(
 
   bool IsFriend = FD->getFriendObjectKind() != Decl::FOK_None;
   if (Previous.empty()) {
+    NestedNameSpecifier FriendQualifier = FD->getQualifier();
+    if (IsFriend && FriendQualifier.isDependent() &&
+        FriendQualifier.getKind() == NestedNameSpecifier::Kind::Type &&
+        FriendQualifier.getAsType()->getAs<TemplateSpecializationType>()) {
+      FD->setDependentTemplateSpecialization(
+          Context, Previous.asUnresolvedSet(), ExplicitTemplateArgs);
+      return false;
+    }
+
     Diag(FD->getLocation(), diag::err_dependent_function_template_spec_no_match)
         << IsFriend;
     for (auto &P : DiscardedCandidates)
@@ -11203,6 +11209,10 @@ TypeResult Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
   if (!NNS)
     return true;
 
+  if (TUK == TagUseKind::Friend &&
+      DiagnosePackIndexingInFriendNNS(NameLoc, SS.getWithLocInContext(Context)))
+    return true;
+
   TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
 
   if (TUK == TagUseKind::Declaration || TUK == TagUseKind::Definition) {

diff  --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 3c45806c47a6e..c57a090a8553e 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3225,42 +3225,12 @@ CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template,
   return TemplateDeductionResult::Success;
 }
 
-/// Complete template argument deduction.
-static TemplateDeductionResult FinishTemplateArgumentDeduction(
-    Sema &S, NamedDecl *Entity, TemplateParameterList *EntityTPL,
-    TemplateDecl *Template, bool PartialOrdering,
-    ArrayRef<TemplateArgumentLoc> Ps, ArrayRef<TemplateArgument> As,
-    SmallVectorImpl<DeducedTemplateArgument> &Deduced,
-    TemplateDeductionInfo &Info, bool CopyDeducedArgs) {
-  Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Entity));
-
-  // C++ [temp.deduct.type]p2:
-  //   [...] or if any template argument remains neither deduced nor
-  //   explicitly specified, template argument deduction fails.
-  Sema::CheckTemplateArgumentInfo CTAI(PartialOrdering);
-  if (auto Result = ConvertDeducedTemplateArguments(
-          S, Entity, EntityTPL, /*IsDeduced=*/PartialOrdering, Deduced, Info,
-          CTAI,
-          /*CurrentInstantiationScope=*/nullptr,
-          /*NumAlreadyConverted=*/0U, /*IsIncomplete=*/nullptr);
-      Result != TemplateDeductionResult::Success)
-    return Result;
-
-  if (CopyDeducedArgs) {
-    // Form the template argument list from the deduced template arguments.
-    TemplateArgumentList *SugaredDeducedArgumentList =
-        TemplateArgumentList::CreateCopy(S.Context, CTAI.SugaredConverted);
-    TemplateArgumentList *CanonicalDeducedArgumentList =
-        TemplateArgumentList::CreateCopy(S.Context, CTAI.CanonicalConverted);
-    Info.reset(SugaredDeducedArgumentList, CanonicalDeducedArgumentList);
-  }
-
+static TemplateDeductionResult CheckDeducedTemplateArgumentList(
+    Sema &S, TemplateDecl *Template, ArrayRef<TemplateArgumentLoc> Ps,
+    ArrayRef<TemplateArgument> As, const MultiLevelTemplateArgumentList &MLTAL,
+    TemplateDeductionInfo &Info) {
   TemplateParameterList *TPL = Template->getTemplateParameters();
   TemplateArgumentListInfo InstArgs(TPL->getLAngleLoc(), TPL->getRAngleLoc());
-  MultiLevelTemplateArgumentList MLTAL(Entity, CTAI.SugaredConverted,
-                                       /*Final=*/true);
-  MLTAL.addOuterRetainedLevels(TPL->getDepth());
-
   if (S.SubstTemplateArguments(Ps, MLTAL, InstArgs)) {
     unsigned ArgIdx = InstArgs.size(), ParamIdx = ArgIdx;
     if (ParamIdx >= TPL->size())
@@ -3336,6 +3306,46 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
   }
   assert(PsStack.empty());
   assert(AsStack.empty());
+  return TemplateDeductionResult::Success;
+}
+
+/// Complete template argument deduction.
+static TemplateDeductionResult FinishTemplateArgumentDeduction(
+    Sema &S, NamedDecl *Entity, TemplateParameterList *EntityTPL,
+    TemplateDecl *Template, bool PartialOrdering,
+    ArrayRef<TemplateArgumentLoc> Ps, ArrayRef<TemplateArgument> As,
+    SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+    TemplateDeductionInfo &Info, bool CopyDeducedArgs) {
+  Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Entity));
+
+  // C++ [temp.deduct.type]p2:
+  //   [...] or if any template argument remains neither deduced nor
+  //   explicitly specified, template argument deduction fails.
+  Sema::CheckTemplateArgumentInfo CTAI(PartialOrdering);
+  if (auto Result = ConvertDeducedTemplateArguments(
+          S, Entity, EntityTPL, /*IsDeduced=*/PartialOrdering, Deduced, Info,
+          CTAI,
+          /*CurrentInstantiationScope=*/nullptr,
+          /*NumAlreadyConverted=*/0U, /*IsIncomplete=*/nullptr);
+      Result != TemplateDeductionResult::Success)
+    return Result;
+
+  if (CopyDeducedArgs) {
+    // Form the template argument list from the deduced template arguments.
+    TemplateArgumentList *SugaredDeducedArgumentList =
+        TemplateArgumentList::CreateCopy(S.Context, CTAI.SugaredConverted);
+    TemplateArgumentList *CanonicalDeducedArgumentList =
+        TemplateArgumentList::CreateCopy(S.Context, CTAI.CanonicalConverted);
+    Info.reset(SugaredDeducedArgumentList, CanonicalDeducedArgumentList);
+  }
+
+  MultiLevelTemplateArgumentList MLTAL(Entity, CTAI.SugaredConverted,
+                                       /*Final=*/true);
+  MLTAL.addOuterRetainedLevels(Template->getTemplateParameters()->getDepth());
+  if (auto Result =
+          CheckDeducedTemplateArgumentList(S, Template, Ps, As, MLTAL, Info);
+      Result != TemplateDeductionResult::Success)
+    return Result;
 
   if (!PartialOrdering) {
     if (auto Result = CheckDeducedArgumentConstraints(
@@ -4112,6 +4122,147 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
   return TemplateDeductionResult::Success;
 }
 
+static void AddFriendTemplateDeductionCandidate(
+    Sema &S, TemplateDecl *TD, TemplateDeductionInfo &Info,
+    TemplateDeductionResult Result, TemplateSpecCandidateSet *FailedTSC) {
+  if (!FailedTSC)
+    return;
+
+  Decl *TemplatedDecl = TD->getTemplatedDecl();
+  for (TemplateSpecCandidate &Candidate : *FailedTSC) {
+    if (Candidate.Specialization &&
+        declaresSameEntity(Candidate.Specialization, TemplatedDecl))
+      return;
+  }
+
+  FailedTSC->addCandidate().set(
+      DeclAccessPair::make(TD, AS_public), TemplatedDecl,
+      MakeDeductionFailureInfo(S.Context, Result, Info));
+}
+
+bool Sema::DeduceTemplateArguments(
+    FriendTemplateDecl *FTD, ClassTemplateDecl *PatternCTD,
+    ClassTemplateDecl *CandidateCTD, ArrayRef<TemplateParameterList *> TPLs,
+    ArrayRef<TemplateArgument> PatternArgs,
+    ArrayRef<TemplateArgument> CandidateArgs, SourceLocation Loc,
+    TemplateSpecCandidateSet *FailedTSC,
+    MultiLevelTemplateArgumentList &DeducedArgs) {
+  EnterExpressionEvaluationContext Unevaluated(
+      *this, ExpressionEvaluationContext::Unevaluated);
+  ContextRAII SavedContext(*this, FTD->getDeclContext());
+  LocalInstantiationScope InstantiationScope(*this);
+  InstantiatingTemplate Inst(*this, Loc, FTD);
+  if (Inst.isInvalid()) {
+    TemplateDeductionInfo Info(Loc);
+    AddFriendTemplateDeductionCandidate(
+        *this, PatternCTD, Info, TemplateDeductionResult::InstantiationDepth,
+        FailedTSC);
+    return false;
+  }
+
+  SmallVector<TemplateArgumentList *, 2> DeducedArgLists;
+  DeducedArgLists.reserve(TPLs.size());
+  for (TemplateParameterList *Params : TPLs) {
+    TemplateDeductionInfo Info(Loc, Params->getDepth());
+    SFINAETrap Trap(*this, Info);
+    SmallVector<DeducedTemplateArgument, 4> Deduced(Params->size());
+    TemplateDeductionResult Result = DeduceTemplateArguments(
+        Params, PatternArgs, CandidateArgs, Info, Deduced,
+        /*NumberOfArgumentsMustMatch=*/false);
+
+    CheckTemplateArgumentInfo CTAI;
+    bool IsIncomplete = false;
+    if (Result == TemplateDeductionResult::Success)
+      Result = ConvertDeducedTemplateArguments(
+          *this, PatternCTD, Params, /*IsDeduced=*/false, Deduced, Info, CTAI,
+          &InstantiationScope, /*NumAlreadyConverted=*/0, &IsIncomplete);
+    if (Result == TemplateDeductionResult::Success && IsIncomplete) {
+      for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
+        if (!Deduced[I].isNull())
+          continue;
+        Info.Param = makeTemplateParameter(Params->getParam(I));
+        break;
+      }
+      Info.reset(
+          TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted),
+          TemplateArgumentList::CreateCopy(Context, CTAI.CanonicalConverted));
+      Result = TemplateDeductionResult::Incomplete;
+    }
+    if (Result == TemplateDeductionResult::Success && Trap.hasErrorOccurred())
+      Result = TemplateDeductionResult::SubstitutionFailure;
+    if (Result != TemplateDeductionResult::Success) {
+      AddFriendTemplateDeductionCandidate(*this, PatternCTD, Info, Result,
+                                          FailedTSC);
+      return false;
+    }
+
+    DeducedArgLists.push_back(
+        TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted));
+  }
+
+  for (TemplateArgumentList *Args : llvm::reverse(DeducedArgLists))
+    DeducedArgs.addOuterTemplateArguments(FTD, Args->asArray(),
+                                          /*Final=*/true);
+  if (!TPLs.empty())
+    DeducedArgs.addOuterRetainedLevels(TPLs.front()->getDepth());
+
+  if (DeducedArgs.isAnyArgInstantiationDependent() &&
+      llvm::any_of(TPLs, [](TemplateParameterList *Params) {
+        return Params->hasAssociatedConstraints();
+      }))
+    return false;
+
+  SmallVector<TemplateArgumentLoc, 8> PatternArgLocs;
+  PatternArgLocs.reserve(PatternArgs.size());
+  for (const TemplateArgument &Arg : PatternArgs)
+    PatternArgLocs.push_back(
+        getTrivialTemplateArgumentLoc(Arg, QualType(), Loc));
+
+  {
+    TemplateDeductionInfo Info(Loc);
+    SFINAETrap Trap(*this, Info);
+    TemplateDeductionResult Result = CheckDeducedTemplateArgumentList(
+        *this, CandidateCTD, PatternArgLocs, CandidateArgs, DeducedArgs, Info);
+    if (Result == TemplateDeductionResult::Success && Trap.hasErrorOccurred())
+      Result = TemplateDeductionResult::SubstitutionFailure;
+    if (Result != TemplateDeductionResult::Success) {
+      AddFriendTemplateDeductionCandidate(*this, PatternCTD, Info, Result,
+                                          FailedTSC);
+      return false;
+    }
+  }
+
+  for (TemplateParameterList *Params : TPLs) {
+    SmallVector<AssociatedConstraint, 3> Constraints;
+    Params->getAssociatedConstraints(Constraints);
+    if (Constraints.empty())
+      continue;
+
+    TemplateDeductionInfo Info(Loc, Params->getDepth());
+    SFINAETrap Trap(*this, Info);
+    if (CheckConstraintSatisfaction(PatternCTD, Constraints, DeducedArgs,
+                                    SourceRange(Loc),
+                                    Info.AssociatedConstraintsSatisfaction) ||
+        !Info.AssociatedConstraintsSatisfaction.IsSatisfied ||
+        Trap.hasErrorOccurred()) {
+      SmallVector<TemplateArgument, 4> CanonicalCandidateArgs;
+      CanonicalCandidateArgs.reserve(CandidateArgs.size());
+      for (const TemplateArgument &Arg : CandidateArgs)
+        CanonicalCandidateArgs.push_back(
+            Context.getCanonicalTemplateArgument(Arg));
+      Info.reset(
+          TemplateArgumentList::CreateCopy(Context, CandidateArgs),
+          TemplateArgumentList::CreateCopy(Context, CanonicalCandidateArgs));
+      AddFriendTemplateDeductionCandidate(
+          *this, PatternCTD, Info,
+          TemplateDeductionResult::ConstraintsNotSatisfied, FailedTSC);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 /// Gets the type of a function for template-argument-deducton
 /// purposes when it's considered as part of an overload set.
 static QualType GetTypeOfFunction(Sema &S, const OverloadExpr::FindResult &R,
@@ -7196,11 +7347,14 @@ Sema::MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs,
 }
 
 void Sema::MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
-                                      unsigned Depth,
+                                      bool OnlyDeduced, unsigned Depth,
                                       llvm::SmallBitVector &Used) {
+  if (OnlyDeduced && hasPackExpansionBeforeEnd(TemplateArgs))
+    return;
+
   for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
-    ::MarkUsedTemplateParameters(Context, TemplateArgs[I],
-                                 /*OnlyDeduced=*/false, Depth, Used);
+    ::MarkUsedTemplateParameters(Context, TemplateArgs[I], OnlyDeduced, Depth,
+                                 Used);
 }
 
 void Sema::MarkUsedTemplateParameters(

diff  --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 3f37b59807f85..40c5a6181c51d 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -889,14 +889,15 @@ SmallVector<unsigned> TemplateParamsReferencedInTemplateArgumentList(
     ArrayRef<TemplateArgument> DeducedArgs) {
 
   llvm::SmallBitVector ReferencedTemplateParams(TemplateParamsList->size());
-  SemaRef.MarkUsedTemplateParameters(
-      DeducedArgs, TemplateParamsList->getDepth(), ReferencedTemplateParams);
+  SemaRef.MarkUsedTemplateParameters(DeducedArgs, /*OnlyDeduced=*/false,
+                                     TemplateParamsList->getDepth(),
+                                     ReferencedTemplateParams);
 
   auto MarkDefaultArgs = [&](auto *Param) {
     if (!Param->hasDefaultArgument())
       return;
     SemaRef.MarkUsedTemplateParameters(
-        Param->getDefaultArgument().getArgument(),
+        Param->getDefaultArgument().getArgument(), /*OnlyDeduced=*/false,
         TemplateParamsList->getDepth(), ReferencedTemplateParams);
   };
 

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 79d106168e9d0..a21f65f687568 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2006,68 +2006,247 @@ Decl *TemplateDeclInstantiator::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
   return IndirectField;
 }
 
-Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
-  // Handle friend type expressions by simply substituting template
-  // parameters into the pattern type and checking the result.
-  if (TypeSourceInfo *Ty = D->getFriendType()) {
-    TypeSourceInfo *InstTy;
-    // If this is an unsupported friend, don't bother substituting template
-    // arguments into it. The actual type referred to won't be used by any
-    // parts of Clang, and may not be valid for instantiating. Just use the
-    // same info for the instantiated friend.
-    if (D->isUnsupportedFriend()) {
-      InstTy = Ty;
-    } else {
-      if (D->isPackExpansion()) {
-        SmallVector<UnexpandedParameterPack, 2> Unexpanded;
-        SemaRef.collectUnexpandedParameterPacks(Ty->getTypeLoc(), Unexpanded);
-        assert(!Unexpanded.empty() && "Pack expansion without packs");
-
-        bool ShouldExpand = true;
-        bool RetainExpansion = false;
-        UnsignedOrNone NumExpansions = std::nullopt;
-        if (SemaRef.CheckParameterPacksForExpansion(
-                D->getEllipsisLoc(), D->getSourceRange(), Unexpanded,
-                TemplateArgs, /*FailOnPackProducingTemplates=*/true,
-                ShouldExpand, RetainExpansion, NumExpansions))
-          return nullptr;
+static std::optional<TemplateName>
+LookupFriendTemplateName(Sema &SemaRef, NestedNameSpecifierLoc QualifierLoc,
+                         DeclarationName Name, SourceLocation NameLoc,
+                         bool HasTemplateKeyword, bool RequireClassTemplate) {
+  if (!QualifierLoc)
+    return TemplateName();
 
-        assert(!RetainExpansion &&
-               "should never retain an expansion for a variadic friend decl");
-
-        if (ShouldExpand) {
-          SmallVector<FriendDecl *> Decls;
-          for (unsigned I = 0; I != *NumExpansions; I++) {
-            Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
-            TypeSourceInfo *TSI = SemaRef.SubstType(
-                Ty, TemplateArgs, D->getEllipsisLoc(), DeclarationName());
-            if (!TSI)
-              return nullptr;
-
-            auto FD =
-                FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
-                                   TSI, D->getFriendLoc());
-
-            FD->setAccess(AS_public);
-            Owner->addDecl(FD);
-            Decls.push_back(FD);
-          }
+  CXXScopeSpec SS;
+  SS.Adopt(QualifierLoc);
 
-          // Just drop this node; we have no use for it anymore.
-          return nullptr;
-        }
-      }
+  DeclContext *DC = SemaRef.computeDeclContext(SS, /*EnteringContext=*/true);
+  if (!DC) {
+    if (QualifierLoc.getNestedNameSpecifier().isDependent())
+      return TemplateName();
+    return std::nullopt;
+  }
+
+  bool IsDependentContext = DC->isDependentContext();
+  if (!IsDependentContext && SemaRef.RequireCompleteDeclContext(SS, DC))
+    return std::nullopt;
+
+  LookupResult Result(SemaRef, Name, NameLoc, Sema::LookupOrdinaryName,
+                      SemaRef.forRedeclarationInCurContext());
+  if (!SemaRef.LookupQualifiedName(Result, DC)) {
+    if (RequireClassTemplate && !IsDependentContext) {
+      SemaRef.Diag(NameLoc, diag::err_no_member_template)
+          << Name << DC << QualifierLoc.getSourceRange();
+      return std::nullopt;
+    }
+    return TemplateName();
+  }
+
+  if (Result.isAmbiguous())
+    return std::nullopt;
+
+  auto *CTD = Result.getAsSingle<ClassTemplateDecl>();
+  if (!CTD) {
+    if (RequireClassTemplate && !IsDependentContext) {
+      SemaRef.Diag(NameLoc, diag::err_redefinition_
diff erent_kind) << Name;
+      SemaRef.Diag(
+          Result.getRepresentativeDecl()->getUnderlyingDecl()->getLocation(),
+          diag::note_previous_definition);
+      return std::nullopt;
+    }
+    return TemplateName();
+  }
+
+  auto *FoundUsingShadow =
+      dyn_cast<UsingShadowDecl>(Result.getRepresentativeDecl());
+
+  return SemaRef.Context.getQualifiedTemplateName(
+      QualifierLoc.getNestedNameSpecifier(), HasTemplateKeyword,
+      FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(CTD));
+}
+
+TypeSourceInfo *
+Sema::SubstFriendType(TypeSourceInfo *TSI,
+                      const MultiLevelTemplateArgumentList &TemplateArgs,
+                      SourceLocation Loc, DeclarationName Entity) {
+  TemplateSpecializationTypeLoc TSTL =
+      TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
+  NestedNameSpecifierLoc QualifierLoc =
+      TSTL ? TSTL.getQualifierLoc() : NestedNameSpecifierLoc();
+  if (!TSTL || !QualifierLoc ||
+      !QualifierLoc.getNestedNameSpecifier().isDependent())
+    return SubstType(TSI, TemplateArgs, Loc, Entity);
+
+  const auto *FriendTST = TSTL.getTypePtr();
+  auto *FriendCTD = dyn_cast_or_null<ClassTemplateDecl>(
+      FriendTST->getTemplateName().getAsTemplateDecl());
+  if (!FriendCTD)
+    return SubstType(TSI, TemplateArgs, Loc, Entity);
+
+  QualifierLoc = SubstNestedNameSpecifierLoc(QualifierLoc, TemplateArgs);
+  if (!QualifierLoc)
+    return nullptr;
+
+  std::optional<TemplateName> InstTemplate = LookupFriendTemplateName(
+      *this, QualifierLoc, FriendCTD->getDeclName(), TSTL.getTemplateNameLoc(),
+      TSTL.getTemplateKeywordLoc().isValid(),
+      /*RequireClassTemplate=*/false);
+  if (!InstTemplate)
+    return nullptr;
+  if (InstTemplate->isNull())
+    return SubstType(TSI, TemplateArgs, Loc, Entity);
+
+  SmallVector<TemplateArgumentLoc, 4> FriendArgLocs;
+  for (unsigned I = 0, N = TSTL.getNumArgs(); I != N; ++I)
+    FriendArgLocs.push_back(TSTL.getArgLoc(I));
+
+  TemplateArgumentListInfo InstArgs(TSTL.getLAngleLoc(), TSTL.getRAngleLoc());
+  if (SubstTemplateArguments(FriendArgLocs, TemplateArgs, InstArgs))
+    return nullptr;
+
+  QualType InstTy =
+      CheckTemplateIdType(FriendTST->getKeyword(), *InstTemplate,
+                          TSTL.getTemplateNameLoc(), InstArgs,
+                          /*Scope=*/nullptr, /*ForNestedNameSpecifier=*/false);
+  if (InstTy.isNull())
+    return nullptr;
+
+  TypeLocBuilder TLB;
+  TLB.push<TemplateSpecializationTypeLoc>(InstTy).set(
+      TSTL.getElaboratedKeywordLoc(), QualifierLoc,
+      TSTL.getTemplateKeywordLoc(), TSTL.getTemplateNameLoc(), InstArgs);
+  return TLB.getTypeSourceInfo(Context, InstTy);
+}
+
+struct SubstitutedFriend {
+  TypeSourceInfo *TypeInfo = nullptr;
+  TemplateName TemplateName;
+
+  bool empty() const { return !TypeInfo && TemplateName.isNull(); }
+};
+
+static std::optional<SubstitutedFriend>
+SubstFriendTemplateType(Sema &SemaRef, TypeSourceInfo *TSI,
+                        TemplateName FriendTemplate,
+                        const MultiLevelTemplateArgumentList &TemplateArgs,
+                        SourceLocation Loc, DeclarationName Entity) {
+  NestedNameSpecifierLoc QualifierLoc = TSI->getTypeLoc().getPrefix();
+  NestedNameSpecifierLoc InstQualifierLoc = QualifierLoc;
+  if (QualifierLoc && QualifierLoc.getNestedNameSpecifier().isDependent()) {
+    InstQualifierLoc =
+        SemaRef.SubstNestedNameSpecifierLoc(QualifierLoc, TemplateArgs);
+    if (!InstQualifierLoc ||
+        SemaRef.CheckDependentFriend(Loc, InstQualifierLoc, /*TPLs=*/{},
+                                     /*IsInstantiation=*/true))
+      return std::nullopt;
+  }
+
+  TemplateName InstFriendTemplate;
+  if (!FriendTemplate.isNull()) {
+    auto DNTL = TSI->getTypeLoc().getAs<DependentNameTypeLoc>();
+    assert(DNTL && "friend class template must have a dependent name type");
+
+    std::optional<TemplateName> InstTemplate = LookupFriendTemplateName(
+        SemaRef, InstQualifierLoc, DNTL.getTypePtr()->getIdentifier(),
+        DNTL.getNameLoc(), /*HasTemplateKeyword=*/false,
+        /*RequireClassTemplate=*/true);
+    if (!InstTemplate)
+      return std::nullopt;
+    if (!InstTemplate->isNull())
+      return SubstitutedFriend{nullptr, *InstTemplate};
+
+    auto *DTN = FriendTemplate.getAsDependentTemplateName();
+    assert(DTN && "unresolved friend template must have a dependent name");
+    InstFriendTemplate = SemaRef.Context.getDependentTemplateName(
+        {InstQualifierLoc.getNestedNameSpecifier(), DTN->getName(),
+         DTN->hasTemplateKeyword()});
+  }
+
+  TypeSourceInfo *InstType =
+      SemaRef.SubstFriendType(TSI, TemplateArgs, Loc, Entity);
+  if (!InstType)
+    return std::nullopt;
+  return SubstitutedFriend{InstType, InstFriendTemplate};
+}
+
+bool TemplateDeclInstantiator::InstantiateFriendPackExpansion(FriendDecl *D) {
+  TypeSourceInfo *TSI = D->getFriendType();
+  assert(TSI && "friend pack expansion must name a type");
+
+  const auto *FTD = dyn_cast<FriendTemplateDecl>(D);
+  ArrayRef<TemplateParameterList *> TPLs;
+  if (FTD)
+    TPLs = FTD->getTemplateParameterLists();
+
+  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+  SemaRef.collectUnexpandedParameterPacks(TSI->getTypeLoc(), Unexpanded);
+  assert(!Unexpanded.empty() && "Pack expansion without packs");
+
+  bool ShouldExpand = true;
+  bool RetainExpansion = false;
+  UnsignedOrNone NumExpansions = std::nullopt;
+  if (SemaRef.CheckParameterPacksForExpansion(
+          D->getEllipsisLoc(), D->getSourceRange(), Unexpanded, TemplateArgs,
+          /*FailOnPackProducingTemplates=*/true, ShouldExpand, RetainExpansion,
+          NumExpansions))
+    return true;
+
+  assert(!RetainExpansion &&
+         "should never retain an expansion for a friend declaration");
+
+  if (!ShouldExpand)
+    return false;
+
+  for (unsigned I = 0; I != *NumExpansions; I++) {
+    Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
+    LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+    SmallVector<TemplateParameterList *, 1> InstTPLs;
+    if (SubstTemplateParameterLists(TPLs, InstTPLs))
+      return true;
 
-      InstTy = SemaRef.SubstType(Ty, TemplateArgs, D->getLocation(),
-                                 DeclarationName());
+    std::optional<SubstitutedFriend> InstFriend;
+    if (FTD)
+      InstFriend = SubstFriendTemplateType(
+          SemaRef, TSI, FTD->getFriendTemplateName(), TemplateArgs,
+          D->getEllipsisLoc(), DeclarationName());
+    else if (TypeSourceInfo *InstType = SemaRef.SubstFriendType(
+                 TSI, TemplateArgs, D->getEllipsisLoc(), DeclarationName()))
+      InstFriend = SubstitutedFriend{InstType, {}};
+    if (!InstFriend || InstFriend->empty())
+      return true;
+
+    FriendDecl *FD;
+    if (FTD) {
+      FriendDecl::FriendUnion ToFriend =
+          InstFriend->TypeInfo ? FriendDecl::FriendUnion(InstFriend->TypeInfo)
+                               : FriendDecl::FriendUnion();
+      FD = FriendTemplateDecl::Create(
+          SemaRef.Context, Owner, D->getLocation(), ToFriend, D->getFriendLoc(),
+          InstTPLs, /*EllipsisLoc=*/{}, InstFriend->TemplateName);
+    } else {
+      assert(InstTPLs.empty() && "unexpected template parameter lists");
+      assert(InstFriend->TemplateName.isNull() &&
+             "non-template friend resolved to a class template");
+      FD = FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+                              InstFriend->TypeInfo, D->getFriendLoc());
     }
+
+    FD->setAccess(AS_public);
+    Owner->addDecl(FD);
+  }
+
+  return true;
+}
+
+Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
+  if (TypeSourceInfo *Ty = D->getFriendType()) {
+    if (D->isPackExpansion() && InstantiateFriendPackExpansion(D))
+      return nullptr;
+
+    TypeSourceInfo *InstTy = SemaRef.SubstFriendType(
+        Ty, TemplateArgs, D->getLocation(), DeclarationName());
     if (!InstTy)
       return nullptr;
 
     FriendDecl *FD = FriendDecl::Create(
         SemaRef.Context, Owner, D->getLocation(), InstTy, D->getFriendLoc());
     FD->setAccess(AS_public);
-    FD->setUnsupportedFriend(D->isUnsupportedFriend());
     Owner->addDecl(FD);
     return FD;
   }
@@ -2086,7 +2265,6 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
     FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
                        cast<NamedDecl>(NewND), D->getFriendLoc());
   FD->setAccess(AS_public);
-  FD->setUnsupportedFriend(D->isUnsupportedFriend());
   Owner->addDecl(FD);
   return FD;
 }
@@ -2851,6 +3029,12 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
     if (!QualifierLoc)
       return nullptr;
   }
+  if (isFriend &&
+      (FunctionTemplate || !D->getTemplateParameterLists().empty()) &&
+      D->getQualifier().isDependent() &&
+      SemaRef.CheckDependentFriend(D->getLocation(), QualifierLoc,
+                                   /*TPLs=*/{}, /*IsInstantiation=*/true))
+    return nullptr;
 
   AssociatedConstraint TrailingRequiresClause = D->getTrailingRequiresClause();
 
@@ -3265,17 +3449,23 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
     if (!QualifierLoc)
       return nullptr;
   }
+  if (isFriend &&
+      (FunctionTemplate || !D->getTemplateParameterLists().empty()) &&
+      D->getQualifier().isDependent() &&
+      SemaRef.CheckDependentFriend(D->getLocation(), QualifierLoc,
+                                   /*TPLs=*/{}, /*IsInstantiation=*/true))
+    return nullptr;
 
   DeclContext *DC = Owner;
   if (isFriend) {
-    if (QualifierLoc) {
+    if (QualifierLoc && !QualifierLoc.getNestedNameSpecifier().isDependent()) {
       CXXScopeSpec SS;
       SS.Adopt(QualifierLoc);
       DC = SemaRef.computeDeclContext(SS);
 
       if (DC && SemaRef.RequireCompleteDeclContext(SS, DC))
         return nullptr;
-    } else {
+    } else if (!QualifierLoc) {
       DC = SemaRef.FindInstantiatedContext(D->getLocation(),
                                            D->getDeclContext(),
                                            TemplateArgs);
@@ -3423,12 +3613,22 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
         return nullptr;
     }
 
-    if (SemaRef.CheckFunctionTemplateSpecialization(
-            Method, DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
-            Previous))
-      Method->setInvalidDecl();
-
-    IsExplicitSpecialization = true;
+    if (QualifierLoc && QualifierLoc.getNestedNameSpecifier().isDependent()) {
+      if (SemaRef.CheckDependentFunctionTemplateSpecialization(
+              Method,
+              DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
+              Previous))
+        Method->setInvalidDecl();
+    } else {
+      if (Previous.empty())
+        SemaRef.LookupQualifiedName(Previous, DC);
+      if (SemaRef.CheckFunctionTemplateSpecialization(
+              Method,
+              DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
+              Previous))
+        Method->setInvalidDecl();
+      IsExplicitSpecialization = true;
+    }
   } else if (const ASTTemplateArgumentListInfo *ArgsWritten =
                  D->getTemplateSpecializationArgsAsWritten()) {
     SemaRef.LookupQualifiedName(Previous, DC);
@@ -4772,14 +4972,48 @@ Decl *TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D)
 }
 
 Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
-  // FIXME: We need to be able to instantiate FriendTemplateDecls.
-  unsigned DiagID = SemaRef.getDiagnostics().getCustomDiagID(
-                                               DiagnosticsEngine::Error,
-                                               "cannot instantiate %0 yet");
-  SemaRef.Diag(D->getLocation(), DiagID)
-    << D->getDeclKindName();
+  ArrayRef<TemplateParameterList *> FriendTPLs = D->getTemplateParameterLists();
 
-  return nullptr;
+  TypeSourceInfo *FriendTSI = D->getFriendType();
+  if (FriendTSI && D->isPackExpansion() && InstantiateFriendPackExpansion(D))
+    return nullptr;
+
+  LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+  SmallVector<TemplateParameterList *, 1> InstTPLs;
+  if (SubstTemplateParameterLists(FriendTPLs, InstTPLs))
+    return nullptr;
+
+  FriendDecl::FriendUnion ToFriend;
+  TemplateName ToTemplate;
+  if (FriendTSI) {
+    std::optional<SubstitutedFriend> Substituted = SubstFriendTemplateType(
+        SemaRef, FriendTSI, D->getFriendTemplateName(), TemplateArgs,
+        D->getLocation(), DeclarationName());
+    if (!Substituted || Substituted->empty())
+      return nullptr;
+    ToFriend = Substituted->TypeInfo;
+    ToTemplate = Substituted->TemplateName;
+  } else if (!D->getFriendTemplateName().isNull()) {
+    if (auto *InstTemplate =
+            cast_or_null<TemplateDecl>(Visit(D->getFriendDecl())))
+      ToTemplate = TemplateName(InstTemplate);
+    else
+      return nullptr;
+  } else {
+    if (auto *InstFriendDecl =
+            cast_or_null<NamedDecl>(Visit(D->getFriendDecl())))
+      ToFriend = InstFriendDecl;
+    else
+      return nullptr;
+  }
+
+  FriendTemplateDecl *InstFriend = FriendTemplateDecl::Create(
+      SemaRef.Context, Owner, D->getLocation(), ToFriend, D->getFriendLoc(),
+      InstTPLs, /*EllipsisLoc=*/{}, ToTemplate);
+
+  InstFriend->setAccess(AS_public);
+  Owner->addDecl(InstFriend);
+  return InstFriend;
 }
 
 Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) {
@@ -4924,6 +5158,33 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
   return InstL;
 }
 
+bool TemplateDeclInstantiator::SubstTemplateParameterLists(
+    ArrayRef<TemplateParameterList *> TPLs,
+    SmallVectorImpl<TemplateParameterList *> &InstTPLs) {
+  llvm::SaveAndRestore RAII(EvaluateConstraints, false);
+  for (TemplateParameterList *L : TPLs) {
+    TemplateParameterList *InstParams = SubstTemplateParams(L);
+    if (!InstParams)
+      return true;
+
+    if (Expr *RequiresClause = L->getRequiresClause()) {
+      ExprResult InstRequiresClause =
+          SemaRef.SubstConstraintExprWithoutSatisfaction(RequiresClause,
+                                                         TemplateArgs);
+      if (!InstRequiresClause.isUsable())
+        return true;
+
+      InstParams = TemplateParameterList::Create(
+          SemaRef.Context, InstParams->getTemplateLoc(),
+          InstParams->getLAngleLoc(), InstParams->asArray(),
+          InstParams->getRAngleLoc(), InstRequiresClause.get());
+    }
+
+    InstTPLs.push_back(InstParams);
+  }
+  return false;
+}
+
 TemplateParameterList *
 Sema::SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
                           const MultiLevelTemplateArgumentList &TemplateArgs,
@@ -5166,9 +5427,8 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
   return InstPartialSpec;
 }
 
-TypeSourceInfo*
-TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
-                              SmallVectorImpl<ParmVarDecl *> &Params) {
+TypeSourceInfo *TemplateDeclInstantiator::SubstFunctionType(
+    FunctionDecl *D, SmallVectorImpl<ParmVarDecl *> &Params) {
   TypeSourceInfo *OldTInfo = D->getTypeSourceInfo();
   assert(OldTInfo && "substituting function without type source info");
   assert(Params.empty() && "parameter vector is non-empty at start");
@@ -5237,8 +5497,10 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
           continue;
         }
 
-        ParmVarDecl *Parm =
-            cast_or_null<ParmVarDecl>(VisitParmVarDecl(OldParam));
+        ParmVarDecl *Parm = SemaRef.SubstParmVarDecl(
+            OldParam, TemplateArgs, /*indexAdjustment=*/0,
+            /*NumExpansions=*/std::nullopt,
+            /*ExpectParameterPack=*/false, EvaluateConstraints);
         if (!Parm)
           return nullptr;
         Params.push_back(Parm);

diff  --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index bec62e7c55606..e973b7ae71954 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2408,26 +2408,37 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
     D->Friend = readDeclAs<NamedDecl>();
   else
     D->Friend = readTypeSourceInfo();
-  for (unsigned i = 0; i != D->NumTPLists; ++i)
-    D->getTrailingObjects()[i] = Record.readTemplateParameterList();
   D->NextFriend = readDeclID().getRawValue();
-  D->UnsupportedFriend = (Record.readInt() != 0);
   D->FriendLoc = readSourceLocation();
   D->EllipsisLoc = readSourceLocation();
 }
 
 void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
   VisitDecl(D);
-  unsigned NumParams = Record.readInt();
-  D->NumParams = NumParams;
-  D->Params = new (Reader.getContext()) TemplateParameterList *[NumParams];
-  for (unsigned i = 0; i != NumParams; ++i)
-    D->Params[i] = Record.readTemplateParameterList();
-  if (Record.readInt()) // HasFriendDecl
+  for (unsigned I = 0; I != D->NumTPLists; ++I)
+    D->getTrailingObjects()[I] = Record.readTemplateParameterList();
+  auto Kind = static_cast<FriendTemplateDeclKind>(Record.readInt());
+  switch (Kind) {
+  case FTDK_Type:
+    D->Friend = readTypeSourceInfo();
+    break;
+  case FTDK_Decl:
     D->Friend = readDeclAs<NamedDecl>();
-  else
+    break;
+  case FTDK_Template:
+    D->Template = Record.readTemplateName();
+    assert(D->Template.getAsTemplateDecl() &&
+           "friend template name must resolve to a template declaration");
+    D->Friend = D->Template.getAsTemplateDecl();
+    break;
+  case FTDK_Dependent:
     D->Friend = readTypeSourceInfo();
+    D->Template = Record.readTemplateName();
+    break;
+  }
+  D->NextFriend = readDeclID().getRawValue();
   D->FriendLoc = readSourceLocation();
+  D->EllipsisLoc = readSourceLocation();
 }
 
 void ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
@@ -4104,10 +4115,11 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
     D = AccessSpecDecl::CreateDeserialized(Context, ID);
     break;
   case DECL_FRIEND:
-    D = FriendDecl::CreateDeserialized(Context, ID, Record.readInt());
+    D = FriendDecl::CreateDeserialized(Context, ID);
     break;
   case DECL_FRIEND_TEMPLATE:
-    D = FriendTemplateDecl::CreateDeserialized(Context, ID);
+    D = FriendTemplateDecl::CreateDeserialized(Context, ID,
+                                               /*NumTPLists=*/Record.readInt());
     break;
   case DECL_CLASS_TEMPLATE:
     D = ClassTemplateDecl::CreateDeserialized(Context, ID);

diff  --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index d8f5ed9afdde1..6f67acf9a6e7e 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1831,9 +1831,6 @@ void ASTDeclWriter::VisitAccessSpecDecl(AccessSpecDecl *D) {
 }
 
 void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
-  // Record the number of friend type template parameter lists here
-  // so as to simplify memory allocation during deserialization.
-  Record.push_back(D->NumTPLists);
   VisitDecl(D);
   bool hasFriendDecl = isa<NamedDecl *>(D->Friend);
   Record.push_back(hasFriendDecl);
@@ -1841,26 +1838,47 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
     Record.AddDeclRef(D->getFriendDecl());
   else
     Record.AddTypeSourceInfo(D->getFriendType());
-  for (unsigned i = 0; i < D->NumTPLists; ++i)
-    Record.AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i));
   Record.AddDeclRef(D->getNextFriend());
-  Record.push_back(D->UnsupportedFriend);
   Record.AddSourceLocation(D->FriendLoc);
   Record.AddSourceLocation(D->EllipsisLoc);
   Code = serialization::DECL_FRIEND;
 }
 
 void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+  // Record the number of template parameter lists here to simplify memory
+  // allocation during deserialization.
+  Record.push_back(D->NumTPLists);
   VisitDecl(D);
-  Record.push_back(D->getNumTemplateParameters());
-  for (unsigned i = 0, e = D->getNumTemplateParameters(); i != e; ++i)
-    Record.AddTemplateParameterList(D->getTemplateParameterList(i));
-  Record.push_back(D->getFriendDecl() != nullptr);
-  if (D->getFriendDecl())
-    Record.AddDeclRef(D->getFriendDecl());
-  else
+  for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+    Record.AddTemplateParameterList(TPL);
+  FriendTemplateDeclKind Kind;
+  if (D->getFriendType()) {
+    Kind = D->Template.isNull() ? FTDK_Type : FTDK_Dependent;
+  } else if (D->Template.isNull()) {
+    assert(D->getFriendDecl());
+    Kind = FTDK_Decl;
+  } else {
+    Kind = FTDK_Template;
+  }
+  Record.push_back(Kind);
+  switch (Kind) {
+  case FTDK_Type:
+    Record.AddTypeSourceInfo(D->getFriendType());
+    break;
+  case FTDK_Dependent:
     Record.AddTypeSourceInfo(D->getFriendType());
-  Record.AddSourceLocation(D->getFriendLoc());
+    Record.AddTemplateName(D->Template);
+    break;
+  case FTDK_Decl:
+    Record.AddDeclRef(D->getFriendDecl());
+    break;
+  case FTDK_Template:
+    Record.AddTemplateName(D->Template);
+    break;
+  }
+  Record.AddDeclRef(D->getNextFriend());
+  Record.AddSourceLocation(D->FriendLoc);
+  Record.AddSourceLocation(D->EllipsisLoc);
   Code = serialization::DECL_FRIEND_TEMPLATE;
 }
 

diff  --git a/clang/test/AST/friend-template-decl.cpp b/clang/test/AST/friend-template-decl.cpp
new file mode 100644
index 0000000000000..c9816771991b9
--- /dev/null
+++ b/clang/test/AST/friend-template-decl.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -std=c++20 -ast-print %s | FileCheck %s --check-prefix=PRINT
+// RUN: %clang_cc1 -std=c++20 -ast-dump -ast-dump-filter C %s | FileCheck %s --check-prefix=DUMP
+// RUN: %clang_cc1 -std=c++20 -ast-dump=json -ast-dump-filter C %s | FileCheck %s --check-prefix=JSON
+// RUN: %clang_cc1 -std=c++20 -x c++-header -emit-pch -o %t %s
+// RUN: %clang_cc1 -std=c++20 -x c++ -include-pch %t -ast-dump-all -ast-dump-filter C /dev/null | FileCheck %s --check-prefix=DUMP
+
+namespace N {
+template <class T> struct A {
+  template <class U> struct B;
+};
+
+template <class V> struct C {
+  template <class T> template <class U> friend struct A<T>::B;
+};
+
+template struct C<int>;
+}
+
+// PRINT:      template<> struct C<int> {
+// PRINT-NEXT:     template <class T> template <class U> friend struct A<T>::B;
+
+// DUMP:      ClassTemplateDecl {{.*}} C
+// DUMP:      FriendTemplateDecl {{.*}} 'struct A<T>::B'
+// DUMP-NEXT:   |-TemplateTypeParmDecl {{.*}} T
+// DUMP-NEXT:   `-TemplateTypeParmDecl {{.*}} U
+// DUMP:      ClassTemplateSpecializationDecl {{.*}} struct C definition
+// DUMP-NOT:  <<<NULL>>>
+// DUMP:      FriendTemplateDecl {{.*}} qualified
+// DUMP-NEXT:   |-NestedNameSpecifier TypeSpec 'A<T>'
+// DUMP-NEXT:   |-ClassTemplateDecl
+// DUMP-NEXT:   |-TemplateTypeParmDecl {{.*}} T
+// DUMP-NEXT:   `-TemplateTypeParmDecl {{.*}} U
+
+// JSON:      "kind": "ClassTemplateSpecializationDecl",
+// JSON:      "kind": "FriendTemplateDecl",
+// JSON-NEXT: "loc": {
+// JSON:      "templateName": "A<T>::B"

diff  --git a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
index f7216ea7eb7b0..6c55e81c58c18 100644
--- a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
+++ b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
@@ -36,7 +36,7 @@ class A {
 public:
   class foo {};
   static int y;
-  template <typename S> friend class B<S>::ty; // expected-warning {{dependent nested name specifier 'B<S>' for friend class declaration is not supported}}
+  template <typename S> friend class B<S>::ty;
 };
 
 template<typename T> class B { typedef int ty; };
@@ -74,7 +74,7 @@ struct {
       friend
 
   float;
-  template<typename T> friend class A<T>::foo; // expected-warning {{not supported}}
+  template<typename T> friend class A<T>::foo;
 } a;
 
 void testA() { (void)sizeof(A<int>); }

diff  --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index 646b82eb40fd7..32ff4d72d30f3 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -375,7 +375,7 @@ namespace cwg1837 { // cwg1837: 3.3
 #endif
 } // namespace cwg1837
 
-namespace cwg1862 { // cwg1862: no
+namespace cwg1862 { // cwg1862: 24
 template<class T>
 struct A {
   struct B {
@@ -416,29 +416,24 @@ struct A<float*> {
 };
 
 class C {
-  int private_int;
+  int private_int; // #cwg1862-C-private_int
 
   template<class T>
   friend struct A<T>::B;
-  // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}}
 
   template<class T>
   friend void A<T>::f();
-  // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}}
 
-  // FIXME: this is ill-formed, because A<T>​::​D does not end with a simple-template-id
   template<class T>
   friend void A<T>::D::g();
-  // expected-warning at -1 {{dependent nested name specifier 'A<T>::D' for friend class declaration is not supported; turning off access control for 'C'}}
+  // expected-error at -1 {{'A<T>::D' does not name a class template}}
 
   template<class T>
   friend int *A<T*>::h();
-  // expected-warning at -1 {{dependent nested name specifier 'A<T *>' for friend class declaration is not supported; turning off access control for 'C'}}
 
   template<class T>
   template<T U>
   friend T A<T>::i();
-  // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}}
 };
 
 C c;
@@ -450,11 +445,16 @@ void A<int>::B::e() { (void)c.private_int; }
 template<class T>
 void A<T>::f() { (void)c.private_int; }
 int A<int>::f() { (void)c.private_int; return 0; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+//   expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
 
-// FIXME: both definition of 'D::g' are not friends, so they don't have access to 'private_int'
 template<class T>
 void A<T>::D::g() { (void)c.private_int; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+//   expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
 void A<int>::D::g() { (void)c.private_int; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+//   expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
 
 template<class T>
 T A<T>::h() { (void)c.private_int; }

diff  --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 8162f9caa8f15..53ff58c7cbffd 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -94,26 +94,26 @@ namespace cwg1909 { // cwg1909: 3.7
   };
 } // namespace cwg1909
 
-namespace cwg1918 { // cwg1918: no
+namespace cwg1918 { // cwg1918: 24
 template<typename T> struct A {
   class B {
     class C {};
   };
 };
 class X {
-  static int x;
-  // FIXME: this is ill-formed, because A<T>::B::C does not end with a simple-template-id
+  static int x; // #cwg1918-X-x
   template <typename T>
   friend class A<T>::B::C;
-  // expected-warning at -1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}}
+  // expected-error at -1 {{'A<T>::B' does not name a class template}}
 };
 template<> struct A<int> {
   typedef struct Q B;
 };
 struct Q {
   class C {
-    // FIXME: 'f' is not a friend, so 'X::x' is not accessible
     int f() { return X::x; }
+    // expected-error at -1 {{'x' is a private member of 'cwg1918::X'}}
+    //   expected-note@#cwg1918-X-x {{implicitly declared private here}}
   };
 };
 } // namespace cwg1918
@@ -159,7 +159,7 @@ derived d2(42, 9);
 #endif
 } // namespace cwg1941
 
-namespace cwg1945 { // cwg1945: no
+namespace cwg1945 { // cwg1945: 24
 template<typename T> struct A {
   class B {
     class C {};
@@ -167,10 +167,9 @@ template<typename T> struct A {
 };
 class X {
   static int x;
-  // FIXME: this is ill-formed, because A<T>::B::C does not end with a simple-template-id
   template <typename T>
   friend class A<T>::B::C;
-  // expected-warning at -1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}}
+  // expected-error at -1 {{'A<T>::B' does not name a class template}}
 };
 } // namespace cwg1945
 

diff  --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index 02c8f30249683..7352da80e7b41 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -178,17 +178,21 @@ namespace cwg2858 { // cwg2858: 19
 
 template<typename... Ts>
 struct A {
-  // FIXME: The nested-name-specifier in the following friend declarations are declarative,
-  // but we don't treat them as such (yet).
   friend void Ts...[0]::f();
+  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
   template<typename U>
   friend void Ts...[0]::g();
+  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
 
   friend struct Ts...[0]::B;
-  // FIXME: The index of the pack-index-specifier is printed as a memory address in the diagnostic.
+  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
   template<typename U>
   friend struct Ts...[0]::C;
-  // since-cxx26-warning at -1 {{dependent nested name specifier 'Ts...[0]' for friend template declaration is not supported; ignoring this friend declaration}}
+  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
+
+  template<typename U>
+  friend struct Ts...[0]::template B<U>::C;
+  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
 };
 
 #endif

diff  --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index 451554a36d70d..eb1898b56762d 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -396,7 +396,7 @@ namespace cwg637 { // cwg637: 3.0
   }
 } // namespace cwg637
 
-namespace cwg638 { // cwg638: no
+namespace cwg638 { // cwg638: 24
   template<typename T> struct A {
     struct B;
     void f();
@@ -407,26 +407,29 @@ namespace cwg638 { // cwg638: no
   };
 
   class X {
-    typedef int type;
+    typedef int type; // #cwg638-X-type
     template<class T> friend struct A<T>::B;
-    // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
     template<class T> friend void A<T>::f();
-    // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
     template<class T> friend void A<T>::g();
-    // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
     template<class T> friend void A<T>::C::h();
-    // expected-warning at -1 {{dependent nested name specifier 'A<T>::C' for friend class declaration is not supported; turning off access control for 'X'}}
+    // expected-error at -1 {{'A<T>::C' does not name a class template}}
   };
 
   template<> struct A<int> {
-    X::type a; // FIXME: private
+    X::type a;
+    // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+    //   expected-note@#cwg638-X-type {{implicitly declared private here}}
     struct B {
       X::type b; // ok
     };
-    int f() { X::type c; } // FIXME: private
+    int f() { X::type c; }
+    // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+    //   expected-note@#cwg638-X-type {{implicitly declared private here}}
     void g() { X::type d; } // ok
     struct D {
-      void h() { X::type e; } // FIXME: private
+      void h() { X::type e; }
+      // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+      //   expected-note@#cwg638-X-type {{implicitly declared private here}}
     };
   };
 } // namespace cwg638

diff  --git a/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
index b8092afaffef3..3ebd32f713371 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
@@ -387,3 +387,29 @@ template <class T> class A {
   friend void foo<T>() {} // expected-error {{ambiguous}} expected-error{{no candidate function template was found for dependent friend function template specialization}}
 };
 }
+
+namespace test19 {
+template <class V> class B;
+
+template <class T> struct A {
+  template <class U> static void f(B<T> &);
+};
+
+template <class V> class B {
+  int n; // #test19-B-n
+  friend void A<V>::template f<int>(B<V> &);
+};
+
+template <class T>
+template <class U>
+void A<T>::f(B<T> &x) {
+  x.n = 0;
+  // expected-error at -1 {{'n' is a private member of 'test19::B<long>'}}
+  //   expected-note@#test19-B-n {{implicitly declared private here}}
+}
+
+template class B<long>;
+template void A<long>::f<int>(B<long> &);
+template void A<long>::f<double>(B<long> &);
+// expected-note at -1 {{in instantiation of function template specialization 'test19::A<long>::f<double>' requested here}}
+}

diff  --git a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
index a292d0de97a39..cb0b046eb4132 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -1,4 +1,8 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++14 -fcxx-exceptions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
 
 namespace test0 {
   template <class T> class A {
@@ -6,7 +10,7 @@ namespace test0 {
   };
 
   class B {
-    template <class T> friend class A<T>::Member; // expected-warning {{not supported}}
+    template <class T> friend class A<T>::Member;
     int n;
   };
 
@@ -19,7 +23,7 @@ namespace test1 {
 
   class C {
     static void foo();
-    template <class T> friend void A<T>::f(); // expected-warning {{not supported}}
+    template <class T> friend void A<T>::f();
   };
 
   template <class T> struct A {
@@ -35,25 +39,30 @@ namespace test1 {
   };
 }
 
-// FIXME: these should fail!
 namespace test2 {
   template <class T> struct A;
 
   class C {
-    static void foo();
-    template <class T> friend void A<T>::g(); // expected-warning {{not supported}}
+    static void foo(); // #test2-C-foo
+    template <class T> friend void A<T>::g();
   };
 
   template <class T> struct A {
     void f() { C::foo(); }
+    // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+    //   expected-note@#test2-C-foo {{implicitly declared private here}}
   };
 
   template <class T> struct A<T*> {
     void f() { C::foo(); }
+    // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+    //   expected-note@#test2-C-foo {{implicitly declared private here}}
   };
 
   template <> struct A<char> {
     void f() { C::foo(); }
+    // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+    //   expected-note@#test2-C-foo {{implicitly declared private here}}
   };
 }
 
@@ -66,7 +75,7 @@ namespace test3 {
 
   template <class U> class C {
     int i;
-    template <class T> friend struct A<T>::Inner; // expected-warning {{not supported}}
+    template <class T> friend struct A<T>::Inner;
   };
 
   template <class T> int A<T>::Inner::foo() {
@@ -79,24 +88,1091 @@ namespace test3 {
 }
 
 namespace test4 {
-  template <class T> struct X {
+  template <class T> struct A {
     template <class U> void operator+=(U);
-    
+
     template <class V>
     template <class U>
-    friend void X<V>::operator+=(U); // expected-warning {{not supported}}
+    friend void A<V>::operator+=(U);
   };
 
-  void test() {   
-    X<int>() += 1.0;
+  void test() {
+    A<int>() += 1.0;
   }
 }
 
 namespace test5 {
   template<template <class> class T> struct A {
-    template<template <class> class U> friend void A<U>::foo(); // expected-warning {{not supported}}
+    template<template <class> class U> friend void A<U>::foo();
   };
 
   template <class> struct B {};
   template class A<B>;
 }
+
+namespace test6 {
+  template <class T> struct A {
+    struct B {
+      static int f();
+    };
+  };
+
+  struct C {
+    int n;
+    template <class T> friend struct A<T>::B;
+  };
+
+  template <class T> int A<T>::B::f() {
+    C c;
+    c.n = 0;
+    return 0;
+  }
+
+  int k = A<int>::B::f();
+}
+
+namespace test7 {
+  template <class T> struct A {
+    struct D {
+      void g();
+    };
+  };
+
+  struct C {
+    template <class T> friend void A<T>::D::g();
+    // expected-error at -1 {{'A<T>::D' does not name a class template}}
+  };
+}
+
+namespace test8 {
+  template <class T> struct A { // #test8-A
+    T h();
+  };
+
+  template <> struct A<int> {
+    int h();
+  };
+
+  template <> struct A<float *> {
+    int *h();
+  };
+
+  class C {
+    int n; // #test8-C-n
+    template <class T> friend int *A<T *>::h();
+  };
+
+  template <class T> T A<T>::h() {
+    return T();
+  }
+
+  int A<int>::h() {
+    C c;
+    c.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test8::C'}}
+    //   expected-note@#test8-C-n {{implicitly declared private here}}
+    //   expected-note@#test8-A {{candidate friend template ignored: could not match 'T *' against 'int'}}
+    return 0;
+  }
+
+  template <> int *A<int *>::h() {
+    C c;
+    c.n = 0;
+    return nullptr;
+  }
+
+  int *A<float *>::h() {
+    C c;
+    c.n = 0;
+    return nullptr;
+  }
+
+  int *t1 = A<int *>().h();
+  int *t2 = A<float *>().h();
+  int t3 = A<int>().h();
+}
+
+namespace test9 {
+  template <class T> struct A {
+    template <T U> T i();
+  };
+
+  template <> struct A<int> {
+    template <int U> int i();
+  };
+
+  struct C {
+    int n;
+    template <class T> template <T U> friend T A<T>::i();
+  };
+
+  template <class T> template <T U> T A<T>::i() {
+    C c;
+    c.n = 0;
+    return U;
+  }
+
+  template <int U> int A<int>::i() {
+    C c;
+    c.n = 0;
+    return U;
+  }
+
+  int x = A<int>().i<1>();
+}
+
+namespace test10 {
+  template <class T> struct A;
+  class C {
+    static void foo(); // #test10-C-foo
+    template <class T> friend void A<T>::f();
+  };
+
+  template <class T> struct A {
+    void f() { C::foo(); }
+  };
+
+  template <> struct A<int> {
+    int f() {
+      C::foo();
+      // expected-error at -1 {{'foo' is a private member of 'test10::C'}}
+      //   expected-note@#test10-C-foo {{implicitly declared private here}}
+      return 0;
+    }
+  };
+}
+
+namespace test11 {
+  template <class> struct C;
+  template <class T> struct A {
+    template <class> struct B;
+  };
+  template <class T> struct D : A<T> {
+    using A<T>::B;
+  };
+
+  template <class T> struct C {
+    int n;
+    template <class U> friend struct D<T>::B;
+  };
+
+  template <> template <class U> struct A<int>::B {
+    static int f(C<int> &c) {
+      c.n = 0;
+      return 0;
+    }
+  };
+
+  int x = A<int>::B<void>::f(*new C<int>);
+}
+
+namespace test12 {
+  template <class T> struct A {
+    template <T> struct B {
+      static int f();
+    };
+  };
+
+  template <class T> struct C {
+    int n;
+    template <class U> template <U V> friend struct A<U>::B;
+  };
+
+  template <class T> template <T V> int A<T>::B<V>::f() {
+    C<T> c;
+    c.n = 0;
+    return 0;
+  }
+
+  int x = A<int>::B<0>::f();
+}
+
+namespace test13 {
+  template <typename T> struct S {
+    template <typename> friend class T::template A<int>::B;
+  };
+}
+
+namespace test14 {
+  template <class T> struct A {
+    template <bool V> struct B {
+      static int f(B<false> &x) { return x.n; }
+
+    private:
+      int n;
+      template <bool> friend struct A<T>::B;
+    };
+  };
+
+  int x = A<int>::B<true>::f(*new A<int>::B<false>);
+}
+
+namespace test15 {
+  template <class T> struct A {
+    T f();
+  };
+
+  template <> struct A<int> {
+    void f();
+  };
+
+  class C {
+    int n; // #test15-C-n
+    template <class T> friend T A<T>::f();
+  };
+
+  void A<int>::f() {
+    C c;
+    c.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test15::C'}}
+    //   expected-note@#test15-C-n {{implicitly declared private here}}
+  }
+}
+
+namespace test16 {
+  template <class T> struct A {
+    template <T U> T i();
+  };
+
+  template <> struct A<int> {
+    template <int U> void i();
+  };
+
+  class C {
+    int n; // #test16-C-n
+    template <class T> template <T U> friend T A<T>::i();
+  };
+
+  template <int U> void A<int>::i() {
+    C c;
+    c.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test16::C'}}
+    //   expected-note@#test16-C-n {{implicitly declared private here}}
+  }
+}
+
+namespace test17 {
+  template <class T> class A;
+
+  template <class T>
+  struct B {
+    template <bool V>
+    struct C {
+      int f(A<T> *p) { return p->x; }
+    };
+  };
+
+  template <class T>
+  class A {
+    friend struct B<T>::template C<true>;
+    int x;
+  };
+
+  template struct B<int>::C<true>;
+}
+
+namespace test18 {
+  template <class T> class A;
+
+  template <class T>
+  struct B {
+    template <bool V>
+    struct C {
+      int f(A<T> *p) { return p->x; }
+      // expected-error at -1 {{'x' is a private member of 'test18::A<int>'}}
+    };
+  };
+
+  template <class T>
+  class A {
+    friend struct B<T>::template C<false>;
+    int x;
+    // expected-note at -1 {{implicitly declared private here}}
+  };
+
+  template struct B<int>::C<true>;
+  // expected-note at -1 {{in instantiation of member function 'test18::B<int>::C<true>::f' requested here}}
+}
+
+namespace test19 {
+  template <class...> struct A {
+    struct B;
+    static void f();
+  };
+
+  struct C {
+    template <class T, class U> // #test19-U-type
+    friend struct A<T>::B;
+    // expected-error at -1 {{template parameter of friend declaration cannot be deduced from 'A<T>'}}
+    //   expected-note@#test19-U-type {{non-deducible template parameter 'U'}}
+
+    template <class T, class U> // #test19-U-function
+    friend void A<T>::f();
+    // expected-error at -1 {{template parameter of friend declaration cannot be deduced from 'A<T>'}}
+    //   expected-note@#test19-U-function {{non-deducible template parameter 'U'}}
+
+    template <class... Ts> // #test19-Ts
+    friend struct A<Ts..., int>::B;
+    // expected-error at -1 {{template parameter of friend declaration cannot be deduced from 'A<Ts..., int>'}}
+    //   expected-note@#test19-Ts {{non-deducible template parameter 'Ts'}}
+  };
+}
+
+namespace test20 {
+  class D;
+
+  template <class T> struct A {
+    template <class, class U> struct B {
+      struct C {
+        static void f(D &);
+      };
+      static void g(D &);
+    };
+  };
+
+  class D {
+    int n;
+
+    template <class T>
+    template <class U>
+    friend struct A<T>::B<T, U>::C;
+
+    template <class T>
+    template <class U>
+    friend void A<T>::B<T, U>::g(D &);
+  };
+
+  template <class T>
+  template <class V, class U>
+  void A<T>::B<V, U>::C::f(D &x) {
+    x.n = 0;
+  }
+
+  template <class T>
+  template <class V, class U>
+  void A<T>::B<V, U>::g(D &x) {
+    x.n = 0;
+  }
+
+  template struct A<int>::B<int, double>;
+}
+
+namespace test21 {
+  class C;
+
+  template <class T> struct A {
+    template <class U> struct B;
+  };
+
+  class C {
+    int n; // #test21-C-n
+    template <class T>
+    template <class U>
+    friend struct A<T>::B;
+  };
+
+  template <> struct A<int> {
+    template <int U> struct B {
+      static void f(C &x) {
+        x.n = 0;
+        // expected-error at -1 {{'n' is a private member of 'test21::C'}}
+        //   expected-note@#test21-C-n {{implicitly declared private here}}
+      }
+    };
+  };
+
+  template struct A<int>::B<0>;
+}
+
+namespace test22 {
+  class C;
+
+  template <class T> struct A {
+    struct B;
+  };
+
+  class C {
+    int n; // #test22-C-n
+    template <class T> friend struct A<T>::B;
+  };
+
+  template <> struct A<int> {
+    union B {
+      static void f(C &x) {
+        x.n = 0;
+        // expected-error at -1 {{'n' is a private member of 'test22::C'}}
+        //   expected-note@#test22-C-n {{implicitly declared private here}}
+      }
+    };
+  };
+}
+
+namespace test23 {
+  class B;
+
+  template <class T> struct A {
+    template <class U> static void f(B &);
+  };
+
+  class B {
+    int n; // #test23-B-n
+    template <class T>
+    template <class U>
+    friend void A<T>::f(B &);
+  };
+
+  template <> struct A<int> {
+    template <int U> static void f(B &x) {
+      x.n = 0;
+      // expected-error at -1 {{'n' is a private member of 'test23::B'}}
+      //   expected-note@#test23-B-n {{implicitly declared private here}}
+    }
+  };
+
+  template void A<int>::f<0>(B &);
+}
+
+namespace test24 {
+  class B;
+
+  template <class T> struct A {
+    static void f(B &);
+  };
+
+  class B {
+    int n; // #test24-B-n
+    template <class T> friend void A<T>::f(B &);
+  };
+
+  template <> struct A<int> {
+    static void f(B &x, ...) {
+      x.n = 0;
+      // expected-error at -1 {{'n' is a private member of 'test24::B'}}
+      //   expected-note@#test24-B-n {{implicitly declared private here}}
+    }
+  };
+}
+
+namespace test25 {
+  class C;
+
+  template <class... Ts> struct A {
+    struct B {
+      static void f(C &);
+    };
+  };
+
+  class C {
+    int n;
+    template <class... Ts> friend struct A<Ts...>::B;
+  };
+
+  template <class... Ts> void A<Ts...>::B::f(C &x) {
+    x.n = 0;
+  }
+
+  template void A<int, double>::B::f(C &);
+}
+
+namespace test26 {
+  class B;
+
+  template <class T> struct A {
+    template <class U> static void f(U);
+  };
+
+  template <> struct A<int> {
+    template <class U> static void f(U);
+  };
+
+  class B {
+    int n;
+    template <class T>
+    template <class U>
+    friend void A<T>::f(U);
+  };
+
+  template <class T>
+  template <class U>
+  void A<T>::f(U) {
+    B b;
+    b.n = 0;
+  }
+
+  template <class U>
+  void A<int>::f(U) {
+    B b;
+    b.n = 0;
+  }
+
+  template void A<long>::f<double>(double);
+  template void A<int>::f<double>(double);
+}
+
+namespace test27 {
+  template <class T> struct A {
+    struct B {
+      template <class U> static void g(U);
+      template <class U> struct C;
+    };
+  };
+
+  template <class V> struct D {
+    template <class U> friend void A<V>::B::g(U);
+    // expected-error at -1 {{'A<V>::B' does not name a class template}}
+
+    template <class U> friend class A<V>::B::C;
+    // expected-error at -1 {{'A<V>::B' does not name a class template}}
+  };
+}
+
+namespace test28 {
+  class B;
+
+  template <class T> struct A {
+    ~A();
+  };
+
+  template <> struct A<int> {
+    ~A();
+  };
+
+  class B {
+    int n;
+    template <class T> friend A<T>::~A();
+  };
+
+  template <class T> A<T>::~A() {
+    B b;
+    b.n = 0;
+  }
+
+  A<int>::~A() {
+    B b;
+    b.n = 0;
+  }
+
+  template struct A<long>;
+}
+
+namespace test29 {
+  template <class T> class D;
+
+  template <class T> struct A {
+    template <class I> struct B {
+      template <class U> struct C {
+        static void h(D<T> &);
+      };
+    };
+  };
+
+  template <class V> class D {
+    int n;
+
+    template <class U>
+    friend class A<V>::B<int>::C;
+  };
+
+  template <class T>
+  template <class I>
+  template <class U>
+  void A<T>::B<I>::C<U>::h(D<T> &x) {
+    x.n = 0;
+  }
+
+  template struct A<int>::B<int>::C<double>;
+}
+
+namespace test30 {
+  class B;
+
+  template <class T> struct A {
+    template <class U> static void f(U);
+  };
+
+  template <> struct A<int> {
+    template <class U> static void f(U *);
+  };
+
+  class B {
+    int n; // #test30-B-n
+    template <class T>
+    template <class U>
+    friend void A<T>::f(U);
+  };
+
+  template <class U>
+  void A<int>::f(U *) {
+    B b;
+    b.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test30::B'}}
+    //   expected-note@#test30-B-n {{implicitly declared private here}}
+  }
+
+  template void A<int>::f<double>(double *);
+}
+
+namespace test31 {
+  class B;
+
+  template <class T> struct A {
+    static void f(B &) noexcept;
+  };
+
+  template <> struct A<int> {
+    static void f(B &);
+  };
+
+  class B {
+    int n; // #test31-B-n
+    template <class T> friend void A<T>::f(B &) noexcept;
+  };
+
+  template <class T> void A<T>::f(B &x) noexcept {
+    x.n = 0;
+  }
+
+  void A<int>::f(B &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test31::B'}}
+    //   expected-note@#test31-B-n {{implicitly declared private here}}
+  }
+
+  template void A<long>::f(B &) noexcept;
+}
+
+namespace test32 {
+  class B;
+
+  template <class T> struct A {
+    A();
+    operator int();
+  };
+
+  template <> struct A<char> {
+    A();
+    operator int();
+  };
+
+  class B {
+    int n;
+    template <class T> friend A<T>::A();
+    template <class T> friend A<T>::operator int();
+  };
+
+  template <class T> A<T>::A() {
+    B b;
+    b.n = 0;
+  }
+
+  template <class T> A<T>::operator int() {
+    B b;
+    b.n = 0;
+    return 0;
+  }
+
+  A<char>::A() {
+    B b;
+    b.n = 0;
+  }
+
+  A<char>::operator int() {
+    B b;
+    b.n = 0;
+    return 0;
+  }
+
+  template struct A<long>;
+}
+
+namespace test33 {
+  struct A {
+    template <class> struct D {
+      struct M;
+      template <class> struct N;
+      static void f();
+      template <class> static void g();
+    };
+  };
+
+  struct B {
+    struct M;
+    template <class> struct N;
+    static void f();
+    template <class> static void g();
+  };
+
+  struct C {
+    template <class> using D = B;
+  };
+
+  template <class P> struct D {
+    template <class U> friend struct P::template D<U>::M;
+    // expected-error at -1 {{'test33::C::template D<U>' does not name a class template}}
+  };
+
+  template struct D<A>;
+  template struct D<C>;
+  // expected-note at -1 {{in instantiation of template class 'test33::D<test33::C>' requested here}}
+
+  template <class P> struct E {
+    template <class U> friend struct P::template D<int>::N;
+    // expected-error at -1 {{'test33::C::template D<int>' does not name a class template}}
+  };
+
+  template struct E<A>;
+  template struct E<C>;
+  // expected-note at -1 {{in instantiation of template class 'test33::E<test33::C>' requested here}}
+
+  template <class P> struct F {
+    template <class U> friend void P::template D<U>::f();
+    // expected-error at -1 {{'test33::C::template D<U>' does not name a class template}}
+  };
+
+  template struct F<A>;
+  template struct F<C>;
+  // expected-note at -1 {{in instantiation of template class 'test33::F<test33::C>' requested here}}
+
+  template <class P> struct G {
+    template <class U> friend void P::template D<int>::g();
+    // expected-error at -1 {{'test33::C::template D<int>' does not name a class template}}
+  };
+
+  template struct G<A>;
+  template struct G<C>;
+  // expected-note at -1 {{in instantiation of template class 'test33::G<test33::C>' requested here}}
+}
+
+namespace test34 {
+  template <class V> class C;
+
+  template <class T> struct A {
+    template <class U> struct B {
+      static void f(C<long> &);
+    };
+  };
+
+  template <class V> class C {
+    int n; // #test34-C-n
+
+    template <class T>
+    friend struct A<T>::template B<V>;
+  };
+
+  template class C<long>;
+
+  template <class T>
+  template <class U>
+  void A<T>::B<U>::f(C<long> &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test34::C<long>'}}
+    //   expected-note@#test34-C-n {{implicitly declared private here}}
+  }
+
+  template struct A<char>::B<long>;
+  template struct A<char>::B<int>;
+  // expected-note at -1 {{in instantiation of member function 'test34::A<char>::B<int>::f' requested here}}
+}
+
+namespace test35 {
+  template <class V> class B;
+
+  template <class T> struct A {
+    template <class U, class V> static void f(B<V> &);
+  };
+
+  template <class V> class B {
+    int n; // #test35-B-n
+
+    template <class T>
+    friend void A<T>::template f<int, V>(B &);
+  };
+
+  template <class T>
+  template <class U, class V>
+  void A<T>::f(B<V> &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test35::B<long>'}}
+    //   expected-note@#test35-B-n {{implicitly declared private here}}
+  }
+
+  template class B<long>;
+  template void A<char>::f<int, long>(B<long> &);
+  template void A<char>::f<double, long>(B<long> &);
+  // expected-note at -1 {{in instantiation of function template specialization 'test35::A<char>::f<double, long>' requested here}}
+}
+
+namespace test36 {
+  template <class V> class B;
+
+  template <class T> struct A {
+    template <class U, class V> static void f(B<V> &, U);
+  };
+
+  template <class V> class B {
+    int n; // #test36-B-n
+
+    template <class T>
+    friend void A<T>::f(B &, int);
+  };
+
+  template <class T>
+  template <class U, class V>
+  void A<T>::f(B<V> &x, U) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test36::B<long>'}}
+    //   expected-note@#test36-B-n {{implicitly declared private here}}
+  }
+
+  template class B<long>;
+  template void A<char>::f<int, long>(B<long> &, int);
+  template void A<char>::f<double, long>(B<long> &, double);
+  // expected-note at -1 {{in instantiation of function template specialization 'test36::A<char>::f<double, long>' requested here}}
+}
+
+namespace test37 {
+  class C;
+
+  template <class T> struct A {
+    template <class U> struct B;
+  };
+
+  template <> struct A<int> {
+    template <class U> struct B {
+      static void f(C &);
+    };
+  };
+
+  class C {
+    int n; // #test37-C-n
+
+    template <class T>
+    friend struct A<T>::B<long>;
+  };
+
+  template <class U>
+  void A<int>::B<U>::f(C &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test37::C'}}
+    //   expected-note@#test37-C-n {{implicitly declared private here}}
+  }
+
+  template struct A<int>::B<long>;
+  template struct A<int>::B<double>;
+  // expected-note at -1 {{in instantiation of member function 'test37::A<int>::B<double>::f' requested here}}
+}
+
+namespace test38 {
+  class B;
+
+  template <class T> struct A {
+    template <class U> static void f(B &);
+  };
+
+  template <> struct A<int> {
+    template <class U> static void f(B &);
+  };
+
+  class B {
+    int n; // #test38-B-n
+
+    template <class T>
+    friend void A<T>::f<long>(B &);
+  };
+
+  template <class U>
+  void A<int>::f(B &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test38::B'}}
+    //   expected-note@#test38-B-n {{implicitly declared private here}}
+  }
+
+  template void A<int>::f<long>(B &);
+  template void A<int>::f<double>(B &);
+  // expected-note at -1 {{in instantiation of function template specialization 'test38::A<int>::f<double>' requested here}}
+}
+
+namespace test39 {
+  class D;
+
+  template <class T> struct A {
+    template <class U> struct B;
+  };
+
+  template <> struct A<int> {
+    struct B {
+      static void f(D &);
+    };
+  };
+
+  template <> struct A<long> {
+    template <class U> union B {
+      static void f(D &);
+    };
+  };
+
+  template <> struct A<char> {
+    template <class U> struct C {
+      static void f(D &);
+    };
+  };
+
+  class D {
+    int n; // #test39-D-n
+
+    template <class T>
+    friend struct A<T>::B<double>;
+  };
+
+  void A<int>::B::f(D &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test39::D'}}
+    //   expected-note@#test39-D-n {{implicitly declared private here}}
+  }
+
+  template <class U>
+  void A<long>::B<U>::f(D &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test39::D'}}
+    //   expected-note@#test39-D-n {{implicitly declared private here}}
+  }
+
+  template <class U>
+  void A<char>::C<U>::f(D &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test39::D'}}
+    //   expected-note@#test39-D-n {{implicitly declared private here}}
+  }
+}
+
+namespace test40 {
+  template <class> class C;
+
+  template <class T> struct A {
+    template <class U> struct B;
+  };
+
+  template <> struct A<int> {
+    struct B { // expected-note {{previous definition is here}}
+      static void f(C<int> &);
+    };
+  };
+
+  template <> struct A<long> {
+    template <class U> struct B {
+      static void f(C<long> &);
+    };
+  };
+
+  template <class V> class C {
+    int n; // #test40-C-n
+
+    template <class U>
+    friend struct A<V>::B; // expected-error {{redefinition of 'B' as 
diff erent kind of symbol}}
+  };
+
+  template class C<int>;
+  // expected-note at -1 {{in instantiation of template class 'test40::C<int>' requested here}}
+
+  void A<int>::B::f(C<int> &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test40::C<int>'}}
+    //   expected-note@#test40-C-n {{implicitly declared private here}}
+  }
+
+  template class C<long>;
+
+  template <class U>
+  void A<long>::B<U>::f(C<long> &x) {
+    x.n = 0;
+  }
+
+  template struct A<long>::B<double>;
+}
+
+namespace test41 {
+  class C;
+
+  template <class T> struct A {
+    struct B;
+  };
+
+  template <> struct A<int> {
+    template <class U> struct B {
+      static void f(C &);
+    };
+  };
+
+  class C {
+    int n; // #test41-C-n
+
+    template <class T>
+    friend struct A<T>::B;
+  };
+
+  template <class U>
+  void A<int>::B<U>::f(C &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test41::C'}}
+    //   expected-note@#test41-C-n {{implicitly declared private here}}
+  }
+
+  template struct A<int>::B<double>;
+}
+
+namespace test42 {
+  template <class> class D;
+
+  struct A {
+    template <class U> struct B {
+      struct C {
+        static void f(D<A> &);
+      };
+      static void g(D<A> &);
+    };
+  };
+
+  template <class T> class D {
+    int n;
+
+    template <class U>
+    friend struct T::template B<U>::C;
+
+    template <class U>
+    friend void T::template B<U>::g(D &);
+  };
+
+  template <class U>
+  void A::B<U>::C::f(D<A> &x) {
+    x.n = 0;
+  }
+
+  template <class U>
+  void A::B<U>::g(D<A> &x) {
+    x.n = 0;
+  }
+
+  template struct A::B<int>;
+}
+
+namespace test43 {
+  template <class T> struct A {
+    static void f(typename T::type);
+  };
+
+  class B {
+    using type = int;
+    int n;
+
+    template <class T> friend void A<T>::f(typename T::type);
+  };
+
+  template <> struct A<B> {
+    static void f(int);
+  };
+
+  void A<B>::f(int) {
+    B b;
+    b.n = 0;
+  }
+}

diff  --git a/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
new file mode 100644
index 0000000000000..f60f3dd42445c
--- /dev/null
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template <class T> struct A;
+template <class T> struct B {
+  void f();
+};
+
+void t1() {
+  struct S {
+    template <class T> friend void f();
+    // expected-error at -1 {{templates can only be declared in namespace or class scope}}
+  };
+}
+
+void t2() {
+  struct S {
+    template <class T> friend struct A;
+    // expected-error at -1 {{templates cannot be declared inside of a local class}}
+  };
+}
+
+void t3() {
+  struct S {
+    template <class T> friend void B<T>::f();
+    // expected-error at -1 {{templates cannot be declared inside of a local class}}
+  };
+}

diff  --git a/clang/test/Index/load-dependent-friend-template.cpp b/clang/test/Index/load-dependent-friend-template.cpp
new file mode 100644
index 0000000000000..855ebd3d95d07
--- /dev/null
+++ b/clang/test/Index/load-dependent-friend-template.cpp
@@ -0,0 +1,15 @@
+// RUN: c-index-test -test-load-source all -std=c++20 %s | FileCheck %s
+
+template <class T> struct A {
+  struct B;
+};
+
+struct C {
+  template <class T> friend struct A<T>::B;
+};
+
+// CHECK: load-dependent-friend-template.cpp:7:8: StructDecl=C:7:8 (Definition)
+// CHECK: load-dependent-friend-template.cpp:8:42: FriendDecl=:8:42
+// CHECK-NEXT: load-dependent-friend-template.cpp:8:19: TemplateTypeParameter=T:8:19 (Definition)
+// CHECK-NEXT: load-dependent-friend-template.cpp:8:36: TemplateRef=A:3:27
+// CHECK-NEXT: load-dependent-friend-template.cpp:8:38: TypeRef=T:8:19

diff  --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp
index f22f3c71f44d2..7f86b5574d78b 100644
--- a/clang/test/Modules/odr_hash.cpp
+++ b/clang/test/Modules/odr_hash.cpp
@@ -2631,6 +2631,32 @@ S5 s5;
 // expected-note at first.h:* {{but in 'FirstModule' found friend function 'T5a'}}
 #endif
 
+#if defined(FIRST) || defined(SECOND)
+template <class T> struct A {
+  struct B;
+};
+#endif
+
+#if defined(FIRST)
+struct S6 {
+  template <class T>
+    requires true
+  friend struct A<T>::B;
+};
+#elif defined(SECOND)
+struct S6 {
+  template <class T>
+    requires(sizeof(T) > 0)
+  friend struct A<T>::B;
+};
+#else
+S6 s6;
+// expected-error at second.h:* {{'Friend::S6' has 
diff erent definitions in 
diff erent modules}}
+// expected-note at second.h:* {{in first definition, possible 
diff erence is here}}
+// expected-note at first.h:* {{definition in module 'FirstModule' is here}}
+// expected-note at first.h:* {{in second definition, possible 
diff erence is here}}
+#endif
+
 #define DECLS            \
   friend class FriendA;  \
   friend struct FriendB; \

diff  --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 621ae912c1ac9..951eb47099919 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -30,6 +30,7 @@ struct E { template<class T> class Nested; };
 template<class... Ts> // expected-note {{template parameter is declared here}}
 struct VS {
   friend Ts...;
+  friend Ts...[0];
 
   friend class Ts...; // expected-error {{declaration of 'Ts' shadows template parameter}}
   // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
@@ -56,13 +57,15 @@ struct VS {
   template<bool... Bs>
   friend class E<Bs>::Nested...; // expected-error {{friend declaration expands pack 'Bs' that is declared it its own template parameter list}}
 
-  // FIXME: Both of these should be valid, but we can't handle these at
-  // the moment because the NNS is dependent.
   template<class ...T>
-  friend class TS<Ts>::Nested...; // expected-warning {{dependent nested name specifier 'TS<Ts>' for friend template declaration is not supported; ignoring this friend declaration}}
+  friend class TS<Ts>::Nested...;
 
   template<class T>
-  friend class D<T, Ts>::Nested...; // expected-warning {{dependent nested name specifier 'D<T, Ts>' for friend class declaration is not supported; turning off access control for 'VS'}}
+  friend class D<T, Ts>::Nested...;
+
+  template<class T>
+  friend class Ts...[0]::Nested...;
+  // expected-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
 };
 
 namespace length_mismatch {

diff  --git a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
index 0b01907c8c722..910b7be9190d7 100644
--- a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
+++ b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
@@ -154,3 +154,60 @@ void f() {
 }
 }
 
+namespace GH104057 {
+template <class... Vs> class C;
+
+template <class T> struct A {
+  template <class U> struct B {
+    static void f(C<int, long> &);
+  };
+};
+
+template <class... Vs> class C {
+  int n; // #GH104057-C-n
+
+  template <class T>
+  friend struct A<T>::template B<Vs>...;
+};
+
+template <class T>
+template <class U>
+void A<T>::B<U>::f(C<int, long> &x) {
+  x.n = 0;
+  // expected-error at -1 {{'n' is a private member of 'GH104057::C<int, long>'}}
+  //   expected-note@#GH104057-C-n {{implicitly declared private here}}
+}
+
+template struct A<char>::B<int>;
+template struct A<char>::B<long>;
+template struct A<char>::B<double>;
+// expected-note at -1 {{in instantiation of member function 'GH104057::A<char>::B<double>::f' requested here}}
+template <class...> class D;
+
+template <class T> struct E { // expected-note {{candidate friend template ignored: could not match 'long' against 'char'}}
+  template <class U> struct F {
+    static void f(D<int, long> &);
+  };
+};
+
+template <class... Vs> class D {
+  int n; // #GH104057-D-n
+
+  template <class U>
+  friend struct E<Vs>::F...;
+};
+
+template <class T>
+template <class U>
+void E<T>::F<U>::f(D<int, long> &x) {
+  x.n = 0;
+  // expected-error at -1 {{'n' is a private member of 'GH104057::D<int, long>'}}
+  //   expected-note@#GH104057-D-n {{implicitly declared private here}}
+}
+
+template struct D<int, long>;
+template struct E<int>::F<char>;
+template struct E<long>::F<double>;
+template struct E<char>::F<float>;
+// expected-note at -1 {{in instantiation of member function 'GH104057::E<char>::F<float>::f' requested here}}
+}

diff  --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index f98005c7e6fb5..975803ed26bc1 100644
--- a/clang/test/SemaCXX/many-template-parameter-lists.cpp
+++ b/clang/test/SemaCXX/many-template-parameter-lists.cpp
@@ -5,7 +5,7 @@
 template <class T>
 struct X {
   template <class U>
-  struct A { // expected-note {{not-yet-instantiated member is declared here}}
+  struct A {
     template <class V>
     struct B {
       template <class W>
@@ -22,15 +22,16 @@ struct X {
     };
   };
 
-  template <class U>
-  template <class V>
-  template <class W>
-  template <class X>
+  template <class U> // expected-note {{non-deducible template parameter 'U'}}
+  template <class V> // expected-note {{non-deducible template parameter 'V'}}
+  template <class W> // expected-note {{non-deducible template parameter 'W'}}
+  template <class X> // expected-note {{non-deducible template parameter 'X'}}
   template <class Y>
   template <class Z>
-  friend void A<U>::template B<V>::template C<W>::template D<X>::template E<Y>::operator+=(Z); // expected-warning {{not supported}} expected-error {{no member 'A' in 'X<int>'; it has not yet been instantiated}}
+  friend void A<U>::template B<V>::template C<W>::template D<X>::template E<Y>::operator+=(Z);
+  // expected-error at -1 {{template parameters of friend declaration cannot be deduced from 'A<U>::template B<V>::template C<W>::template D<X>::template E<Y>'}}
 };
 
 void test() {
-  X<int>::A<int>::B<int>::C<int>::D<int>::E<int>() += 1.0; // expected-note {{in instantiation of template class 'X<int>' requested here}}
+  X<int>::A<int>::B<int>::C<int>::D<int>::E<int>() += 1.0;
 }

diff  --git a/clang/test/SemaTemplate/GH71595.cpp b/clang/test/SemaTemplate/GH71595.cpp
index daec9410e547a..c5c5d13036233 100644
--- a/clang/test/SemaTemplate/GH71595.cpp
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -18,17 +18,21 @@ void f() {
 template<class A>
 class temp {
     template<C<temp> T>
-    friend void g(); // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
+    friend void g();
+    // expected-error at -1 {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
 
-    temp();
+    temp(); // #temp-ctor
 };
 
 template<C<temp<int>> T>
 void g() {
     auto v = temp<T>();
+    // expected-error at -1 {{calling a private constructor of class 'temp<int>'}}
+    //   expected-note@#temp-ctor {{implicitly declared private here}}
 }
 
 void h() {
     f<int>();
     g<int>();
+    // expected-note at -1 {{in instantiation of function template specialization 'g<int>' requested here}}
 }

diff  --git a/clang/test/SemaTemplate/concepts-friends.cpp b/clang/test/SemaTemplate/concepts-friends.cpp
index 11287aa773b1b..7fc0f043dd9a4 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -566,3 +566,61 @@ struct Test {
 };
 
 }
+
+namespace GH104057 {
+template <class T> concept A = requires { typename T::type; }; // #GH104057_A
+
+struct B {
+  using type = int;
+};
+struct C {};
+
+template <class T> struct D {
+  static void f()
+    requires A<T>; // #GH104057_D_f
+};
+
+class E {
+  static int n;
+  template <A T> friend void D<T>::f();
+};
+
+template <class T> struct F {
+  template <A U> // #GH104057_F_TPL
+  static void f() // #GH104057_F_f
+    requires A<T>;
+};
+
+class G {
+  static int n;
+  template <A T> template <A U>
+  friend void F<T>::f()
+    requires A<T>;
+};
+
+template <class T>
+void D<T>::f() requires A<T> {
+  E::n = 0;
+}
+
+template <class T>
+template <A U>
+void F<T>::f() requires A<T> {
+  G::n = 0;
+}
+
+void test() {
+  D<B>::f();
+  D<C>::f();
+  // expected-error at -1 {{invalid reference to function 'f': constraints not satisfied}}
+  //   expected-note@#GH104057_D_f {{because 'GH104057::C' does not satisfy 'A'}}
+  //   expected-note@#GH104057_A {{because 'typename T::type' would be invalid: no type named 'type' in 'GH104057::C'}}
+
+  F<B>::f<B>();
+  F<B>::f<C>();
+  // expected-error at -1 {{no matching function for call to 'f'}}
+  //   expected-note@#GH104057_F_f {{candidate template ignored: constraints not satisfied}}
+  //   expected-note@#GH104057_F_TPL {{because 'GH104057::C' does not satisfy 'A'}}
+  //   expected-note@#GH104057_A {{because 'typename T::type' would be invalid: no type named 'type' in 'GH104057::C'}}
+}
+} // namespace GH104057

diff  --git a/clang/test/SemaTemplate/ctad.cpp b/clang/test/SemaTemplate/ctad.cpp
index 7a8a72a62e992..2681ee2a6081e 100644
--- a/clang/test/SemaTemplate/ctad.cpp
+++ b/clang/test/SemaTemplate/ctad.cpp
@@ -19,27 +19,31 @@ namespace pr41427 {
 namespace Access {
   struct B {
   protected:
-    struct type {};
+    struct type {}; // #Access-B-type
   };
-  template<typename T> struct D : B { // expected-note {{not viable}} \
-                                         expected-note {{implicit deduction guide declared as 'template <typename T> D(Access::D<T>) -> Access::D<T>'}}
-    D(T, typename T::type); // expected-note {{private member}} \
-                            // expected-note {{implicit deduction guide declared as 'template <typename T> D(T, typename T::type) -> Access::D<T>'}}
+  template<typename T> struct D : B { // #Access-D
+    D(T, typename T::type); // #Access-D-ctor
+    // expected-error at -1 {{'type' is a private member of 'Access::Y'}}
+    //   expected-note@#Access-Y-type {{implicitly declared private here}}
+    //   expected-note@#Access-D-ctor {{implicit deduction guide declared as 'template <typename T> D(T, typename T::type) -> Access::D<T>'}}
   };
   D b = {B(), {}};
 
   class X {
     using type = int;
   };
-  D x = {X(), {}}; // expected-error {{no viable constructor or deduction guide}}
+  D x = {X(), {}};
+  // expected-error at -1 {{no viable constructor or deduction guide}}
+  //   expected-note@#Access-D {{implicit deduction guide declared as 'template <typename T> D(Access::D<T>) -> Access::D<T>'}}
+  //   expected-note@#Access-D {{candidate function template not viable: requires 1 argument, but 2 were provided}}
+  //   expected-note@#Access-D-ctor {{candidate template ignored: substitution failure [with T = X]: 'type' is a private member of 'Access::X'}}
 
-  // Once we implement proper support for dependent nested name specifiers in
-  // friends, this should still work.
   class Y {
-    template <typename T> friend D<T>::D(T, typename T::type); // expected-warning {{dependent nested name specifier}}
-    struct type {};
+    template <typename T> friend D<T>::D(T, typename T::type);
+    struct type {}; // #Access-Y-type
   };
   D y = {Y(), {}};
+  // expected-note at -1 {{in instantiation of template class 'Access::D<Access::Y>' requested here}}
 
   class Z {
     template <typename T> friend class D;

diff  --git a/clang/test/SemaTemplate/dependent-friend-constraints.cpp b/clang/test/SemaTemplate/dependent-friend-constraints.cpp
new file mode 100644
index 0000000000000..d4aeb7180deb5
--- /dev/null
+++ b/clang/test/SemaTemplate/dependent-friend-constraints.cpp
@@ -0,0 +1,263 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+namespace test0 {
+  class C;
+
+  template <class T> struct A { // #test0-A
+    struct B {
+      static void f(C &);
+    };
+  };
+
+  class C {
+    int n; // #test0-C-n
+    template <class T>
+      requires __is_same(T, int) // #test0-requires
+    friend struct A<T>::B;
+  };
+
+  template <class T> void A<T>::B::f(C &x) {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test0::C'}}
+    //   expected-note@#test0-C-n {{implicitly declared private here}}
+    //   expected-note@#test0-A {{candidate template ignored: constraints not satisfied [with T = double]}}
+    //   expected-note@#test0-requires {{because '__is_same(double, int)' evaluated to false}}
+  }
+
+  template void A<int>::B::f(C &);
+  template void A<double>::B::f(C &);
+  // expected-note at -1 {{in instantiation of member function 'test0::A<double>::B::f' requested here}}
+}
+
+namespace test1 {
+  class C;
+
+  template <class T> struct A {
+    template <class U>
+      requires __is_same(T, U)
+    struct B;
+  };
+
+  class C {
+    int n;
+    template <class T>
+    template <class U>
+      requires __is_same(T, U)
+    friend struct A<T>::B;
+  };
+
+  template <> struct A<int> {
+    template <class U>
+      requires __is_same(int, U)
+    struct B {
+      static void f(C &x) { x.n = 0; }
+    };
+  };
+
+  template struct A<int>::B<int>;
+}
+
+namespace test2 {
+  class C;
+
+  template <class T> struct A {
+    template <class U>
+      requires __is_same(T, U)
+    struct B;
+  };
+
+  class C {
+    int n; // #test2-C-n
+    template <class T>
+    template <class U>
+      requires __is_same(T, U)
+    friend struct A<T>::B;
+  };
+
+  template <> struct A<int> {
+    template <class U>
+      requires (sizeof(U) != 0)
+    struct B {
+      static void f(C &x) {
+        x.n = 0;
+        // expected-error at -1 {{'n' is a private member of 'test2::C'}}
+        //   expected-note@#test2-C-n {{implicitly declared private here}}
+      }
+    };
+  };
+
+  template struct A<int>::B<int>;
+}
+
+namespace test3 {
+  class B;
+
+  template <class T> struct A {
+    template <class U>
+      requires __is_same(T, U)
+    static void f(B &);
+  };
+
+  class B {
+    int n;
+    template <class T>
+    template <class U>
+      requires __is_same(T, U)
+    friend void A<T>::f(B &);
+  };
+
+  template <> struct A<int> {
+    template <class U>
+      requires __is_same(int, U)
+    static void f(B &x) {
+      x.n = 0;
+    }
+  };
+
+  template void A<int>::f<int>(B &);
+}
+
+namespace test4 {
+  class B;
+
+  template <class T> struct A {
+    template <class U>
+      requires __is_same(T, U)
+    static void f(B &);
+  };
+
+  class B {
+    int n; // #test4-B-n
+    template <class T>
+    template <class U>
+      requires __is_same(T, U)
+    friend void A<T>::f(B &);
+  };
+
+  template <> struct A<int> {
+    template <class U>
+      requires (sizeof(U) != 0)
+    static void f(B &x) {
+      x.n = 0;
+      // expected-error at -1 {{'n' is a private member of 'test4::B'}}
+      //   expected-note@#test4-B-n {{implicitly declared private here}}
+    }
+  };
+
+  template void A<int>::f<int>(B &);
+}
+
+namespace test5 {
+  class B;
+
+  template <class T> struct A {
+    template <class U>
+    static void f(B &) requires (sizeof(U) != 0);
+  };
+
+  class B {
+    int n;
+    template <class T>
+    template <class U>
+    friend void A<T>::f(B &) requires (sizeof(U) != 0);
+  };
+
+  template <> struct A<int> {
+    template <class U>
+    static void f(B &x) requires (sizeof(U) != 0) {
+      x.n = 0;
+    }
+  };
+
+  template void A<int>::f<int>(B &);
+}
+
+namespace test6 {
+  class B;
+
+  template <class T> struct A {
+    template <class U>
+    static void f(B &) requires (sizeof(U) != 0);
+  };
+
+  class B {
+    int n; // #test6-B-n
+    template <class T>
+    template <class U>
+    friend void A<T>::f(B &) requires (sizeof(U) != 0);
+  };
+
+  template <> struct A<int> {
+    template <class U>
+    static void f(B &x) requires (sizeof(U) > 1) {
+      x.n = 0;
+      // expected-error at -1 {{'n' is a private member of 'test6::B'}}
+      //   expected-note@#test6-B-n {{implicitly declared private here}}
+    }
+  };
+
+  template void A<int>::f<int>(B &);
+}
+
+namespace test7 {
+  template <class T> struct A; // #test7-A
+
+  template <class V> class C {
+    int n; // #test7-C-n
+    template <class T>
+      requires __is_same(T, V) // #test7-requires
+    friend struct A<T>::B;
+  };
+
+  template <> struct A<int> {
+    struct B {
+      static void f(C<int> &x) { x.n = 0; }
+    };
+  };
+
+  template <> struct A<double> {
+    struct B {
+      static void f(C<int> &x) {
+        x.n = 0;
+        // expected-error at -1 {{'n' is a private member of 'test7::C<int>'}}
+        //   expected-note@#test7-C-n {{implicitly declared private here}}
+        //   expected-note@#test7-A {{candidate template ignored: constraints not satisfied [with T = double]}}
+        //   expected-note@#test7-requires {{because '__is_same(double, int)' evaluated to false}}
+      }
+    };
+  };
+}
+
+namespace test8 {
+  template <class T, class U>
+  concept Same = __is_same(T, U);
+
+  class B;
+
+  template <class T> struct A {
+    template <class U>
+    static void f(B &) requires Same<T, T>;
+  };
+
+  template <> struct A<int> {
+    template <class U>
+    static void f(B &) requires Same<U, U>;
+  };
+
+  class B {
+    int n; // #test8-B-n
+
+    template <class T>
+    template <class U>
+    friend void A<T>::f(B &) requires Same<T, T>;
+  };
+
+  template <class U>
+  void A<int>::f(B &x) requires Same<U, U> {
+    x.n = 0;
+    // expected-error at -1 {{'n' is a private member of 'test8::B'}}
+    //   expected-note@#test8-B-n {{implicitly declared private here}}
+  }
+
+  template void A<int>::f<double>(B &);
+}

diff  --git a/clang/test/SemaTemplate/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp
index 2b5a226c3b33c..05ab57cebfec8 100644
--- a/clang/test/SemaTemplate/friend-template.cpp
+++ b/clang/test/SemaTemplate/friend-template.cpp
@@ -111,6 +111,31 @@ namespace test5 {
   };
 }
 
+namespace GH104057 {
+template <class T>
+struct A { // #GH104057-A
+  template <class> struct B;
+
+private:
+  static void f(); // #GH104057-A-f
+  template <class U> friend struct A<U *>::B;
+};
+
+template <class T>
+template <class U> struct A<T>::B {
+  static void g() {
+    A<int>::f();
+    // expected-error at -1 {{'f' is a private member of 'GH104057::A<int>'}}
+    //   expected-note@#GH104057-A-f {{declared private here}}
+    //   expected-note@#GH104057-A {{candidate friend template ignored: could not match 'U *' against 'double'}}
+  }
+};
+
+void test() {
+  A<double>::B<int>::g(); // expected-note {{in instantiation of member function 'GH104057::A<double>::B<int>::g' requested here}}
+}
+}
+
 // PR6022
 namespace PR6022 {
   template <class T1, class T2 , class T3  > class A;
@@ -235,20 +260,19 @@ namespace rdar11147355 {
   template <class T>
   struct A {
     template <class U> class B;
-    template <class S> template <class U> friend class A<S>::B; // expected-warning {{dependent nested name specifier 'A<S>' for friend template declaration is not supported; ignoring this friend declaration}}
+    template <class S> template <class U> friend class A<S>::B;
   private:
-    int n; // expected-note {{here}}
+    int n;
   };
 
   template <class S> template <class U> class A<S>::B {
   public:
-    // FIXME: This should be permitted.
-    int f(A<S*> a) { return a.n; } // expected-error {{private}}
+    int f(A<S*> a) { return a.n; }
   };
 
   A<double>::B<double>  ab;
   A<double*> a;
-  int k = ab.f(a); // expected-note {{instantiation of}}
+  int k = ab.f(a);
 }
 
 namespace RedeclUnrelated {

diff  --git a/clang/test/SemaTemplate/nested-template.cpp b/clang/test/SemaTemplate/nested-template.cpp
index b5da1b1c3ccd4..fcf9a1d647ab9 100644
--- a/clang/test/SemaTemplate/nested-template.cpp
+++ b/clang/test/SemaTemplate/nested-template.cpp
@@ -168,3 +168,17 @@ namespace RefPack {
   int arr[10];
   void g() { A<k>().f(arr); }
 }
+
+namespace GH104057 {
+  template <class T>
+  struct A {
+    template <bool> struct B {};
+  };
+
+  template <class T>
+  struct C {
+    using type = typename A<T>::template B<true>;
+  };
+
+  A<int>::B<true> *p = (C<int>::type *)0;
+}

diff  --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index e485e6e2e5547..47d6d2cb51efd 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1312,6 +1312,14 @@ bool CursorVisitor::VisitFriendDecl(FriendDecl *D) {
   return false;
 }
 
+bool CursorVisitor::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+  for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+    if (VisitTemplateParameters(TPL))
+      return true;
+
+  return VisitFriendDecl(D);
+}
+
 bool CursorVisitor::VisitDecompositionDecl(DecompositionDecl *D) {
   for (auto *B : D->bindings()) {
     if (Visit(MakeCXCursor(B, TU, RegionOfInterest)))

diff  --git a/clang/tools/libclang/CursorVisitor.h b/clang/tools/libclang/CursorVisitor.h
index d5ab699756988..1b359d2920e11 100644
--- a/clang/tools/libclang/CursorVisitor.h
+++ b/clang/tools/libclang/CursorVisitor.h
@@ -248,6 +248,7 @@ class CursorVisitor : public DeclVisitor<CursorVisitor, bool>,
   bool VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
   bool VisitStaticAssertDecl(StaticAssertDecl *D);
   bool VisitFriendDecl(FriendDecl *D);
+  bool VisitFriendTemplateDecl(FriendTemplateDecl *D);
   bool VisitDecompositionDecl(DecompositionDecl *D);
   bool VisitConceptDecl(ConceptDecl *D);
   bool VisitTypeConstraint(const TypeConstraint &TC);

diff  --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index 4919977300fb8..195b8ab4c4e66 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -666,6 +666,126 @@ TEST(Decl, TemplateArgumentDefaulted) {
   EXPECT_TRUE(ArgList.get(3).getIsDefaulted());
 }
 
+TEST(Decl, InstantiatedDependentFriendTemplate) {
+  StringRef Code = R"cpp(
+    template <class T> struct A {
+      template <class U> struct B;
+    };
+
+    template <class T> struct C {
+      template <class U> friend struct A<T>::B;
+    };
+
+    template struct C<int>;
+  )cpp";
+
+  auto AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"});
+  ASTContext &Ctx = AST->getASTContext();
+  const auto *CTemplate = selectFirst<ClassTemplateDecl>(
+      "c", match(classTemplateDecl(hasName("C")).bind("c"), Ctx));
+  ASSERT_NE(CTemplate, nullptr);
+
+  const FriendTemplateDecl *DependentFriend = nullptr;
+  for (const Decl *D : CTemplate->getTemplatedDecl()->decls()) {
+    if (const auto *FTD = dyn_cast<FriendTemplateDecl>(D)) {
+      DependentFriend = FTD;
+      break;
+    }
+  }
+  ASSERT_NE(DependentFriend, nullptr);
+  ASSERT_NE(DependentFriend->getFriendType(), nullptr);
+  EXPECT_TRUE(DependentFriend->getFriendTemplateName().isDependent());
+
+  const auto *CSpecialization = selectFirst<ClassTemplateSpecializationDecl>(
+      "c", match(classTemplateSpecializationDecl(hasName("C")).bind("c"), Ctx));
+  ASSERT_NE(CSpecialization, nullptr);
+
+  const FriendTemplateDecl *InstFriend = nullptr;
+  for (const Decl *D : CSpecialization->decls()) {
+    if (const auto *FTD = dyn_cast<FriendTemplateDecl>(D)) {
+      InstFriend = FTD;
+      break;
+    }
+  }
+  ASSERT_NE(InstFriend, nullptr);
+  ASSERT_EQ(InstFriend->getFriendType(), nullptr);
+  ASSERT_FALSE(InstFriend->getFriendTemplateName().isNull());
+
+  const FriendDecl *Friend = InstFriend;
+  EXPECT_EQ(Friend->getFriendDecl(),
+            InstFriend->getFriendTemplateName().getAsTemplateDecl());
+  EXPECT_EQ(InstFriend->getSourceRange().getEnd(), InstFriend->getLocation());
+}
+
+TEST(Decl, InstantiatedDependentFriendTemplateParameters) {
+  StringRef Code = R"cpp(
+    template <class T> struct A {
+      template <class U> struct B;
+    };
+
+    template <class V> struct C {
+      template <class U>
+      friend struct A<U>::template B<V>;
+    };
+
+    template struct C<int>;
+  )cpp";
+
+  auto AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"});
+  ASTContext &Ctx = AST->getASTContext();
+  const auto *CSpecialization = selectFirst<ClassTemplateSpecializationDecl>(
+      "c", match(classTemplateSpecializationDecl(hasName("C")).bind("c"), Ctx));
+  ASSERT_NE(CSpecialization, nullptr);
+  ASSERT_NE(CSpecialization->friend_begin(), CSpecialization->friend_end());
+
+  const auto *Friend =
+      dyn_cast<FriendTemplateDecl>(*CSpecialization->friend_begin());
+  ASSERT_NE(Friend, nullptr);
+  ASSERT_EQ(Friend->getTemplateParameterLists().size(), 1u);
+  ASSERT_NE(Friend->getFriendType(), nullptr);
+
+  TemplateSpecializationTypeLoc FriendTL =
+      Friend->getFriendType()
+          ->getTypeLoc()
+          .castAs<TemplateSpecializationTypeLoc>();
+  const Type *QualifierType =
+      FriendTL.getQualifierLoc().getNestedNameSpecifier().getAsType();
+  ASSERT_NE(QualifierType, nullptr);
+  const auto *TST = QualifierType->getAs<TemplateSpecializationType>();
+  ASSERT_NE(TST, nullptr);
+  const auto *TTP =
+      TST->template_arguments()[0].getAsType()->getAs<TemplateTypeParmType>();
+  ASSERT_NE(TTP, nullptr);
+  EXPECT_EQ(TTP->getDecl(),
+            Friend->getTemplateParameterLists().front()->getParam(0));
+}
+
+TEST(Decl, InvalidFunctionFriendIsRetained) {
+  StringRef Code = R"cpp(
+    int f();
+    struct A {
+      friend void f();
+    };
+  )cpp";
+
+  IgnoringDiagConsumer Diags;
+  auto AST = tooling::buildASTFromCodeWithArgs(
+      Code, {"-std=c++20"}, "input.cc", "clang-tool",
+      std::make_shared<PCHContainerOperations>(),
+      tooling::getClangStripDependencyFileAdjuster(),
+      tooling::FileContentMappings(), &Diags);
+  ASTContext &Ctx = AST->getASTContext();
+  const auto *Record = selectFirst<CXXRecordDecl>(
+      "a", match(cxxRecordDecl(hasName("A"), isDefinition()).bind("a"), Ctx));
+  ASSERT_NE(Record, nullptr);
+  ASSERT_NE(Record->friend_begin(), Record->friend_end());
+
+  const FriendDecl *Friend = *Record->friend_begin();
+  EXPECT_TRUE(Friend->isInvalidDecl());
+  ASSERT_NE(Friend->getFriendDecl(), nullptr);
+  EXPECT_TRUE(Friend->getFriendDecl()->isInvalidDecl());
+}
+
 TEST(Decl, CXXDestructorDeclsShouldHaveWellFormedNameInfoRanges) {
   // GH71161
   llvm::Annotations Code(R"cpp(

diff  --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp
index 24e20c7471f3c..a368474f2841a 100644
--- a/clang/unittests/AST/StructuralEquivalenceTest.cpp
+++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp
@@ -863,6 +863,25 @@ TEST_F(StructuralEquivalenceRecordTest, SameFriendsSameOrder) {
   EXPECT_TRUE(testStructuralMatch(t));
 }
 
+TEST_F(StructuralEquivalenceRecordTest, InstantiatedFriendTemplates) {
+  std::string Code = R"(
+    template <class T> struct A {
+      template <class U> struct B;
+    };
+    template <class T> struct C {
+      template <class U> friend struct A<T>::B;
+    };
+    template struct C<int>;
+  )";
+  auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
+      Code, Code, Lang_CXX11, classTemplateSpecializationDecl(hasName("C")));
+  ASSERT_NE(get<0>(Decls)->friend_begin(), get<0>(Decls)->friend_end());
+  auto *Friend = dyn_cast<FriendTemplateDecl>(*get<0>(Decls)->friend_begin());
+  ASSERT_NE(Friend, nullptr);
+  ASSERT_EQ(Friend->getFriendType(), nullptr);
+  EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
 struct StructuralEquivalenceLambdaTest : StructuralEquivalenceTest {};
 
 TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentMethods) {

diff  --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index a2a96f470d94a..ec29200198554 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -4516,7 +4516,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td>[<a href="https://wg21.link/temp.friend">temp.friend</a>]</td>
     <td>CD2</td>
     <td>Explicit specialization and friendship</td>
-    <td class="none" align="center">No</td>
+    <td class="unreleased" align="center">Clang 24</td>
   </tr>
   <tr id="639">
     <td><a href="https://cplusplus.github.io/CWG/issues/639.html">639</a></td>
@@ -12819,7 +12819,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td>[<a href="https://wg21.link/temp.friend">temp.friend</a>]</td>
     <td>CD5</td>
     <td>Determining “corresponding members” for friendship</td>
-    <td class="none" align="center">No</td>
+    <td class="unreleased" align="center">Clang 24</td>
   </tr>
   <tr id="1863">
     <td><a href="https://cplusplus.github.io/CWG/issues/1863.html">1863</a></td>
@@ -13215,7 +13215,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td>[<a href="https://wg21.link/temp.friend">temp.friend</a>]</td>
     <td>CD5</td>
     <td><TT>friend</TT> templates with dependent scopes</td>
-    <td class="none" align="center">No</td>
+    <td class="unreleased" align="center">Clang 24</td>
   </tr>
   <tr class="open" id="1919">
     <td><a href="https://cplusplus.github.io/CWG/issues/1919.html">1919</a></td>
@@ -13404,7 +13404,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td>[<a href="https://wg21.link/temp.friend">temp.friend</a>]</td>
     <td>CD5</td>
     <td>Friend declarations naming members of class templates in non-templates</td>
-    <td class="none" align="center">No</td>
+    <td class="unreleased" align="center">Clang 24</td>
   </tr>
   <tr id="1946">
     <td><a href="https://cplusplus.github.io/CWG/issues/1946.html">1946</a></td>


        


More information about the cfe-commits mailing list