[llvm-branch-commits] [clang-tools-extra] 57a04e1 - Revert "[Clang] Support friend declarations with a dependent nested-name-spec…"

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Aug 16 00:53:20 PDT 2026


Author: Oleksandr Tarasiuk
Date: 2026-08-16T10:53:15+03:00
New Revision: 57a04e139f56b4f30b9207b50b936194c98e1b8a

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

LOG: Revert "[Clang] Support friend declarations with a dependent nested-name-spec…"

This reverts commit 0b672437c396b700fdb94bd64b176b33f084053b.

Added: 
    

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

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


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

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

diff  --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 6d2659337a9c8..1f8c210263677 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -15,12 +15,18 @@
 #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>
 
@@ -43,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:
@@ -53,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 FL, SourceLocation EllipsisLoc = {})
-      : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FL), 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();
@@ -83,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
@@ -97,26 +126,72 @@ 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.
-  virtual NamedDecl *getFriendDecl() const {
+  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.

diff  --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index f5b26b0aab9ba..ed1a517247943 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"
@@ -2458,78 +2457,76 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
 /// \code
 /// template \<typename T> class A {
 ///   friend class MyVector<T>; // not a friend template
-///   template \<typename U> friend class B; // friend class template
+///   template \<typename U> friend class B; // not a friend template
 ///   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 *> FriendTPLists,
-                     TemplateName Template = {})
-      : FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc,
-                   EllipsisLoc),
-        NumTPLists(FriendTPLists.size()), Template(Template) {
-    assert(!FriendTPLists.empty());
-    llvm::copy(FriendTPLists, getTrailingObjects());
-  }
+  // The parameter list.
+  TemplateParameterList **Params = nullptr;
 
-  FriendTemplateDecl(EmptyShell Empty, unsigned NumFriendTPLists)
-      : FriendDecl(Decl::FriendTemplate, Empty), NumTPLists(NumFriendTPLists) {
-    assert(NumFriendTPLists != 0);
-  }
+  // The declaration that's a friend of this class.
+  FriendUnion Friend;
 
-public:
-  friend class ASTDeclReader;
-  friend class ASTDeclWriter;
-  friend TrailingObjects;
+  // Location of the 'friend' specifier.
+  SourceLocation FriendLoc;
 
-  enum class FriendTemplateEntityKind { Type, Template, Decl };
+  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) {}
 
-  static FriendTemplateDecl *
-  Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
-         FriendUnion Friend, SourceLocation FriendLoc,
-         ArrayRef<TemplateParameterList *> FriendTPLists,
-         SourceLocation EllipsisLoc = {}, TemplateName Template = {});
+  FriendTemplateDecl(EmptyShell Empty) : Decl(Decl::FriendTemplate, Empty) {}
+
+public:
+  friend class ASTDeclReader;
 
   static FriendTemplateDecl *
   Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
-         TemplateName Template, SourceLocation FriendLoc,
-         ArrayRef<TemplateParameterList *> FriendTPLists,
-         SourceLocation EllipsisLoc = {});
+         MutableArrayRef<TemplateParameterList *> Params, FriendUnion Friend,
+         SourceLocation FriendLoc);
 
-  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                                unsigned NumFriendTPLists);
+  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
 
-  SourceRange getSourceRange() const override LLVM_READONLY;
+  /// If this friend declaration names a templated type (or
+  /// a dependent member type of a templated type), return that
+  /// type;  otherwise return null.
+  TypeSourceInfo *getFriendType() const {
+    return Friend.dyn_cast<TypeSourceInfo*>();
+  }
 
-  TemplateName getFriendTemplateName() const { return Template; }
+  /// If this friend declaration names a templated function (or
+  /// a member function of a templated type), return that type;
+  /// otherwise return null.
+  NamedDecl *getFriendDecl() const {
+    return Friend.dyn_cast<NamedDecl*>();
+  }
 
-  FriendTemplateEntityKind getFriendKind() const {
-    if (getFriendType())
-      return FriendTemplateEntityKind::Type;
-    if (Template.isNull())
-      return FriendTemplateEntityKind::Decl;
-    return FriendTemplateEntityKind::Template;
+  /// Retrieves the location of the 'friend' keyword.
+  SourceLocation getFriendLoc() const {
+    return FriendLoc;
   }
 
-  NamedDecl *getFriendDecl() const override {
-    if (NamedDecl *ND = Friend.dyn_cast<NamedDecl *>())
-      return ND;
-    return Template.getAsTemplateDecl();
+  TemplateParameterList *getTemplateParameterList(unsigned i) const {
+    assert(i <= NumParams);
+    return Params[i];
   }
 
-  ArrayRef<TemplateParameterList *> getTemplateParameterLists() const {
-    return ArrayRef(getTrailingObjects(), NumTPLists);
+  unsigned getNumTemplateParameters() const {
+    return NumParams;
   }
 
   // Implement isa/cast/dyncast/etc.

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

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index d435cda19fa49..12204dcfb5938 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1739,15 +1739,17 @@ DEF_TRAVERSE_DECL(FriendDecl, {
 })
 
 DEF_TRAVERSE_DECL(FriendTemplateDecl, {
-  const TemplateName Template = D->getFriendTemplateName();
   if (D->getFriendType())
     TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
-  else if (!Template.isNull())
-    TRY_TO(TraverseTemplateName(Template));
   else
     TRY_TO(TraverseDecl(D->getFriendDecl()));
-  for (TemplateParameterList *TPL : D->getTemplateParameterLists())
-    TRY_TO(TraverseTemplateParameterListHelper(TPL));
+  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));
+    }
+  }
 })
 
 DEF_TRAVERSE_DECL(LinkageSpecDecl, {})

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

diff  --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 114c6ae5282ef..8d1dc4936cc72 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 CXXExpansionStmt : DeclNode<Decl>, DeclContext;

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index a6cc17980e2c2..d44f69a815784 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1206,6 +1206,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 7c1e61120b0f2..b314c17ad27bd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1934,6 +1934,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">,
@@ -1943,16 +1953,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_pack_indexing_in_friend : Error<
-  "a pack indexing specifier cannot be used in a nested name specifier of a "
-  "friend declaration">;
-def err_dependent_friend_not_member_of_template_spec : Error<
-  "%0 does not name a class template">;
-def err_dependent_friend_not_member : Error<
-  "friend declaration does not name a member of a class template specialization">;
-def err_dependent_friend_undeduced_params : Error<
-  "%select{template parameter|template parameters}0 of friend declaration "
-  "cannot be deduced from %1">;
 
 def err_invalid_base_in_interface : Error<
   "interface type cannot inherit from "
@@ -5325,16 +5325,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
 // 
diff erent types with the same name.  Remove this note when the above note
 // can handle that case properly.
 def note_ovl_candidate_non_deduced_mismatch_qualified : Note<
     "candidate template ignored: could not match %q0 against %q1">;
-def note_friend_template_non_deduced_mismatch_qualified : Note<
-    "candidate friend template ignored: could not match %q0 against %q1">;
 
 // Note that we don't treat templates 
diff erently for this diagnostic.
 def note_ovl_candidate_arity : Note<"candidate "

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

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

diff  --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h
index 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 974761b236411..b582cbdadc070 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2123,14 +2123,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,
-  FTDK_Dependent = 3,
-};
-
 /// Kinds of cleanup objects owned by ExprWithCleanups.
 enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };
 

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 725ebbd6ec987..01c5385ff5bb5 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -185,8 +185,6 @@ namespace clang {
       return Importer.importInto(To, From);
     }
 
-    Expected<FriendDecl::FriendUnion> importFriendUnion(FriendDecl *D);
-
     // Use this to import pointers of specific type.
     template <typename ImportT>
     [[nodiscard]] Error importInto(ImportT *&To, ImportT *From) {
@@ -541,7 +539,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);
@@ -4395,15 +4392,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);
 }
 
@@ -4423,28 +4424,8 @@ static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
   }
 
   assert(FriendPosition && "Friend decl not found in own parent.");
-  return {FriendCount, *FriendPosition};
-}
-
-Expected<FriendDecl::FriendUnion>
-ASTNodeImporter::importFriendUnion(FriendDecl *D) {
-  if (NamedDecl *FriendD = D->getFriendDecl()) {
-    NamedDecl *ToFriendD;
-    if (Error Err = importInto(ToFriendD, FriendD))
-      return std::move(Err);
 
-    if (FriendD->getFriendObjectKind() != Decl::FOK_None &&
-        !FriendD->isInIdentifierNamespace(Decl::IDNS_NonMemberOperator))
-      ToFriendD->setObjectOfFriendDecl(false);
-
-    return ToFriendD;
-  }
-
-  // The friend is a type, not a decl.
-  auto TSIOrErr = import(D->getFriendType());
-  if (TSIOrErr)
-    return *TSIOrErr;
-  return TSIOrErr.takeError();
+  return {FriendCount, *FriendPosition};
 }
 
 ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
@@ -4473,10 +4454,32 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
 
   // Not found. Create it.
   // The declarations will be put into order later by ImportDeclContext.
-  auto ToFUOrErr = importFriendUnion(D);
-  if (!ToFUOrErr)
-    return ToFUOrErr.takeError();
-  FriendDecl::FriendUnion ToFU = *ToFUOrErr;
+  FriendDecl::FriendUnion ToFU;
+  if (NamedDecl *FriendD = D->getFriendDecl()) {
+    NamedDecl *ToFriendD;
+    if (Error Err = importInto(ToFriendD, FriendD))
+      return std::move(Err);
+
+    if (FriendD->getFriendObjectKind() != Decl::FOK_None &&
+        !(FriendD->isInIdentifierNamespace(Decl::IDNS_NonMemberOperator)))
+      ToFriendD->setObjectOfFriendDecl(false);
+
+    ToFU = ToFriendD;
+  } else { // The friend is a type, not a decl.
+    if (auto TSIOrErr = import(D->getFriendType()))
+      ToFU = *TSIOrErr;
+    else
+      return TSIOrErr.takeError();
+  }
+
+  SmallVector<TemplateParameterList *, 1> ToTPLists(D->NumTPLists);
+  auto **FromTPLists = D->getTrailingObjects();
+  for (unsigned I = 0; I < D->NumTPLists; I++) {
+    if (auto ListOrErr = import(FromTPLists[I]))
+      ToTPLists[I] = *ListOrErr;
+    else
+      return ListOrErr.takeError();
+  }
 
   auto LocationOrErr = import(D->getLocation());
   if (!LocationOrErr)
@@ -4491,7 +4494,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());
@@ -4500,74 +4503,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;
-  if (D->getFriendKind() !=
-      FriendTemplateDecl::FriendTemplateEntityKind::Template) {
-    auto ToFUOrErr = importFriendUnion(D);
-    if (!ToFUOrErr)
-      return ToFUOrErr.takeError();
-    ToFU = *ToFUOrErr;
-  }
-
-  TemplateName ToTemplate;
-  const TemplateName FromTemplate = D->getFriendTemplateName();
-  if (!FromTemplate.isNull()) {
-    if (Error Err = importInto(ToTemplate, FromTemplate))
-      return std::move(Err);
-  }
-
-  ArrayRef<TemplateParameterList *> FromTPLs = D->getTemplateParameterLists();
-  SmallVector<TemplateParameterList *, 1> ToTPLs(FromTPLs.size());
-  if (Error Err = ImportContainerChecked(FromTPLs, ToTPLs))
-    return std::move(Err);
-
-  auto LocationOrErr = import(D->getLocation());
-  if (!LocationOrErr)
-    return LocationOrErr.takeError();
-
-  auto FriendLocOrErr = import(D->getFriendLoc());
-  if (!FriendLocOrErr)
-    return FriendLocOrErr.takeError();
-
-  auto EllipsisLocOrErr = import(D->getEllipsisLoc());
-  if (!EllipsisLocOrErr)
-    return EllipsisLocOrErr.takeError();
-
-  FriendTemplateDecl *FTD;
-  if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
-                              *LocationOrErr, ToFU, *FriendLocOrErr, ToTPLs,
-                              *EllipsisLocOrErr, ToTemplate))
-    return FTD;
-
-  FTD->setAccess(D->getAccess());
-  FTD->setLexicalDeclContext(LexicalDC);
-  LexicalDC->addDeclInternal(FTD);
-  return FTD;
-}
-
 ExpectedDecl ASTNodeImporter::VisitObjCIvarDecl(ObjCIvarDecl *D) {
   // Import the major distinguishing characteristics of an ivar.
   DeclContext *DC, *LexicalDC;

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

diff  --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index d730b4f00fba2..6bfc2eb62b284 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -13,18 +13,28 @@
 
 #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>
 
 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) ||
@@ -36,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 {
@@ -59,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 1312ffd080342..4be3e977b815e 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);
@@ -890,17 +889,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);
   }
@@ -909,27 +915,6 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
     Out << "...";
 }
 
-void DeclPrinter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
-  for (TemplateParameterList *TPL : D->getTemplateParameterLists())
-    printTemplateParameters(TPL);
-
-  TemplateName TN = D->getFriendTemplateName();
-  if (D->getFriendType() || TN.isNull()) {
-    VisitFriendDecl(D);
-  } else {
-    Out << "friend ";
-    if (auto *CTD =
-            dyn_cast_if_present<ClassTemplateDecl>(TN.getAsTemplateDecl()))
-      Out << CTD->getTemplatedDecl()->getKindName() << ' ';
-    TN.print(Out, Policy,
-             Policy.SuppressScope ? TemplateName::Qualified::None
-                                  : TemplateName::Qualified::AsWritten);
-
-    if (D->isPackExpansion())
-      Out << "...";
-  }
-}
-
 void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
   prettyPrintPragmas(D);
   // FIXME: add printing of pragma attributes if required.

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

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

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

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

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

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

diff  --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index f1e328ccba426..0f291900d5612 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1178,9 +1178,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);
@@ -1189,7 +1188,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 cc2af8feb3794..9b4f59613d33b 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -21,8 +21,6 @@
 #include "clang/Sema/DelayedDiagnostic.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
-#include "clang/Sema/Template.h"
-#include "clang/Sema/TemplateDeduction.h"
 #include "llvm/ADT/ScopeExit.h"
 
 using namespace clang;
@@ -274,163 +272,7 @@ struct AccessTarget : public AccessedEntity {
   mutable const CXXRecordDecl *InstanceContext;
   const CXXRecordDecl *DeclaringClass;
 };
-} // namespace
 
-static CanQual<FunctionProtoType> GetCanonicalFunctionProto(ASTContext &Context,
-                                                            QualType Ty) {
-  return Context.getCanonicalType(Ty)->getAs<FunctionProtoType>();
-}
-
-static CanQual<FunctionProtoType>
-GetCanonicalFunctionProto(ASTContext &Context, const FunctionDecl *FD) {
-  return GetCanonicalFunctionProto(Context, FD->getType());
-}
-
-static const TemplateSpecializationType *
-GetQualifierClassTemplateSpecializationType(ASTContext &Context,
-                                            NestedNameSpecifier NNS) {
-  if (!NNS || NNS.getKind() != NestedNameSpecifier::Kind::Type)
-    return nullptr;
-
-  QualType Ty(NNS.getAsType(), 0);
-  if (const auto *ICNT = Ty->getAs<InjectedClassNameType>())
-    Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(Context);
-
-  const auto *TST = Ty->getAsNonAliasTemplateSpecializationType();
-  if (TST && isa_and_nonnull<ClassTemplateDecl>(
-                 TST->getTemplateName().getAsTemplateDecl()))
-    return TST;
-
-  return nullptr;
-}
-
-static FunctionTemplateDecl *TryGetFunctionTemplateDecl(FunctionDecl *FD) {
-  if (auto *FTD = FD->getPrimaryTemplate())
-    return FTD->getCanonicalDecl();
-
-  if (auto *FTD = FD->getDescribedFunctionTemplate())
-    return FTD->getCanonicalDecl();
-
-  if (FunctionDecl *Pattern =
-          FD->getTemplateInstantiationPattern(/*ForDefinition=*/false)) {
-    if (auto *FTD = Pattern->getDescribedFunctionTemplate())
-      return FTD->getCanonicalDecl();
-    if (auto *FTD = Pattern->getPrimaryTemplate())
-      return FTD->getCanonicalDecl();
-  }
-
-  return nullptr;
-}
-
-static ClassTemplateDecl *GetClassTemplatePattern(ClassTemplateDecl *CTD) {
-  while (ClassTemplateDecl *Pattern = CTD->getInstantiatedFromMemberTemplate())
-    CTD = Pattern;
-  return CTD;
-}
-
-static ClassTemplateDecl *GetClassTemplateDecl(CXXRecordDecl *RD) {
-  if (auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD))
-    return Spec->getSpecializedTemplate();
-  return RD->getDescribedClassTemplate();
-}
-
-static TemplateParameterList *
-SubstTemplateParameterList(Sema &S, TemplateParameterList *TPL, DeclContext *DC,
-                           const MultiLevelTemplateArgumentList &Args) {
-  TemplateParameterList *InstTPL =
-      S.SubstTemplateParams(TPL, DC, Args,
-                            /*EvaluateConstraints=*/false);
-  if (!InstTPL || !TPL->getRequiresClause())
-    return InstTPL;
-
-  ExprResult InstRequiresClause =
-      S.SubstConstraintExprWithoutSatisfaction(TPL->getRequiresClause(), Args);
-  if (!InstRequiresClause.isUsable())
-    return nullptr;
-
-  return TemplateParameterList::Create(
-      S.Context, InstTPL->getTemplateLoc(), InstTPL->getLAngleLoc(),
-      InstTPL->asArray(), InstTPL->getRAngleLoc(), InstRequiresClause.get());
-}
-
-static AccessResult
-DeduceTemplateArguments(Sema &S, FriendTemplateDecl *FTD, DeclContext *DC,
-                        const TemplateSpecializationType *TST,
-                        ArrayRef<TemplateParameterList *> TPLs,
-                        TemplateSpecCandidateSet *FailedTSC,
-                        MultiLevelTemplateArgumentList &DeducedArgs) {
-  const auto *CandidateRD = dyn_cast<CXXRecordDecl>(DC);
-  if (!CandidateRD)
-    return AR_inaccessible;
-
-  ClassTemplateDecl *CandidateCTD = CandidateRD->getDescribedClassTemplate();
-  ArrayRef<TemplateArgument> CandidateArgs;
-  if (CandidateCTD) {
-    CandidateArgs = CandidateCTD->getInjectedTemplateArgs(S.Context);
-  } else {
-    const auto *CandidateSpec =
-        dyn_cast<ClassTemplateSpecializationDecl>(CandidateRD);
-    if (!CandidateSpec)
-      return AR_inaccessible;
-    CandidateCTD = CandidateSpec->getSpecializedTemplate();
-    CandidateArgs = CandidateSpec->getTemplateArgs().asArray();
-  }
-
-  auto *PatternCTD = dyn_cast_if_present<ClassTemplateDecl>(
-      TST->getTemplateName().getAsTemplateDecl());
-  if (!PatternCTD || !declaresSameEntity(GetClassTemplatePattern(CandidateCTD),
-                                         GetClassTemplatePattern(PatternCTD)))
-    return AR_inaccessible;
-
-  if (S.DeduceTemplateArguments(FTD, PatternCTD, CandidateCTD, TPLs,
-                                TST->template_arguments(), CandidateArgs,
-                                FTD->getLocation(), FailedTSC, DeducedArgs))
-    return AR_accessible;
-
-  return CandidateRD->isDependentContext() ? AR_dependent : AR_inaccessible;
-}
-
-class FriendTemplateMatchContext {
-  Sema &S;
-  FriendTemplateDecl *FTD;
-  Sema::InstantiatingTemplate Inst;
-  TemplateDeductionInfo Info;
-  MultiLevelTemplateArgumentList DeducedArgs;
-  Sema::SFINAETrap Trap;
-  LocalInstantiationScope InstantiationScope;
-  AccessResult Result = AR_inaccessible;
-
-public:
-  FriendTemplateMatchContext(Sema &S, FriendTemplateDecl *FTD)
-      : S(S), FTD(FTD), Inst(S, FTD->getLocation(), FTD),
-        Info(FTD->getLocation()), Trap(S, Info), InstantiationScope(S) {}
-
-  AccessResult deduce(DeclContext *DC, const TemplateSpecializationType *TST,
-                      ArrayRef<TemplateParameterList *> TPLs,
-                      TemplateSpecCandidateSet *FailedTSC) {
-    if (Inst.isInvalid())
-      return Result = AR_inaccessible;
-    return Result = DeduceTemplateArguments(S, FTD, DC, TST, TPLs, FailedTSC,
-                                            DeducedArgs);
-  }
-
-  AccessResult getAccessResult() const { return Result; }
-  MultiLevelTemplateArgumentList &getDeducedArgs() { return DeducedArgs; }
-
-  bool hasDeducedArgs() const { return Result == AR_accessible; }
-  bool hasErrorOccurred() const { return Trap.hasErrorOccurred(); }
-};
-
-static bool HasSameFunctionType(Sema &S, QualType FriendType,
-                                QualType ContextType, SourceLocation Loc) {
-  if (!S.Context.hasSameFunctionTypeIgnoringExceptionSpec(FriendType,
-                                                          ContextType))
-    return false;
-
-  const auto *FriendFPT = FriendType->castAs<FunctionProtoType>();
-  const auto *ContextFPT = ContextType->castAs<FunctionProtoType>();
-  return !S.CheckEquivalentExceptionSpec(S.PDiag(), S.PDiag(), FriendFPT, Loc,
-                                         ContextFPT, Loc);
 }
 
 /// Checks whether one class might instantiate to the other.
@@ -442,12 +284,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;
@@ -505,7 +343,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;
 
@@ -524,7 +364,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;
 
@@ -535,66 +375,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());
 }
 
@@ -655,7 +478,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.
@@ -669,7 +492,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.
@@ -691,7 +515,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;
   }
 
@@ -710,390 +534,21 @@ 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;
 
-    if (Friend == FTD)
-      return AR_accessible;
-
-    if (EC.isDependent() && MightInstantiateTo(S.Context, FTD, Friend))
-      OnFailure = AR_dependent;
-  }
-
-  return OnFailure;
-}
-
-static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
-                                  NamedDecl *ND) {
-  ND = cast<NamedDecl>(ND->getCanonicalDecl());
-  if (ClassTemplateDecl *CTD = dyn_cast<ClassTemplateDecl>(ND))
-    return MatchesFriend(S, EC, CTD);
-
-  if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
-    return MatchesFriend(S, EC, FTD);
-
-  if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(ND))
-    return MatchesFriend(S, EC, RD);
-
-  assert(isa<FunctionDecl>(ND) && "unknown friend decl kind");
-  return MatchesFriend(S, EC, cast<FunctionDecl>(ND));
-}
-
-static AccessResult MatchesFriend(Sema &S, FriendTemplateDecl *FTD,
-                                  DeclarationName FriendName,
-                                  TagTypeKind FriendTagKind,
-                                  ClassTemplateDecl *ContextCTD,
-                                  const TemplateSpecializationType *FriendTST,
-                                  ArrayRef<TemplateParameterList *> TPLs,
-                                  TemplateParameterList *MemberTPL,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  if (FriendName != ContextCTD->getDeclName())
-    return AR_inaccessible;
-
-  if ((FriendTagKind == TagTypeKind::Union) !=
-      ContextCTD->getTemplatedDecl()->isUnion())
-    return AR_inaccessible;
-
-  DeclContext *ContextDC = ContextCTD->getDeclContext();
-  AccessResult OnFailure =
-      ContextDC->isDependentContext() ? AR_dependent : AR_inaccessible;
-
-  FriendTemplateMatchContext FTMC(S, FTD);
-  AccessResult Result = FTMC.deduce(ContextDC, FriendTST, TPLs, FailedTSC);
-  if (!FTMC.hasDeducedArgs())
-    return Result;
-
-  TemplateParameterList *InstTPL = SubstTemplateParameterList(
-      S, MemberTPL, ContextDC, FTMC.getDeducedArgs());
-  if (!InstTPL || FTMC.hasErrorOccurred())
-    return OnFailure;
-
-  Sema::TemplateCompareNewDeclInfo FriendInfo(
-      ContextDC, FTD->getLexicalDeclContext(), FTD->getLocation());
-  if (S.TemplateParameterListsAreEqual(
-          FriendInfo, InstTPL, ContextCTD, ContextCTD->getTemplateParameters(),
-          /*Complain=*/false, Sema::TPL_TemplateMatch))
-    return AR_accessible;
-  return OnFailure;
-}
-
-static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
-                                  FriendTemplateDecl *FTD,
-                                  ClassTemplateDecl *FriendCTD,
-                                  NestedNameSpecifier Qualifier,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  const auto *FriendTST =
-      GetQualifierClassTemplateSpecializationType(S.Context, Qualifier);
-  if (!FriendTST)
-    return MatchesFriend(S, EC, FriendCTD);
-
-  ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
-
-  AccessResult OnFailure = AR_inaccessible;
-  for (CXXRecordDecl *ContextRD : EC.Records) {
-    ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD);
-    if (!ContextCTD)
-      continue;
-
-    AccessResult Result =
-        MatchesFriend(S, FTD, FriendCTD->getDeclName(),
-                      FriendCTD->getTemplatedDecl()->getTagKind(), ContextCTD,
-                      FriendTST, TPLs.drop_back(), TPLs.back(), FailedTSC);
-    if (Result == AR_accessible)
-      return AR_accessible;
-    if (Result == AR_dependent)
-      OnFailure = AR_dependent;
-  }
-
-  return OnFailure;
-}
-
-static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
-                                  FriendTemplateDecl *FTD,
-                                  TemplateName FriendTemplate,
-                                  ClassTemplateDecl *FriendCTD,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  NestedNameSpecifier Qualifier = FriendTemplate.getQualifier();
-  if (FriendTemplate.getAsUsingShadowDecl())
-    Qualifier = FriendCTD->getTemplatedDecl()->getQualifier();
-  return MatchesFriend(S, EC, FTD, FriendCTD, Qualifier, FailedTSC);
-}
-
-static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
-                                  FriendTemplateDecl *FTD,
-                                  ClassTemplateDecl *FriendCTD,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  return MatchesFriend(S, EC, FTD, FriendCTD,
-                       FriendCTD->getTemplatedDecl()->getQualifier(),
-                       FailedTSC);
-}
-
-static AccessResult MatchesFriend(Sema &S, FriendTemplateDecl *FTD,
-                                  FunctionDecl *FriendFD,
-                                  FunctionDecl *ContextFD,
-                                  const TemplateSpecializationType *FriendTST,
-                                  ArrayRef<TemplateParameterList *> TPLs,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  if (!MightInstantiateTo(S.Context, ContextFD->getDeclName(),
-                          FriendFD->getDeclName()))
-    return AR_inaccessible;
-
-  FunctionTemplateDecl *FriendTemplate =
-      FriendFD->getDescribedFunctionTemplate();
-  FunctionTemplateDecl *ContextTemplate = TryGetFunctionTemplateDecl(ContextFD);
-
-  if (FriendTemplate && !ContextTemplate)
-    return AR_inaccessible;
-
-  DeclContext *ContextDC = ContextFD->getDeclContext();
-  AccessResult OnFailure =
-      ContextDC->isDependentContext() ? AR_dependent : AR_inaccessible;
-
-  FriendTemplateMatchContext FTMC(S, FTD);
-  AccessResult Result = FTMC.deduce(ContextDC, FriendTST, TPLs, FailedTSC);
-  if (!FTMC.hasDeducedArgs())
-    return Result;
-
-  Sema::TemplateCompareNewDeclInfo FriendInfo(
-      ContextDC, FTD->getLexicalDeclContext(), FTD->getLocation());
-  if (FriendTemplate) {
-    TemplateParameterList *InstTPL =
-        SubstTemplateParameterList(S, FriendTemplate->getTemplateParameters(),
-                                   ContextDC, FTMC.getDeducedArgs());
-    if (!InstTPL || !S.TemplateParameterListsAreEqual(
-                        FriendInfo, InstTPL, ContextTemplate,
-                        ContextTemplate->getTemplateParameters(),
-                        /*Complain=*/false, Sema::TPL_TemplateMatch))
-      return OnFailure;
-
-    ContextFD = ContextTemplate->getTemplatedDecl();
-  }
-
-  Sema::ContextRAII SavedContext(S, FTD->getDeclContext());
-  QualType InstFriendType =
-      S.SubstType(FriendFD->getType(), FTMC.getDeducedArgs(),
-                  FriendFD->getLocation(), FriendFD->getDeclName());
-  SavedContext.pop();
-  if (InstFriendType.isNull() || FTMC.hasErrorOccurred())
-    return OnFailure;
-
-  if (ContextTemplate && !FriendTemplate) {
-    AccessResult OnSpecializationFailure =
-        ContextFD->isDependentContext() ? AR_dependent : OnFailure;
-    const ASTTemplateArgumentListInfo *ArgsWritten =
-        FriendFD->getTemplateSpecializationArgsAsWritten();
-    TemplateArgumentListInfo InstArgs;
-    if (ArgsWritten) {
-      InstArgs.setLAngleLoc(ArgsWritten->getLAngleLoc());
-      InstArgs.setRAngleLoc(ArgsWritten->getRAngleLoc());
-      if (S.SubstTemplateArguments(ArgsWritten->arguments(),
-                                   FTMC.getDeducedArgs(), InstArgs))
-        return OnSpecializationFailure;
-    }
-
-    FunctionDecl *ContextSpecialization = nullptr;
-    TemplateDeductionInfo FunctionInfo(FTD->getLocation());
-    if (S.DeduceTemplateArguments(
-            ContextTemplate, ArgsWritten ? &InstArgs : nullptr, InstFriendType,
-            ContextSpecialization,
-            FunctionInfo) != TemplateDeductionResult::Success ||
-        !ContextSpecialization || FTMC.hasErrorOccurred() ||
-        !declaresSameEntity(ContextSpecialization, ContextFD))
-      return OnSpecializationFailure;
-
-    ContextFD = ContextSpecialization;
-  }
-
-  if (!HasSameFunctionType(S, InstFriendType, ContextFD->getType(),
-                           FTD->getLocation()) ||
-      FTMC.hasErrorOccurred())
-    return OnFailure;
-
-  if (!FriendTemplate)
-    return AR_accessible;
-
-  AssociatedConstraint FriendRequiresClause =
-      FriendFD->getTrailingRequiresClause();
-  AssociatedConstraint ContextRequiresClause =
-      ContextFD->getTrailingRequiresClause();
-  if (FriendRequiresClause.isNull() != ContextRequiresClause.isNull())
-    return AR_inaccessible;
-
-  if (!FriendRequiresClause)
-    return AR_accessible;
-
-  ExprResult InstFriendRequiresClause =
-      S.SubstConstraintExprWithoutSatisfaction(
-          const_cast<Expr *>(FriendRequiresClause.ConstraintExpr),
-          FTMC.getDeducedArgs());
+    FTD = FTD->getCanonicalDecl();
 
-  if (!InstFriendRequiresClause.isUsable())
-    return OnFailure;
-
-  if (!S.AreConstraintExpressionsEqual(
-          ContextFD, ContextRequiresClause.ConstraintExpr, FriendInfo,
-          InstFriendRequiresClause.get()))
-    return OnFailure;
-  return FTMC.hasErrorOccurred() ? AR_inaccessible : AR_accessible;
-}
-
-static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
-                                  FriendTemplateDecl *FTD,
-                                  FunctionDecl *FriendFD,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
-      S.Context, FriendFD->getQualifier());
-  if (!FriendTST)
-    return AR_inaccessible;
-
-  ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
-
-  AccessResult OnFailure = AR_inaccessible;
-  for (FunctionDecl *ContextFD : EC.Functions) {
-    AccessResult Result =
-        MatchesFriend(S, FTD, FriendFD, ContextFD, FriendTST, TPLs, FailedTSC);
-    if (Result == AR_accessible)
+    if (Friend == FTD)
       return AR_accessible;
 
-    if (Result == AR_dependent)
+    if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend))
       OnFailure = AR_dependent;
   }
-  return OnFailure;
-}
-
-static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
-                                  FriendTemplateDecl *FTD, NamedDecl *Friend,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  TemplateName FriendTemplate = FTD->getFriendTemplateName();
-  if (auto *FriendCTD = dyn_cast_if_present<ClassTemplateDecl>(
-          FriendTemplate.getAsTemplateDecl()))
-    return MatchesFriend(S, EC, FTD, FriendTemplate, FriendCTD, FailedTSC);
-  if (auto *FriendCTD = dyn_cast<ClassTemplateDecl>(Friend))
-    return MatchesFriend(S, EC, FTD, FriendCTD, FailedTSC);
-  if (FunctionDecl *FriendFD = Friend->getAsFunction())
-    return MatchesFriend(S, EC, FTD, FriendFD, FailedTSC);
-  return MatchesFriend(S, EC, Friend);
-}
-
-static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
-                                  FriendTemplateDecl *FTD,
-                                  TypeSourceInfo *FriendTSI,
-                                  TemplateSpecCandidateSet *FailedTSC) {
-  QualType FriendType = FriendTSI->getType();
-  if (!FriendType->isDependentType())
-    return MatchesFriend(S, EC, S.Context.getCanonicalType(FriendType));
-
-  AccessResult OnFailure = AR_inaccessible;
-  if (auto FriendTSTL =
-          FriendTSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>()) {
-    const auto *FriendTST = FriendTSTL.getTypePtr();
-    const auto *FriendQTST = GetQualifierClassTemplateSpecializationType(
-        S.Context, FriendTSTL.getQualifierLoc().getNestedNameSpecifier());
-    if (!FriendQTST)
-      return OnFailure;
-
-    ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
-
-    TemplateName FriendTemplate = FriendTST->getTemplateName();
-    DeclarationName FriendName;
-    if (TemplateDecl *TD = FriendTemplate.getAsTemplateDecl())
-      FriendName = TD->getDeclName();
-    else if (DependentTemplateName *DTN =
-                 FriendTemplate.getAsDependentTemplateName())
-      FriendName = DTN->getName().getIdentifier();
-
-    TagTypeKind FriendTagKind =
-        TypeWithKeyword::getTagTypeKindForKeyword(FriendTST->getKeyword());
-
-    for (CXXRecordDecl *ContextRD : EC.Records) {
-      ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD);
-      if (!ContextCTD)
-        continue;
-
-      if (FriendName && ContextCTD->getDeclName() != FriendName)
-        continue;
-
-      if ((FriendTagKind == TagTypeKind::Union) !=
-          ContextCTD->getTemplatedDecl()->isUnion())
-        continue;
-
-      FriendTemplateMatchContext FTMC(S, FTD);
-      AccessResult Result =
-          FTMC.deduce(ContextRD->getDeclContext(), FriendQTST, TPLs, FailedTSC);
-      if (!FTMC.hasDeducedArgs()) {
-        if (Result == AR_dependent)
-          OnFailure = AR_dependent;
-        continue;
-      }
-
-      TypeSourceInfo *InstFriendTSI =
-          S.SubstFriendType(FriendTSI, FTMC.getDeducedArgs(),
-                            FTD->getLocation(), DeclarationName());
-      if (InstFriendTSI && !FTMC.hasErrorOccurred() &&
-          S.Context.hasSameType(InstFriendTSI->getType(),
-                                S.Context.getCanonicalTagType(ContextRD)))
-        return AR_accessible;
-
-      if (ContextRD->isDependentContext())
-        OnFailure = AR_dependent;
-    }
-
-    return OnFailure;
-  }
-
-  const auto *FriendDNT = FriendType->getAs<DependentNameType>();
-  if (!FriendDNT)
-    return OnFailure;
-
-  const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
-      S.Context, FriendDNT->getQualifier());
-  if (!FriendTST)
-    return OnFailure;
 
-  ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
-
-  TagTypeKind FriendTagKind =
-      TypeWithKeyword::getTagTypeKindForKeyword(FriendDNT->getKeyword());
-  for (CXXRecordDecl *ContextRD : EC.Records) {
-    if (ContextRD->getDeclName() != FriendDNT->getIdentifier())
-      continue;
-
-    if (ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD)) {
-      if (FTD->getFriendTemplateName().isNull()) {
-        if (FailedTSC) {
-          MultiLevelTemplateArgumentList DeducedArgs;
-          DeduceTemplateArguments(S, FTD, ContextCTD->getDeclContext(),
-                                  FriendTST, TPLs, FailedTSC, DeducedArgs);
-        }
-        continue;
-      }
-
-      AccessResult Result = MatchesFriend(
-          S, FTD, FriendDNT->getIdentifier(), FriendTagKind, ContextCTD,
-          FriendTST, TPLs.drop_back(), TPLs.back(), FailedTSC);
-      if (Result == AR_accessible)
-        return AR_accessible;
-      if (Result == AR_dependent)
-        OnFailure = AR_dependent;
-      continue;
-    }
-
-    if (!FTD->getFriendTemplateName().isNull())
-      continue;
-
-    if ((FriendTagKind == TagTypeKind::Union) != ContextRD->isUnion())
-      continue;
-
-    MultiLevelTemplateArgumentList DeducedArgs;
-    AccessResult Result =
-        DeduceTemplateArguments(S, FTD, ContextRD->getDeclContext(), FriendTST,
-                                TPLs, FailedTSC, DeducedArgs);
-    if (Result == AR_accessible)
-      return AR_accessible;
-    if (Result == AR_dependent)
-      OnFailure = AR_dependent;
-  }
   return OnFailure;
 }
 
@@ -1102,47 +557,40 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
 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 (isa<ClassTemplateDecl>(Friend))
+    return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
+
+  if (isa<FunctionTemplateDecl>(Friend))
+    return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
 
-  if (TypeSourceInfo *TSI = FTD->getFriendType())
-    return MatchesFriend(S, EC, FTD, TSI, FailedTSC);
+  if (isa<CXXRecordDecl>(Friend))
+    return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
 
-  NamedDecl *Friend = FTD->getFriendDecl();
-  assert(Friend && "friend template must name a type or declaration");
-  return MatchesFriend(S, EC, FTD, Friend, FailedTSC);
+  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;
 
@@ -1166,7 +614,6 @@ namespace {
 struct ProtectedFriendContext {
   Sema &S;
   const EffectiveContext &EC;
-  TemplateSpecCandidateSet *FailedTSC;
   const CXXRecordDecl *NamingClass;
   bool CheckDependent;
   bool EverDependent;
@@ -1176,19 +623,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;
@@ -1275,9 +721,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);
@@ -1285,20 +731,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");
 
@@ -1418,8 +863,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;
@@ -1427,7 +871,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;
@@ -1540,8 +984,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;
@@ -1737,8 +1180,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:
@@ -1781,8 +1223,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;
@@ -1886,9 +1327,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();
 
@@ -1900,8 +1341,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
@@ -1930,7 +1370,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
@@ -1982,30 +1422,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;
@@ -2015,20 +1450,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.
@@ -2523,8 +1944,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/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 3b3c9a476421d..bd239adb0f215 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -4497,7 +4497,6 @@ CXCursorKind clang::getCursorKindForDecl(const Decl *D) {
   case Decl::StaticAssert:
     return CXCursor_StaticAssert;
   case Decl::Friend:
-  case Decl::FriendTemplate:
     return CXCursor_FriendDecl;
   case Decl::TranslationUnit:
     return CXCursor_TranslationUnit;

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

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

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

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

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 81bc6c68f377d..97744b6e8836b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12420,9 +12420,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*>()) ||
@@ -12669,10 +12668,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;
         }
@@ -12688,9 +12684,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;
   }
@@ -13641,12 +13635,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() {
@@ -13698,7 +13690,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 dff681454ae5a..643392833759d 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1977,9 +1977,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))
@@ -3048,8 +3053,6 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier(
       if (TemplateDecl *Template = Name.getAsTemplateDecl()) {
         ExpectedTemplateParams = Template->getTemplateParameters();
         NeedNonemptyTemplateHeader = true;
-      } else if (Name.getAsDependentTemplateName()) {
-        NeedNonemptyTemplateHeader = true;
       } else if (Name.getAsDeducedTemplateName()) {
         // FIXME:  We actually could/should check the template arguments here
         // against the corresponding template parameter list.
@@ -9578,15 +9581,6 @@ bool Sema::CheckDependentFunctionTemplateSpecialization(
 
   bool IsFriend = FD->getFriendObjectKind() != Decl::FOK_None;
   if (Previous.empty()) {
-    NestedNameSpecifier FriendQualifier = FD->getQualifier();
-    if (IsFriend && FriendQualifier.isDependent() &&
-        FriendQualifier.getKind() == NestedNameSpecifier::Kind::Type &&
-        FriendQualifier.getAsType()->getAs<TemplateSpecializationType>()) {
-      FD->setDependentTemplateSpecialization(
-          Context, Previous.asUnresolvedSet(), ExplicitTemplateArgs);
-      return false;
-    }
-
     Diag(FD->getLocation(), diag::err_dependent_function_template_spec_no_match)
         << IsFriend;
     for (auto &P : DiscardedCandidates)
@@ -11209,10 +11203,6 @@ TypeResult Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
   if (!NNS)
     return true;
 
-  if (TUK == TagUseKind::Friend &&
-      DiagnosePackIndexingInFriendNNS(NameLoc, SS.getWithLocInContext(Context)))
-    return true;
-
   TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
 
   if (TUK == TagUseKind::Declaration || TUK == TagUseKind::Definition) {

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

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

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index a21f65f687568..79d106168e9d0 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2006,247 +2006,68 @@ Decl *TemplateDeclInstantiator::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
   return IndirectField;
 }
 
-static std::optional<TemplateName>
-LookupFriendTemplateName(Sema &SemaRef, NestedNameSpecifierLoc QualifierLoc,
-                         DeclarationName Name, SourceLocation NameLoc,
-                         bool HasTemplateKeyword, bool RequireClassTemplate) {
-  if (!QualifierLoc)
-    return TemplateName();
-
-  CXXScopeSpec SS;
-  SS.Adopt(QualifierLoc);
-
-  DeclContext *DC = SemaRef.computeDeclContext(SS, /*EnteringContext=*/true);
-  if (!DC) {
-    if (QualifierLoc.getNestedNameSpecifier().isDependent())
-      return TemplateName();
-    return std::nullopt;
-  }
-
-  bool IsDependentContext = DC->isDependentContext();
-  if (!IsDependentContext && SemaRef.RequireCompleteDeclContext(SS, DC))
-    return std::nullopt;
-
-  LookupResult Result(SemaRef, Name, NameLoc, Sema::LookupOrdinaryName,
-                      SemaRef.forRedeclarationInCurContext());
-  if (!SemaRef.LookupQualifiedName(Result, DC)) {
-    if (RequireClassTemplate && !IsDependentContext) {
-      SemaRef.Diag(NameLoc, diag::err_no_member_template)
-          << Name << DC << QualifierLoc.getSourceRange();
-      return std::nullopt;
-    }
-    return TemplateName();
-  }
-
-  if (Result.isAmbiguous())
-    return std::nullopt;
-
-  auto *CTD = Result.getAsSingle<ClassTemplateDecl>();
-  if (!CTD) {
-    if (RequireClassTemplate && !IsDependentContext) {
-      SemaRef.Diag(NameLoc, diag::err_redefinition_
diff erent_kind) << Name;
-      SemaRef.Diag(
-          Result.getRepresentativeDecl()->getUnderlyingDecl()->getLocation(),
-          diag::note_previous_definition);
-      return std::nullopt;
-    }
-    return TemplateName();
-  }
-
-  auto *FoundUsingShadow =
-      dyn_cast<UsingShadowDecl>(Result.getRepresentativeDecl());
-
-  return SemaRef.Context.getQualifiedTemplateName(
-      QualifierLoc.getNestedNameSpecifier(), HasTemplateKeyword,
-      FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(CTD));
-}
-
-TypeSourceInfo *
-Sema::SubstFriendType(TypeSourceInfo *TSI,
-                      const MultiLevelTemplateArgumentList &TemplateArgs,
-                      SourceLocation Loc, DeclarationName Entity) {
-  TemplateSpecializationTypeLoc TSTL =
-      TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
-  NestedNameSpecifierLoc QualifierLoc =
-      TSTL ? TSTL.getQualifierLoc() : NestedNameSpecifierLoc();
-  if (!TSTL || !QualifierLoc ||
-      !QualifierLoc.getNestedNameSpecifier().isDependent())
-    return SubstType(TSI, TemplateArgs, Loc, Entity);
-
-  const auto *FriendTST = TSTL.getTypePtr();
-  auto *FriendCTD = dyn_cast_or_null<ClassTemplateDecl>(
-      FriendTST->getTemplateName().getAsTemplateDecl());
-  if (!FriendCTD)
-    return SubstType(TSI, TemplateArgs, Loc, Entity);
-
-  QualifierLoc = SubstNestedNameSpecifierLoc(QualifierLoc, TemplateArgs);
-  if (!QualifierLoc)
-    return nullptr;
-
-  std::optional<TemplateName> InstTemplate = LookupFriendTemplateName(
-      *this, QualifierLoc, FriendCTD->getDeclName(), TSTL.getTemplateNameLoc(),
-      TSTL.getTemplateKeywordLoc().isValid(),
-      /*RequireClassTemplate=*/false);
-  if (!InstTemplate)
-    return nullptr;
-  if (InstTemplate->isNull())
-    return SubstType(TSI, TemplateArgs, Loc, Entity);
-
-  SmallVector<TemplateArgumentLoc, 4> FriendArgLocs;
-  for (unsigned I = 0, N = TSTL.getNumArgs(); I != N; ++I)
-    FriendArgLocs.push_back(TSTL.getArgLoc(I));
-
-  TemplateArgumentListInfo InstArgs(TSTL.getLAngleLoc(), TSTL.getRAngleLoc());
-  if (SubstTemplateArguments(FriendArgLocs, TemplateArgs, InstArgs))
-    return nullptr;
-
-  QualType InstTy =
-      CheckTemplateIdType(FriendTST->getKeyword(), *InstTemplate,
-                          TSTL.getTemplateNameLoc(), InstArgs,
-                          /*Scope=*/nullptr, /*ForNestedNameSpecifier=*/false);
-  if (InstTy.isNull())
-    return nullptr;
-
-  TypeLocBuilder TLB;
-  TLB.push<TemplateSpecializationTypeLoc>(InstTy).set(
-      TSTL.getElaboratedKeywordLoc(), QualifierLoc,
-      TSTL.getTemplateKeywordLoc(), TSTL.getTemplateNameLoc(), InstArgs);
-  return TLB.getTypeSourceInfo(Context, InstTy);
-}
-
-struct SubstitutedFriend {
-  TypeSourceInfo *TypeInfo = nullptr;
-  TemplateName TemplateName;
-
-  bool empty() const { return !TypeInfo && TemplateName.isNull(); }
-};
-
-static std::optional<SubstitutedFriend>
-SubstFriendTemplateType(Sema &SemaRef, TypeSourceInfo *TSI,
-                        TemplateName FriendTemplate,
-                        const MultiLevelTemplateArgumentList &TemplateArgs,
-                        SourceLocation Loc, DeclarationName Entity) {
-  NestedNameSpecifierLoc QualifierLoc = TSI->getTypeLoc().getPrefix();
-  NestedNameSpecifierLoc InstQualifierLoc = QualifierLoc;
-  if (QualifierLoc && QualifierLoc.getNestedNameSpecifier().isDependent()) {
-    InstQualifierLoc =
-        SemaRef.SubstNestedNameSpecifierLoc(QualifierLoc, TemplateArgs);
-    if (!InstQualifierLoc ||
-        SemaRef.CheckDependentFriend(Loc, InstQualifierLoc, /*TPLs=*/{},
-                                     /*IsInstantiation=*/true))
-      return std::nullopt;
-  }
-
-  TemplateName InstFriendTemplate;
-  if (!FriendTemplate.isNull()) {
-    auto DNTL = TSI->getTypeLoc().getAs<DependentNameTypeLoc>();
-    assert(DNTL && "friend class template must have a dependent name type");
-
-    std::optional<TemplateName> InstTemplate = LookupFriendTemplateName(
-        SemaRef, InstQualifierLoc, DNTL.getTypePtr()->getIdentifier(),
-        DNTL.getNameLoc(), /*HasTemplateKeyword=*/false,
-        /*RequireClassTemplate=*/true);
-    if (!InstTemplate)
-      return std::nullopt;
-    if (!InstTemplate->isNull())
-      return SubstitutedFriend{nullptr, *InstTemplate};
-
-    auto *DTN = FriendTemplate.getAsDependentTemplateName();
-    assert(DTN && "unresolved friend template must have a dependent name");
-    InstFriendTemplate = SemaRef.Context.getDependentTemplateName(
-        {InstQualifierLoc.getNestedNameSpecifier(), DTN->getName(),
-         DTN->hasTemplateKeyword()});
-  }
-
-  TypeSourceInfo *InstType =
-      SemaRef.SubstFriendType(TSI, TemplateArgs, Loc, Entity);
-  if (!InstType)
-    return std::nullopt;
-  return SubstitutedFriend{InstType, InstFriendTemplate};
-}
-
-bool TemplateDeclInstantiator::InstantiateFriendPackExpansion(FriendDecl *D) {
-  TypeSourceInfo *TSI = D->getFriendType();
-  assert(TSI && "friend pack expansion must name a type");
-
-  const auto *FTD = dyn_cast<FriendTemplateDecl>(D);
-  ArrayRef<TemplateParameterList *> TPLs;
-  if (FTD)
-    TPLs = FTD->getTemplateParameterLists();
-
-  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
-  SemaRef.collectUnexpandedParameterPacks(TSI->getTypeLoc(), Unexpanded);
-  assert(!Unexpanded.empty() && "Pack expansion without packs");
-
-  bool ShouldExpand = true;
-  bool RetainExpansion = false;
-  UnsignedOrNone NumExpansions = std::nullopt;
-  if (SemaRef.CheckParameterPacksForExpansion(
-          D->getEllipsisLoc(), D->getSourceRange(), Unexpanded, TemplateArgs,
-          /*FailOnPackProducingTemplates=*/true, ShouldExpand, RetainExpansion,
-          NumExpansions))
-    return true;
-
-  assert(!RetainExpansion &&
-         "should never retain an expansion for a friend declaration");
-
-  if (!ShouldExpand)
-    return false;
-
-  for (unsigned I = 0; I != *NumExpansions; I++) {
-    Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
-    LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
-    SmallVector<TemplateParameterList *, 1> InstTPLs;
-    if (SubstTemplateParameterLists(TPLs, InstTPLs))
-      return true;
-
-    std::optional<SubstitutedFriend> InstFriend;
-    if (FTD)
-      InstFriend = SubstFriendTemplateType(
-          SemaRef, TSI, FTD->getFriendTemplateName(), TemplateArgs,
-          D->getEllipsisLoc(), DeclarationName());
-    else if (TypeSourceInfo *InstType = SemaRef.SubstFriendType(
-                 TSI, TemplateArgs, D->getEllipsisLoc(), DeclarationName()))
-      InstFriend = SubstitutedFriend{InstType, {}};
-    if (!InstFriend || InstFriend->empty())
-      return true;
-
-    FriendDecl *FD;
-    if (FTD) {
-      FriendDecl::FriendUnion ToFriend =
-          InstFriend->TypeInfo ? FriendDecl::FriendUnion(InstFriend->TypeInfo)
-                               : FriendDecl::FriendUnion();
-      FD = FriendTemplateDecl::Create(
-          SemaRef.Context, Owner, D->getLocation(), ToFriend, D->getFriendLoc(),
-          InstTPLs, /*EllipsisLoc=*/{}, InstFriend->TemplateName);
+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 {
-      assert(InstTPLs.empty() && "unexpected template parameter lists");
-      assert(InstFriend->TemplateName.isNull() &&
-             "non-template friend resolved to a class template");
-      FD = FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
-                              InstFriend->TypeInfo, D->getFriendLoc());
-    }
-
-    FD->setAccess(AS_public);
-    Owner->addDecl(FD);
-  }
+      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;
 
-  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);
+          }
 
-Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
-  if (TypeSourceInfo *Ty = D->getFriendType()) {
-    if (D->isPackExpansion() && InstantiateFriendPackExpansion(D))
-      return nullptr;
+          // Just drop this node; we have no use for it anymore.
+          return nullptr;
+        }
+      }
 
-    TypeSourceInfo *InstTy = SemaRef.SubstFriendType(
-        Ty, TemplateArgs, D->getLocation(), DeclarationName());
+      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;
   }
@@ -2265,6 +2086,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;
 }
@@ -3029,12 +2851,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
     if (!QualifierLoc)
       return nullptr;
   }
-  if (isFriend &&
-      (FunctionTemplate || !D->getTemplateParameterLists().empty()) &&
-      D->getQualifier().isDependent() &&
-      SemaRef.CheckDependentFriend(D->getLocation(), QualifierLoc,
-                                   /*TPLs=*/{}, /*IsInstantiation=*/true))
-    return nullptr;
 
   AssociatedConstraint TrailingRequiresClause = D->getTrailingRequiresClause();
 
@@ -3449,23 +3265,17 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
     if (!QualifierLoc)
       return nullptr;
   }
-  if (isFriend &&
-      (FunctionTemplate || !D->getTemplateParameterLists().empty()) &&
-      D->getQualifier().isDependent() &&
-      SemaRef.CheckDependentFriend(D->getLocation(), QualifierLoc,
-                                   /*TPLs=*/{}, /*IsInstantiation=*/true))
-    return nullptr;
 
   DeclContext *DC = Owner;
   if (isFriend) {
-    if (QualifierLoc && !QualifierLoc.getNestedNameSpecifier().isDependent()) {
+    if (QualifierLoc) {
       CXXScopeSpec SS;
       SS.Adopt(QualifierLoc);
       DC = SemaRef.computeDeclContext(SS);
 
       if (DC && SemaRef.RequireCompleteDeclContext(SS, DC))
         return nullptr;
-    } else if (!QualifierLoc) {
+    } else {
       DC = SemaRef.FindInstantiatedContext(D->getLocation(),
                                            D->getDeclContext(),
                                            TemplateArgs);
@@ -3613,22 +3423,12 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
         return nullptr;
     }
 
-    if (QualifierLoc && QualifierLoc.getNestedNameSpecifier().isDependent()) {
-      if (SemaRef.CheckDependentFunctionTemplateSpecialization(
-              Method,
-              DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
-              Previous))
-        Method->setInvalidDecl();
-    } else {
-      if (Previous.empty())
-        SemaRef.LookupQualifiedName(Previous, DC);
-      if (SemaRef.CheckFunctionTemplateSpecialization(
-              Method,
-              DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
-              Previous))
-        Method->setInvalidDecl();
-      IsExplicitSpecialization = true;
-    }
+    if (SemaRef.CheckFunctionTemplateSpecialization(
+            Method, DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
+            Previous))
+      Method->setInvalidDecl();
+
+    IsExplicitSpecialization = true;
   } else if (const ASTTemplateArgumentListInfo *ArgsWritten =
                  D->getTemplateSpecializationArgsAsWritten()) {
     SemaRef.LookupQualifiedName(Previous, DC);
@@ -4972,48 +4772,14 @@ Decl *TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D)
 }
 
 Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
-  ArrayRef<TemplateParameterList *> FriendTPLs = D->getTemplateParameterLists();
+  // 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();
 
-  TypeSourceInfo *FriendTSI = D->getFriendType();
-  if (FriendTSI && D->isPackExpansion() && InstantiateFriendPackExpansion(D))
-    return nullptr;
-
-  LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
-  SmallVector<TemplateParameterList *, 1> InstTPLs;
-  if (SubstTemplateParameterLists(FriendTPLs, InstTPLs))
-    return nullptr;
-
-  FriendDecl::FriendUnion ToFriend;
-  TemplateName ToTemplate;
-  if (FriendTSI) {
-    std::optional<SubstitutedFriend> Substituted = SubstFriendTemplateType(
-        SemaRef, FriendTSI, D->getFriendTemplateName(), TemplateArgs,
-        D->getLocation(), DeclarationName());
-    if (!Substituted || Substituted->empty())
-      return nullptr;
-    ToFriend = Substituted->TypeInfo;
-    ToTemplate = Substituted->TemplateName;
-  } else if (!D->getFriendTemplateName().isNull()) {
-    if (auto *InstTemplate =
-            cast_or_null<TemplateDecl>(Visit(D->getFriendDecl())))
-      ToTemplate = TemplateName(InstTemplate);
-    else
-      return nullptr;
-  } else {
-    if (auto *InstFriendDecl =
-            cast_or_null<NamedDecl>(Visit(D->getFriendDecl())))
-      ToFriend = InstFriendDecl;
-    else
-      return nullptr;
-  }
-
-  FriendTemplateDecl *InstFriend = FriendTemplateDecl::Create(
-      SemaRef.Context, Owner, D->getLocation(), ToFriend, D->getFriendLoc(),
-      InstTPLs, /*EllipsisLoc=*/{}, ToTemplate);
-
-  InstFriend->setAccess(AS_public);
-  Owner->addDecl(InstFriend);
-  return InstFriend;
+  return nullptr;
 }
 
 Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) {
@@ -5158,33 +4924,6 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
   return InstL;
 }
 
-bool TemplateDeclInstantiator::SubstTemplateParameterLists(
-    ArrayRef<TemplateParameterList *> TPLs,
-    SmallVectorImpl<TemplateParameterList *> &InstTPLs) {
-  llvm::SaveAndRestore RAII(EvaluateConstraints, false);
-  for (TemplateParameterList *L : TPLs) {
-    TemplateParameterList *InstParams = SubstTemplateParams(L);
-    if (!InstParams)
-      return true;
-
-    if (Expr *RequiresClause = L->getRequiresClause()) {
-      ExprResult InstRequiresClause =
-          SemaRef.SubstConstraintExprWithoutSatisfaction(RequiresClause,
-                                                         TemplateArgs);
-      if (!InstRequiresClause.isUsable())
-        return true;
-
-      InstParams = TemplateParameterList::Create(
-          SemaRef.Context, InstParams->getTemplateLoc(),
-          InstParams->getLAngleLoc(), InstParams->asArray(),
-          InstParams->getRAngleLoc(), InstRequiresClause.get());
-    }
-
-    InstTPLs.push_back(InstParams);
-  }
-  return false;
-}
-
 TemplateParameterList *
 Sema::SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
                           const MultiLevelTemplateArgumentList &TemplateArgs,
@@ -5427,8 +5166,9 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
   return InstPartialSpec;
 }
 
-TypeSourceInfo *TemplateDeclInstantiator::SubstFunctionType(
-    FunctionDecl *D, SmallVectorImpl<ParmVarDecl *> &Params) {
+TypeSourceInfo*
+TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
+                              SmallVectorImpl<ParmVarDecl *> &Params) {
   TypeSourceInfo *OldTInfo = D->getTypeSourceInfo();
   assert(OldTInfo && "substituting function without type source info");
   assert(Params.empty() && "parameter vector is non-empty at start");
@@ -5497,10 +5237,8 @@ TypeSourceInfo *TemplateDeclInstantiator::SubstFunctionType(
           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 e973b7ae71954..bec62e7c55606 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2408,37 +2408,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();
-  auto Kind = static_cast<FriendTemplateDeclKind>(Record.readInt());
-  switch (Kind) {
-  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();
-    assert(D->Template.getAsTemplateDecl() &&
-           "friend template name must resolve to a template declaration");
-    D->Friend = D->Template.getAsTemplateDecl();
-    break;
-  case FTDK_Dependent:
+  else
     D->Friend = readTypeSourceInfo();
-    D->Template = Record.readTemplateName();
-    break;
-  }
-  D->NextFriend = readDeclID().getRawValue();
   D->FriendLoc = readSourceLocation();
-  D->EllipsisLoc = readSourceLocation();
 }
 
 void ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
@@ -4115,11 +4104,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 6f67acf9a6e7e..d8f5ed9afdde1 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1831,6 +1831,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);
@@ -1838,47 +1841,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 template parameter lists here to simplify memory
-  // allocation during deserialization.
-  Record.push_back(D->NumTPLists);
   VisitDecl(D);
-  for (TemplateParameterList *TPL : D->getTemplateParameterLists())
-    Record.AddTemplateParameterList(TPL);
-  FriendTemplateDeclKind Kind;
-  if (D->getFriendType()) {
-    Kind = D->Template.isNull() ? FTDK_Type : FTDK_Dependent;
-  } else if (D->Template.isNull()) {
-    assert(D->getFriendDecl());
-    Kind = FTDK_Decl;
-  } else {
-    Kind = FTDK_Template;
-  }
-  Record.push_back(Kind);
-  switch (Kind) {
-  case FTDK_Type:
-    Record.AddTypeSourceInfo(D->getFriendType());
-    break;
-  case FTDK_Dependent:
-    Record.AddTypeSourceInfo(D->getFriendType());
-    Record.AddTemplateName(D->Template);
-    break;
-  case FTDK_Decl:
+  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());
-    break;
-  case FTDK_Template:
-    Record.AddTemplateName(D->Template);
-    break;
-  }
-  Record.AddDeclRef(D->getNextFriend());
-  Record.AddSourceLocation(D->FriendLoc);
-  Record.AddSourceLocation(D->EllipsisLoc);
+  else
+    Record.AddTypeSourceInfo(D->getFriendType());
+  Record.AddSourceLocation(D->getFriendLoc());
   Code = serialization::DECL_FRIEND_TEMPLATE;
 }
 

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

diff  --git a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
index 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 32ff4d72d30f3..646b82eb40fd7 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -375,7 +375,7 @@ namespace cwg1837 { // cwg1837: 3.3
 #endif
 } // namespace cwg1837
 
-namespace cwg1862 { // cwg1862: 24
+namespace cwg1862 { // cwg1862: no
 template<class T>
 struct A {
   struct B {
@@ -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 {{'A<T>::D' does not name a class template}}
+  // 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: both definition of 'D::g' are not friends, so they don't have access to 'private_int'
 template<class T>
 void A<T>::D::g() { (void)c.private_int; }
-// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
-//   expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
 void A<int>::D::g() { (void)c.private_int; }
-// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
-//   expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
 
 template<class T>
 T A<T>::h() { (void)c.private_int; }

diff  --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 53ff58c7cbffd..8162f9caa8f15 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -94,26 +94,26 @@ namespace cwg1909 { // cwg1909: 3.7
   };
 } // namespace cwg1909
 
-namespace cwg1918 { // cwg1918: 24
+namespace cwg1918 { // cwg1918: no
 template<typename T> struct A {
   class B {
     class C {};
   };
 };
 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 {{'A<T>::B' does not name a class template}}
+  // 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
@@ -159,7 +159,7 @@ derived d2(42, 9);
 #endif
 } // namespace cwg1941
 
-namespace cwg1945 { // cwg1945: 24
+namespace cwg1945 { // cwg1945: no
 template<typename T> struct A {
   class B {
     class C {};
@@ -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 {{'A<T>::B' does not name a class template}}
+  // 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 7352da80e7b41..02c8f30249683 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -178,21 +178,17 @@ namespace cwg2858 { // cwg2858: 19
 
 template<typename... Ts>
 struct A {
+  // FIXME: The nested-name-specifier in the following friend declarations are declarative,
+  // but we don't treat them as such (yet).
   friend void Ts...[0]::f();
-  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
   template<typename U>
   friend void Ts...[0]::g();
-  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
 
   friend struct Ts...[0]::B;
-  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
+  // 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 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
-
-  template<typename U>
-  friend struct Ts...[0]::template B<U>::C;
-  // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
+  // 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 eb1898b56762d..451554a36d70d 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -396,7 +396,7 @@ namespace cwg637 { // cwg637: 3.0
   }
 } // namespace cwg637
 
-namespace cwg638 { // cwg638: 24
+namespace cwg638 { // cwg638: no
   template<typename T> struct A {
     struct B;
     void f();
@@ -407,29 +407,26 @@ namespace cwg638 { // cwg638: 24
   };
 
   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 {{'A<T>::C' does not name a class template}}
+    // 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/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
index 3ebd32f713371..b8092afaffef3 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
@@ -387,29 +387,3 @@ template <class T> class A {
   friend void foo<T>() {} // expected-error {{ambiguous}} expected-error{{no candidate function template was found for dependent friend function template specialization}}
 };
 }
-
-namespace test19 {
-template <class V> class B;
-
-template <class T> struct A {
-  template <class U> static void f(B<T> &);
-};
-
-template <class V> class B {
-  int n; // #test19-B-n
-  friend void A<V>::template f<int>(B<V> &);
-};
-
-template <class T>
-template <class U>
-void A<T>::f(B<T> &x) {
-  x.n = 0;
-  // expected-error at -1 {{'n' is a private member of 'test19::B<long>'}}
-  //   expected-note@#test19-B-n {{implicitly declared private here}}
-}
-
-template class B<long>;
-template void A<long>::f<int>(B<long> &);
-template void A<long>::f<double>(B<long> &);
-// expected-note at -1 {{in instantiation of function template specialization 'test19::A<long>::f<double>' requested here}}
-}

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

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

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

diff  --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 951eb47099919..621ae912c1ac9 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -30,7 +30,6 @@ struct E { template<class T> class Nested; };
 template<class... Ts> // expected-note {{template parameter is declared here}}
 struct VS {
   friend Ts...;
-  friend Ts...[0];
 
   friend class Ts...; // expected-error {{declaration of 'Ts' shadows template parameter}}
   // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
@@ -57,15 +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...;
-
-  template<class T>
-  friend class Ts...[0]::Nested...;
-  // expected-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
+  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/cxx2c-variadic-friends.cpp b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
index 910b7be9190d7..0b01907c8c722 100644
--- a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
+++ b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
@@ -154,60 +154,3 @@ void f() {
 }
 }
 
-namespace GH104057 {
-template <class... Vs> class C;
-
-template <class T> struct A {
-  template <class U> struct B {
-    static void f(C<int, long> &);
-  };
-};
-
-template <class... Vs> class C {
-  int n; // #GH104057-C-n
-
-  template <class T>
-  friend struct A<T>::template B<Vs>...;
-};
-
-template <class T>
-template <class U>
-void A<T>::B<U>::f(C<int, long> &x) {
-  x.n = 0;
-  // expected-error at -1 {{'n' is a private member of 'GH104057::C<int, long>'}}
-  //   expected-note@#GH104057-C-n {{implicitly declared private here}}
-}
-
-template struct A<char>::B<int>;
-template struct A<char>::B<long>;
-template struct A<char>::B<double>;
-// expected-note at -1 {{in instantiation of member function 'GH104057::A<char>::B<double>::f' requested here}}
-template <class...> class D;
-
-template <class T> struct E { // expected-note {{candidate friend template ignored: could not match 'long' against 'char'}}
-  template <class U> struct F {
-    static void f(D<int, long> &);
-  };
-};
-
-template <class... Vs> class D {
-  int n; // #GH104057-D-n
-
-  template <class U>
-  friend struct E<Vs>::F...;
-};
-
-template <class T>
-template <class U>
-void E<T>::F<U>::f(D<int, long> &x) {
-  x.n = 0;
-  // expected-error at -1 {{'n' is a private member of 'GH104057::D<int, long>'}}
-  //   expected-note@#GH104057-D-n {{implicitly declared private here}}
-}
-
-template struct D<int, long>;
-template struct E<int>::F<char>;
-template struct E<long>::F<double>;
-template struct E<char>::F<float>;
-// expected-note at -1 {{in instantiation of member function 'GH104057::E<char>::F<float>::f' requested here}}
-}

diff  --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index 975803ed26bc1..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>
@@ -22,16 +22,15 @@ struct X {
     };
   };
 
-  template <class U> // expected-note {{non-deducible template parameter 'U'}}
-  template <class V> // expected-note {{non-deducible template parameter 'V'}}
-  template <class W> // expected-note {{non-deducible template parameter 'W'}}
-  template <class X> // expected-note {{non-deducible template parameter 'X'}}
+  template <class U>
+  template <class V>
+  template <class W>
+  template <class X>
   template <class Y>
   template <class Z>
-  friend void A<U>::template B<V>::template C<W>::template D<X>::template E<Y>::operator+=(Z);
-  // expected-error at -1 {{template parameters of friend declaration cannot be deduced from 'A<U>::template B<V>::template C<W>::template D<X>::template E<Y>'}}
+  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;
+  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 7fc0f043dd9a4..11287aa773b1b 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -566,61 +566,3 @@ struct Test {
 };
 
 }
-
-namespace GH104057 {
-template <class T> concept A = requires { typename T::type; }; // #GH104057_A
-
-struct B {
-  using type = int;
-};
-struct C {};
-
-template <class T> struct D {
-  static void f()
-    requires A<T>; // #GH104057_D_f
-};
-
-class E {
-  static int n;
-  template <A T> friend void D<T>::f();
-};
-
-template <class T> struct F {
-  template <A U> // #GH104057_F_TPL
-  static void f() // #GH104057_F_f
-    requires A<T>;
-};
-
-class G {
-  static int n;
-  template <A T> template <A U>
-  friend void F<T>::f()
-    requires A<T>;
-};
-
-template <class T>
-void D<T>::f() requires A<T> {
-  E::n = 0;
-}
-
-template <class T>
-template <A U>
-void F<T>::f() requires A<T> {
-  G::n = 0;
-}
-
-void test() {
-  D<B>::f();
-  D<C>::f();
-  // expected-error at -1 {{invalid reference to function 'f': constraints not satisfied}}
-  //   expected-note@#GH104057_D_f {{because 'GH104057::C' does not satisfy 'A'}}
-  //   expected-note@#GH104057_A {{because 'typename T::type' would be invalid: no type named 'type' in 'GH104057::C'}}
-
-  F<B>::f<B>();
-  F<B>::f<C>();
-  // expected-error at -1 {{no matching function for call to 'f'}}
-  //   expected-note@#GH104057_F_f {{candidate template ignored: constraints not satisfied}}
-  //   expected-note@#GH104057_F_TPL {{because 'GH104057::C' does not satisfy 'A'}}
-  //   expected-note@#GH104057_A {{because 'typename T::type' would be invalid: no type named 'type' in 'GH104057::C'}}
-}
-} // namespace GH104057

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

diff  --git a/clang/test/SemaTemplate/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp
index 05ab57cebfec8..2b5a226c3b33c 100644
--- a/clang/test/SemaTemplate/friend-template.cpp
+++ b/clang/test/SemaTemplate/friend-template.cpp
@@ -111,31 +111,6 @@ namespace test5 {
   };
 }
 
-namespace GH104057 {
-template <class T>
-struct A { // #GH104057-A
-  template <class> struct B;
-
-private:
-  static void f(); // #GH104057-A-f
-  template <class U> friend struct A<U *>::B;
-};
-
-template <class T>
-template <class U> struct A<T>::B {
-  static void g() {
-    A<int>::f();
-    // expected-error at -1 {{'f' is a private member of 'GH104057::A<int>'}}
-    //   expected-note@#GH104057-A-f {{declared private here}}
-    //   expected-note@#GH104057-A {{candidate friend template ignored: could not match 'U *' against 'double'}}
-  }
-};
-
-void test() {
-  A<double>::B<int>::g(); // expected-note {{in instantiation of member function 'GH104057::A<double>::B<int>::g' requested here}}
-}
-}
-
 // PR6022
 namespace PR6022 {
   template <class T1, class T2 , class T3  > class A;
@@ -260,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 {

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

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

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

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

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

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


        


More information about the llvm-branch-commits mailing list