[clang] dcf1b8f - [Clang] support friend declarations with a dependent nested-name-specifier (#191268)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 8 02:52:58 PDT 2026


Author: Oleksandr Tarasiuk
Date: 2026-07-08T12:52:47+03:00
New Revision: dcf1b8f2c00b38777f71297afc7b575cd2d9300a

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

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

Fixes #104057

---

This patch adds support for friend declarations with a dependent NNS

Added: 
    clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp

Modified: 
    clang-tools-extra/clang-doc/Serialize.cpp
    clang/docs/ReleaseNotes.md
    clang/include/clang/AST/ASTStructuralEquivalence.h
    clang/include/clang/AST/DeclFriend.h
    clang/include/clang/AST/DeclTemplate.h
    clang/include/clang/AST/RecursiveASTVisitor.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/ODRHash.cpp
    clang/lib/Sema/Sema.cpp
    clang/lib/Sema/SemaAccess.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/lib/Sema/SemaTemplateDeduction.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/p5.cpp
    clang/test/Parser/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

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 4d7d9f2909096..772cd64dd93f4 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -243,6 +243,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 
 - `__is_trivially_equality_comparable` no longer returns false for all enum types. (#GH132672)
 - `auto` parameters are now available in all C++ language modes as an extension.
+- Clang now supports friend declarations with a dependent nested name specifier. (#GH104057)
 
 #### C++2d Feature Support
 

diff  --git a/clang/include/clang/AST/ASTStructuralEquivalence.h b/clang/include/clang/AST/ASTStructuralEquivalence.h
index 6f82de1ae136d..89d7f8d6ba8ff 100644
--- a/clang/include/clang/AST/ASTStructuralEquivalence.h
+++ b/clang/include/clang/AST/ASTStructuralEquivalence.h
@@ -135,6 +135,8 @@ struct StructuralEquivalenceContext {
   /// \c VisitedDecls members) and can cause faulty equivalent results.
   bool IsEquivalent(Stmt *S1, Stmt *S2);
 
+  bool IsEquivalent(TemplateParameterList *TPL1, TemplateParameterList *TPL2);
+
   /// Find the index of the given anonymous struct/union within its
   /// context.
   ///

diff  --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 1f8c210263677..c68028d22bd54 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -15,20 +15,13 @@
 #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>
 
 namespace clang {
 
@@ -49,9 +42,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 +52,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 FriendL, SourceLocation EllipsisLoc = {})
+      : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FriendL),
+        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 +82,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,74 +96,27 @@ 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 {
     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.
 class CXXRecordDecl::friend_iterator {
   friend class CXXRecordDecl;

diff  --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 4f5a4e1b7b8a6..95d5ab1b11bf2 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"
@@ -2461,45 +2462,51 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
 ///   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 *> FriendTypeTPLists,
+                     TemplateName Template = {})
+      : FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc,
+                   EllipsisLoc),
+        NumTPLists(FriendTypeTPLists.size()), Template(Template) {
+    llvm::copy(FriendTypeTPLists, getTrailingObjects());
+  }
 
-  FriendTemplateDecl(EmptyShell Empty) : Decl(Decl::FriendTemplate, Empty) {}
+  FriendTemplateDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
+      : FriendDecl(Decl::FriendTemplate, Empty),
+        NumTPLists(NumFriendTypeTPLists) {}
 
 public:
   friend class ASTDeclReader;
+  friend class ASTDeclWriter;
+  friend TrailingObjects;
 
   static FriendTemplateDecl *
   Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
-         MutableArrayRef<TemplateParameterList *> Params, FriendUnion Friend,
-         SourceLocation FriendLoc);
+         FriendUnion Friend, SourceLocation FriendLoc,
+         ArrayRef<TemplateParameterList *> FriendTypeTPLists = {},
+         SourceLocation EllipsisLoc = {});
+
+  static FriendTemplateDecl *
+  Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
+         TemplateName Template, SourceLocation FriendLoc,
+         ArrayRef<TemplateParameterList *> FriendTypeTPLists = {},
+         SourceLocation EllipsisLoc = {});
+
+  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                                unsigned FriendTypeNumTPLists);
 
-  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
+  SourceRange getSourceRange() const override LLVM_READONLY;
 
   /// If this friend declaration names a templated type (or
   /// a dependent member type of a templated type), return that
@@ -2512,21 +2519,16 @@ class FriendTemplateDecl : public Decl {
   /// a member function of a templated type), return that type;
   /// otherwise return null.
   NamedDecl *getFriendDecl() const {
-    return Friend.dyn_cast<NamedDecl*>();
+    if (TemplateDecl *TD = Template.getAsTemplateDecl())
+      return TD;
+    return Friend.dyn_cast<NamedDecl *>();
   }
 
-  /// Retrieves the location of the 'friend' keyword.
-  SourceLocation getFriendLoc() const {
-    return FriendLoc;
-  }
+  TemplateName getFriendTemplateName() const { return Template; }
 
-  TemplateParameterList *getTemplateParameterList(unsigned i) const {
-    assert(i <= NumParams);
-    return Params[i];
-  }
-
-  unsigned getNumTemplateParameters() const {
-    return NumParams;
+  ArrayRef<TemplateParameterList *>
+  getFriendTypeTemplateParameterLists() const {
+    return ArrayRef(getTrailingObjects(), NumTPLists);
   }
 
   // Implement isa/cast/dyncast/etc.

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index b77443f1fa0ab..4552437d3db9a 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1733,12 +1733,16 @@ DEF_TRAVERSE_DECL(FriendDecl, {
 })
 
 DEF_TRAVERSE_DECL(FriendTemplateDecl, {
-  if (D->getFriendType())
-    TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
-  else
-    TRY_TO(TraverseDecl(D->getFriendDecl()));
-  for (unsigned I = 0, E = D->getNumTemplateParameters(); I < E; ++I) {
-    TemplateParameterList *TPL = D->getTemplateParameterList(I);
+  TemplateName Template = D->getFriendTemplateName();
+  if (Template.isNull()) {
+    if (D->getFriendType())
+      TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
+    else
+      TRY_TO(TraverseDecl(D->getFriendDecl()));
+  } else {
+    TRY_TO(TraverseTemplateName(Template));
+  }
+  for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists()) {
     for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = TPL->end();
          ITPL != ETPL; ++ITPL) {
       TRY_TO(TraverseDecl(*ITPL));

diff  --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index ffb58b43812dc..c55a3d68184f4 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 Block : DeclNode<Decl, "blocks">, DeclContext;

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 73fb97aeeb302..16ee72c80dbc8 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1198,7 +1198,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 38d9e4046d3a5..3fa5bce9fb04f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1916,16 +1916,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">,
@@ -1935,6 +1925,8 @@ 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_dependent_friend_not_member : Error<
+  "friend declaration does not name a member of a class template specialization">;
 
 def err_invalid_base_in_interface : Error<
   "interface type cannot inherit from "
@@ -5290,11 +5282,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 f7d0d493e7081..e658f57968b99 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12018,6 +12018,9 @@ class Sema final : public SemaBase {
   bool CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous);
   void CompleteMemberSpecialization(NamedDecl *Member, LookupResult &Previous);
 
+  bool CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
+                            TemplateParameterList *FPL);
+
   // Explicit instantiation of a class template specialization
   DeclResult ActOnExplicitInstantiation(
       Scope *S, SourceLocation ExternLoc, SourceLocation TemplateLoc,
@@ -12751,6 +12754,15 @@ class Sema final : public SemaBase {
             return false;
           });
 
+  /// Finish template argument deduction for a template declaration, checking
+  /// the deduced template arguments for completeness and forming the deduced
+  /// template argument list.
+  TemplateDeductionResult FinishTemplateArgumentDeduction(
+      TemplateDecl *TD, TemplateParameterList *TPL,
+      ArrayRef<TemplateArgument> PatternArgs, ArrayRef<TemplateArgument> Args,
+      SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+      sema::TemplateDeductionInfo &Info, bool CopyDeducedArgs);
+
   /// Perform template argument deduction from a function call
   /// (C++ [temp.deduct.call]).
   ///

diff  --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index b0170c21feb1a..42febace0a671 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -715,7 +715,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);
 
@@ -724,6 +724,10 @@ enum class TemplateSubstitutionKind : char {
     TemplateParameterList *
       SubstTemplateParams(TemplateParameterList *List);
 
+    bool SubstTemplateParameterLists(
+        ArrayRef<TemplateParameterList *> TPL,
+        SmallVectorImpl<TemplateParameterList *> &InstTPL);
+
     bool SubstQualifier(const DeclaratorDecl *OldDecl,
                         DeclaratorDecl *NewDecl);
     bool SubstQualifier(const TagDecl *OldDecl,
@@ -734,6 +738,11 @@ enum class TemplateSubstitutionKind : char {
         ArrayRef<TemplateArgument> Converted,
         VarTemplateSpecializationDecl *PrevDecl = nullptr);
 
+    template <typename FriendTy>
+    bool
+    InstantiateFriendPackExpansion(FriendTy *D, TypeSourceInfo *TSI,
+                                   ArrayRef<TemplateParameterList *> TPL = {});
+
     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 279380de2f7fe..b581a70a073b5 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2109,6 +2109,13 @@ enum CtorInitializerType {
   CTOR_INITIALIZER_INDIRECT_MEMBER
 };
 
+/// Kinds of friend payloads owned by FriendTemplateDecl.
+enum FriendTemplateDeclKind {
+  FTDK_Type = 0,
+  FTDK_Decl = 1,
+  FTDK_Template = 2,
+};
+
 /// 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 567d2d07298a3..da1b6c2e908c6 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -538,6 +538,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);
@@ -4560,19 +4561,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);
 }
 
@@ -4592,7 +4589,6 @@ static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
   }
 
   assert(FriendPosition && "Friend decl not found in own parent.");
-
   return {FriendCount, *FriendPosition};
 }
 
@@ -4608,7 +4604,8 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   const auto *RD = cast<CXXRecordDecl>(DC);
   SmallVector<FriendDecl *, 2> ImportedEquivalentFriends;
   for (FriendDecl *ImportedFriend : RD->friends())
-    if (IsEquivalentFriend(Importer, D, ImportedFriend))
+    if (ImportedFriend->getKind() == Decl::Friend &&
+        IsEquivalentFriend(Importer, D, ImportedFriend))
       ImportedEquivalentFriends.push_back(ImportedFriend);
 
   FriendCountAndPosition CountAndPosition =
@@ -4640,15 +4637,6 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
       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 LocationOrErr = import(D->getLocation());
   if (!LocationOrErr)
     return LocationOrErr.takeError();
@@ -4662,7 +4650,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());
@@ -4671,6 +4659,92 @@ 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;
+  TemplateName ToTemplate;
+  TemplateName FromTemplate = D->getFriendTemplateName();
+  if (FromTemplate.isNull()) {
+    if (NamedDecl *FriendD = D->getFriendDecl()) {
+      NamedDecl *ToFriendD;
+      if (Error Err = importInto(ToFriendD, FriendD))
+        return std::move(Err);
+      ToFU = ToFriendD;
+    } else {
+      if (auto TSIOrErr = import(D->getFriendType()))
+        ToFU = *TSIOrErr;
+      else
+        return TSIOrErr.takeError();
+    }
+  } else {
+    if (auto TemplateOrErr = import(FromTemplate))
+      ToTemplate = *TemplateOrErr;
+    else
+      return TemplateOrErr.takeError();
+  }
+
+  ArrayRef<TemplateParameterList *> TPLs =
+      D->getFriendTypeTemplateParameterLists();
+  SmallVector<TemplateParameterList *, 1> ToParams(TPLs.size());
+  for (unsigned I = 0, N = TPLs.size(); I != N; ++I) {
+    if (auto ParamsOrErr = import(TPLs[I]))
+      ToParams[I] = *ParamsOrErr;
+    else
+      return ParamsOrErr.takeError();
+  }
+
+  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 (ToTemplate.isNull()) {
+    if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
+                                *LocationOrErr, ToFU, *FriendLocOrErr, ToParams,
+                                *EllipsisLocOrErr))
+      return FTD;
+  } else {
+    if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
+                                *LocationOrErr, ToTemplate, *FriendLocOrErr,
+                                ToParams, *EllipsisLocOrErr))
+      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 e0b62591a6a73..d3b47ebe5520c 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2290,7 +2290,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
       return false;
   }
 
-  return true;
+  return IsStructurallyEquivalent(Context, Params1->getRequiresClause(),
+                                  Params2->getRequiresClause());
 }
 
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
@@ -2441,6 +2442,45 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
   return false;
 }
 
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+                                     FriendTemplateDecl *FTD1,
+                                     FriendTemplateDecl *FTD2) {
+  ArrayRef<TemplateParameterList *> TPL1 =
+      FTD1->getFriendTypeTemplateParameterLists();
+  ArrayRef<TemplateParameterList *> TPL2 =
+      FTD2->getFriendTypeTemplateParameterLists();
+  bool EquivalentTemplateParameterLists = llvm::equal(
+      TPL1, TPL2,
+      [&Context](TemplateParameterList *LHS, TemplateParameterList *RHS) {
+        return IsStructurallyEquivalent(Context, LHS, RHS);
+      });
+  if (!EquivalentTemplateParameterLists)
+    return false;
+
+  TemplateName TN1 = FTD1->getFriendTemplateName();
+  TemplateName TN2 = FTD2->getFriendTemplateName();
+  if (TN1.isNull() != TN2.isNull())
+    return false;
+
+  if (TN1.isNull()) {
+    if ((FTD1->getFriendType() && FTD2->getFriendDecl()) ||
+        (FTD1->getFriendDecl() && FTD2->getFriendType()))
+      return false;
+
+    if (FTD1->getFriendDecl() && FTD2->getFriendDecl())
+      return IsStructurallyEquivalent(Context, FTD1->getFriendDecl(),
+                                      FTD2->getFriendDecl());
+
+    if (FTD1->getFriendType() && FTD2->getFriendType())
+      return IsStructurallyEquivalent(Context, FTD1->getFriendType()->getType(),
+                                      FTD2->getFriendType()->getType());
+
+    return false;
+  }
+
+  return IsStructurallyEquivalent(Context, TN1, TN2);
+}
+
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
                                      TypedefNameDecl *D1, TypedefNameDecl *D2) {
   if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
@@ -2780,6 +2820,23 @@ bool StructuralEquivalenceContext::CheckCommonEquivalence(Decl *D1, Decl *D2) {
   return true;
 }
 
+bool StructuralEquivalenceContext::IsEquivalent(TemplateParameterList *TPL1,
+                                                TemplateParameterList *TPL2) {
+  assert(DeclsToCheck.empty());
+  assert(VisitedDecls.empty());
+
+  if (TPL1 == TPL2)
+    return true;
+
+  if (!TPL1 || !TPL2)
+    return false;
+
+  if (!::IsStructurallyEquivalent(*this, TPL1, TPL2))
+    return false;
+
+  return !Finish();
+}
+
 bool StructuralEquivalenceContext::CheckKindSpecificEquivalence(
     Decl *D1, Decl *D2) {
 

diff  --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index 6bfc2eb62b284..aea0600441d25 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -13,11 +13,9 @@
 
 #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>
 
@@ -25,16 +23,9 @@ 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 +37,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 +60,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 95591d3a09431..71546c5b43070 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);
@@ -888,24 +889,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);
   }
@@ -914,6 +908,24 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
     Out << "...";
 }
 
+void DeclPrinter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+  for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
+    printTemplateParameters(TPL);
+
+  TemplateName TN = D->getFriendTemplateName();
+  if (TN.isNull()) {
+    VisitFriendDecl(D);
+  } else {
+    Out << "friend ";
+    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 275fe364e306d..124ebcc50223b 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1236,21 +1236,50 @@ 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 *> FriendTypeTPLists,
+                           SourceLocation EllipsisLoc) {
+  std::size_t Extra =
+      FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          FriendTypeTPLists.size());
+  auto *FTD = new (Context, DC, Extra) FriendTemplateDecl(
+      DC, Loc, Friend, FriendLoc, EllipsisLoc, FriendTypeTPLists);
+  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 *> FriendTypeTPLists,
+                           SourceLocation EllipsisLoc) {
+  std::size_t Extra =
+      FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          FriendTypeTPLists.size());
+  auto *FTD = new (Context, DC, Extra)
+      FriendTemplateDecl(DC, Loc, FriendUnion(), FriendLoc, EllipsisLoc,
+                         FriendTypeTPLists, Template);
+  cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD);
+  return FTD;
+}
+
+FriendTemplateDecl *
+FriendTemplateDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                       unsigned NumFriendTypeTPLists) {
+  std::size_t Extra =
+      FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          NumFriendTypeTPLists);
+  return new (C, ID, Extra)
+      FriendTemplateDecl(EmptyShell(), NumFriendTypeTPLists);
+}
+
+SourceRange FriendTemplateDecl::getSourceRange() const {
+  SourceLocation Begin =
+      getFriendTypeTemplateParameterLists().front()->getTemplateLoc();
+  SourceLocation End = FriendDecl::getSourceRange().getEnd();
+  return SourceRange(Begin, End);
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..b85204083cca9 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");
@@ -473,6 +475,19 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
     Hash.AddBoolean(D->isPackExpansion());
   }
 
+  void VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+    for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
+      Hash.AddTemplateParameterList(TPL);
+
+    TemplateName TN = D->getFriendTemplateName();
+    Hash.AddBoolean(TN.isNull());
+    if (TN.isNull()) {
+      VisitFriendDecl(D);
+    } else {
+      Hash.AddTemplateName(TN);
+    }
+  }
+
   void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
     // Only care about default arguments as part of the definition.
     const bool hasDefaultArgument =

diff  --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 78fbc9e31842d..ed3ff4bf277bc 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1163,8 +1163,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);
@@ -1173,7 +1174,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 17415b4185eff..582f2f779d761 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -19,8 +19,11 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Sema/DelayedDiagnostic.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
+#include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateDeduction.h"
 
 using namespace clang;
 using namespace sema;
@@ -274,6 +277,243 @@ struct AccessTarget : public AccessedEntity {
 
 }
 
+static void AddFriendTemplateDeductionCandidate(
+    Sema &S, TemplateDecl *TD, Decl *Declaration, TemplateDeductionInfo &Info,
+    TemplateDeductionResult Result, TemplateSpecCandidateSet *FailedTSC) {
+  if (!FailedTSC)
+    return;
+
+  for (TemplateSpecCandidate &Candidate : *FailedTSC) {
+    if (!Candidate.Specialization)
+      continue;
+
+    if (declaresSameEntity(Candidate.Specialization, Declaration))
+      return;
+  }
+
+  FailedTSC->addCandidate().set(
+      DeclAccessPair::make(TD, AS_public), Declaration,
+      MakeDeductionFailureInfo(S.Context, Result, Info));
+}
+
+static bool
+AreTemplateArgumentsStructurallyEqual(ArrayRef<TemplateArgument> LHS,
+                                      ArrayRef<TemplateArgument> RHS) {
+  return llvm::equal(
+      LHS, RHS, [](const TemplateArgument &LHS, const TemplateArgument &RHS) {
+        return LHS.structurallyEquals(RHS);
+      });
+}
+
+static bool DeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
+                                    TemplateDecl *TD,
+                                    ArrayRef<TemplateArgument> PatternArgs,
+                                    ArrayRef<TemplateArgument> Args,
+                                    TemplateDeductionInfo &Info,
+                                    TemplateSpecCandidateSet *FailedTSC,
+                                    bool CopyDeducedArgs) {
+  EnterExpressionEvaluationContext Unevaluated(
+      S, Sema::ExpressionEvaluationContext::Unevaluated);
+  Sema::SFINAETrap Trap(S, Info);
+  LocalInstantiationScope InstantiationScope(S);
+  SmallVector<DeducedTemplateArgument, 4> Deduced(TPL->size());
+  TemplateDeductionResult DeductionResult =
+      S.DeduceTemplateArguments(TPL, PatternArgs, Args, Info, Deduced,
+                                /*NumberOfArgumentsMustMatch=*/false);
+  if (DeductionResult != TemplateDeductionResult::Success) {
+    AddFriendTemplateDeductionCandidate(S, TD, TD->getTemplatedDecl(), Info,
+                                        DeductionResult, FailedTSC);
+    return false;
+  }
+
+  SmallVector<TemplateArgument, 4> InstArgs(Deduced.begin(), Deduced.end());
+  Sema::InstantiatingTemplate Inst(S, Info.getLocation(), TD, InstArgs);
+  if (Inst.isInvalid()) {
+    AddFriendTemplateDeductionCandidate(
+        S, TD, TD->getTemplatedDecl(), Info,
+        TemplateDeductionResult::InstantiationDepth, FailedTSC);
+    return false;
+  }
+
+  TemplateDeductionResult Result;
+  S.runWithSufficientStackSpace(Info.getLocation(), [&] {
+    Result = S.FinishTemplateArgumentDeduction(
+        TD, TPL, PatternArgs, Args, Deduced, Info,
+        /*CopyDeducedArgs=*/CopyDeducedArgs);
+  });
+
+  if (Result != TemplateDeductionResult::Success || Trap.hasErrorOccurred()) {
+    TemplateDeductionResult Failure =
+        Result != TemplateDeductionResult::Success
+            ? Result
+            : TemplateDeductionResult::SubstitutionFailure;
+    AddFriendTemplateDeductionCandidate(S, TD, TD->getTemplatedDecl(), Info,
+                                        Failure, FailedTSC);
+    return false;
+  }
+
+  return true;
+}
+
+static bool
+DeduceTemplateArguments(Sema &S, TemplateParameterList *TPL, TemplateDecl *TD,
+                        ArrayRef<TemplateArgument> PatternArgs,
+                        ArrayRef<TemplateArgument> Args, SourceLocation Loc,
+                        TemplateSpecCandidateSet *FailedTSC,
+                        SmallVectorImpl<TemplateArgument> &DeducedArgs) {
+  if (AreTemplateArgumentsStructurallyEqual(PatternArgs, Args)) {
+    DeducedArgs.assign(Args.begin(), Args.end());
+    return true;
+  }
+
+  TemplateDeductionInfo Info(FailedTSC ? FailedTSC->getLocation() : Loc);
+  if (!DeduceTemplateArguments(S, TPL, TD, PatternArgs, Args, Info, FailedTSC,
+                               /*CopyDeducedArgs=*/true))
+    return false;
+
+  TemplateArgumentList *CanonicalDeducedArgs = Info.takeCanonical();
+  DeducedArgs.assign(CanonicalDeducedArgs->asArray().begin(),
+                     CanonicalDeducedArgs->asArray().end());
+
+  return true;
+}
+
+static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
+                                       TemplateDecl *TD,
+                                       ArrayRef<TemplateArgument> PatternArgs,
+                                       ArrayRef<TemplateArgument> Args,
+                                       SourceLocation Loc,
+                                       TemplateSpecCandidateSet *FailedTSC) {
+  if (AreTemplateArgumentsStructurallyEqual(PatternArgs, Args))
+    return true;
+
+  TemplateDeductionInfo Info(FailedTSC ? FailedTSC->getLocation() : Loc);
+  return DeduceTemplateArguments(S, TPL, TD, PatternArgs, Args, Info, FailedTSC,
+                                 /*CopyDeducedArgs=*/false);
+}
+
+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)
+    return nullptr;
+
+  QualType Ty(NNS.getAsType(), 0);
+  if (Ty.isNull())
+    return nullptr;
+
+  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 bool DeduceFriendContextTemplateArguments(
+    Sema &S, DeclContext *DC, ClassTemplateDecl *FriendCTD,
+    ArrayRef<TemplateArgument> FriendArgs, TemplateParameterList *FriendTPL,
+    SourceLocation Loc, TemplateSpecCandidateSet *FailedTSC,
+    SmallVectorImpl<TemplateArgument> &DeducedArgs) {
+  const auto *RD = dyn_cast<CXXRecordDecl>(DC);
+  if (!RD)
+    return false;
+
+  ClassTemplateDecl *ContextCTD = RD->getDescribedClassTemplate();
+  ArrayRef<TemplateArgument> ContextArgs;
+  if (ContextCTD) {
+    ContextArgs = ContextCTD->getInjectedTemplateArgs(S.Context);
+  } else {
+    const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+    if (!CTSD)
+      return false;
+    ContextCTD = CTSD->getSpecializedTemplate();
+    ContextArgs = CTSD->getTemplateArgs().asArray();
+  }
+
+  if (!declaresSameEntity(ContextCTD, FriendCTD))
+    return false;
+
+  return DeduceTemplateArguments(S, FriendTPL, FriendCTD, FriendArgs,
+                                 ContextArgs, Loc, FailedTSC, DeducedArgs);
+}
+
+static bool MatchesFriendContext(Sema &S, DeclContext *DC,
+                                 ClassTemplateDecl *FriendCTD,
+                                 ArrayRef<TemplateArgument> FriendArgs,
+                                 TemplateParameterList *FriendTPL,
+                                 SourceLocation Loc,
+                                 TemplateSpecCandidateSet *FailedTSC) {
+  SmallVector<TemplateArgument, 4> DeducedArgs;
+  return DeduceFriendContextTemplateArguments(
+      S, DC, FriendCTD, FriendArgs, FriendTPL, Loc, FailedTSC, DeducedArgs);
+}
+
+static bool MatchesFriendContext(Sema &S, DeclContext *DC,
+                                 ClassTemplateDecl *FriendCTD,
+                                 DeclContext *FriendDC,
+                                 ArrayRef<TemplateArgument> FriendArgs,
+                                 TemplateParameterList *FriendTPL,
+                                 SourceLocation Loc,
+                                 TemplateSpecCandidateSet *FailedTSC) {
+  auto GetClassTemplateContext =
+      [](const DeclContext *DC,
+         ClassTemplateDecl *Template) -> const CXXRecordDecl * {
+    const auto *RD = dyn_cast<CXXRecordDecl>(DC);
+    if (RD) {
+      ClassTemplateDecl *CTD = RD->getDescribedClassTemplate();
+      if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+        CTD = CTSD->getSpecializedTemplate();
+
+      if (declaresSameEntity(CTD, Template))
+        return RD;
+    }
+    return nullptr;
+  };
+
+  if (const auto *FriendRecord = dyn_cast<CXXRecordDecl>(FriendDC)) {
+    const CXXRecordDecl *FriendContext =
+        GetClassTemplateContext(FriendRecord->getDeclContext(), FriendCTD);
+    const CXXRecordDecl *Context = GetClassTemplateContext(DC, FriendCTD);
+    if (declaresSameEntity(FriendContext, Context))
+      return true;
+  }
+
+  return MatchesFriendContext(S, DC, FriendCTD, FriendArgs, FriendTPL, Loc,
+                              FailedTSC);
+}
+
 /// Checks whether one class might instantiate to the other.
 static bool MightInstantiateTo(const CXXRecordDecl *From,
                                const CXXRecordDecl *To) {
@@ -283,8 +523,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;
@@ -342,9 +586,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;
 
@@ -363,7 +605,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;
 
@@ -374,49 +616,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());
 }
 
@@ -477,7 +736,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.
@@ -491,8 +750,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.
@@ -514,7 +772,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;
   }
 
@@ -533,63 +791,330 @@ 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, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FriendTD,
+                                  ClassTemplateDecl *FriendCTD,
+                                  NestedNameSpecifier Qualifier,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  AccessResult OnFailure = AR_inaccessible;
+  const auto *FriendTST =
+      GetQualifierClassTemplateSpecializationType(S.Context, Qualifier);
+  if (!FriendTST)
+    return MatchesFriend(S, EC, FriendCTD);
+
+  auto *FriendContextCTD = dyn_cast<ClassTemplateDecl>(
+      FriendTST->getTemplateName().getAsTemplateDecl());
+  if (!FriendContextCTD)
+    return OnFailure;
+
+  ArrayRef<TemplateParameterList *> FriendTPLists =
+      FriendTD->getFriendTypeTemplateParameterLists();
+  if (FriendTPLists.empty())
+    return OnFailure;
+
+  TemplateParameterList *FriendTPL = FriendTPLists.front();
+  if (!FriendTPL)
+    return OnFailure;
+
+  ArrayRef<TemplateArgument> FriendArgs = FriendTST->template_arguments();
+  for (CXXRecordDecl *RD : EC.Records) {
+    ClassTemplateDecl *ContextCTD = nullptr;
+    if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+      ContextCTD = CTSD->getSpecializedTemplate();
+    else
+      ContextCTD = RD->getDescribedClassTemplate();
+
+    if (!ContextCTD)
+      continue;
+
+    if (!MightInstantiateTo(ContextCTD->getTemplatedDecl(),
+                            FriendCTD->getTemplatedDecl()))
+      continue;
+
+    if (MatchesFriendContext(S, RD->getDeclContext(), FriendContextCTD,
+                             FriendTD->getDeclContext(), FriendArgs, FriendTPL,
+                             FriendTD->getLocation(), FailedTSC))
+      return AR_accessible;
+  }
+
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FriendTD,
+                                  TemplateName Template,
+                                  ClassTemplateDecl *FriendCTD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  NestedNameSpecifier Qualifier = Template.getQualifier();
+  if (Template.getAsUsingShadowDecl())
+    Qualifier = FriendCTD->getTemplatedDecl()->getQualifier();
+  return MatchesFriend(S, EC, FriendTD, FriendCTD, Qualifier, FailedTSC);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FriendTD,
+                                  ClassTemplateDecl *FriendCTD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  return MatchesFriend(S, EC, FriendTD, FriendCTD,
+                       FriendCTD->getTemplatedDecl()->getQualifier(),
+                       FailedTSC);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FriendTD,
+                                  FunctionTemplateDecl *FriendFTD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  AccessResult OnFailure = AR_inaccessible;
+  const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
+      S.Context, FriendFTD->getTemplatedDecl()->getQualifier());
+  if (!FriendTST)
+    return OnFailure;
+
+  auto *FriendCTD = dyn_cast<ClassTemplateDecl>(
+      FriendTST->getTemplateName().getAsTemplateDecl());
+  if (!FriendCTD)
+    return OnFailure;
+
+  ArrayRef<TemplateParameterList *> FriendTPLists =
+      FriendTD->getFriendTypeTemplateParameterLists();
+  if (FriendTPLists.empty())
+    return OnFailure;
+
+  TemplateParameterList *FriendTPL = FriendTPLists.front();
+  if (!FriendTPL)
+    return OnFailure;
+
+  ArrayRef<TemplateArgument> FriendArgs = FriendTST->template_arguments();
+  SourceLocation FriendLoc = FriendTD->getLocation();
+
+  for (FunctionDecl *FD : EC.Functions) {
+    if (!MatchesFriendContext(S, FD->getDeclContext(), FriendCTD, FriendArgs,
+                              FriendTPL, FriendLoc, FailedTSC))
+      continue;
+
+    FunctionTemplateDecl *ContextFTD = TryGetFunctionTemplateDecl(FD);
+    if (ContextFTD && MightInstantiateTo(S.Context, FriendFTD, ContextFTD))
+      return AR_accessible;
+  }
+
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FriendTD,
+                                  FunctionDecl *FriendFD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  AccessResult OnFailure = AR_inaccessible;
+  const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
+      S.Context, FriendFD->getQualifier());
+  if (!FriendTST)
+    return OnFailure;
+
+  auto *FriendCTD = dyn_cast<ClassTemplateDecl>(
+      FriendTST->getTemplateName().getAsTemplateDecl());
+  if (!FriendCTD)
+    return OnFailure;
+
+  ArrayRef<TemplateParameterList *> FriendTPLists =
+      FriendTD->getFriendTypeTemplateParameterLists();
+  if (FriendTPLists.empty())
+    return OnFailure;
+
+  TemplateParameterList *FriendTPL = FriendTPLists.front();
+  if (!FriendTPL)
+    return OnFailure;
+
+  ArrayRef<TemplateArgument> FriendArgs = FriendTST->template_arguments();
+  SourceLocation FriendLoc = FriendTD->getLocation();
+
+  for (FunctionDecl *FD : EC.Functions) {
+    if (!MightInstantiateTo(S.Context, FD->getDeclName(),
+                            FriendFD->getDeclName()))
+      continue;
+
+    SmallVector<TemplateArgument, 4> DeducedArgs;
+    if (!DeduceFriendContextTemplateArguments(
+            S, FD->getDeclContext(), FriendCTD, FriendArgs, FriendTPL,
+            FriendLoc, FailedTSC, DeducedArgs))
+      continue;
+
+    CanQual<FunctionProtoType> ContextProto =
+        GetCanonicalFunctionProto(S.Context, FD);
+    Sema::InstantiatingTemplate Inst(S, FriendFD->getLocation(), FriendCTD,
+                                     DeducedArgs);
+    if (Inst.isInvalid())
+      continue;
+
+    TemplateDeductionInfo Info(FriendFD->getLocation());
+    Sema::SFINAETrap Trap(S, Info);
+    MultiLevelTemplateArgumentList Args(FriendCTD, DeducedArgs, /*Final=*/true);
+    QualType FriendType =
+        S.SubstType(FriendFD->getType(), Args, FriendFD->getLocation(),
+                    FriendFD->getDeclName());
+    if (FriendType.isNull() || Trap.hasErrorOccurred())
+      continue;
+
+    CanQual<FunctionProtoType> FriendProto =
+        GetCanonicalFunctionProto(S.Context, FriendType);
+    if (MightInstantiateTo(ContextProto, FriendProto))
+      return AR_accessible;
+  }
+
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD, NamedDecl *ND,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  TemplateName Template = FTD->getFriendTemplateName();
+  if (auto *CTD =
+          dyn_cast_if_present<ClassTemplateDecl>(Template.getAsTemplateDecl()))
+    return MatchesFriend(S, EC, FTD, Template, CTD, FailedTSC);
+
+  if (auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
+    return MatchesFriend(S, EC, FTD, CTD, FailedTSC);
+
+  if (auto *TD = dyn_cast<FunctionTemplateDecl>(ND))
+    return MatchesFriend(S, EC, FTD, TD, FailedTSC);
+
+  if (auto *FD = dyn_cast<FunctionDecl>(ND))
+    return MatchesFriend(S, EC, FTD, FD, FailedTSC);
+
+  return MatchesFriend(S, EC, ND);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD, TypeSourceInfo *TSI,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  QualType TypeAsWritten = TSI->getType();
+  if (!TypeAsWritten->isDependentType())
+    return MatchesFriend(S, EC, S.Context.getCanonicalType(TypeAsWritten));
+
+  AccessResult OnFailure = EC.isDependent() ? AR_dependent : AR_inaccessible;
+  const auto *DNT = TypeAsWritten->getAs<DependentNameType>();
+  if (!DNT)
+    return OnFailure;
+
+  NestedNameSpecifier NNS = DNT->getQualifier();
+  if (!NNS)
+    return OnFailure;
+
+  const auto *TST = GetQualifierClassTemplateSpecializationType(S.Context, NNS);
+  if (!TST)
+    return OnFailure;
+
+  auto *CTD =
+      dyn_cast<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl());
+  if (!CTD)
+    return OnFailure;
+
+  ArrayRef<TemplateParameterList *> FriendTPLists =
+      FTD->getFriendTypeTemplateParameterLists();
+  if (FriendTPLists.empty())
+    return OnFailure;
+
+  TemplateParameterList *TPL = FriendTPLists.front();
+  if (!TPL)
+    return OnFailure;
+
+  for (CXXRecordDecl *RD : EC.Records) {
+    if (RD->getDeclName() != DNT->getIdentifier())
+      continue;
+
+    const auto *CTSD =
+        dyn_cast<ClassTemplateSpecializationDecl>(RD->getDeclContext());
+    if (!CTSD)
+      continue;
+
+    if (!declaresSameEntity(CTSD->getSpecializedTemplate(), CTD))
+      continue;
+
+    if (CanDeduceTemplateArguments(S, TPL, CTD, TST->template_arguments(),
+                                   CTSD->getTemplateArgs().asArray(),
+                                   FTD->getLocation(), FailedTSC))
+      return AR_accessible;
+  }
+
+  return OnFailure;
+}
+
 /// Determines whether the given friend declaration matches anything
 /// in the effective context.
 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.
+  return AR_inaccessible;
+}
 
-  if (isa<ClassTemplateDecl>(Friend))
-    return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD,
+                                  TemplateSpecCandidateSet *FailedTSC) {
+  if (FTD->isInvalidDecl())
+    return AR_accessible;
 
-  if (isa<FunctionTemplateDecl>(Friend))
-    return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
+  if (NamedDecl *ND = FTD->getFriendDecl())
+    return MatchesFriend(S, EC, FTD, ND, FailedTSC);
 
-  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));
+  return AR_inaccessible;
 }
 
-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;
 
@@ -613,6 +1138,7 @@ namespace {
 struct ProtectedFriendContext {
   Sema &S;
   const EffectiveContext &EC;
+  TemplateSpecCandidateSet *FailedTSC;
   const CXXRecordDecl *NamingClass;
   bool CheckDependent;
   bool EverDependent;
@@ -622,18 +1148,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;
@@ -720,9 +1247,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);
@@ -730,19 +1257,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");
 
@@ -862,7 +1390,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;
@@ -870,7 +1399,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;
@@ -983,7 +1512,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;
@@ -1179,7 +1709,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:
@@ -1222,7 +1753,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;
@@ -1326,9 +1858,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();
 
@@ -1340,7 +1872,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
@@ -1369,7 +1902,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
@@ -1421,25 +1954,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;
@@ -1449,6 +1987,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.
@@ -1933,7 +2485,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/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 65b46562b166e..407dacefe76a6 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18083,6 +18083,31 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
   return Decl;
 }
 
+bool Sema::CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
+                                TemplateParameterList *FPL) {
+  if (!NNS.isDependent() || !FPL || FPL->size() == 0)
+    return false;
+
+  assert(NNS.getKind() == NestedNameSpecifier::Kind::Type &&
+         "dependent nested-name-specifier must be a type");
+  QualType T(NNS.getCanonical().getAsType(), 0);
+
+  if (const auto *PIT = dyn_cast<PackIndexingType>(T))
+    T = PIT->getPattern();
+
+  if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
+    if (isa_and_nonnull<ClassTemplateDecl>(
+            TST->getTemplateName().getAsTemplateDecl()))
+      return false;
+  }
+
+  if (isa<InjectedClassNameType>(T))
+    return false;
+
+  Diag(Loc, diag::err_dependent_friend_not_member);
+  return true;
+}
+
 DeclResult Sema::ActOnTemplatedFriendTag(
     Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc,
     CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
@@ -18098,16 +18123,17 @@ DeclResult Sema::ActOnTemplatedFriendTag(
               TagLoc, NameLoc, SS, nullptr, TempParamLists, /*friend*/ true,
               IsMemberSpecialization, Invalid)) {
     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)
@@ -18152,9 +18178,8 @@ DeclResult Sema::ActOnTemplatedFriendTag(
     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;
@@ -18180,25 +18205,35 @@ DeclResult Sema::ActOnTemplatedFriendTag(
     }
   }
 
-  // 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);
+  NestedNameSpecifier NNS = SS.getScopeRep();
+  if (EllipsisLoc.isInvalid() &&
+      CheckDependentFriend(TagLoc, NNS, TempParamLists.front()))
+    return true;
+
   ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
-  QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
+  QualType T = Context.getDependentNameType(ETK, NNS, 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 =
-      FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
-                         EllipsisLoc, TempParamLists);
+  FriendDecl *Friend;
+  if (TempParamLists.empty())
+    Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+                                EllipsisLoc);
+  else {
+    if (CheckTemplateDeclScope(S, TempParamLists.back()))
+      return true;
+
+    Friend = FriendTemplateDecl::Create(Context, CurContext, NameLoc, TSI,
+                                        FriendLoc, TempParamLists, EllipsisLoc);
+  }
+
   Friend->setAccess(AS_public);
-  Friend->setUnsupportedFriend(true);
   CurContext->addDecl(Friend);
+
   return Friend;
 }
 
@@ -18299,11 +18334,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);
 
@@ -18380,7 +18418,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();
     }
   }
@@ -18501,6 +18539,11 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
     assert(isa<CXXRecordDecl>(DC) && "friend declaration not in class?");
   }
 
+  if (TemplateParams.size() && SS.isValid() &&
+      CheckDependentFriend(NameInfo.getLoc(), SS.getScopeRep(),
+                           TemplateParams.front()))
+    return nullptr;
+
   if (!DC->isRecord()) {
     int DiagArg = -1;
     switch (D.getName().getKind()) {
@@ -18569,81 +18612,79 @@ 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);
+  if (ND->isInvalidDecl())
+    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);
-      }
-    }
+  if (DC->isRecord())
+    CheckFriendAccess(ND);
 
-    // 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);
-    }
+  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()) {
+      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);
+  }
 
-    // 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);
-    }
+  ArrayRef<TemplateParameterList *> TPL = FD->getTemplateParameterLists();
+  FriendDecl *Friend;
+  if (TPL.size() > 0 && SS.isValid()) {
+    if (CheckTemplateDeclScope(S, TPL.back()))
+      return nullptr;
+
+    Friend =
+        FriendTemplateDecl::Create(Context, CurContext, D.getIdentifierLoc(),
+                                   ND, DS.getFriendSpecLoc(), TPL);
+  } else {
+    Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), ND,
+                                DS.getFriendSpecLoc());
   }
 
-  warnOnReservedIdentifier(ND);
+  Friend->setAccess(AS_public);
+  CurContext->addDecl(Friend);
 
   return ND;
 }

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c8b05a9f94bd0..19141890ca230 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12394,8 +12394,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*>()) ||
@@ -12642,7 +12643,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;
         }
@@ -12658,7 +12662,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;
   }
@@ -13609,10 +13615,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() {
@@ -13664,7 +13672,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 ad0e653e254dd..b06bf2d25ad6a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1958,14 +1958,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))
@@ -11407,6 +11402,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
     LookupQualifiedName(Result, Ctx, SS);
   else
     LookupName(Result, CurScope);
+
   unsigned DiagID = 0;
   Decl *Referenced = nullptr;
   switch (Result.getResultKind()) {

diff  --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index d93b528facbcc..adabbe69903db 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4112,6 +4112,16 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
   return TemplateDeductionResult::Success;
 }
 
+TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
+    TemplateDecl *TD, TemplateParameterList *TPL,
+    ArrayRef<TemplateArgument> PatternArgs, ArrayRef<TemplateArgument> Args,
+    SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+    sema::TemplateDeductionInfo &Info, bool CopyDeducedArgs) {
+  return ::FinishTemplateArgumentDeduction(
+      *this, TD, TPL, TD, /*PartialOrdering=*/false, PatternArgs, Args, Deduced,
+      Info, CopyDeducedArgs);
+}
+
 /// 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,

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c56203f10ac3c..8311ed5349639 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1989,68 +1989,69 @@ 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;
+template <typename FriendTy>
+bool TemplateDeclInstantiator::InstantiateFriendPackExpansion(
+    FriendTy *D, TypeSourceInfo *TSI, ArrayRef<TemplateParameterList *> TPL) {
+  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+  SemaRef.collectUnexpandedParameterPacks(TSI->getTypeLoc(), Unexpanded);
+  assert(!Unexpanded.empty() && "Pack expansion without packs");
 
-        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);
-          }
+  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;
 
-          // Just drop this node; we have no use for it anymore.
-          return nullptr;
-        }
-      }
+  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);
+    SmallVector<TemplateParameterList *, 1> InstTPL;
+    if (SubstTemplateParameterLists(TPL, InstTPL))
+      return true;
+
+    TypeSourceInfo *InstTy = SemaRef.SubstType(
+        TSI, TemplateArgs, D->getEllipsisLoc(), DeclarationName());
+    if (!InstTy)
+      return true;
 
-      InstTy = SemaRef.SubstType(Ty, TemplateArgs, D->getLocation(),
-                                 DeclarationName());
+    FriendDecl *FD;
+    if (isa<FriendTemplateDecl>(D))
+      FD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+                                      InstTy, D->getFriendLoc(), InstTPL);
+    else {
+      assert(InstTPL.empty() && "unexpected template parameter lists");
+      FD = FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(), InstTy,
+                              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, Ty))
+      return nullptr;
+
+    TypeSourceInfo *InstTy = SemaRef.SubstType(
+        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;
   }
@@ -2069,7 +2070,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;
 }
@@ -4711,12 +4711,75 @@ 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 *> TPLists =
+      D->getFriendTypeTemplateParameterLists();
+
+  Decl *FTD = nullptr;
+  if (TypeSourceInfo *FT = D->getFriendType()) {
+    if (D->isPackExpansion() && InstantiateFriendPackExpansion(D, FT, TPLists))
+      return nullptr;
+
+    SmallVector<TemplateParameterList *, 1> TPL;
+    if (SubstTemplateParameterLists(TPLists, TPL))
+      return nullptr;
+
+    TemplateName Template;
+    if (auto DNT = FT->getTypeLoc().getAs<DependentNameTypeLoc>()) {
+      NestedNameSpecifierLoc QualifierLoc = SemaRef.SubstNestedNameSpecifierLoc(
+          DNT.getQualifierLoc(), TemplateArgs);
+      if (QualifierLoc) {
+        CXXScopeSpec SS;
+        SS.Adopt(QualifierLoc);
+
+        DeclContext *DC =
+            SemaRef.computeDeclContext(SS, /*EnteringContext=*/true);
+        if (DC && !DC->isDependentContext() &&
+            SemaRef.RequireCompleteDeclContext(SS, DC))
+          DC = nullptr;
+        if (DC) {
+          LookupResult Result(SemaRef, DNT.getTypePtr()->getIdentifier(),
+                              DNT.getNameLoc(), Sema::LookupOrdinaryName,
+                              SemaRef.forRedeclarationInCurContext());
+          SemaRef.LookupQualifiedName(Result, DC);
+          if (auto *CTD = Result.getAsSingle<ClassTemplateDecl>()) {
+            auto *FoundUsingShadow =
+                dyn_cast<UsingShadowDecl>(Result.getRepresentativeDecl());
+            Template = FoundUsingShadow ? TemplateName(FoundUsingShadow)
+                                        : TemplateName(CTD);
+            Template = SemaRef.Context.getQualifiedTemplateName(
+                QualifierLoc.getNestedNameSpecifier(),
+                /*TemplateKeyword=*/false, Template);
+          }
+        }
+      }
+    }
+
+    if (Template.isNull()) {
+      if (TypeSourceInfo *InstTy = SemaRef.SubstType(
+              FT, TemplateArgs, D->getLocation(), DeclarationName())) {
+        FTD =
+            FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+                                       InstTy, D->getFriendLoc(), TPL);
+      }
+    } else {
+      FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+                                       Template, D->getFriendLoc(), TPL);
+    }
+  } else {
+    SmallVector<TemplateParameterList *, 1> TPL;
+    if (SubstTemplateParameterLists(TPLists, TPL))
+      return nullptr;
+
+    if (auto *InstND = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
+            D->getLocation(), D->getFriendDecl(), TemplateArgs)))
+      FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+                                       InstND, D->getFriendLoc(), TPL);
+  }
+
+  if (FTD) {
+    FTD->setAccess(AS_public);
+    Owner->addDecl(FTD);
+  }
 
   return nullptr;
 }
@@ -4856,6 +4919,20 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
   return InstL;
 }
 
+bool TemplateDeclInstantiator::SubstTemplateParameterLists(
+    ArrayRef<TemplateParameterList *> TPL,
+    SmallVectorImpl<TemplateParameterList *> &InstTPL) {
+  LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+  for (TemplateParameterList *L : TPL) {
+    TemplateParameterList *InstParams = SubstTemplateParams(L);
+    if (!InstParams)
+      return true;
+
+    InstTPL.push_back(InstParams);
+  }
+  return false;
+}
+
 TemplateParameterList *
 Sema::SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
                           const MultiLevelTemplateArgumentList &TemplateArgs,
@@ -5169,8 +5246,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 370a6970d531a..ee359268a0e3c 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2404,26 +2404,29 @@ 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
-    D->Friend = readDeclAs<NamedDecl>();
-  else
+  for (unsigned i = 0; i != D->NumTPLists; ++i)
+    D->getTrailingObjects()[i] = Record.readTemplateParameterList();
+  switch (Record.readInt()) {
+  case FTDK_Type:
     D->Friend = readTypeSourceInfo();
+    break;
+  case FTDK_Decl:
+    D->Friend = readDeclAs<NamedDecl>();
+    break;
+  case FTDK_Template:
+    D->Template = Record.readTemplateName();
+    break;
+  }
+  D->NextFriend = readDeclID().getRawValue();
   D->FriendLoc = readSourceLocation();
+  D->EllipsisLoc = readSourceLocation();
 }
 
 void ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
@@ -4078,10 +4081,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 f271769d8edf6..3b192590fe7f8 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1828,9 +1828,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);
@@ -1838,26 +1835,34 @@ 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 friend type template parameter lists here
+  // so as 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
-    Record.AddTypeSourceInfo(D->getFriendType());
-  Record.AddSourceLocation(D->getFriendLoc());
+  for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
+    Record.AddTemplateParameterList(TPL);
+  if (D->Template.isNull()) {
+    if (D->getFriendDecl()) {
+      Record.push_back(FTDK_Decl);
+      Record.AddDeclRef(D->getFriendDecl());
+    } else {
+      Record.push_back(FTDK_Type);
+      Record.AddTypeSourceInfo(D->getFriendType());
+    }
+  } else {
+    Record.push_back(FTDK_Template);
+    Record.AddTemplateName(D->Template);
+  }
+  Record.AddDeclRef(D->getNextFriend());
+  Record.AddSourceLocation(D->FriendLoc);
+  Record.AddSourceLocation(D->EllipsisLoc);
   Code = serialization::DECL_FRIEND_TEMPLATE;
 }
 

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 f0424a1021abc..3891bcc5b1975 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -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 {{friend declaration does not name a member of a class template specialization}}
 
   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'
+// FIXME: the primary template definition of 'D::g' is not a friend either,
+// so it should not have access to 'private_int'.
 template<class T>
 void A<T>::D::g() { (void)c.private_int; }
 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..a3ab88e82bed3 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -101,19 +101,19 @@ 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
+  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 {{friend declaration does not name a member of a class template specialization}}
 };
 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
@@ -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 {{friend declaration does not name a member of a class template specialization}}
 };
 } // namespace cwg1945
 

diff  --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index 02c8f30249683..e574a5f48c507 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -183,12 +183,13 @@ struct A {
   friend void Ts...[0]::f();
   template<typename U>
   friend void Ts...[0]::g();
+  // since-cxx26-error at -1 {{friend declaration does not name a member of a class template specialization}}
 
   friend struct Ts...[0]::B;
   // FIXME: The index of the pack-index-specifier is printed as a memory address in the diagnostic.
   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 {{friend declaration does not name a member of a class template specialization}}
 };
 
 #endif

diff  --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index 3ba2b372cb715..68ac7e2346727 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -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 {{friend declaration does not name a member of a class template specialization}}
   };
 
   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/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
index a292d0de97a39..ae754c5990972 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -6,7 +6,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 +19,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 +35,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 +71,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() {
@@ -81,22 +86,264 @@ namespace test3 {
 namespace test4 {
   template <class T> struct X {
     template <class U> void operator+=(U);
-    
+
     template <class V>
     template <class U>
-    friend void X<V>::operator+=(U); // expected-warning {{not supported}}
+    friend void X<V>::operator+=(U);
   };
 
-  void test() {   
+  void test() {
     X<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 {{friend declaration does not name a member of a class template specialization}}
+  };
+}
+
+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 X<int>::Y;
+    // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
+  };
+}
+
+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}}
+  }
+}

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/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 621ae912c1ac9..23451e1a80a12 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -56,13 +56,11 @@ 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...;
 };
 
 namespace length_mismatch {

diff  --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index f98005c7e6fb5..ad6de9ee5b25e 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>
@@ -28,9 +28,12 @@ struct X {
   template <class 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); // #X-friend-operator-plus-eq
+  // expected-error at -1 {{no member 'operator+=' in 'X<int>'; it has not yet been instantiated}}
+  //   expected-note@#X-friend-operator-plus-eq {{not-yet-instantiated member is declared here}}
 };
 
 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;
+  // expected-note at -1 {{in instantiation of template class 'X<int>' requested here}}
 }

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..747ebd3246b47 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -566,3 +566,61 @@ struct Test {
 };
 
 }
+
+namespace DependentFriends {
+template <class T> concept X = requires { typename T::type; }; // #DependentFriends_X
+
+struct A {
+  using type = int;
+};
+struct B {};
+
+template <class T> struct C {
+  static void f()
+    requires X<T>; // #DependentFriends_C_f
+};
+
+class D {
+  static int n;
+  template <X T> friend void C<T>::f();
+};
+
+template <class T> struct E {
+  template <X U> // #DependentFriends_E_TPL
+  static void f() // #DependentFriends_E_f
+    requires X<T>;
+};
+
+class F {
+  static int n;
+  template <X T> template <X U>
+  friend void E<T>::f()
+    requires X<T>;
+};
+
+template <class T>
+void C<T>::f() requires X<T> {
+  D::n = 0;
+}
+
+template <class T>
+template <X U>
+void E<T>::f() requires X<T> {
+  F::n = 0;
+}
+
+void test() {
+  C<A>::f();
+  C<B>::f();
+  // expected-error at -1 {{invalid reference to function 'f': constraints not satisfied}}
+  //   expected-note@#DependentFriends_C_f {{because 'DependentFriends::B' does not satisfy 'X'}}
+  //   expected-note@#DependentFriends_X {{because 'typename T::type' would be invalid: no type named 'type' in 'DependentFriends::B'}}
+
+  E<A>::f<A>();
+  E<A>::f<B>();
+  // expected-error at -1 {{no matching function for call to 'f'}}
+  //   expected-note@#DependentFriends_E_f {{candidate template ignored: constraints not satisfied}}
+  //   expected-note@#DependentFriends_E_TPL {{because 'DependentFriends::B' does not satisfy 'X'}}
+  //   expected-note@#DependentFriends_X {{because 'typename T::type' would be invalid: no type named 'type' in 'DependentFriends::B'}}
+}
+}

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/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp
index 2b5a226c3b33c..4a8f23a56dd22 100644
--- a/clang/test/SemaTemplate/friend-template.cpp
+++ b/clang/test/SemaTemplate/friend-template.cpp
@@ -235,20 +235,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 {
@@ -338,3 +337,28 @@ class Foo {
   bool aux;
 };
 }
+
+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}}
+}
+}


        


More information about the cfe-commits mailing list