[clang] [clang-tools-extra] Revert "[Clang] support friend declarations with a dependent nested-name-specifier" (PR #208302)

Oleksandr Tarasiuk via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 8 12:53:58 PDT 2026


https://github.com/a-tarasyuk created https://github.com/llvm/llvm-project/pull/208302

Reverts llvm/llvm-project#191268

---

Revert dependent friend support due to a crash in access checking https://github.com/llvm/llvm-project/issues/208290

>From 372ff7bd46045385890e77d04da0862631c7fd9f Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 8 Jul 2026 22:19:57 +0300
Subject: [PATCH] =?UTF-8?q?Revert=20"[Clang]=20support=20friend=20declarat?=
 =?UTF-8?q?ions=20with=20a=20dependent=20nested-name-spec=E2=80=A6"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit dcf1b8f2c00b38777f71297afc7b575cd2d9300a.
---
 clang-tools-extra/clang-doc/Serialize.cpp     |   3 +
 clang/docs/ReleaseNotes.md                    |   1 -
 .../clang/AST/ASTStructuralEquivalence.h      |   2 -
 clang/include/clang/AST/DeclFriend.h          | 125 ++-
 clang/include/clang/AST/DeclTemplate.h        |  82 +-
 clang/include/clang/AST/RecursiveASTVisitor.h |  16 +-
 clang/include/clang/Basic/DeclNodes.td        |   2 +-
 clang/include/clang/Basic/DiagnosticGroups.td |   1 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  17 +-
 clang/include/clang/Sema/Sema.h               |  12 -
 clang/include/clang/Sema/Template.h           |  11 +-
 clang/include/clang/Sema/TemplateDeduction.h  |  17 +-
 .../include/clang/Serialization/ASTBitCodes.h |   7 -
 clang/lib/AST/ASTImporter.cpp                 | 110 +--
 clang/lib/AST/ASTStructuralEquivalence.cpp    |  59 +-
 clang/lib/AST/DeclFriend.cpp                  |  64 +-
 clang/lib/AST/DeclPrinter.cpp                 |  36 +-
 clang/lib/AST/DeclTemplate.cpp                |  55 +-
 clang/lib/AST/ODRHash.cpp                     |  15 -
 clang/lib/Sema/Sema.cpp                       |   5 +-
 clang/lib/Sema/SemaAccess.cpp                 | 749 +++---------------
 clang/lib/Sema/SemaDeclCXX.cpp                | 229 +++---
 clang/lib/Sema/SemaOverload.cpp               |  24 +-
 clang/lib/Sema/SemaTemplate.cpp               |  10 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |  10 -
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 201 ++---
 clang/lib/Serialization/ASTReaderDecl.cpp     |  30 +-
 clang/lib/Serialization/ASTWriterDecl.cpp     |  35 +-
 .../class.access/class.friend/p3-cxx0x.cpp    |   4 +-
 clang/test/CXX/drs/cwg18xx.cpp                |  16 +-
 clang/test/CXX/drs/cwg19xx.cpp                |  11 +-
 clang/test/CXX/drs/cwg28xx.cpp                |   3 +-
 clang/test/CXX/drs/cwg6xx.cpp                 |  19 +-
 .../CXX/temp/temp.decls/temp.friend/p5.cpp    | 267 +------
 .../CXX/temp/temp.decls/temp.friend/p6.cpp    |  27 -
 clang/test/Parser/cxx2c-variadic-friends.cpp  |   6 +-
 .../SemaCXX/many-template-parameter-lists.cpp |   9 +-
 clang/test/SemaTemplate/GH71595.cpp           |   8 +-
 clang/test/SemaTemplate/concepts-friends.cpp  |  58 --
 clang/test/SemaTemplate/ctad.cpp              |  24 +-
 clang/test/SemaTemplate/friend-template.cpp   |  34 +-
 41 files changed, 592 insertions(+), 1822 deletions(-)
 delete mode 100644 clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp

diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 5fa23416949c4..50118e0472075 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -1029,6 +1029,9 @@ 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 b49a9db3d5fca..0626c41774960 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -246,7 +246,6 @@ 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 89d7f8d6ba8ff..6f82de1ae136d 100644
--- a/clang/include/clang/AST/ASTStructuralEquivalence.h
+++ b/clang/include/clang/AST/ASTStructuralEquivalence.h
@@ -135,8 +135,6 @@ 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 c68028d22bd54..1f8c210263677 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -15,13 +15,20 @@
 #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 {
 
@@ -42,7 +49,9 @@ class ASTContext;
 /// @endcode
 ///
 /// The semantic context of a friend decl is its declaring class.
-class FriendDecl : public Decl {
+class FriendDecl final
+    : public Decl,
+      private llvm::TrailingObjects<FriendDecl, TemplateParameterList *> {
   LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION();
 
 public:
@@ -52,28 +61,46 @@ class FriendDecl : public Decl {
   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;
 
-  FriendDecl(Kind K, DeclContext *DC, SourceLocation L, FriendUnion Friend,
-             SourceLocation FriendL, SourceLocation EllipsisLoc = {})
-      : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FriendL),
-        Friend(Friend), 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, EmptyShell Empty) : Decl(K, Empty) {}
+  FriendDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
+      : Decl(Decl::Friend, Empty), UnsupportedFriend(false),
+        NumTPLists(NumFriendTypeTPLists) {}
 
   FriendDecl *getNextFriend() {
-    if (NextFriend.isOffset())
-      return getNextFriendSlowCase();
-    return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
+    if (!NextFriend.isOffset())
+      return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
+    return getNextFriendSlowCase();
   }
 
   FriendDecl *getNextFriendSlowCase();
@@ -82,11 +109,14 @@ class FriendDecl : public Decl {
   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 = {});
-  static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
+  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);
 
   /// If this friend declaration names an (untemplated but possibly
   /// dependent) type, return the type; otherwise return null.  This
@@ -96,27 +126,74 @@ class FriendDecl : public Decl {
     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; }
 
-  SourceLocation getFriendLoc() const { return FriendLoc; }
+  /// 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());
+  }
 
-  SourceRange getSourceRange() const override LLVM_READONLY;
+  /// Determines if this friend kind is unsupported.
+  bool isUnsupportedFriend() const {
+    return UnsupportedFriend;
+  }
+  void setUnsupportedFriend(bool Unsupported) {
+    UnsupportedFriend = Unsupported;
+  }
 
   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 >= firstFriend && K <= lastFriend;
-  }
+  static bool classofKind(Kind K) { return K == Decl::Friend; }
 };
+
 /// 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 95d5ab1b11bf2..4f5a4e1b7b8a6 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -19,7 +19,6 @@
 #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"
@@ -2462,51 +2461,45 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
 ///   template \<typename U> friend class Foo<T>::Nested; // friend template
 /// };
 /// \endcode
-class FriendTemplateDecl final
-    : public FriendDecl,
-      private llvm::TrailingObjects<FriendTemplateDecl,
-                                    TemplateParameterList *> {
-  void anchor() override;
+///
+/// \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 *>;
 
 private:
-  unsigned NumTPLists = 0;
-  TemplateName Template;
+  // The number of template parameters;  always non-zero.
+  unsigned NumParams = 0;
 
-  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());
-  }
+  // 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(EmptyShell Empty, unsigned NumFriendTypeTPLists)
-      : FriendDecl(Decl::FriendTemplate, Empty),
-        NumTPLists(NumFriendTypeTPLists) {}
+  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(EmptyShell Empty) : Decl(Decl::FriendTemplate, Empty) {}
 
 public:
   friend class ASTDeclReader;
-  friend class ASTDeclWriter;
-  friend TrailingObjects;
 
   static FriendTemplateDecl *
   Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
-         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);
+         MutableArrayRef<TemplateParameterList *> Params, FriendUnion Friend,
+         SourceLocation FriendLoc);
 
-  SourceRange getSourceRange() const override LLVM_READONLY;
+  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
 
   /// If this friend declaration names a templated type (or
   /// a dependent member type of a templated type), return that
@@ -2519,16 +2512,21 @@ class FriendTemplateDecl final
   /// a member function of a templated type), return that type;
   /// otherwise return null.
   NamedDecl *getFriendDecl() const {
-    if (TemplateDecl *TD = Template.getAsTemplateDecl())
-      return TD;
-    return Friend.dyn_cast<NamedDecl *>();
+    return Friend.dyn_cast<NamedDecl*>();
   }
 
-  TemplateName getFriendTemplateName() const { return Template; }
+  /// Retrieves the location of the 'friend' keyword.
+  SourceLocation getFriendLoc() const {
+    return FriendLoc;
+  }
 
-  ArrayRef<TemplateParameterList *>
-  getFriendTypeTemplateParameterLists() const {
-    return ArrayRef(getTrailingObjects(), NumTPLists);
+  TemplateParameterList *getTemplateParameterList(unsigned i) const {
+    assert(i <= NumParams);
+    return Params[i];
+  }
+
+  unsigned getNumTemplateParameters() const {
+    return NumParams;
   }
 
   // Implement isa/cast/dyncast/etc.
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 4552437d3db9a..b77443f1fa0ab 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1733,16 +1733,12 @@ DEF_TRAVERSE_DECL(FriendDecl, {
 })
 
 DEF_TRAVERSE_DECL(FriendTemplateDecl, {
-  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()) {
+  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);
     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 c55a3d68184f4..ffb58b43812dc 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<Friend>;
+def FriendTemplate : DeclNode<Decl>;
 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 d353a86f228ba..79583534b9bbd 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1198,6 +1198,7 @@ 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 3fa5bce9fb04f..38d9e4046d3a5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1916,6 +1916,16 @@ 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">,
@@ -1925,8 +1935,6 @@ 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 "
@@ -5282,16 +5290,11 @@ 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
 // different 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 differently 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 e658f57968b99..f7d0d493e7081 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12018,9 +12018,6 @@ 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,
@@ -12754,15 +12751,6 @@ 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 42febace0a671..b0170c21feb1a 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,10 +724,6 @@ 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,
@@ -738,11 +734,6 @@ 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 dd9fe46c9bf8a..39c909d73f565 100644
--- a/clang/include/clang/Sema/TemplateDeduction.h
+++ b/clang/include/clang/Sema/TemplateDeduction.h
@@ -311,11 +311,6 @@ 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
@@ -342,8 +337,7 @@ struct TemplateSpecCandidate {
   }
 
   /// Diagnose a template argument deduction failure.
-  void NoteDeductionFailure(Sema &S, bool ForTakingAddress,
-                            TemplateSpecCandidateSetKind CandidateSetKind);
+  void NoteDeductionFailure(Sema &S, bool ForTakingAddress);
 };
 
 /// TemplateSpecCandidateSet - A set of generalized overload candidates,
@@ -359,16 +353,11 @@ class TemplateSpecCandidateSet {
   // attribute on parameters.
   bool ForTakingAddress;
 
-  TemplateSpecCandidateSetKind CandidateSetKind;
-
   void destroyCandidates();
 
 public:
-  TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false,
-                           TemplateSpecCandidateSetKind CandidateSetKind =
-                               TemplateSpecCandidateSetKind::Normal)
-      : Loc(Loc), ForTakingAddress(ForTakingAddress),
-        CandidateSetKind(CandidateSetKind) {}
+  TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false)
+      : Loc(Loc), ForTakingAddress(ForTakingAddress) {}
   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 b581a70a073b5..279380de2f7fe 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2109,13 +2109,6 @@ 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 da1b6c2e908c6..567d2d07298a3 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -538,7 +538,6 @@ 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);
@@ -4561,15 +4560,19 @@ struct FriendCountAndPosition {
 
 static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
                                FriendDecl *FD2) {
-  if (FD1->getKind() != FD2->getKind())
+  if ((!FD1->getFriendType()) != (!FD2->getFriendType()))
     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);
 }
 
@@ -4589,6 +4592,7 @@ static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
   }
 
   assert(FriendPosition && "Friend decl not found in own parent.");
+
   return {FriendCount, *FriendPosition};
 }
 
@@ -4604,8 +4608,7 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   const auto *RD = cast<CXXRecordDecl>(DC);
   SmallVector<FriendDecl *, 2> ImportedEquivalentFriends;
   for (FriendDecl *ImportedFriend : RD->friends())
-    if (ImportedFriend->getKind() == Decl::Friend &&
-        IsEquivalentFriend(Importer, D, ImportedFriend))
+    if (IsEquivalentFriend(Importer, D, ImportedFriend))
       ImportedEquivalentFriends.push_back(ImportedFriend);
 
   FriendCountAndPosition CountAndPosition =
@@ -4637,6 +4640,15 @@ 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();
@@ -4650,7 +4662,7 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
   FriendDecl *FrD;
   if (GetImportedOrCreateDecl(FrD, D, Importer.getToContext(), DC,
                               *LocationOrErr, ToFU, *FriendLocOrErr,
-                              *EllipsisLocOrErr))
+                              *EllipsisLocOrErr, ToTPLists))
     return FrD;
 
   FrD->setAccess(D->getAccess());
@@ -4659,92 +4671,6 @@ 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 d3b47ebe5520c..e0b62591a6a73 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2290,8 +2290,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
       return false;
   }
 
-  return IsStructurallyEquivalent(Context, Params1->getRequiresClause(),
-                                  Params2->getRequiresClause());
+  return true;
 }
 
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
@@ -2442,45 +2441,6 @@ 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()))
@@ -2820,23 +2780,6 @@ 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 aea0600441d25..6bfc2eb62b284 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -13,9 +13,11 @@
 
 #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/AST/ExternalASTSource.h"
+#include "clang/Basic/LLVM.h"
 #include <cassert>
 #include <cstddef>
 
@@ -23,9 +25,16 @@ using namespace clang;
 
 void FriendDecl::anchor() {}
 
-FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
-                               FriendUnion Friend, SourceLocation FriendL,
-                               SourceLocation EllipsisLoc) {
+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) {
 #ifndef NDEBUG
   if (const auto *D = dyn_cast<NamedDecl *>(Friend)) {
     assert(isa<FunctionDecl>(D) ||
@@ -37,22 +46,25 @@ FriendDecl *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
 
-  auto *FD =
-      new (C, DC) FriendDecl(Decl::Friend, DC, L, Friend, FriendL, EllipsisLoc);
+  std::size_t Extra =
+      FriendDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          FriendTypeTPLists.size());
+  auto *FD = new (C, DC, Extra)
+      FriendDecl(DC, L, Friend, FriendL, EllipsisLoc, FriendTypeTPLists);
   cast<CXXRecordDecl>(DC)->pushFriendDecl(FD);
   return FD;
 }
 
-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 *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                           unsigned FriendTypeNumTPLists) {
+  std::size_t Extra =
+      additionalSizeToAlloc<TemplateParameterList *>(FriendTypeNumTPLists);
+  return new (C, ID, Extra) FriendDecl(EmptyShell(), FriendTypeNumTPLists);
 }
 
 FriendDecl *CXXRecordDecl::getFirstFriend() const {
@@ -60,29 +72,3 @@ 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 71546c5b43070..95591d3a09431 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -70,7 +70,6 @@ namespace {
     void VisitEmptyDecl(EmptyDecl *D);
     void VisitFunctionDecl(FunctionDecl *D);
     void VisitFriendDecl(FriendDecl *D);
-    void VisitFriendTemplateDecl(FriendTemplateDecl *D);
     void VisitFieldDecl(FieldDecl *D);
     void VisitVarDecl(VarDecl *D);
     void VisitLabelDecl(LabelDecl *D);
@@ -889,17 +888,24 @@ 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);
   }
@@ -908,24 +914,6 @@ 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 124ebcc50223b..275fe364e306d 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1236,50 +1236,21 @@ void FriendTemplateDecl::anchor() {}
 
 FriendTemplateDecl *
 FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC,
-                           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::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;
+                           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);
 }
 
-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);
+FriendTemplateDecl *FriendTemplateDecl::CreateDeserialized(ASTContext &C,
+                                                           GlobalDeclID ID) {
+  return new (C, ID) FriendTemplateDecl(EmptyShell());
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index b85204083cca9..46a4e256ea3e5 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -164,9 +164,7 @@ 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");
@@ -475,19 +473,6 @@ 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 ed3ff4bf277bc..78fbc9e31842d 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1163,9 +1163,8 @@ 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 = Friend->getFriendType()) {
+    if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
       // Friend classes are available as the TypeSourceInfo of the FriendDecl.
       if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl())
         Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete);
@@ -1174,7 +1173,7 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
     } else {
       // Friend functions are available through the NamedDecl of FriendDecl.
       if (const FunctionDecl *FD =
-              dyn_cast<FunctionDecl>(Friend->getFriendDecl()))
+          dyn_cast<FunctionDecl>((*I)->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 582f2f779d761..17415b4185eff 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -19,11 +19,8 @@
 #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;
@@ -277,243 +274,6 @@ 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) {
@@ -523,12 +283,8 @@ 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;
@@ -586,7 +342,9 @@ static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived,
   return OnFailure;
 }
 
-static bool MightInstantiateTo(DeclContext *Context, DeclContext *Friend) {
+
+static bool MightInstantiateTo(Sema &S, DeclContext *Context,
+                               DeclContext *Friend) {
   if (Friend == Context)
     return true;
 
@@ -605,7 +363,7 @@ static bool MightInstantiateTo(DeclContext *Context, DeclContext *Friend) {
 
 // Asks whether the type in 'context' can ever instantiate to the type
 // in 'friend'.
-static bool MightInstantiateTo(CanQualType Context, CanQualType Friend) {
+static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) {
   if (Friend == Context)
     return true;
 
@@ -616,66 +374,49 @@ static bool MightInstantiateTo(CanQualType Context, CanQualType Friend) {
   return true;
 }
 
-static bool MightInstantiateTo(CanQual<FunctionProtoType> Context,
-                               CanQual<FunctionProtoType> Friend) {
-  if (Friend.getQualifiers() != Context.getQualifiers())
-    return false;
-
-  if (Friend->getNumParams() != Context->getNumParams())
+static bool MightInstantiateTo(Sema &S,
+                               FunctionDecl *Context,
+                               FunctionDecl *Friend) {
+  if (Context->getDeclName() != Friend->getDeclName())
     return false;
 
-  if (!MightInstantiateTo(Context->getReturnType(), Friend->getReturnType()))
+  if (!MightInstantiateTo(S,
+                          Context->getDeclContext(),
+                          Friend->getDeclContext()))
     return false;
 
-  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;
+  CanQual<FunctionProtoType> FriendTy
+    = S.Context.getCanonicalType(Friend->getType())
+         ->getAs<FunctionProtoType>();
+  CanQual<FunctionProtoType> ContextTy
+    = S.Context.getCanonicalType(Context->getType())
+         ->getAs<FunctionProtoType>();
 
-  if (Context.getNameKind() != Friend.getNameKind())
+  // There isn't any way that I know of to add qualifiers
+  // during instantiation.
+  if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
     return false;
 
-  switch (Context.getNameKind()) {
-  case DeclarationName::CXXConstructorName:
-  case DeclarationName::CXXDestructorName:
-  case DeclarationName::CXXConversionFunctionName:
-    return MightInstantiateTo(Ctx.getCanonicalType(Context.getCXXNameType()),
-                              Ctx.getCanonicalType(Friend.getCXXNameType()));
-
-  default:
+  if (FriendTy->getNumParams() != ContextTy->getNumParams())
     return false;
-  }
-}
 
-static bool MightInstantiateTo(ASTContext &Ctx, FunctionDecl *Context,
-                               FunctionDecl *Friend) {
-  if (!MightInstantiateTo(Ctx, Context->getDeclName(), Friend->getDeclName()))
+  if (!MightInstantiateTo(S, ContextTy->getReturnType(),
+                          FriendTy->getReturnType()))
     return false;
 
-  DeclContext *ContextDC = Context->getDeclContext();
-  DeclContext *FriendDC = Friend->getDeclContext();
-
-  if (!FriendDC->isDependentContext() &&
-      !MightInstantiateTo(ContextDC, FriendDC))
-    return false;
-
-  CanQual<FunctionProtoType> FriendTy = GetCanonicalFunctionProto(Ctx, Friend);
-  CanQual<FunctionProtoType> ContextTy =
-      GetCanonicalFunctionProto(Ctx, Context);
+  for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I)
+    if (!MightInstantiateTo(S, ContextTy->getParamType(I),
+                            FriendTy->getParamType(I)))
+      return false;
 
-  return MightInstantiateTo(ContextTy, FriendTy);
+  return true;
 }
 
-static bool MightInstantiateTo(ASTContext &Ctx, FunctionTemplateDecl *Context,
+static bool MightInstantiateTo(Sema &S,
+                               FunctionTemplateDecl *Context,
                                FunctionTemplateDecl *Friend) {
-  return MightInstantiateTo(Ctx, Context->getTemplatedDecl(),
+  return MightInstantiateTo(S,
+                            Context->getTemplatedDecl(),
                             Friend->getTemplatedDecl());
 }
 
@@ -736,7 +477,7 @@ static AccessResult MatchesFriend(Sema &S,
     }
 
     // It's a match.
-    if (declaresSameEntity(Friend, CTD))
+    if (Friend == CTD->getCanonicalDecl())
       return AR_accessible;
 
     // If the context isn't dependent, it can't be a dependent match.
@@ -750,7 +491,8 @@ 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(CTD->getDeclContext(), Friend->getDeclContext()))
+    if (!MightInstantiateTo(S, CTD->getDeclContext(),
+                            Friend->getDeclContext()))
       continue;
 
     // Otherwise, it's a dependent match.
@@ -772,7 +514,7 @@ static AccessResult MatchesFriend(Sema &S,
     if (Friend == *I)
       return AR_accessible;
 
-    if (EC.isDependent() && MightInstantiateTo(S.Context, *I, Friend))
+    if (EC.isDependent() && MightInstantiateTo(S, *I, Friend))
       OnFailure = AR_dependent;
   }
 
@@ -791,330 +533,63 @@ static AccessResult MatchesFriend(Sema &S,
   for (SmallVectorImpl<FunctionDecl*>::const_iterator
          I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
 
-    FunctionTemplateDecl *FTD = TryGetFunctionTemplateDecl(*I);
+    FunctionTemplateDecl *FTD = (*I)->getPrimaryTemplate();
+    if (!FTD)
+      FTD = (*I)->getDescribedFunctionTemplate();
     if (!FTD)
       continue;
 
+    FTD = FTD->getCanonicalDecl();
+
     if (Friend == FTD)
       return AR_accessible;
 
-    if (EC.isDependent() && MightInstantiateTo(S.Context, FTD, Friend))
+    if (EC.isDependent() && MightInstantiateTo(S, 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 friend declaration.
-  if (FriendD->isInvalidDecl())
+  // Whitelist accesses if there's an invalid or unsupported friend
+  // declaration.
+  if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend())
     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());
 
-  return AR_inaccessible;
-}
+  NamedDecl *Friend
+    = cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl());
 
-static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
-                                  FriendTemplateDecl *FTD,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  if (FTD->isInvalidDecl())
-    return AR_accessible;
+  // FIXME: declarations with dependent or templated scope.
 
-  if (NamedDecl *ND = FTD->getFriendDecl())
-    return MatchesFriend(S, EC, FTD, ND, FailedTSC);
+  if (isa<ClassTemplateDecl>(Friend))
+    return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
 
-  if (TypeSourceInfo *TSI = FTD->getFriendType())
-    return MatchesFriend(S, EC, FTD, TSI, FailedTSC);
+  if (isa<FunctionTemplateDecl>(Friend))
+    return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
 
-  return AR_inaccessible;
+  if (isa<CXXRecordDecl>(Friend))
+    return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
+
+  assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
+  return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
 }
 
-static AccessResult GetFriendKind(Sema &S, const EffectiveContext &EC,
-                                  const CXXRecordDecl *Class,
-                                  TemplateSpecCandidateSet *FailedTSC) {
+static AccessResult GetFriendKind(Sema &S,
+                                  const EffectiveContext &EC,
+                                  const CXXRecordDecl *Class) {
   AccessResult OnFailure = AR_inaccessible;
 
   // Okay, check friends.
-  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) {
+  for (auto *Friend : Class->friends()) {
+    switch (MatchesFriend(S, EC, Friend)) {
     case AR_accessible:
       return AR_accessible;
 
@@ -1138,7 +613,6 @@ namespace {
 struct ProtectedFriendContext {
   Sema &S;
   const EffectiveContext &EC;
-  TemplateSpecCandidateSet *FailedTSC;
   const CXXRecordDecl *NamingClass;
   bool CheckDependent;
   bool EverDependent;
@@ -1148,19 +622,18 @@ struct ProtectedFriendContext {
 
   ProtectedFriendContext(Sema &S, const EffectiveContext &EC,
                          const CXXRecordDecl *InstanceContext,
-                         const CXXRecordDecl *NamingClass,
-                         TemplateSpecCandidateSet *FailedTSC)
-      : S(S), EC(EC), FailedTSC(FailedTSC), NamingClass(NamingClass),
-        CheckDependent(InstanceContext->isDependentContext() ||
-                       NamingClass->isDependentContext()),
-        EverDependent(false) {}
+                         const CXXRecordDecl *NamingClass)
+    : S(S), EC(EC), 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], FailedTSC)) {
+      switch (GetFriendKind(S, EC, CurPath[I])) {
       case AR_accessible:   return true;
       case AR_inaccessible: continue;
       case AR_dependent:    EverDependent = true; continue;
@@ -1247,9 +720,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, TemplateSpecCandidateSet *FailedTSC) {
+static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
+                                           const CXXRecordDecl *InstanceContext,
+                                           const CXXRecordDecl *NamingClass) {
   assert(InstanceContext == nullptr ||
          InstanceContext->getCanonicalDecl() == InstanceContext);
   assert(NamingClass->getCanonicalDecl() == NamingClass);
@@ -1257,20 +730,19 @@ static AccessResult GetProtectedFriendKind(
   // 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, FailedTSC);
+  if (!InstanceContext) return GetFriendKind(S, EC, NamingClass);
 
-  ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass, FailedTSC);
+  ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass);
   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,
-                              TemplateSpecCandidateSet *FailedTSC) {
+                              const AccessTarget &Target) {
   assert(NamingClass->getCanonicalDecl() == NamingClass &&
          "declaration should be canonicalized before being passed here");
 
@@ -1390,8 +862,7 @@ static AccessResult HasAccess(Sema &S, const EffectiveContext &EC,
       if (!InstanceContext) return AR_dependent;
     }
 
-    switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass,
-                                   FailedTSC)) {
+    switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) {
     case AR_accessible: return AR_accessible;
     case AR_inaccessible: return OnFailure;
     case AR_dependent: return AR_dependent;
@@ -1399,7 +870,7 @@ static AccessResult HasAccess(Sema &S, const EffectiveContext &EC,
     llvm_unreachable("impossible friendship kind");
   }
 
-  switch (GetFriendKind(S, EC, NamingClass, FailedTSC)) {
+  switch (GetFriendKind(S, EC, NamingClass)) {
   case AR_accessible: return AR_accessible;
   case AR_inaccessible: return OnFailure;
   case AR_dependent: return AR_dependent;
@@ -1512,8 +983,7 @@ static CXXBasePath *FindBestPath(Sema &S,
       AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
       PathAccess = std::max(PathAccess, BaseAccess);
 
-      switch (HasAccess(S, EC, NC, PathAccess, Target,
-                        /*FailedTSC=*/nullptr)) {
+      switch (HasAccess(S, EC, NC, PathAccess, Target)) {
       case AR_inaccessible: break;
       case AR_accessible:
         PathAccess = AS_public;
@@ -1709,8 +1179,7 @@ static void DiagnoseAccessPath(Sema &S,
     accessSoFar = D->getAccess();
     const CXXRecordDecl *declaringClass = entity.getDeclaringClass();
 
-    switch (HasAccess(S, EC, declaringClass, accessSoFar, entity,
-                      /*FailedTSC=*/nullptr)) {
+    switch (HasAccess(S, EC, declaringClass, accessSoFar, entity)) {
     // If the declaration is accessible when named in its declaring
     // class, then we must be constrained by the path.
     case AR_accessible:
@@ -1753,8 +1222,7 @@ static void DiagnoseAccessPath(Sema &S,
       accessSoFar = baseAccess;
     }
 
-    switch (HasAccess(S, EC, derivingClass, accessSoFar, entity,
-                      /*FailedTSC=*/nullptr)) {
+    switch (HasAccess(S, EC, derivingClass, accessSoFar, entity)) {
     case AR_inaccessible: break;
     case AR_accessible:
       accessSoFar = AS_public;
@@ -1858,9 +1326,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,
-                                 TemplateSpecCandidateSet *FailedTSC) {
+static AccessResult IsAccessible(Sema &S,
+                                 const EffectiveContext &EC,
+                                 AccessTarget &Entity) {
   // Determine the actual naming class.
   const CXXRecordDecl *NamingClass = Entity.getEffectiveNamingClass();
 
@@ -1872,8 +1340,7 @@ static AccessResult IsAccessible(Sema &S, const EffectiveContext &EC,
   // 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, FailedTSC)) {
+    switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) {
     case AR_dependent:
       // This is actually an interesting policy decision.  We don't
       // *have* to delay immediately here: we can do the full access
@@ -1902,7 +1369,7 @@ static AccessResult IsAccessible(Sema &S, const EffectiveContext &EC,
     const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
 
     FinalAccess = Target->getAccess();
-    switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity, FailedTSC)) {
+    switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) {
     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
@@ -1954,30 +1421,25 @@ static void DelayDependentAccess(Sema &S,
                               Entity.getDiag());
 }
 
-static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
+/// Checks access to an entity from the given effective context.
+static AccessResult CheckEffectiveAccess(Sema &S,
+                                         const EffectiveContext &EC,
                                          SourceLocation Loc,
-                                         AccessTarget &Entity,
-                                         TemplateSpecCandidateSet *FailedTSC) {
-  assert((Entity.isQuiet() || FailedTSC) &&
-         "non-quiet access check requires a candidate set");
+                                         AccessTarget &Entity) {
+  assert(Entity.getAccess() != AS_public && "called for public access!");
 
-  switch (IsAccessible(S, EC, Entity, FailedTSC)) {
+  switch (IsAccessible(S, EC, Entity)) {
   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())
-      return AR_inaccessible;
-
-    DiagnoseBadAccess(S, Loc, EC, Entity);
-    FailedTSC->NoteCandidates(S, Loc);
+    if (!Entity.isQuiet())
+      DiagnoseBadAccess(S, Loc, EC, Entity);
     return AR_inaccessible;
-  }
 
   case AR_accessible:
     return AR_accessible;
@@ -1987,20 +1449,6 @@ static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
   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.
@@ -2485,8 +1933,7 @@ 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, /*FailedTSC=*/nullptr) !=
-           ::AR_inaccessible;
+    return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible;
   }
 
   if (ObjCIvarDecl *Ivar = dyn_cast<ObjCIvarDecl>(Target)) {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 407dacefe76a6..65b46562b166e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18083,31 +18083,6 @@ 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,
@@ -18123,17 +18098,16 @@ 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;
 
-      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();
-      }
+      return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS,
+                                Name, NameLoc, Attr, TemplateParams, AS_public,
+                                /*ModulePrivateLoc=*/SourceLocation(),
+                                FriendLoc, TempParamLists.size() - 1,
+                                TempParamLists.data(), IsMemberSpecialization)
+          .get();
     } else {
       // The "template<>" header is extraneous.
       Diag(TemplateParams->getTemplateLoc(), diag::err_template_tag_noparams)
@@ -18178,8 +18152,9 @@ DeclResult Sema::ActOnTemplatedFriendTag(
     if (T.isNull())
       return true;
 
-    FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI,
-                                            FriendLoc, EllipsisLoc);
+    FriendDecl *Friend =
+        FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+                           EllipsisLoc, TempParamLists);
     Friend->setAccess(AS_public);
     CurContext->addDecl(Friend);
     return Friend;
@@ -18205,35 +18180,25 @@ DeclResult Sema::ActOnTemplatedFriendTag(
     }
   }
 
-  NestedNameSpecifier NNS = SS.getScopeRep();
-  if (EllipsisLoc.isInvalid() &&
-      CheckDependentFriend(TagLoc, NNS, TempParamLists.front()))
-    return true;
-
+  // Handle the case of a templated-scope friend class.  e.g.
+  //   template <class T> class A<T>::B;
+  // FIXME: we don't support these right now.
+  Diag(NameLoc, diag::warn_template_qualified_friend_unsupported)
+    << SS.getScopeRep() << SS.getRange() << cast<CXXRecordDecl>(CurContext);
   ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
-  QualType T = Context.getDependentNameType(ETK, NNS, Name);
+  QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
   TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T);
-
   DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>();
   TL.setElaboratedKeywordLoc(TagLoc);
   TL.setQualifierLoc(SS.getWithLocInContext(Context));
   TL.setNameLoc(NameLoc);
 
-  FriendDecl *Friend;
-  if (TempParamLists.empty())
-    Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
-                                EllipsisLoc);
-  else {
-    if (CheckTemplateDeclScope(S, TempParamLists.back()))
-      return true;
-
-    Friend = FriendTemplateDecl::Create(Context, CurContext, NameLoc, TSI,
-                                        FriendLoc, TempParamLists, EllipsisLoc);
-  }
-
+  FriendDecl *Friend =
+      FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+                         EllipsisLoc, TempParamLists);
   Friend->setAccess(AS_public);
+  Friend->setUnsupportedFriend(true);
   CurContext->addDecl(Friend);
-
   return Friend;
 }
 
@@ -18334,14 +18299,11 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
   // friend a member of an arbitrary specialization of your template).
 
   Decl *D;
-  if (!TempParams.empty()) {
-    if (CheckTemplateDeclScope(S, TempParams.back()))
-      return nullptr;
-
+  if (!TempParams.empty())
     // TODO: Support variadic friend template decls?
-    D = FriendTemplateDecl::Create(Context, CurContext, Loc, TSI, FriendLoc,
-                                   TempParams, EllipsisLoc);
-  } else
+    D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI,
+                                   FriendLoc);
+  else
     D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(),
                            TSI, FriendLoc, EllipsisLoc);
 
@@ -18418,7 +18380,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();
+          << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange());
       SS.clear();
     }
   }
@@ -18539,11 +18501,6 @@ 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()) {
@@ -18612,79 +18569,81 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
       PushOnScopeChains(ND, EnclosingScope, /*AddToContext=*/ false);
   }
 
-  warnOnReservedIdentifier(ND);
-
-  if (ND->isInvalidDecl())
-    return ND;
+  FriendDecl *FrD = FriendDecl::Create(Context, CurContext,
+                                       D.getIdentifierLoc(), ND,
+                                       DS.getFriendSpecLoc());
+  FrD->setAccess(AS_public);
+  CurContext->addDecl(FrD);
 
-  if (DC->isRecord())
-    CheckFriendAccess(ND);
+  if (ND->isInvalidDecl()) {
+    FrD->setInvalidDecl();
+  } else {
+    if (DC->isRecord()) CheckFriendAccess(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()) {
-      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);
-  }
+    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);
+      }
+    }
 
-  ArrayRef<TemplateParameterList *> TPL = FD->getTemplateParameterLists();
-  FriendDecl *Friend;
-  if (TPL.size() > 0 && SS.isValid()) {
-    if (CheckTemplateDeclScope(S, TPL.back()))
-      return nullptr;
+    // 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);
+    }
 
-    Friend =
-        FriendTemplateDecl::Create(Context, CurContext, D.getIdentifierLoc(),
-                                   ND, DS.getFriendSpecLoc(), TPL);
-  } else {
-    Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), ND,
-                                DS.getFriendSpecLoc());
+    // 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);
+    }
   }
 
-  Friend->setAccess(AS_public);
-  CurContext->addDecl(Friend);
+  warnOnReservedIdentifier(ND);
 
   return ND;
 }
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 19141890ca230..c8b05a9f94bd0 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12394,9 +12394,8 @@ 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,
-                                 TemplateSpecCandidateSetKind CandidateSetKind =
-                                     TemplateSpecCandidateSetKind::Normal) {
+                                 unsigned NumArgs,
+                                 bool TakingCandidateAddress) {
   TemplateParameter Param = DeductionFailure.getTemplateParameter();
   NamedDecl *ParamD;
   (ParamD = Param.dyn_cast<TemplateTypeParmDecl*>()) ||
@@ -12643,10 +12642,7 @@ 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(),
-                 CandidateSetKind ==
-                         TemplateSpecCandidateSetKind::FriendTemplate
-                     ? diag::note_friend_template_non_deduced_mismatch_qualified
-                     : diag::note_ovl_candidate_non_deduced_mismatch_qualified)
+                 diag::note_ovl_candidate_non_deduced_mismatch_qualified)
               << FirstTN.getAsTemplateDecl() << SecondTN.getAsTemplateDecl();
           return;
         }
@@ -12662,9 +12658,7 @@ 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(),
-           CandidateSetKind == TemplateSpecCandidateSetKind::FriendTemplate
-               ? diag::note_friend_template_non_deduced_mismatch
-               : diag::note_ovl_candidate_non_deduced_mismatch)
+           diag::note_ovl_candidate_non_deduced_mismatch)
         << FirstTA << SecondTA;
     return;
   }
@@ -13615,12 +13609,10 @@ 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,
-    TemplateSpecCandidateSetKind CandidateSetKind) {
+void TemplateSpecCandidate::NoteDeductionFailure(Sema &S,
+                                                 bool ForTakingAddress) {
   DiagnoseBadDeduction(S, FoundDecl, Specialization, // pattern
-                       DeductionFailure, /*NumArgs=*/0, ForTakingAddress,
-                       CandidateSetKind);
+                       DeductionFailure, /*NumArgs=*/0, ForTakingAddress);
 }
 
 void TemplateSpecCandidateSet::destroyCandidates() {
@@ -13672,7 +13664,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, CandidateSetKind);
+    Cand->NoteDeductionFailure(S, ForTakingAddress);
   }
 
   if (I != E)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index b06bf2d25ad6a..ad0e653e254dd 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1958,9 +1958,14 @@ DeclResult Sema::CheckClassTemplate(
   if (SS.isNotEmpty() && !SS.isInvalid()) {
     SemanticContext = computeDeclContext(SS, true);
     if (!SemanticContext) {
-      Diag(NameLoc, diag::err_template_qualified_declarator_no_match)
+      // 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)
           << SS.getScopeRep() << SS.getRange();
-      return true;
+      return TUK != TagUseKind::Friend;
     }
 
     if (RequireCompleteDeclContext(SS, SemanticContext))
@@ -11402,7 +11407,6 @@ 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 adabbe69903db..d93b528facbcc 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4112,16 +4112,6 @@ 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 8311ed5349639..c56203f10ac3c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1989,69 +1989,68 @@ Decl *TemplateDeclInstantiator::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
   return IndirectField;
 }
 
-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");
-
-  bool ShouldExpand = true;
-  bool RetainExpansion = false;
-  UnsignedOrNone NumExpansions = std::nullopt;
-  if (SemaRef.CheckParameterPacksForExpansion(
-          D->getEllipsisLoc(), D->getSourceRange(), Unexpanded, TemplateArgs,
-          /*FailOnPackProducingTemplates=*/true, ShouldExpand, RetainExpansion,
-          NumExpansions))
-    return true;
-
-  assert(!RetainExpansion &&
-         "should never retain an expansion for a friend declaration");
-
-  if (!ShouldExpand)
-    return false;
+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;
 
-  for (unsigned I = 0; I != *NumExpansions; I++) {
-    Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
-    SmallVector<TemplateParameterList *, 1> InstTPL;
-    if (SubstTemplateParameterLists(TPL, InstTPL))
-      return true;
+        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);
+          }
 
-    TypeSourceInfo *InstTy = SemaRef.SubstType(
-        TSI, TemplateArgs, D->getEllipsisLoc(), DeclarationName());
-    if (!InstTy)
-      return true;
+          // Just drop this node; we have no use for it anymore.
+          return nullptr;
+        }
+      }
 
-    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());
+      InstTy = SemaRef.SubstType(Ty, TemplateArgs, D->getLocation(),
+                                 DeclarationName());
     }
-
-    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;
   }
@@ -2070,6 +2069,7 @@ 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,75 +4711,12 @@ Decl *TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D)
 }
 
 Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
-  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);
-  }
+  // 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();
 
   return nullptr;
 }
@@ -4919,20 +4856,6 @@ 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,
@@ -5246,10 +5169,8 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
           continue;
         }
 
-        ParmVarDecl *Parm = SemaRef.SubstParmVarDecl(
-            OldParam, TemplateArgs, /*indexAdjustment=*/0,
-            /*NumExpansions=*/std::nullopt,
-            /*ExpectParameterPack=*/false, EvaluateConstraints);
+        ParmVarDecl *Parm =
+            cast_or_null<ParmVarDecl>(VisitParmVarDecl(OldParam));
         if (!Parm)
           return nullptr;
         Params.push_back(Parm);
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index ee359268a0e3c..370a6970d531a 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2404,29 +2404,26 @@ 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);
-  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:
+  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>();
-    break;
-  case FTDK_Template:
-    D->Template = Record.readTemplateName();
-    break;
-  }
-  D->NextFriend = readDeclID().getRawValue();
+  else
+    D->Friend = readTypeSourceInfo();
   D->FriendLoc = readSourceLocation();
-  D->EllipsisLoc = readSourceLocation();
 }
 
 void ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
@@ -4081,11 +4078,10 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
     D = AccessSpecDecl::CreateDeserialized(Context, ID);
     break;
   case DECL_FRIEND:
-    D = FriendDecl::CreateDeserialized(Context, ID);
+    D = FriendDecl::CreateDeserialized(Context, ID, Record.readInt());
     break;
   case DECL_FRIEND_TEMPLATE:
-    D = FriendTemplateDecl::CreateDeserialized(Context, ID,
-                                               /*NumTPLists=*/Record.readInt());
+    D = FriendTemplateDecl::CreateDeserialized(Context, ID);
     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 3b192590fe7f8..f271769d8edf6 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1828,6 +1828,9 @@ 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);
@@ -1835,34 +1838,26 @@ 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);
-  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);
+  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());
   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 6c55e81c58c18..f7216ea7eb7b0 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;
+  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 T> class B { typedef int ty; };
@@ -74,7 +74,7 @@ struct {
       friend
 
   float;
-  template<typename T> friend class A<T>::foo;
+  template<typename T> friend class A<T>::foo; // expected-warning {{not supported}}
 } a;
 
 void testA() { (void)sizeof(A<int>); }
diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index 3891bcc5b1975..f0424a1021abc 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -416,24 +416,29 @@ struct A<float*> {
 };
 
 class C {
-  int private_int; // #cwg1862-C-private_int
+  int 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-error at -1 {{friend declaration does not name a member of a class template specialization}}
+  // expected-warning at -1 {{dependent nested name specifier 'A<T>::D' for friend class declaration is not supported; turning off access control for 'C'}}
 
   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;
@@ -445,16 +450,11 @@ 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: the primary template definition of 'D::g' is not a friend either,
-// so it should not have access to 'private_int'.
+// FIXME: both definition of 'D::g' are not friends, so they don't have access to 'private_int'
 template<class T>
 void A<T>::D::g() { (void)c.private_int; }
 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 a3ab88e82bed3..8162f9caa8f15 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; // #cwg1918-X-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-error at -1 {{friend declaration does not name a member of a class template specialization}}
+  // expected-warning at -1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}}
 };
 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,9 +167,10 @@ 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-error at -1 {{friend declaration does not name a member of a class template specialization}}
+  // expected-warning at -1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}}
 };
 } // namespace cwg1945
 
diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index e574a5f48c507..02c8f30249683 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -183,13 +183,12 @@ 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-error at -1 {{friend declaration does not name a member of a class template specialization}}
+  // since-cxx26-warning at -1 {{dependent nested name specifier 'Ts...[0]' for friend template declaration is not supported; ignoring this friend declaration}}
 };
 
 #endif
diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index 68ac7e2346727..3ba2b372cb715 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -407,29 +407,26 @@ namespace cwg638 { // cwg638: no
   };
 
   class X {
-    typedef int type; // #cwg638-X-type
+    typedef int 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-error at -1 {{friend declaration does not name a member of a class template specialization}}
+    // expected-warning at -1 {{dependent nested name specifier 'A<T>::C' for friend class declaration is not supported; turning off access control for 'X'}}
   };
 
   template<> struct A<int> {
-    X::type a;
-    // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
-    //   expected-note@#cwg638-X-type {{implicitly declared private here}}
+    X::type a; // FIXME: private
     struct B {
       X::type b; // ok
     };
-    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}}
+    int f() { X::type c; } // FIXME: private
     void g() { X::type d; } // ok
     struct D {
-      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}}
+      void h() { X::type e; } // FIXME: private
     };
   };
 } // 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 ae754c5990972..a292d0de97a39 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;
+    template <class T> friend class A<T>::Member; // expected-warning {{not supported}}
     int n;
   };
 
@@ -19,7 +19,7 @@ namespace test1 {
 
   class C {
     static void foo();
-    template <class T> friend void A<T>::f();
+    template <class T> friend void A<T>::f(); // expected-warning {{not supported}}
   };
 
   template <class T> struct A {
@@ -35,30 +35,25 @@ namespace test1 {
   };
 }
 
+// FIXME: these should fail!
 namespace test2 {
   template <class T> struct A;
 
   class C {
-    static void foo(); // #test2-C-foo
-    template <class T> friend void A<T>::g();
+    static void foo();
+    template <class T> friend void A<T>::g(); // expected-warning {{not supported}}
   };
 
   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}}
   };
 }
 
@@ -71,7 +66,7 @@ namespace test3 {
 
   template <class U> class C {
     int i;
-    template <class T> friend struct A<T>::Inner;
+    template <class T> friend struct A<T>::Inner; // expected-warning {{not supported}}
   };
 
   template <class T> int A<T>::Inner::foo() {
@@ -86,264 +81,22 @@ 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);
+    friend void X<V>::operator+=(U); // expected-warning {{not supported}}
   };
 
-  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();
+    template<template <class> class U> friend void A<U>::foo(); // expected-warning {{not supported}}
   };
 
   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
deleted file mode 100644
index f60f3dd42445c..0000000000000
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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 23451e1a80a12..621ae912c1ac9 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -56,11 +56,13 @@ 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...;
+  friend class TS<Ts>::Nested...; // expected-warning {{dependent nested name specifier 'TS<Ts>' for friend template declaration is not supported; ignoring this friend declaration}}
 
   template<class T>
-  friend class D<T, Ts>::Nested...;
+  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'}}
 };
 
 namespace length_mismatch {
diff --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index ad6de9ee5b25e..f98005c7e6fb5 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 {
+  struct A { // expected-note {{not-yet-instantiated member is declared here}}
     template <class V>
     struct B {
       template <class W>
@@ -28,12 +28,9 @@ 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); // #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}}
+  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}}
 };
 
 void test() {
-  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}}
+  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}}
 }
diff --git a/clang/test/SemaTemplate/GH71595.cpp b/clang/test/SemaTemplate/GH71595.cpp
index c5c5d13036233..daec9410e547a 100644
--- a/clang/test/SemaTemplate/GH71595.cpp
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -18,21 +18,17 @@ void f() {
 template<class A>
 class temp {
     template<C<temp> T>
-    friend void g();
-    // expected-error at -1 {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
+    friend void g(); // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
 
-    temp(); // #temp-ctor
+    temp();
 };
 
 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 747ebd3246b47..11287aa773b1b 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -566,61 +566,3 @@ 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 2681ee2a6081e..7a8a72a62e992 100644
--- a/clang/test/SemaTemplate/ctad.cpp
+++ b/clang/test/SemaTemplate/ctad.cpp
@@ -19,31 +19,27 @@ namespace pr41427 {
 namespace Access {
   struct B {
   protected:
-    struct type {}; // #Access-B-type
+    struct type {};
   };
-  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>'}}
+  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>'}}
   };
   D b = {B(), {}};
 
   class X {
     using type = int;
   };
-  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'}}
+  D x = {X(), {}}; // expected-error {{no viable constructor or deduction guide}}
 
+  // 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);
-    struct type {}; // #Access-Y-type
+    template <typename T> friend D<T>::D(T, typename T::type); // expected-warning {{dependent nested name specifier}}
+    struct 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 4a8f23a56dd22..2b5a226c3b33c 100644
--- a/clang/test/SemaTemplate/friend-template.cpp
+++ b/clang/test/SemaTemplate/friend-template.cpp
@@ -235,19 +235,20 @@ namespace rdar11147355 {
   template <class T>
   struct A {
     template <class U> class B;
-    template <class S> template <class U> friend class A<S>::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}}
   private:
-    int n;
+    int n; // expected-note {{here}}
   };
 
   template <class S> template <class U> class A<S>::B {
   public:
-    int f(A<S*> a) { return a.n; }
+    // FIXME: This should be permitted.
+    int f(A<S*> a) { return a.n; } // expected-error {{private}}
   };
 
   A<double>::B<double>  ab;
   A<double*> a;
-  int k = ab.f(a);
+  int k = ab.f(a); // expected-note {{instantiation of}}
 }
 
 namespace RedeclUnrelated {
@@ -337,28 +338,3 @@ 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