https://github.com/a-tarasyuk updated 
https://github.com/llvm/llvm-project/pull/191268

>From 8d30639cfaf8910e7a4052b8dfc2bbf38e686332 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <[email protected]>
Date: Thu, 9 Apr 2026 18:04:29 +0300
Subject: [PATCH] [Clang] support friend declarations with a dependent
 nested-name-specifier

---
 clang/include/clang/AST/DeclFriend.h          | 117 ++-----
 clang/include/clang/AST/DeclTemplate.h        |  67 ++--
 clang/include/clang/AST/RecursiveASTVisitor.h |   5 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +
 clang/include/clang/Sema/Sema.h               |  29 +-
 clang/include/clang/Sema/Template.h           |   3 +-
 clang/lib/AST/ASTImporter.cpp                 | 105 +++++-
 clang/lib/AST/ASTStructuralEquivalence.cpp    |  18 +
 clang/lib/AST/DeclFriend.cpp                  |  64 ++--
 clang/lib/AST/DeclPrinter.cpp                 |  17 +-
 clang/lib/AST/DeclTemplate.cpp                |  39 ++-
 clang/lib/Sema/Sema.cpp                       |   5 +-
 clang/lib/Sema/SemaAccess.cpp                 | 331 +++++++++++++++---
 clang/lib/Sema/SemaDeclCXX.cpp                | 209 +++++++----
 clang/lib/Sema/SemaTemplate.cpp               |  24 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |   4 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  99 ++++--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 115 +++---
 clang/lib/Serialization/ASTReaderDecl.cpp     |  14 +-
 clang/lib/Serialization/ASTWriterDecl.cpp     |  12 +-
 .../class.access/class.friend/p3-cxx0x.cpp    |   4 +-
 clang/test/CXX/drs/cwg18xx.cpp                |  11 +-
 clang/test/CXX/drs/cwg19xx.cpp                |   8 +-
 clang/test/CXX/drs/cwg6xx.cpp                 |  17 +-
 .../CXX/temp/temp.decls/temp.friend/p5.cpp    | 151 +++++++-
 .../CXX/temp/temp.decls/temp.friend/p6.cpp    |  24 ++
 .../SemaCXX/many-template-parameter-lists.cpp |   6 +-
 clang/test/SemaTemplate/GH71595.cpp           |   6 +-
 clang/test/SemaTemplate/ctad.cpp              |   2 +-
 29 files changed, 1044 insertions(+), 464 deletions(-)
 create mode 100644 clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp

diff --git a/clang/include/clang/AST/DeclFriend.h 
b/clang/include/clang/AST/DeclFriend.h
index 1f8c210263677..2cd5a1af17fd8 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -15,20 +15,14 @@
 #define LLVM_CLANG_AST_DECLFRIEND_H
 
 #include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/ExternalASTSource.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/LLVM.h"
-#include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
-#include "llvm/Support/TrailingObjects.h"
 #include <cassert>
-#include <iterator>
 
 namespace clang {
 
@@ -49,9 +43,7 @@ class ASTContext;
 /// @endcode
 ///
 /// The semantic context of a friend decl is its declaring class.
-class FriendDecl final
-    : public Decl,
-      private llvm::TrailingObjects<FriendDecl, TemplateParameterList *> {
+class FriendDecl : public Decl {
   LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION();
 
 public:
@@ -61,46 +53,35 @@ class FriendDecl final
   friend class CXXRecordDecl;
   friend class CXXRecordDecl::friend_iterator;
 
-  // The declaration that's a friend of this class.
-  FriendUnion Friend;
-
-  // A pointer to the next friend in the sequence.
-  LazyDeclPtr NextFriend;
-
-  // Location of the 'friend' specifier.
-  SourceLocation FriendLoc;
-
   // Location of the '...', if present.
   SourceLocation EllipsisLoc;
 
+  SourceLocation FriendLoc;
+
   /// 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());
-  }
+protected:
+  // The declaration that's a friend of this class.
+  FriendUnion Friend;
 
-  FriendDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
-      : Decl(Decl::Friend, Empty), UnsupportedFriend(false),
-        NumTPLists(NumFriendTypeTPLists) {}
+  LazyDeclPtr NextFriend;
+
+  FriendDecl(Kind K, DeclContext *DC, SourceLocation L, FriendUnion Friend,
+             SourceLocation FriendL, SourceLocation EllipsisLoc = {})
+      : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FriendL),
+        UnsupportedFriend(false), Friend(Friend), NextFriend() {}
+
+  FriendDecl(Kind K, EmptyShell Empty)
+      : Decl(K, Empty), UnsupportedFriend(false) {}
 
   FriendDecl *getNextFriend() {
-    if (!NextFriend.isOffset())
-      return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
-    return getNextFriendSlowCase();
+    if (NextFriend.isOffset())
+      return getNextFriendSlowCase();
+    return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
   }
 
   FriendDecl *getNextFriendSlowCase();
@@ -109,14 +90,11 @@ class FriendDecl final
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
   friend class ASTNodeImporter;
-  friend TrailingObjects;
 
-  static FriendDecl *
-  Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend_,
-         SourceLocation FriendL, SourceLocation EllipsisLoc = {},
-         ArrayRef<TemplateParameterList *> FriendTypeTPLists = {});
-  static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                        unsigned FriendTypeNumTPLists);
+  static FriendDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L,
+                            FriendUnion Friend_, SourceLocation FriendL,
+                            SourceLocation EllipsisLoc = {});
+  static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
 
   /// If this friend declaration names an (untemplated but possibly
   /// dependent) type, return the type; otherwise return null.  This
@@ -126,58 +104,18 @@ class FriendDecl final
     return Friend.dyn_cast<TypeSourceInfo*>();
   }
 
-  unsigned getFriendTypeNumTemplateParameterLists() const {
-    return NumTPLists;
-  }
-
-  TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const {
-    return getTrailingObjects(NumTPLists)[N];
-  }
-
   /// If this friend declaration doesn't name a type, return the inner
   /// declaration.
   NamedDecl *getFriendDecl() const {
     return Friend.dyn_cast<NamedDecl *>();
   }
 
-  /// Retrieves the location of the 'friend' keyword.
-  SourceLocation getFriendLoc() const {
-    return FriendLoc;
-  }
-
   /// Retrieves the location of the '...', if present.
   SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
 
-  /// Retrieves the source range for the friend declaration.
-  SourceRange getSourceRange() const override LLVM_READONLY {
-    if (TypeSourceInfo *TInfo = getFriendType()) {
-      SourceLocation StartL = (NumTPLists == 0)
-                                  ? getFriendLoc()
-                                  : getTrailingObjects()[0]->getTemplateLoc();
-      SourceLocation EndL = isPackExpansion() ? getEllipsisLoc()
-                                              : 
TInfo->getTypeLoc().getEndLoc();
-      return SourceRange(StartL, EndL);
-    }
-
-    if (isPackExpansion())
-      return SourceRange(getFriendLoc(), getEllipsisLoc());
-
-    if (NamedDecl *ND = getFriendDecl()) {
-      if (const auto *FD = dyn_cast<FunctionDecl>(ND))
-        return FD->getSourceRange();
-      if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
-        return FTD->getSourceRange();
-      if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
-        return CTD->getSourceRange();
-      if (const auto *DD = dyn_cast<DeclaratorDecl>(ND)) {
-        if (DD->getOuterLocStart() != DD->getInnerLocStart())
-          return DD->getSourceRange();
-      }
-      return SourceRange(getFriendLoc(), ND->getEndLoc());
-    }
-
-    return SourceRange(getFriendLoc(), getLocation());
-  }
+  SourceLocation getFriendLoc() const { return FriendLoc; }
+
+  SourceRange getSourceRange() const override LLVM_READONLY;
 
   /// Determines if this friend kind is unsupported.
   bool isUnsupportedFriend() const {
@@ -191,9 +129,10 @@ class FriendDecl final
 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
-  static bool classofKind(Kind K) { return K == Decl::Friend; }
+  static bool classofKind(Kind K) {
+    return K == Decl::Friend || K == Decl::FriendTemplate;
+  }
 };
-
 /// An iterator over the friend declarations of a class.
 class CXXRecordDecl::friend_iterator {
   friend class CXXRecordDecl;
diff --git a/clang/include/clang/AST/DeclTemplate.h 
b/clang/include/clang/AST/DeclTemplate.h
index a4a1bb9c13c79..1653c26910b8b 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -19,6 +19,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/Redeclarable.h"
 #include "clang/AST/TemplateBase.h"
@@ -2473,42 +2474,43 @@ class ClassTemplateDecl : public 
RedeclarableTemplateDecl {
 ///
 /// \note This class is not currently in use.  All of the above
 /// will yield a FriendDecl, not a FriendTemplateDecl.
-class FriendTemplateDecl : public Decl {
-  virtual void anchor();
-
-public:
-  using FriendUnion = llvm::PointerUnion<NamedDecl *,TypeSourceInfo *>;
+class FriendTemplateDecl final
+    : public FriendDecl,
+      private llvm::TrailingObjects<FriendTemplateDecl,
+                                    TemplateParameterList *> {
+  void anchor() override;
 
 private:
-  // The number of template parameters;  always non-zero.
-  unsigned NumParams = 0;
+  unsigned NumTPLists : 31;
 
-  // The parameter list.
-  TemplateParameterList **Params = nullptr;
-
-  // The declaration that's a friend of this class.
-  FriendUnion Friend;
-
-  // Location of the 'friend' specifier.
-  SourceLocation FriendLoc;
-
-  FriendTemplateDecl(DeclContext *DC, SourceLocation Loc,
-                     TemplateParameterList **Params, unsigned NumParams,
-                     FriendUnion Friend, SourceLocation FriendLoc)
-      : Decl(Decl::FriendTemplate, DC, Loc), NumParams(NumParams),
-        Params(Params), Friend(Friend), FriendLoc(FriendLoc) {}
+  FriendTemplateDecl(DeclContext *DC, SourceLocation Loc, FriendUnion Friend,
+                     SourceLocation FriendLoc, SourceLocation EllipsisLoc,
+                     ArrayRef<TemplateParameterList *> FriendTypeTPLists)
+      : FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc,
+                   EllipsisLoc),
+        NumTPLists(FriendTypeTPLists.size()) {
+    llvm::copy(FriendTypeTPLists, getTrailingObjects());
+  }
 
-  FriendTemplateDecl(EmptyShell Empty) : Decl(Decl::FriendTemplate, Empty) {}
+  FriendTemplateDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
+      : FriendDecl(Decl::FriendTemplate, Empty),
+        NumTPLists(NumFriendTypeTPLists) {}
 
 public:
   friend class ASTDeclReader;
+  friend class ASTDeclWriter;
+  friend TrailingObjects;
 
   static FriendTemplateDecl *
   Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
-         MutableArrayRef<TemplateParameterList *> Params, FriendUnion Friend,
-         SourceLocation FriendLoc);
+         FriendUnion Friend, SourceLocation FriendLoc,
+         ArrayRef<TemplateParameterList *> FriendTypeTPLists = {},
+         SourceLocation EllipsisLoc = {});
 
-  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID 
ID);
+  static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                                unsigned FriendTypeNumTPLists);
+
+  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
@@ -2524,19 +2526,12 @@ class FriendTemplateDecl : public Decl {
     return Friend.dyn_cast<NamedDecl*>();
   }
 
-  /// Retrieves the location of the 'friend' keyword.
-  SourceLocation getFriendLoc() const {
-    return FriendLoc;
+  TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const {
+    assert(N < NumTPLists);
+    return getTrailingObjects()[N];
   }
 
-  TemplateParameterList *getTemplateParameterList(unsigned i) const {
-    assert(i <= NumParams);
-    return Params[i];
-  }
-
-  unsigned getNumTemplateParameters() const {
-    return NumParams;
-  }
+  unsigned getFriendTypeNumTemplateParameterLists() const { return NumTPLists; 
}
 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index ce6ad723191e0..c06ceec13b4dd 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1727,8 +1727,9 @@ DEF_TRAVERSE_DECL(FriendTemplateDecl, {
     TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
   else
     TRY_TO(TraverseDecl(D->getFriendDecl()));
-  for (unsigned I = 0, E = D->getNumTemplateParameters(); I < E; ++I) {
-    TemplateParameterList *TPL = D->getTemplateParameterList(I);
+  for (unsigned I = 0, E = D->getFriendTypeNumTemplateParameterLists(); I < E;
+       ++I) {
+    TemplateParameterList *TPL = D->getFriendTypeTemplateParameterList(I);
     for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = 
TPL->end();
          ITPL != ETPL; ++ITPL) {
       TRY_TO(TraverseDecl(*ITPL));
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6d2fae551566f..9835c66c71816 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1921,6 +1921,8 @@ def err_friend_template_decl_multiple_specifiers: Error<
   "a friend declaration that befriends a template must contain exactly one 
type-specifier">;
 def friend_template_decl_malformed_pack_expansion : Error<
   "friend declaration expands pack %0 that is declared it its own template 
parameter list">;
+def err_dependent_friend_not_member : Error<
+  "friend declaration does not name a member of a class template 
specialization">;
 
 def err_invalid_base_in_interface : Error<
   "interface type cannot inherit from "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 760555d9c8b9b..7fee5c399fed2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1772,7 +1772,7 @@ class Sema final : public SemaBase {
       bool ForceCheck = false, bool ForceUnprivileged = false);
 
   /// Checks access to all the declarations in the given result set.
-  void CheckLookupAccess(const LookupResult &R);
+  void CheckLookupAccess(const LookupResult &R, DeclContext *DC = nullptr);
 
   /// Checks access to Target from the given class. The check will take access
   /// specifiers into account, but no member access expressions and such.
@@ -12012,6 +12012,9 @@ class Sema final : public SemaBase {
   bool CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous);
   void CompleteMemberSpecialization(NamedDecl *Member, LookupResult &Previous);
 
+  bool CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
+                            TemplateParameterList *FPL);
+
   // Explicit instantiation of a class template specialization
   DeclResult ActOnExplicitInstantiation(
       Scope *S, SourceLocation ExternLoc, SourceLocation TemplateLoc,
@@ -12388,7 +12391,8 @@ class Sema final : public SemaBase {
                              SourceLocation KeywordLoc,
                              NestedNameSpecifierLoc QualifierLoc,
                              const IdentifierInfo &II, SourceLocation IILoc,
-                             bool DeducedTSTContext = true);
+                             bool DeducedTSTContext = true,
+                             DeclContext *DC = nullptr);
 
   /// Rebuilds a type within the context of the current instantiation.
   ///
@@ -13823,15 +13827,27 @@ class Sema final : public SemaBase {
                             const MultiLevelTemplateArgumentList &TemplateArgs,
                             SourceLocation Loc, DeclarationName Entity,
                             bool AllowDeducedTST = false);
+  TypeSourceInfo *SubstType(TypeSourceInfo *T,
+                            const MultiLevelTemplateArgumentList &TemplateArgs,
+                            SourceLocation Loc, DeclarationName Entity,
+                            DeclContext *DC, bool AllowDeducedTST = false);
 
   QualType SubstType(QualType T,
                      const MultiLevelTemplateArgumentList &TemplateArgs,
                      SourceLocation Loc, DeclarationName Entity,
                      bool *IsIncompleteSubstitution = nullptr);
+  QualType SubstType(QualType T,
+                     const MultiLevelTemplateArgumentList &TemplateArgs,
+                     SourceLocation Loc, DeclarationName Entity,
+                     DeclContext *DC, bool *IsIncompleteSubstitution = 
nullptr);
 
   TypeSourceInfo *SubstType(TypeLoc TL,
                             const MultiLevelTemplateArgumentList &TemplateArgs,
                             SourceLocation Loc, DeclarationName Entity);
+  TypeSourceInfo *SubstType(TypeLoc TL,
+                            const MultiLevelTemplateArgumentList &TemplateArgs,
+                            SourceLocation Loc, DeclarationName Entity,
+                            DeclContext *DC);
 
   /// A form of SubstType intended specifically for instantiating the
   /// type of a FunctionDecl.  Its purpose is solely to force the
@@ -13840,7 +13856,8 @@ class Sema final : public SemaBase {
   TypeSourceInfo *SubstFunctionDeclType(
       TypeSourceInfo *T, const MultiLevelTemplateArgumentList &TemplateArgs,
       SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext,
-      Qualifiers ThisTypeQuals, bool EvaluateConstraints = true);
+      Qualifiers ThisTypeQuals, DeclContext *DC = nullptr,
+      bool EvaluateConstraints = true);
   void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
                           const MultiLevelTemplateArgumentList &Args);
   bool SubstExceptionSpec(SourceLocation Loc,
@@ -13851,7 +13868,8 @@ class Sema final : public SemaBase {
   SubstParmVarDecl(ParmVarDecl *D,
                    const MultiLevelTemplateArgumentList &TemplateArgs,
                    int indexAdjustment, UnsignedOrNone NumExpansions,
-                   bool ExpectParameterPack, bool EvaluateConstraints = true);
+                   bool ExpectParameterPack, DeclContext *DC = nullptr,
+                   bool EvaluateConstraints = true);
 
   /// Substitute the given template arguments into the given set of
   /// parameters, producing the set of parameter types that would be generated
@@ -13861,7 +13879,8 @@ class Sema final : public SemaBase {
                       const MultiLevelTemplateArgumentList &TemplateArgs,
                       SmallVectorImpl<QualType> &ParamTypes,
                       SmallVectorImpl<ParmVarDecl *> *OutParams,
-                      ExtParameterInfoBuilder &ParamInfos);
+                      ExtParameterInfoBuilder &ParamInfos,
+                      DeclContext *DC = nullptr);
 
   /// Substitute the given template arguments into the default argument.
   bool SubstDefaultArgument(SourceLocation Loc, ParmVarDecl *Param,
diff --git a/clang/include/clang/Sema/Template.h 
b/clang/include/clang/Sema/Template.h
index b0170c21feb1a..26268f1e04f07 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -715,7 +715,8 @@ enum class TemplateSubstitutionKind : char {
 
     // Helper functions for instantiating methods.
     TypeSourceInfo *SubstFunctionType(FunctionDecl *D,
-                             SmallVectorImpl<ParmVarDecl *> &Params);
+                                      SmallVectorImpl<ParmVarDecl *> &Params,
+                                      DeclContext *DC = nullptr);
     bool InitFunctionInstantiation(FunctionDecl *New, FunctionDecl *Tmpl);
     bool InitMethodInstantiation(CXXMethodDecl *New, CXXMethodDecl *Tmpl);
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 41ba98c53247d..7fc5b830638cc 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -537,6 +537,7 @@ namespace clang {
     ExpectedDecl VisitFieldDecl(FieldDecl *D);
     ExpectedDecl VisitIndirectFieldDecl(IndirectFieldDecl *D);
     ExpectedDecl VisitFriendDecl(FriendDecl *D);
+    ExpectedDecl VisitFriendTemplateDecl(FriendTemplateDecl *D);
     ExpectedDecl VisitObjCIvarDecl(ObjCIvarDecl *D);
     ExpectedDecl VisitVarDecl(VarDecl *D);
     ExpectedDecl VisitImplicitParamDecl(ImplicitParamDecl *D);
@@ -4545,6 +4546,24 @@ static bool IsEquivalentFriend(ASTImporter &Importer, 
FriendDecl *FD1,
   return Ctx.IsEquivalent(FD1, FD2);
 }
 
+static bool IsEquivalentFriend(ASTImporter &Importer, FriendTemplateDecl *FTD1,
+                               FriendTemplateDecl *FTD2) {
+  if ((!FTD1->getFriendType()) != (!FTD2->getFriendType()))
+    return false;
+
+  if (const TypeSourceInfo *TSI = FTD1->getFriendType())
+    return Importer.IsStructurallyEquivalent(
+        TSI->getType(), FTD2->getFriendType()->getType(), /*Complain=*/false);
+
+  ASTImporter::NonEquivalentDeclSet NonEquivalentDecls;
+  StructuralEquivalenceContext Ctx(
+      Importer.getToContext().getLangOpts(), FTD1->getASTContext(),
+      FTD2->getASTContext(), NonEquivalentDecls,
+      StructuralEquivalenceKind::Default, /* StrictTypeSpelling = */ false,
+      /* Complain = */ false);
+  return Ctx.IsEquivalent(FTD1, FTD2);
+}
+
 static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
                                                         FriendDecl *FD) {
   unsigned int FriendCount = 0;
@@ -4561,7 +4580,6 @@ static FriendCountAndPosition 
getFriendCountAndPosition(ASTImporter &Importer,
   }
 
   assert(FriendPosition && "Friend decl not found in own parent.");
-
   return {FriendCount, *FriendPosition};
 }
 
@@ -4576,9 +4594,11 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl 
*D) {
   // We try to maintain order and count of redundant friend declarations.
   const auto *RD = cast<CXXRecordDecl>(DC);
   SmallVector<FriendDecl *, 2> ImportedEquivalentFriends;
-  for (FriendDecl *ImportedFriend : RD->friends())
-    if (IsEquivalentFriend(Importer, D, ImportedFriend))
-      ImportedEquivalentFriends.push_back(ImportedFriend);
+  for (FriendDecl *ImportedFriend : RD->friends()) {
+    if (ImportedFriend->getKind() == Decl::Friend &&
+        IsEquivalentFriend(Importer, D, cast<FriendDecl>(ImportedFriend)))
+      ImportedEquivalentFriends.push_back(cast<FriendDecl>(ImportedFriend));
+  }
 
   FriendCountAndPosition CountAndPosition =
       getFriendCountAndPosition(Importer, D);
@@ -4609,15 +4629,6 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl 
*D) {
       return TSIOrErr.takeError();
   }
 
-  SmallVector<TemplateParameterList *, 1> ToTPLists(D->NumTPLists);
-  auto **FromTPLists = D->getTrailingObjects();
-  for (unsigned I = 0; I < D->NumTPLists; I++) {
-    if (auto ListOrErr = import(FromTPLists[I]))
-      ToTPLists[I] = *ListOrErr;
-    else
-      return ListOrErr.takeError();
-  }
-
   auto LocationOrErr = import(D->getLocation());
   if (!LocationOrErr)
     return LocationOrErr.takeError();
@@ -4631,7 +4642,7 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl 
*D) {
   FriendDecl *FrD;
   if (GetImportedOrCreateDecl(FrD, D, Importer.getToContext(), DC,
                               *LocationOrErr, ToFU, *FriendLocOrErr,
-                              *EllipsisLocOrErr, ToTPLists))
+                              *EllipsisLocOrErr))
     return FrD;
 
   FrD->setAccess(D->getAccess());
@@ -4640,6 +4651,72 @@ 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()) {
+    if (isa<FriendTemplateDecl>(ImportedFriend) &&
+        IsEquivalentFriend(Importer, D,
+                           cast<FriendTemplateDecl>(ImportedFriend)))
+      ImportedEquivalentFriends.push_back(
+          cast<FriendTemplateDecl>(ImportedFriend));
+  }
+
+  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 (NamedDecl *FriendD = D->getFriendDecl()) {
+    NamedDecl *ToFriendD;
+    if (Error Err = importInto(ToFriendD, FriendD))
+      return std::move(Err);
+    ToFU = ToFriendD;
+  } else {
+    if (auto TSIOrErr = import(D->getFriendType()))
+      ToFU = *TSIOrErr;
+    else
+      return TSIOrErr.takeError();
+  }
+
+  SmallVector<TemplateParameterList *, 1> ToParams(
+      D->getFriendTypeNumTemplateParameterLists());
+  for (unsigned I = 0, N = D->getFriendTypeNumTemplateParameterLists(); I != N;
+       ++I) {
+    if (auto ParamsOrErr = import(D->getFriendTypeTemplateParameterList(I)))
+      ToParams[I] = *ParamsOrErr;
+    else
+      return ParamsOrErr.takeError();
+  }
+
+  auto LocationOrErr = import(D->getLocation());
+  if (!LocationOrErr)
+    return LocationOrErr.takeError();
+
+  auto FriendLocOrErr = import(D->getFriendLoc());
+  if (!FriendLocOrErr)
+    return FriendLocOrErr.takeError();
+
+  FriendTemplateDecl *FTD;
+  if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
+                              *LocationOrErr, ToFU, *FriendLocOrErr, ToParams))
+    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 9d970651a9e65..9c7198e5c42ec 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2430,6 +2430,24 @@ static bool 
IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
   return false;
 }
 
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+                                     FriendTemplateDecl *FTD1,
+                                     FriendTemplateDecl *FTD2) {
+  if ((FTD1->getFriendType() && FTD2->getFriendDecl()) ||
+      (FTD1->getFriendDecl() && FTD2->getFriendType()))
+    return false;
+
+  if (FTD1->getFriendDecl() && FTD2->getFriendDecl())
+    return IsStructurallyEquivalent(Context, FTD1->getFriendDecl(),
+                                    FTD2->getFriendDecl());
+
+  if (FTD1->getFriendType() && FTD2->getFriendType())
+    return IsStructurallyEquivalent(Context, FTD1->getFriendType()->getType(),
+                                    FTD2->getFriendType()->getType());
+
+  return false;
+}
+
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
                                      TypedefNameDecl *D1, TypedefNameDecl *D2) 
{
   if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
diff --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index 6bfc2eb62b284..aea0600441d25 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -13,11 +13,9 @@
 
 #include "clang/AST/DeclFriend.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
-#include "clang/Basic/LLVM.h"
+#include "clang/AST/ExternalASTSource.h"
 #include <cassert>
 #include <cstddef>
 
@@ -25,16 +23,9 @@ using namespace clang;
 
 void FriendDecl::anchor() {}
 
-FriendDecl *FriendDecl::getNextFriendSlowCase() {
-  return cast_or_null<FriendDecl>(
-                           
NextFriend.get(getASTContext().getExternalSource()));
-}
-
-FriendDecl *
-FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
-                   FriendUnion Friend, SourceLocation FriendL,
-                   SourceLocation EllipsisLoc,
-                   ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
+FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation 
L,
+                               FriendUnion Friend, SourceLocation FriendL,
+                               SourceLocation EllipsisLoc) {
 #ifndef NDEBUG
   if (const auto *D = dyn_cast<NamedDecl *>(Friend)) {
     assert(isa<FunctionDecl>(D) ||
@@ -46,25 +37,22 @@ FriendDecl::Create(ASTContext &C, DeclContext *DC, 
SourceLocation L,
     // to the original declaration when instantiating members.
     assert(D->getFriendObjectKind() ||
            (cast<CXXRecordDecl>(DC)->getTemplateSpecializationKind()));
-    // These template parameters are for friend types only.
-    assert(FriendTypeTPLists.empty());
   }
 #endif
 
-  std::size_t Extra =
-      FriendDecl::additionalSizeToAlloc<TemplateParameterList *>(
-          FriendTypeTPLists.size());
-  auto *FD = new (C, DC, Extra)
-      FriendDecl(DC, L, Friend, FriendL, EllipsisLoc, FriendTypeTPLists);
+  auto *FD =
+      new (C, DC) FriendDecl(Decl::Friend, DC, L, Friend, FriendL, 
EllipsisLoc);
   cast<CXXRecordDecl>(DC)->pushFriendDecl(FD);
   return FD;
 }
 
-FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
-                                           unsigned FriendTypeNumTPLists) {
-  std::size_t Extra =
-      additionalSizeToAlloc<TemplateParameterList *>(FriendTypeNumTPLists);
-  return new (C, ID, Extra) FriendDecl(EmptyShell(), FriendTypeNumTPLists);
+FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
+  return new (C, ID) FriendDecl(Decl::Friend, EmptyShell());
+}
+
+FriendDecl *FriendDecl::getNextFriendSlowCase() {
+  return cast_or_null<FriendDecl>(
+      NextFriend.get(getASTContext().getExternalSource()));
 }
 
 FriendDecl *CXXRecordDecl::getFirstFriend() const {
@@ -72,3 +60,29 @@ FriendDecl *CXXRecordDecl::getFirstFriend() const {
   Decl *First = data().FirstFriend.get(Source);
   return First ? cast<FriendDecl>(First) : nullptr;
 }
+
+SourceRange FriendDecl::getSourceRange() const {
+  if (TypeSourceInfo *TInfo = getFriendType()) {
+    SourceLocation EndL =
+        isPackExpansion() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc();
+    return SourceRange(getFriendLoc(), EndL);
+  }
+
+  if (isPackExpansion())
+    return SourceRange(getFriendLoc(), getEllipsisLoc());
+
+  if (NamedDecl *ND = getFriendDecl()) {
+    if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+      return FD->getSourceRange();
+    if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+      return FTD->getSourceRange();
+    if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
+      return CTD->getSourceRange();
+    if (const auto *DD = dyn_cast<DeclaratorDecl>(ND)) {
+      if (DD->getOuterLocStart() != DD->getInnerLocStart())
+        return DD->getSourceRange();
+    }
+    return SourceRange(getFriendLoc(), ND->getEndLoc());
+  }
+  return SourceRange(getFriendLoc(), getLocation());
+}
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 5e377a6c0c247..755f93e1ab22f 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -888,24 +888,17 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
 
 void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
   if (TypeSourceInfo *TSI = D->getFriendType()) {
-    unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
-    for (unsigned i = 0; i < NumTPLists; ++i)
-      printTemplateParameters(D->getFriendTypeTemplateParameterList(i));
     Out << "friend ";
     Out << TSI->getType().getAsString(Policy);
-  }
-  else if (FunctionDecl *FD =
-      dyn_cast<FunctionDecl>(D->getFriendDecl())) {
+  } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D->getFriendDecl())) {
     Out << "friend ";
     VisitFunctionDecl(FD);
-  }
-  else if (FunctionTemplateDecl *FTD =
-           dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) {
+  } else if (FunctionTemplateDecl *FTD =
+                 dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) {
     Out << "friend ";
     VisitFunctionTemplateDecl(FTD);
-  }
-  else if (ClassTemplateDecl *CTD =
-           dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) {
+  } else if (ClassTemplateDecl *CTD =
+                 dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) {
     Out << "friend ";
     VisitRedeclarableTemplateDecl(CTD);
   }
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 99d02fdc99e92..6c19c347d87fc 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1231,21 +1231,34 @@ void FriendTemplateDecl::anchor() {}
 
 FriendTemplateDecl *
 FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC,
-                           SourceLocation L,
-                           MutableArrayRef<TemplateParameterList *> Params,
-                           FriendUnion Friend, SourceLocation FLoc) {
-  TemplateParameterList **TPL = nullptr;
-  if (!Params.empty()) {
-    TPL = new (Context) TemplateParameterList *[Params.size()];
-    llvm::copy(Params, TPL);
-  }
-  return new (Context, DC)
-      FriendTemplateDecl(DC, L, TPL, Params.size(), Friend, FLoc);
+                           SourceLocation Loc, FriendUnion Friend,
+                           SourceLocation FriendLoc,
+                           ArrayRef<TemplateParameterList *> FriendTypeTPLists,
+                           SourceLocation EllipsisLoc) {
+  std::size_t Extra =
+      FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          FriendTypeTPLists.size());
+  auto *FTD = new (Context, DC, Extra) FriendTemplateDecl(
+      DC, Loc, Friend, FriendLoc, EllipsisLoc, FriendTypeTPLists);
+  cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD);
+  return FTD;
 }
 
-FriendTemplateDecl *FriendTemplateDecl::CreateDeserialized(ASTContext &C,
-                                                           GlobalDeclID ID) {
-  return new (C, ID) FriendTemplateDecl(EmptyShell());
+FriendTemplateDecl *
+FriendTemplateDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+                                       unsigned NumFriendTypeTPLists) {
+  std::size_t Extra =
+      FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+          NumFriendTypeTPLists);
+  return new (C, ID, Extra)
+      FriendTemplateDecl(EmptyShell(), NumFriendTypeTPLists);
+}
+
+SourceRange FriendTemplateDecl::getSourceRange() const {
+  SourceLocation Begin =
+      getFriendTypeTemplateParameterList(0)->getTemplateLoc();
+  SourceLocation End = FriendDecl::getSourceRange().getEnd();
+  return SourceRange(Begin, End);
 }
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index ef45d5842c795..27c99ef7691a6 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1163,8 +1163,9 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
   for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
                                       E = RD->friend_end();
        I != E && Complete; ++I) {
+    FriendDecl *Friend = *I;
     // Check if friend classes and methods are complete.
-    if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
+    if (TypeSourceInfo *TSI = Friend->getFriendType()) {
       // Friend classes are available as the TypeSourceInfo of the FriendDecl.
       if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl())
         Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete);
@@ -1173,7 +1174,7 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
     } else {
       // Friend functions are available through the NamedDecl of FriendDecl.
       if (const FunctionDecl *FD =
-          dyn_cast<FunctionDecl>((*I)->getFriendDecl()))
+              dyn_cast<FunctionDecl>(Friend->getFriendDecl()))
         Complete = FD->isDefined();
       else
         // This is a template friend, give up.
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 17415b4185eff..1cd2710f79fe7 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -21,6 +21,7 @@
 #include "clang/Sema/DelayedDiagnostic.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
+#include "clang/Sema/Template.h"
 
 using namespace clang;
 using namespace sema;
@@ -274,6 +275,65 @@ struct AccessTarget : public AccessedEntity {
 
 }
 
+static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
+                                       ArrayRef<TemplateArgument> PatternArgs,
+                                       ArrayRef<TemplateArgument> Args,
+                                       SourceLocation Loc) {
+  TemplateDeductionInfo Info(Loc);
+  SmallVector<DeducedTemplateArgument, 4> Deduced(TPL->size());
+  S.DeduceTemplateArguments(TPL, PatternArgs, Args, Info, Deduced,
+                            /*NumberOfArgumentsMustMatch=*/false);
+
+  for (const DeducedTemplateArgument &Arg : Deduced)
+    if (Arg.isNull())
+      return false;
+
+  return true;
+}
+
+static bool MatchesFriendContext(Sema &S, FunctionDecl *FD,
+                                 ClassTemplateDecl *FriendCTD,
+                                 ArrayRef<TemplateArgument> FriendArgs,
+                                 TemplateParameterList *FriendTPL,
+                                 SourceLocation Loc) {
+  const auto *RD = dyn_cast<CXXRecordDecl>(FD->getDeclContext());
+  if (!RD)
+    return false;
+
+  ClassTemplateDecl *ContextCTD = RD->getDescribedClassTemplate();
+  ArrayRef<TemplateArgument> ContextArgs;
+  if (ContextCTD) {
+    ContextArgs = ContextCTD->getInjectedTemplateArgs(S.Context);
+  } else {
+    const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+    if (!CTSD)
+      return false;
+    ContextCTD = CTSD->getSpecializedTemplate();
+    ContextArgs = CTSD->getTemplateArgs().asArray();
+  }
+
+  if (ContextCTD->getCanonicalDecl() != FriendCTD->getCanonicalDecl())
+    return false;
+
+  return CanDeduceTemplateArguments(S, FriendTPL, FriendArgs, ContextArgs, 
Loc);
+}
+
+static const TemplateSpecializationType *
+TryGetTemplateSpecializationType(Sema &S, NestedNameSpecifier NNS) {
+  if (!NNS)
+    return nullptr;
+
+  QualType Ty(NNS.getAsType(), 0);
+  if (Ty.isNull())
+    return nullptr;
+
+  Ty = S.Context.getCanonicalType(Ty);
+  if (const auto *ICNT = Ty->getAs<InjectedClassNameType>())
+    Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(S.Context);
+
+  return Ty->getAs<TemplateSpecializationType>();
+}
+
 /// Checks whether one class might instantiate to the other.
 static bool MightInstantiateTo(const CXXRecordDecl *From,
                                const CXXRecordDecl *To) {
@@ -283,8 +343,12 @@ static bool MightInstantiateTo(const CXXRecordDecl *From,
 
   const DeclContext *FromDC = From->getDeclContext()->getPrimaryContext();
   const DeclContext *ToDC = To->getDeclContext()->getPrimaryContext();
-  if (FromDC == ToDC) return true;
-  if (FromDC->isFileContext() || ToDC->isFileContext()) return false;
+
+  if (FromDC == ToDC)
+    return true;
+
+  if (FromDC->isFileContext() || ToDC->isFileContext())
+    return false;
 
   // Be conservative.
   return true;
@@ -342,7 +406,6 @@ static AccessResult IsDerivedFromInclusive(const 
CXXRecordDecl *Derived,
   return OnFailure;
 }
 
-
 static bool MightInstantiateTo(Sema &S, DeclContext *Context,
                                DeclContext *Friend) {
   if (Friend == Context)
@@ -374,49 +437,49 @@ static bool MightInstantiateTo(Sema &S, CanQualType 
Context, CanQualType Friend)
   return true;
 }
 
-static bool MightInstantiateTo(Sema &S,
-                               FunctionDecl *Context,
-                               FunctionDecl *Friend) {
-  if (Context->getDeclName() != Friend->getDeclName())
+static bool MightInstantiateTo(Sema &S, CanQual<FunctionProtoType> Context,
+                               CanQual<FunctionProtoType> Friend) {
+  if (Friend.getQualifiers() != Context.getQualifiers())
     return false;
 
-  if (!MightInstantiateTo(S,
-                          Context->getDeclContext(),
-                          Friend->getDeclContext()))
+  if (Friend->getNumParams() != Context->getNumParams())
     return false;
 
-  CanQual<FunctionProtoType> FriendTy
-    = S.Context.getCanonicalType(Friend->getType())
-         ->getAs<FunctionProtoType>();
-  CanQual<FunctionProtoType> ContextTy
-    = S.Context.getCanonicalType(Context->getType())
-         ->getAs<FunctionProtoType>();
-
-  // There isn't any way that I know of to add qualifiers
-  // during instantiation.
-  if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
+  if (!MightInstantiateTo(S, Context->getReturnType(), 
Friend->getReturnType()))
     return false;
 
-  if (FriendTy->getNumParams() != ContextTy->getNumParams())
+  for (unsigned I = 0, E = Friend->getNumParams(); I != E; ++I)
+    if (!MightInstantiateTo(S, Context->getParamType(I),
+                            Friend->getParamType(I)))
+      return false;
+
+  return true;
+}
+
+static bool MightInstantiateTo(Sema &S, FunctionDecl *Context,
+                               FunctionDecl *Friend) {
+  if (Context->getDeclName() != Friend->getDeclName())
     return false;
 
-  if (!MightInstantiateTo(S, ContextTy->getReturnType(),
-                          FriendTy->getReturnType()))
+  DeclContext *ContextDC = Context->getDeclContext();
+  DeclContext *FriendDC = Friend->getDeclContext();
+
+  if (!FriendDC->isDependentContext() &&
+      !MightInstantiateTo(S, ContextDC, FriendDC))
     return false;
 
-  for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I)
-    if (!MightInstantiateTo(S, ContextTy->getParamType(I),
-                            FriendTy->getParamType(I)))
-      return false;
+  CanQual<FunctionProtoType> FriendTy =
+      
S.Context.getCanonicalType(Friend->getType())->getAs<FunctionProtoType>();
+  CanQual<FunctionProtoType> ContextTy =
+      S.Context.getCanonicalType(Context->getType())
+          ->getAs<FunctionProtoType>();
 
-  return true;
+  return MightInstantiateTo(S, ContextTy, FriendTy);
 }
 
-static bool MightInstantiateTo(Sema &S,
-                               FunctionTemplateDecl *Context,
+static bool MightInstantiateTo(Sema &S, FunctionTemplateDecl *Context,
                                FunctionTemplateDecl *Friend) {
-  return MightInstantiateTo(S,
-                            Context->getTemplatedDecl(),
+  return MightInstantiateTo(S, Context->getTemplatedDecl(),
                             Friend->getTemplatedDecl());
 }
 
@@ -551,6 +614,165 @@ static AccessResult MatchesFriend(Sema &S,
   return OnFailure;
 }
 
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  NamedDecl *ND) {
+  ND = cast<NamedDecl>(ND->getCanonicalDecl());
+  if (ClassTemplateDecl *CTD = dyn_cast<ClassTemplateDecl>(ND))
+    return MatchesFriend(S, EC, CTD);
+
+  if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+    return MatchesFriend(S, EC, FTD);
+
+  if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(ND))
+    return MatchesFriend(S, EC, RD);
+
+  assert(isa<FunctionDecl>(ND) && "unknown friend decl kind");
+  return MatchesFriend(S, EC, cast<FunctionDecl>(ND));
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FriendTD,
+                                  FunctionTemplateDecl *FriendFTD) {
+  AccessResult OnFailure = AR_inaccessible;
+  NestedNameSpecifier FriendNNS = 
FriendFTD->getTemplatedDecl()->getQualifier();
+  const auto *FriendTST = TryGetTemplateSpecializationType(S, FriendNNS);
+  if (!FriendTST)
+    return OnFailure;
+
+  auto *FriendCTD = dyn_cast<ClassTemplateDecl>(
+      FriendTST->getTemplateName().getAsTemplateDecl());
+  if (!FriendCTD)
+    return OnFailure;
+
+  TemplateParameterList *FriendTPL =
+      FriendTD->getFriendTypeTemplateParameterList(0);
+
+  if (!FriendTPL)
+    return OnFailure;
+
+  for (FunctionDecl *FD : EC.Functions) {
+    if (!MatchesFriendContext(S, FD, FriendCTD, 
FriendTST->template_arguments(),
+                              FriendTPL, FriendTD->getLocation()))
+      continue;
+
+    FunctionTemplateDecl *ContextFTD = FD->getPrimaryTemplate();
+    if (!ContextFTD)
+      ContextFTD = FD->getDescribedFunctionTemplate();
+
+    if (ContextFTD && MightInstantiateTo(S, FriendFTD, ContextFTD))
+      return AR_accessible;
+  }
+
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FriendTD,
+                                  FunctionDecl *FriendFD) {
+  AccessResult OnFailure = AR_inaccessible;
+  NestedNameSpecifier FriendNNS = FriendFD->getQualifier();
+  const auto *FriendTST = TryGetTemplateSpecializationType(S, FriendNNS);
+  if (!FriendTST)
+    return OnFailure;
+
+  auto *FriendCTD = dyn_cast<ClassTemplateDecl>(
+      FriendTST->getTemplateName().getAsTemplateDecl());
+  if (!FriendCTD)
+    return OnFailure;
+
+  TemplateParameterList *FriendTPL =
+      FriendTD->getFriendTypeTemplateParameterList(0);
+  if (!FriendTPL)
+    return OnFailure;
+
+  CanQual<FunctionProtoType> FriendProto =
+      S.Context.getCanonicalType(FriendFD->getType())
+          ->getAs<FunctionProtoType>();
+
+  for (FunctionDecl *FD : EC.Functions) {
+    if (FD->getDeclName() != FriendFD->getDeclName())
+      continue;
+
+    if (!MatchesFriendContext(S, FD, FriendCTD, 
FriendTST->template_arguments(),
+                              FriendTPL, FriendTD->getLocation()))
+      continue;
+
+    CanQual<FunctionProtoType> ContextProto =
+        S.Context.getCanonicalType(FD->getType())->getAs<FunctionProtoType>();
+    if (MightInstantiateTo(S, ContextProto, FriendProto))
+      return AR_accessible;
+  }
+
+  return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD, NamedDecl *ND) {
+  if (auto *TD = dyn_cast<FunctionTemplateDecl>(ND))
+    return MatchesFriend(S, EC, FTD, TD);
+
+  if (auto *FD = dyn_cast<FunctionDecl>(ND))
+    return MatchesFriend(S, EC, FTD, FD);
+
+  return MatchesFriend(S, EC, ND);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD,
+                                  TypeSourceInfo *TSI) {
+  QualType TypeAsWritten = TSI->getType();
+  if (!TypeAsWritten->isDependentType())
+    return MatchesFriend(S, EC, S.Context.getCanonicalType(TypeAsWritten));
+
+  AccessResult OnFailure = EC.isDependent() ? AR_dependent : AR_inaccessible;
+  const auto *DNT = TypeAsWritten->getAs<DependentNameType>();
+  if (!DNT)
+    return OnFailure;
+
+  NestedNameSpecifier NNS = DNT->getQualifier();
+  if (!NNS)
+    return OnFailure;
+
+  const auto *T = NNS.getAsType();
+  if (!T)
+    return OnFailure;
+
+  const auto *TST =
+      S.Context.getCanonicalType(T)->getAsNonAliasTemplateSpecializationType();
+  if (!TST)
+    return OnFailure;
+
+  auto *CTD =
+      dyn_cast<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl());
+  if (!CTD)
+    return OnFailure;
+
+  TemplateParameterList *TPL = FTD->getFriendTypeTemplateParameterList(0);
+  if (!TPL)
+    return OnFailure;
+
+  for (CXXRecordDecl *RD : EC.Records) {
+    if (RD->getDeclName() != DNT->getIdentifier())
+      continue;
+
+    const auto *CTSD =
+        dyn_cast<ClassTemplateSpecializationDecl>(RD->getDeclContext());
+    if (!CTSD)
+      continue;
+
+    if (CTSD->getSpecializedTemplate()->getCanonicalDecl() !=
+        CTD->getCanonicalDecl())
+      continue;
+
+    if (CanDeduceTemplateArguments(S, TPL, TST->template_arguments(),
+                                   CTSD->getTemplateArgs().asArray(),
+                                   FTD->getLocation()))
+      return AR_accessible;
+  }
+
+  return OnFailure;
+}
+
 /// Determines whether the given friend declaration matches anything
 /// in the effective context.
 static AccessResult MatchesFriend(Sema &S,
@@ -561,25 +783,27 @@ static AccessResult MatchesFriend(Sema &S,
   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());
 
-  NamedDecl *Friend
-    = cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl());
-
-  // FIXME: declarations with dependent or templated scope.
+  return AR_inaccessible;
+}
 
-  if (isa<ClassTemplateDecl>(Friend))
-    return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+                                  FriendTemplateDecl *FTD) {
+  if (FTD->isInvalidDecl() || FTD->isUnsupportedFriend())
+    return AR_accessible;
 
-  if (isa<FunctionTemplateDecl>(Friend))
-    return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
+  if (NamedDecl *ND = FTD->getFriendDecl())
+    return MatchesFriend(S, EC, FTD, ND);
 
-  if (isa<CXXRecordDecl>(Friend))
-    return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
+  if (TypeSourceInfo *TSI = FTD->getFriendType())
+    return MatchesFriend(S, EC, FTD, TSI);
 
-  assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
-  return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
+  return AR_inaccessible;
 }
 
 static AccessResult GetFriendKind(Sema &S,
@@ -588,8 +812,14 @@ static AccessResult GetFriendKind(Sema &S,
   AccessResult OnFailure = AR_inaccessible;
 
   // Okay, check friends.
-  for (auto *Friend : Class->friends()) {
-    switch (MatchesFriend(S, EC, Friend)) {
+  for (FriendDecl *Friend : Class->friends()) {
+    AccessResult AR;
+    if (auto *FTD = dyn_cast<FriendTemplateDecl>(Friend))
+      AR = MatchesFriend(S, EC, FTD);
+    else
+      AR = MatchesFriend(S, EC, cast<FriendDecl>(Friend));
+
+    switch (AR) {
     case AR_accessible:
       return AR_accessible;
 
@@ -1450,7 +1680,8 @@ static AccessResult CheckEffectiveAccess(Sema &S,
 }
 
 static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
-                                      AccessTarget &Entity) {
+                                      AccessTarget &Entity,
+                                      DeclContext *DC = nullptr) {
   // If the access path is public, it's accessible everywhere.
   if (Entity.getAccess() == AS_public)
     return Sema::AR_accessible;
@@ -1491,7 +1722,7 @@ static Sema::AccessResult CheckAccess(Sema &S, 
SourceLocation Loc,
     }
   }
 
-  EffectiveContext EC(S.CurContext);
+  EffectiveContext EC(DC ? DC : S.CurContext);
   switch (CheckEffectiveAccess(S, EC, Loc, Entity)) {
   case AR_accessible: return Sema::AR_accessible;
   case AR_inaccessible: return Sema::AR_inaccessible;
@@ -1904,7 +2135,7 @@ Sema::AccessResult 
Sema::CheckBaseClassAccess(SourceLocation AccessLoc,
       ForceCheck, ForceUnprivileged);
 }
 
-void Sema::CheckLookupAccess(const LookupResult &R) {
+void Sema::CheckLookupAccess(const LookupResult &R, DeclContext *DC) {
   assert(getLangOpts().AccessControl
          && "performing access check without access control");
   assert(R.getNamingClass() && "performing access check without naming class");
@@ -1915,7 +2146,7 @@ void Sema::CheckLookupAccess(const LookupResult &R) {
                           R.getNamingClass(), I.getPair(),
                           R.getBaseObjectType());
       Entity.setDiag(diag::err_access);
-      CheckAccess(*this, R.getNameLoc(), Entity);
+      CheckAccess(*this, R.getNameLoc(), Entity, DC);
     }
   }
 }
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index c1d3960e65ef6..6435dc250470d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -58,6 +58,7 @@
 #include <set>
 
 using namespace clang;
+using namespace sema;
 
 
//===----------------------------------------------------------------------===//
 // CheckDefaultArgumentVisitor
@@ -6211,8 +6212,8 @@ static void CheckAbstractClassUsage(AbstractUsageInfo 
&Info,
     if (D->isImplicit()) continue;
 
     // Step through friends to the befriended declaration.
-    if (auto *FD = dyn_cast<FriendDecl>(D)) {
-      D = FD->getFriendDecl();
+    if (D->getKind() == Decl::Friend) {
+      D = cast<FriendDecl>(D)->getFriendDecl();
       if (!D) continue;
     }
 
@@ -7330,9 +7331,9 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl 
*Record) {
 
       if (!isa<CXXDestructorDecl>(M))
         CompleteMemberFunction(M);
-    } else if (auto *F = dyn_cast<FriendDecl>(D)) {
+    } else if (D->getKind() == Decl::Friend) {
       CheckForDefaultedFunction(
-          dyn_cast_or_null<FunctionDecl>(F->getFriendDecl()));
+          
dyn_cast_or_null<FunctionDecl>(cast<FriendDecl>(D)->getFriendDecl()));
     }
   }
 
@@ -18029,6 +18030,33 @@ Decl 
*Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
   return Decl;
 }
 
+bool Sema::CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
+                                TemplateParameterList *FPL) {
+  if (!NNS || !FPL || FPL->size() == 0)
+    return false;
+
+  if (NNS.isDependent()) {
+    if (NNS.getKind() == NestedNameSpecifier::Kind::Type) {
+      QualType T(NNS.getCanonical().getAsType(), 0);
+      if (isa<PackIndexingType>(T))
+        return false;
+
+      if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
+        if (isa<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl()))
+          return false;
+      }
+
+      if (isa<InjectedClassNameType>(T))
+        return false;
+    }
+
+    Diag(Loc, diag::err_dependent_friend_not_member);
+    return true;
+  }
+
+  return false;
+}
+
 DeclResult Sema::ActOnTemplatedFriendTag(
     Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation 
TagLoc,
     CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
@@ -18099,9 +18127,8 @@ DeclResult Sema::ActOnTemplatedFriendTag(
     if (T.isNull())
       return true;
 
-    FriendDecl *Friend =
-        FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
-                           EllipsisLoc, TempParamLists);
+    FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI,
+                                            FriendLoc, EllipsisLoc);
     Friend->setAccess(AS_public);
     CurContext->addDecl(Friend);
     return Friend;
@@ -18127,25 +18154,41 @@ DeclResult Sema::ActOnTemplatedFriendTag(
     }
   }
 
-  // Handle the case of a templated-scope friend class.  e.g.
-  //   template <class T> class A<T>::B;
-  // FIXME: we don't support these right now.
-  Diag(NameLoc, diag::warn_template_qualified_friend_unsupported)
-    << SS.getScopeRep() << SS.getRange() << cast<CXXRecordDecl>(CurContext);
+  NestedNameSpecifier NNS = SS.getScopeRep();
+  if (EllipsisLoc.isInvalid() &&
+      CheckDependentFriend(TagLoc, NNS, TempParamLists.front()))
+    return true;
+
   ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
-  QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
+  QualType T = Context.getDependentNameType(ETK, NNS, Name);
   TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T);
+
   DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>();
   TL.setElaboratedKeywordLoc(TagLoc);
   TL.setQualifierLoc(SS.getWithLocInContext(Context));
   TL.setNameLoc(NameLoc);
 
-  FriendDecl *Friend =
-      FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
-                         EllipsisLoc, TempParamLists);
+  Decl *Friend;
+  if (TempParamLists.empty())
+    Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+                                EllipsisLoc);
+  else {
+    if (CheckTemplateDeclScope(S, TempParamLists.back()))
+      return true;
+
+    Friend = FriendTemplateDecl::Create(Context, CurContext, NameLoc, TSI,
+                                        FriendLoc, TempParamLists, 
EllipsisLoc);
+  }
+
+  if (EllipsisLoc.isValid() && NNS.isDependent()) {
+    Diag(NameLoc, diag::warn_template_qualified_friend_unsupported)
+        << SS.getScopeRep() << SS.getRange() << 
cast<CXXRecordDecl>(CurContext);
+    cast<FriendDecl>(Friend)->setUnsupportedFriend(true);
+  }
+
   Friend->setAccess(AS_public);
-  Friend->setUnsupportedFriend(true);
   CurContext->addDecl(Friend);
+
   return Friend;
 }
 
@@ -18246,11 +18289,14 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const 
DeclSpec &DS,
   // friend a member of an arbitrary specialization of your template).
 
   Decl *D;
-  if (!TempParams.empty())
+  if (!TempParams.empty()) {
+    if (CheckTemplateDeclScope(S, TempParams.back()))
+      return nullptr;
+
     // TODO: Support variadic friend template decls?
-    D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI,
-                                   FriendLoc);
-  else
+    D = FriendTemplateDecl::Create(Context, CurContext, Loc, TSI, FriendLoc,
+                                   TempParams, EllipsisLoc);
+  } else
     D = FriendDecl::Create(Context, CurContext, 
TSI->getTypeLoc().getBeginLoc(),
                            TSI, FriendLoc, EllipsisLoc);
 
@@ -18437,6 +18483,11 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, 
Declarator &D,
     assert(isa<CXXRecordDecl>(DC) && "friend declaration not in class?");
   }
 
+  if (TemplateParams.size() && SS.isValid() &&
+      CheckDependentFriend(NameInfo.getLoc(), SS.getScopeRep(),
+                           TemplateParams.front()))
+    return nullptr;
+
   if (!DC->isRecord()) {
     int DiagArg = -1;
     switch (D.getName().getKind()) {
@@ -18500,54 +18551,51 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, 
Declarator &D,
       PushOnScopeChains(ND, EnclosingScope, /*AddToContext=*/ false);
   }
 
-  FriendDecl *FrD = FriendDecl::Create(Context, CurContext,
-                                       D.getIdentifierLoc(), ND,
-                                       DS.getFriendSpecLoc());
-  FrD->setAccess(AS_public);
-  CurContext->addDecl(FrD);
+  warnOnReservedIdentifier(ND);
 
-  if (ND->isInvalidDecl()) {
-    FrD->setInvalidDecl();
-  } else {
-    if (DC->isRecord()) CheckFriendAccess(ND);
+  if (ND->isInvalidDecl())
+    return ND;
 
-    FunctionDecl *FD;
-    if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
-      FD = FTD->getTemplatedDecl();
-    else
-      FD = cast<FunctionDecl>(ND);
-
-    // C++ [class.friend]p6:
-    //   A function may be defined in a friend declaration of a class if and
-    //   only if the class is a non-local class, and the function name is
-    //   unqualified.
-    if (D.isFunctionDefinition()) {
-      // Qualified friend function definition.
-      if (SS.isNotEmpty()) {
-        // FIXME: We should only do this if the scope specifier names the
-        // innermost enclosing namespace; otherwise the fixit changes the
-        // meaning of the code.
-        SemaDiagnosticBuilder DB =
-            Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def);
-
-        DB << SS.getScopeRep();
-        if (DC->isFileContext())
-          DB << FixItHint::CreateRemoval(SS.getRange());
-
-        // Friend function defined in a local class.
-      } else if (FunctionContainingLocalClass) {
-        Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class);
-
-        // Per [basic.pre]p4, a template-id is not a name. Therefore, if we 
have
-        // a template-id, the function name is not unqualified because these is
-        // no name. While the wording requires some reading in-between the
-        // lines, GCC, MSVC, and EDG all consider a friend function
-        // specialization definitions to be de facto explicit specialization
-        // and diagnose them as such.
-      } else if (isTemplateId) {
-        Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def);
-      }
+  if (DC->isRecord())
+    CheckFriendAccess(ND);
+
+  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);
     }
+  }
 
     // C++11 [dcl.fct.default]p4: If a friend declaration specifies a
     // default argument expression, that declaration shall be a definition
@@ -18565,18 +18613,27 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, 
Declarator &D,
         Diag(FD->getLocation(), 
diag::err_friend_decl_with_def_arg_must_be_def);
     }
 
-    // Mark templated-scope function declarations as unsupported.
-    if (FD->getNumTemplateParameterLists() && SS.isValid()) {
-      Diag(FD->getLocation(), diag::warn_template_qualified_friend_unsupported)
-        << SS.getScopeRep() << SS.getRange()
-        << cast<CXXRecordDecl>(CurContext);
-      FrD->setUnsupportedFriend(true);
-    }
-  }
+    unsigned NumTPLists = FD->getNumTemplateParameterLists();
+    Decl *Friend;
+    if (NumTPLists && SS.isValid()) {
+      SmallVector<TemplateParameterList *, 1> TPL(NumTPLists);
+      for (unsigned I = 0, N = NumTPLists; I != N; ++I)
+        TPL[I] = FD->getTemplateParameterList(I);
 
-  warnOnReservedIdentifier(ND);
+      if (CheckTemplateDeclScope(S, TPL.back()))
+        return nullptr;
 
-  return ND;
+      Friend =
+          FriendTemplateDecl::Create(Context, CurContext, D.getIdentifierLoc(),
+                                     ND, DS.getFriendSpecLoc(), TPL);
+    } else {
+      Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), 
ND,
+                                  DS.getFriendSpecLoc());
+    }
+    Friend->setAccess(AS_public);
+    CurContext->addDecl(Friend);
+
+    return ND;
 }
 
 void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c436b7018a2bd..8938add183b0f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -11266,14 +11266,11 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
   return T;
 }
 
-/// Build the type that describes a C++ typename specifier,
-/// e.g., "typename T::type".
-QualType
-Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
-                        SourceLocation KeywordLoc,
-                        NestedNameSpecifierLoc QualifierLoc,
-                        const IdentifierInfo &II,
-                        SourceLocation IILoc, bool DeducedTSTContext) {
+QualType Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
+                                 SourceLocation KeywordLoc,
+                                 NestedNameSpecifierLoc QualifierLoc,
+                                 const IdentifierInfo &II, SourceLocation 
IILoc,
+                                 bool DeducedTSTContext, DeclContext *DC) {
   assert((Keyword != ElaboratedTypeKeyword::None) == KeywordLoc.isValid());
 
   CXXScopeSpec SS;
@@ -11303,10 +11300,17 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
 
   DeclarationName Name(&II);
   LookupResult Result(*this, Name, IILoc, LookupOrdinaryName);
-  if (Ctx)
+  if (Ctx) {
     LookupQualifiedName(Result, Ctx, SS);
-  else
+    if (getLangOpts().AccessControl && DC) {
+      Result.suppressAccessDiagnostics();
+      if (Result.isClassLookup())
+        CheckLookupAccess(Result, DC);
+    }
+  } else {
     LookupName(Result, CurScope);
+  }
+
   unsigned DiagID = 0;
   Decl *Referenced = nullptr;
   switch (Result.getResultKind()) {
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c71c40526ccdc..5733fca7d6abe 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3641,7 +3641,7 @@ TemplateDeductionResult 
Sema::SubstituteExplicitTemplateArguments(
   if (Proto->hasTrailingReturn()) {
     if (SubstParmTypes(Function->getLocation(), Function->parameters(),
                        Proto->getExtParameterInfosOrNull(), MLTAL, ParamTypes,
-                       /*params=*/nullptr, ExtParamInfos))
+                       /*params=*/nullptr, ExtParamInfos, Function))
       return TemplateDeductionResult::SubstitutionFailure;
   }
 
@@ -3683,7 +3683,7 @@ TemplateDeductionResult 
Sema::SubstituteExplicitTemplateArguments(
   if (!Proto->hasTrailingReturn() &&
       SubstParmTypes(Function->getLocation(), Function->parameters(),
                      Proto->getExtParameterInfosOrNull(), MLTAL, ParamTypes,
-                     /*params*/ nullptr, ExtParamInfos))
+                     /*params*/ nullptr, ExtParamInfos, Function))
     return TemplateDeductionResult::SubstitutionFailure;
 
   if (FunctionType) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 5381a5a6f110d..53d5497e21daf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1321,6 +1321,7 @@ namespace {
     const MultiLevelTemplateArgumentList &TemplateArgs;
     SourceLocation Loc;
     DeclarationName Entity;
+    DeclContext *DC;
     // Whether to evaluate the C++20 constraints or simply substitute into 
them.
     bool EvaluateConstraints = true;
     // Whether Substitution was Incomplete, that is, we tried to substitute in
@@ -1342,9 +1343,10 @@ namespace {
     TemplateInstantiator(Sema &SemaRef,
                          const MultiLevelTemplateArgumentList &TemplateArgs,
                          SourceLocation Loc, DeclarationName Entity,
+                         DeclContext *DC = nullptr,
                          bool BailOutOnIncomplete = false)
         : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
-          Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}
+          Entity(Entity), DC(DC), BailOutOnIncomplete(BailOutOnIncomplete) {}
 
     void setEvaluateConstraints(bool B) {
       EvaluateConstraints = B;
@@ -1359,7 +1361,7 @@ namespace {
     TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
                          SourceLocation Loc,
                          const MultiLevelTemplateArgumentList &TemplateArgs)
-        : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
+        : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), 
DC(nullptr),
           BailOutOnIncomplete(false) {
       if (!SemaRef.CurrentCachedTemplateArgs)
         return;
@@ -1369,6 +1371,29 @@ namespace {
           Arg.Profile(V, SemaRef.Context);
     }
 
+    QualType RebuildDependentNameType(ElaboratedTypeKeyword Keyword,
+                                      SourceLocation KeywordLoc,
+                                      NestedNameSpecifierLoc QualifierLoc,
+                                      const IdentifierInfo *Id,
+                                      SourceLocation IdLoc,
+                                      bool DeducedTSTContext) {
+      CXXScopeSpec SS;
+      SS.Adopt(QualifierLoc);
+
+      NestedNameSpecifier NNS = QualifierLoc.getNestedNameSpecifier();
+      if (NNS.isDependent()) {
+        if (!SemaRef.computeDeclContext(SS))
+          return SemaRef.Context.getDependentNameType(Keyword, NNS, Id);
+      }
+      if (Keyword == ElaboratedTypeKeyword::None ||
+          Keyword == ElaboratedTypeKeyword::Typename) {
+        return SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, 
*Id,
+                                         IdLoc, DeducedTSTContext, DC);
+      }
+      return inherited::RebuildDependentNameType(
+          Keyword, KeywordLoc, QualifierLoc, Id, IdLoc, DeducedTSTContext);
+    }
+
     /// Determine whether the given type \p T has already been
     /// transformed.
     ///
@@ -2442,7 +2467,7 @@ ParmVarDecl 
*TemplateInstantiator::TransformFunctionTypeParam(
     bool ExpectParameterPack) {
   auto NewParm = SemaRef.SubstParmVarDecl(
       OldParm, TemplateArgs, indexAdjustment, NumExpansions,
-      ExpectParameterPack, EvaluateConstraints);
+      ExpectParameterPack, DC, EvaluateConstraints);
   if (NewParm && SemaRef.getLangOpts().OpenCL)
     SemaRef.deduceOpenCLAddressSpace(NewParm);
   return NewParm;
@@ -2831,9 +2856,8 @@ TemplateInstantiator::TransformNestedRequirement(
 
 TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
                                 const MultiLevelTemplateArgumentList &Args,
-                                SourceLocation Loc,
-                                DeclarationName Entity,
-                                bool AllowDeducedTST) {
+                                SourceLocation Loc, DeclarationName Entity,
+                                DeclContext *DC, bool AllowDeducedTST) {
   assert(!CodeSynthesisContexts.empty() &&
          "Cannot perform an instantiation without some context on the "
          "instantiation stack");
@@ -2842,15 +2866,22 @@ TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
       !T->getType()->isVariablyModifiedType())
     return T;
 
-  TemplateInstantiator Instantiator(*this, Args, Loc, Entity);
+  TemplateInstantiator Instantiator(*this, Args, Loc, Entity, DC);
   return AllowDeducedTST ? Instantiator.TransformTypeWithDeducedTST(T)
                          : Instantiator.TransformType(T);
 }
 
+TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
+                                const MultiLevelTemplateArgumentList &Args,
+                                SourceLocation Loc, DeclarationName Entity,
+                                bool AllowDeducedTST) {
+  return SubstType(T, Args, Loc, Entity, /*DC=*/nullptr, AllowDeducedTST);
+}
+
 TypeSourceInfo *Sema::SubstType(TypeLoc TL,
                                 const MultiLevelTemplateArgumentList &Args,
-                                SourceLocation Loc,
-                                DeclarationName Entity) {
+                                SourceLocation Loc, DeclarationName Entity,
+                                DeclContext *DC) {
   assert(!CodeSynthesisContexts.empty() &&
          "Cannot perform an instantiation without some context on the "
          "instantiation stack");
@@ -2867,7 +2898,7 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
     return TLB.getTypeSourceInfo(Context, TL.getType());
   }
 
-  TemplateInstantiator Instantiator(*this, Args, Loc, Entity);
+  TemplateInstantiator Instantiator(*this, Args, Loc, Entity, DC);
   TypeLocBuilder TLB;
   TLB.reserve(TL.getFullDataSize());
   QualType Result = Instantiator.TransformType(TLB, TL);
@@ -2877,11 +2908,25 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
   return TLB.getTypeSourceInfo(Context, Result);
 }
 
+TypeSourceInfo *Sema::SubstType(TypeLoc TL,
+                                const MultiLevelTemplateArgumentList &Args,
+                                SourceLocation Loc, DeclarationName Entity) {
+  return SubstType(TL, Args, Loc, Entity, /*DC=*/nullptr);
+}
+
 /// Deprecated form of the above.
 QualType Sema::SubstType(QualType T,
                          const MultiLevelTemplateArgumentList &TemplateArgs,
                          SourceLocation Loc, DeclarationName Entity,
                          bool *IsIncompleteSubstitution) {
+  return SubstType(T, TemplateArgs, Loc, Entity, /*DC=*/nullptr,
+                   IsIncompleteSubstitution);
+}
+
+QualType Sema::SubstType(QualType T,
+                         const MultiLevelTemplateArgumentList &TemplateArgs,
+                         SourceLocation Loc, DeclarationName Entity,
+                         DeclContext *DC, bool *IsIncompleteSubstitution) {
   assert(!CodeSynthesisContexts.empty() &&
          "Cannot perform an instantiation without some context on the "
          "instantiation stack");
@@ -2892,7 +2937,7 @@ QualType Sema::SubstType(QualType T,
     return T;
 
   TemplateInstantiator Instantiator(
-      *this, TemplateArgs, Loc, Entity,
+      *this, TemplateArgs, Loc, Entity, DC,
       /*BailOutOnIncomplete=*/IsIncompleteSubstitution != nullptr);
   QualType QT = Instantiator.TransformType(T);
   if (IsIncompleteSubstitution && Instantiator.getIsIncomplete())
@@ -2922,13 +2967,10 @@ static bool 
NeedsInstantiationAsFunctionType(TypeSourceInfo *T) {
   return false;
 }
 
-TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
-                                const MultiLevelTemplateArgumentList &Args,
-                                SourceLocation Loc,
-                                DeclarationName Entity,
-                                CXXRecordDecl *ThisContext,
-                                Qualifiers ThisTypeQuals,
-                                bool EvaluateConstraints) {
+TypeSourceInfo *Sema::SubstFunctionDeclType(
+    TypeSourceInfo *T, const MultiLevelTemplateArgumentList &Args,
+    SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext,
+    Qualifiers ThisTypeQuals, DeclContext *DC, bool EvaluateConstraints) {
   assert(!CodeSynthesisContexts.empty() &&
          "Cannot perform an instantiation without some context on the "
          "instantiation stack");
@@ -2936,7 +2978,7 @@ TypeSourceInfo 
*Sema::SubstFunctionDeclType(TypeSourceInfo *T,
   if (!NeedsInstantiationAsFunctionType(T))
     return T;
 
-  TemplateInstantiator Instantiator(*this, Args, Loc, Entity);
+  TemplateInstantiator Instantiator(*this, Args, Loc, Entity, DC);
   Instantiator.setEvaluateConstraints(EvaluateConstraints);
 
   TypeLocBuilder TLB;
@@ -3111,11 +3153,10 @@ bool Sema::SubstTypeConstraint(
           : SourceLocation());
 }
 
-ParmVarDecl *
-Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
-                       const MultiLevelTemplateArgumentList &TemplateArgs,
-                       int indexAdjustment, UnsignedOrNone NumExpansions,
-                       bool ExpectParameterPack, bool EvaluateConstraint) {
+ParmVarDecl *Sema::SubstParmVarDecl(
+    ParmVarDecl *OldParm, const MultiLevelTemplateArgumentList &TemplateArgs,
+    int indexAdjustment, UnsignedOrNone NumExpansions, bool 
ExpectParameterPack,
+    DeclContext *DC, bool EvaluateConstraint) {
   TypeSourceInfo *OldTSI = OldParm->getTypeSourceInfo();
   TypeSourceInfo *NewTSI = nullptr;
 
@@ -3125,7 +3166,7 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
     // We have a function parameter pack. Substitute into the pattern of the
     // expansion.
     NewTSI = SubstType(ExpansionTL.getPatternLoc(), TemplateArgs,
-                       OldParm->getLocation(), OldParm->getDeclName());
+                       OldParm->getLocation(), OldParm->getDeclName(), DC);
     if (!NewTSI)
       return nullptr;
 
@@ -3147,7 +3188,7 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
     }
   } else {
     NewTSI = SubstType(OldTSI, TemplateArgs, OldParm->getLocation(),
-                       OldParm->getDeclName());
+                       OldParm->getDeclName(), DC, /*AllowDeducedTST=*/false);
   }
 
   if (!NewTSI)
@@ -3237,13 +3278,13 @@ bool Sema::SubstParmTypes(
     const MultiLevelTemplateArgumentList &TemplateArgs,
     SmallVectorImpl<QualType> &ParamTypes,
     SmallVectorImpl<ParmVarDecl *> *OutParams,
-    ExtParameterInfoBuilder &ParamInfos) {
+    ExtParameterInfoBuilder &ParamInfos, DeclContext *DC) {
   assert(!CodeSynthesisContexts.empty() &&
          "Cannot perform an instantiation without some context on the "
          "instantiation stack");
 
-  TemplateInstantiator Instantiator(*this, TemplateArgs, Loc,
-                                    DeclarationName());
+  TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, 
DeclarationName(),
+                                    DC);
   return Instantiator.TransformFunctionTypeParams(
       Loc, Params, nullptr, ExtParamInfos, ParamTypes, OutParams, ParamInfos);
 }
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 09c2482168ab7..ef739c2a96983 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2766,7 +2766,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
   }
 
   SmallVector<ParmVarDecl *, 4> Params;
-  TypeSourceInfo *TInfo = SubstFunctionType(D, Params);
+  TypeSourceInfo *TInfo = SubstFunctionType(D, Params, nullptr);
   if (!TInfo)
     return nullptr;
   QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
@@ -3180,29 +3180,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
     D->setTypeSourceInfo(TSI);
   }
 
-  SmallVector<ParmVarDecl *, 4> Params;
-  TypeSourceInfo *TInfo = SubstFunctionType(D, Params);
-  if (!TInfo)
-    return nullptr;
-  QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
-
-  if (TemplateParams && TemplateParams->size()) {
-    auto *LastParam =
-        dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
-    if (LastParam && LastParam->isImplicit() &&
-        LastParam->hasTypeConstraint()) {
-      // In abbreviated templates, the type-constraints of invented template
-      // type parameters are instantiated with the function type, invalidating
-      // the TemplateParameterList which relied on the template type parameter
-      // not having a type constraint. Recreate the TemplateParameterList with
-      // the updated parameter list.
-      TemplateParams = TemplateParameterList::Create(
-          SemaRef.Context, TemplateParams->getTemplateLoc(),
-          TemplateParams->getLAngleLoc(), TemplateParams->asArray(),
-          TemplateParams->getRAngleLoc(), TemplateParams->getRequiresClause());
-    }
-  }
-
   NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc();
   if (QualifierLoc) {
     QualifierLoc = SemaRef.SubstNestedNameSpecifierLoc(QualifierLoc,
@@ -3233,14 +3210,40 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
 
   DeclarationNameInfo NameInfo
     = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
+  if (!NameInfo.getName())
+    return nullptr;
+
+  CXXMethodDecl *Method = nullptr;
+  SourceLocation StartLoc = D->getInnerLocStart();
+
+  SmallVector<ParmVarDecl *, 4> Params;
+  TypeSourceInfo *TInfo =
+      SubstFunctionType(D, Params, dyn_cast<CXXConstructorDecl>(D));
+  if (!TInfo)
+    return nullptr;
+
+  QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
+
+  if (TemplateParams && TemplateParams->size()) {
+    auto *LastParam =
+        dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
+    if (LastParam && LastParam->isImplicit() &&
+        LastParam->hasTypeConstraint()) {
+      // In abbreviated templates, the type-constraints of invented template
+      // type parameters are instantiated with the function type, invalidating
+      // the TemplateParameterList which relied on the template type parameter
+      // not having a type constraint. Recreate the TemplateParameterList with
+      // the updated parameter list.
+      TemplateParams = TemplateParameterList::Create(
+          SemaRef.Context, TemplateParams->getTemplateLoc(),
+          TemplateParams->getLAngleLoc(), TemplateParams->asArray(),
+          TemplateParams->getRAngleLoc(), TemplateParams->getRequiresClause());
+    }
+  }
 
   if (FunctionRewriteKind != RewriteKind::None)
     adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo);
 
-  // Build the instantiated method declaration.
-  CXXMethodDecl *Method = nullptr;
-
-  SourceLocation StartLoc = D->getInnerLocStart();
   if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
     Method = CXXConstructorDecl::Create(
         SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
@@ -4712,12 +4715,36 @@ Decl 
*TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D)
 }
 
 Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) 
{
-  // FIXME: We need to be able to instantiate FriendTemplateDecls.
-  unsigned DiagID = SemaRef.getDiagnostics().getCustomDiagID(
-                                               DiagnosticsEngine::Error,
-                                               "cannot instantiate %0 yet");
-  SemaRef.Diag(D->getLocation(), DiagID)
-    << D->getDeclKindName();
+  unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
+  SmallVector<TemplateParameterList *, 1> TPL(NumTPLists);
+  for (unsigned I = 0, N = NumTPLists; I != N; ++I) {
+    TemplateParameterList *InstParams =
+        SubstTemplateParams(D->getFriendTypeTemplateParameterList(I));
+    if (!InstParams)
+      return nullptr;
+
+    TPL[I] = InstParams;
+  }
+
+  Decl *FTD = nullptr;
+  if (TypeSourceInfo *FT = D->getFriendType()) {
+    TypeSourceInfo *TSI = SemaRef.SubstType(FT, TemplateArgs, D->getLocation(),
+                                            DeclarationName());
+    if (TSI)
+      FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, 
D->getLocation(),
+                                       TSI, D->getFriendLoc(), TPL);
+  } else {
+    NamedDecl *ND = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
+        D->getLocation(), D->getFriendDecl(), TemplateArgs));
+    if (ND)
+      FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, 
D->getLocation(),
+                                       ND, D->getFriendLoc(), TPL);
+  }
+
+  if (FTD) {
+    FTD->setAccess(AS_public);
+    Owner->addDecl(FTD);
+  }
 
   return nullptr;
 }
@@ -5099,9 +5126,8 @@ 
TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
   return InstPartialSpec;
 }
 
-TypeSourceInfo*
-TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
-                              SmallVectorImpl<ParmVarDecl *> &Params) {
+TypeSourceInfo *TemplateDeclInstantiator::SubstFunctionType(
+    FunctionDecl *D, SmallVectorImpl<ParmVarDecl *> &Params, DeclContext *DC) {
   TypeSourceInfo *OldTInfo = D->getTypeSourceInfo();
   assert(OldTInfo && "substituting function without type source info");
   assert(Params.empty() && "parameter vector is non-empty at start");
@@ -5115,7 +5141,7 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl 
*D,
 
   TypeSourceInfo *NewTInfo = SemaRef.SubstFunctionDeclType(
       OldTInfo, TemplateArgs, D->getTypeSpecStartLoc(), D->getDeclName(),
-      ThisContext, ThisTypeQuals, EvaluateConstraints);
+      ThisContext, ThisTypeQuals, DC, EvaluateConstraints);
   if (!NewTInfo)
     return nullptr;
 
@@ -5170,8 +5196,9 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl 
*D,
           continue;
         }
 
-        ParmVarDecl *Parm =
-            cast_or_null<ParmVarDecl>(VisitParmVarDecl(OldParam));
+        ParmVarDecl *Parm = SemaRef.SubstParmVarDecl(
+            OldParam, TemplateArgs, /*indexAdjustment=*/0, std::nullopt,
+            /*ExpectParameterPack=*/false, DC, EvaluateConstraints);
         if (!Parm)
           return nullptr;
         Params.push_back(Parm);
@@ -5191,8 +5218,8 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl 
*D,
     SmallVector<QualType, 4> ParamTypes;
     Sema::ExtParameterInfoBuilder ExtParamInfos;
     if (SemaRef.SubstParmTypes(D->getLocation(), D->parameters(), nullptr,
-                               TemplateArgs, ParamTypes, &Params,
-                               ExtParamInfos))
+                               TemplateArgs, ParamTypes, &Params, 
ExtParamInfos,
+                               DC))
       return nullptr;
   }
 
@@ -5248,7 +5275,7 @@ bool Sema::addInstantiatedParametersToScope(
       if (!PatternDecl->getType()->isDependentType()) {
         QualType T = SubstType(PatternParam->getType(), TemplateArgs,
                                FunctionParam->getLocation(),
-                               FunctionParam->getDeclName());
+                               FunctionParam->getDeclName(), Function);
         if (T.isNull())
           return true;
         FunctionParam->setType(T);
@@ -5273,7 +5300,7 @@ bool Sema::addInstantiatedParametersToScope(
           Sema::ArgPackSubstIndexRAII SubstIndex(*this, Arg);
           QualType T =
               SubstType(PatternType, TemplateArgs, 
FunctionParam->getLocation(),
-                        FunctionParam->getDeclName());
+                        FunctionParam->getDeclName(), Function);
           if (T.isNull())
             return true;
           FunctionParam->setType(T);
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index 9033ea55bc5e2..ed7ece7b9fdcc 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2397,8 +2397,6 @@ 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();
@@ -2407,15 +2405,13 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
 
 void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
   VisitDecl(D);
-  unsigned NumParams = Record.readInt();
-  D->NumParams = NumParams;
-  D->Params = new (Reader.getContext()) TemplateParameterList *[NumParams];
-  for (unsigned i = 0; i != NumParams; ++i)
-    D->Params[i] = Record.readTemplateParameterList();
+  for (unsigned i = 0; i != D->NumTPLists; ++i)
+    D->getTrailingObjects()[i] = Record.readTemplateParameterList();
   if (Record.readInt()) // HasFriendDecl
     D->Friend = readDeclAs<NamedDecl>();
   else
     D->Friend = readTypeSourceInfo();
+  D->NextFriend = readDeclID().getRawValue();
   D->FriendLoc = readSourceLocation();
 }
 
@@ -4044,10 +4040,10 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
     D = AccessSpecDecl::CreateDeserialized(Context, ID);
     break;
   case DECL_FRIEND:
-    D = FriendDecl::CreateDeserialized(Context, ID, Record.readInt());
+    D = FriendDecl::CreateDeserialized(Context, ID);
     break;
   case DECL_FRIEND_TEMPLATE:
-    D = FriendTemplateDecl::CreateDeserialized(Context, ID);
+    D = FriendTemplateDecl::CreateDeserialized(Context, ID, Record.readInt());
     break;
   case DECL_CLASS_TEMPLATE:
     D = ClassTemplateDecl::CreateDeserialized(Context, ID);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp 
b/clang/lib/Serialization/ASTWriterDecl.cpp
index e415ac1e47862..9311715a6ddaa 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1819,7 +1819,6 @@ void ASTDeclWriter::VisitAccessSpecDecl(AccessSpecDecl 
*D) {
 void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
   // Record the number of friend type template parameter lists here
   // so as to simplify memory allocation during deserialization.
-  Record.push_back(D->NumTPLists);
   VisitDecl(D);
   bool hasFriendDecl = isa<NamedDecl *>(D->Friend);
   Record.push_back(hasFriendDecl);
@@ -1827,8 +1826,6 @@ 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);
@@ -1838,15 +1835,16 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
 
 void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
   VisitDecl(D);
-  Record.push_back(D->getNumTemplateParameters());
-  for (unsigned i = 0, e = D->getNumTemplateParameters(); i != e; ++i)
-    Record.AddTemplateParameterList(D->getTemplateParameterList(i));
+  Record.push_back(D->NumTPLists);
+  for (unsigned i = 0, e = D->NumTPLists; i != e; ++i)
+    Record.AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i));
   Record.push_back(D->getFriendDecl() != nullptr);
   if (D->getFriendDecl())
     Record.AddDeclRef(D->getFriendDecl());
   else
     Record.AddTypeSourceInfo(D->getFriendType());
-  Record.AddSourceLocation(D->getFriendLoc());
+  Record.AddDeclRef(D->getNextFriend());
+  Record.AddSourceLocation(D->FriendLoc);
   Code = serialization::DECL_FRIEND_TEMPLATE;
 }
 
diff --git a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp 
b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
index f7216ea7eb7b0..6c55e81c58c18 100644
--- a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
+++ b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
@@ -36,7 +36,7 @@ class A {
 public:
   class foo {};
   static int y;
-  template <typename S> friend class B<S>::ty; // expected-warning {{dependent 
nested name specifier 'B<S>' for friend class declaration is not supported}}
+  template <typename S> friend class B<S>::ty;
 };
 
 template<typename T> class B { typedef int ty; };
@@ -74,7 +74,7 @@ struct {
       friend
 
   float;
-  template<typename T> friend class A<T>::foo; // expected-warning {{not 
supported}}
+  template<typename T> friend class A<T>::foo;
 } a;
 
 void testA() { (void)sizeof(A<int>); }
diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index ebee9bc4c3e16..236423067dcb5 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -420,25 +420,20 @@ class C {
 
   template<class T>
   friend struct A<T>::B;
-  // expected-warning@-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@-1 {{dependent nested name specifier 'A<T>' for friend 
class declaration is not supported; turning off access control for 'C'}}
 
-  // FIXME: this is ill-formed, because A<T>​::​D does not end with a 
simple-template-id
   template<class T>
   friend void A<T>::D::g();
-  // expected-warning@-1 {{dependent nested name specifier 'A<T>::D' for 
friend class declaration is not supported; turning off access control for 'C'}}
+  // expected-error@-1 {{friend declaration does not name a member of a class 
template specialization}}
 
   template<class T>
   friend int *A<T*>::h();
-  // expected-warning@-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@-1 {{dependent nested name specifier 'A<T>' for friend 
class declaration is not supported; turning off access control for 'C'}}
 };
 
 C c;
@@ -450,11 +445,15 @@ 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@-1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note@-30 {{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; }
 void A<int>::D::g() { (void)c.private_int; }
+// expected-error@-1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note@-37 {{implicitly declared private here}}
 
 template<class T>
 T A<T>::h() { (void)c.private_int; }
diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 8162f9caa8f15..4f7031ddec602 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -102,18 +102,18 @@ template<typename T> struct A {
 };
 class X {
   static int x;
-  // FIXME: this is ill-formed, because A<T>::B::C does not end with a 
simple-template-id
   template <typename T>
   friend class A<T>::B::C;
-  // expected-warning@-1 {{dependent nested name specifier 'A<T>::B' for 
friend class declaration is not supported; turning off access control for 'X'}}
+  // expected-error@-1 {{friend declaration does not name a member of a class 
template specialization}}
 };
 template<> struct A<int> {
   typedef struct Q B;
 };
 struct Q {
   class C {
-    // FIXME: 'f' is not a friend, so 'X::x' is not accessible
     int f() { return X::x; }
+    // expected-error@-1 {{'x' is a private member of 'cwg1918::X'}}
+    // expected-note@-12 {{implicitly declared private here}}
   };
 };
 } // namespace cwg1918
@@ -170,7 +170,7 @@ class X {
   // FIXME: this is ill-formed, because A<T>::B::C does not end with a 
simple-template-id
   template <typename T>
   friend class A<T>::B::C;
-  // expected-warning@-1 {{dependent nested name specifier 'A<T>::B' for 
friend class declaration is not supported; turning off access control for 'X'}}
+  // expected-error@-1 {{friend declaration does not name a member of a class 
template specialization}}
 };
 } // namespace cwg1945
 
diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index b7b2ebf700375..1fca7390c371d 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -409,24 +409,27 @@ namespace cwg638 { // cwg638: no
   class X {
     typedef int type;
     template<class T> friend struct A<T>::B;
-    // expected-warning@-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@-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@-1 {{dependent nested name specifier 'A<T>' for friend 
class declaration is not supported; turning off access control for 'X'}}
     template<class T> friend void A<T>::C::h();
-    // expected-warning@-1 {{dependent nested name specifier 'A<T>::C' for 
friend class declaration is not supported; turning off access control for 'X'}}
+    // expected-error@-1 {{friend declaration does not name a member of a 
class template specialization}}
   };
 
   template<> struct A<int> {
-    X::type a; // FIXME: private
+    X::type a;
+    // expected-error@-1 {{'type' is a private member of 'cwg638::X'}}
+    // expected-note@-11 {{implicitly declared private here}}
     struct B {
       X::type b; // ok
     };
-    int f() { X::type c; } // FIXME: private
+    int f() { X::type c; }
+    // expected-error@-1 {{'type' is a private member of 'cwg638::X'}}
+    // expected-note@-17 {{implicitly declared private here}}
     void g() { X::type d; } // ok
     struct D {
-      void h() { X::type e; } // FIXME: private
+      void h() { X::type e; }
+      // expected-error@-1 {{'type' is a private member of 'cwg638::X'}}
+      // expected-note@-22 {{implicitly declared private here}}
     };
   };
 } // namespace cwg638
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp 
b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
index a292d0de97a39..5b03aba342f8b 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -6,7 +6,7 @@ namespace test0 {
   };
 
   class B {
-    template <class T> friend class A<T>::Member; // expected-warning {{not 
supported}}
+    template <class T> friend class A<T>::Member;
     int n;
   };
 
@@ -19,7 +19,7 @@ namespace test1 {
 
   class C {
     static void foo();
-    template <class T> friend void A<T>::f(); // expected-warning {{not 
supported}}
+    template <class T> friend void A<T>::f();
   };
 
   template <class T> struct A {
@@ -35,25 +35,24 @@ namespace test1 {
   };
 }
 
-// FIXME: these should fail!
 namespace test2 {
   template <class T> struct A;
 
   class C {
-    static void foo();
-    template <class T> friend void A<T>::g(); // expected-warning {{not 
supported}}
+    static void foo(); // expected-note 3 {{implicitly declared private here}}
+    template <class T> friend void A<T>::g();
   };
 
   template <class T> struct A {
-    void f() { C::foo(); }
+    void f() { C::foo(); } // expected-error {{'foo' is a private member of 
'test2::C'}}
   };
 
   template <class T> struct A<T*> {
-    void f() { C::foo(); }
+    void f() { C::foo(); } // expected-error {{'foo' is a private member of 
'test2::C'}}
   };
 
   template <> struct A<char> {
-    void f() { C::foo(); }
+    void f() { C::foo(); } // expected-error {{'foo' is a private member of 
'test2::C'}}
   };
 }
 
@@ -66,7 +65,7 @@ namespace test3 {
 
   template <class U> class C {
     int i;
-    template <class T> friend struct A<T>::Inner; // expected-warning {{not 
supported}}
+    template <class T> friend struct A<T>::Inner;
   };
 
   template <class T> int A<T>::Inner::foo() {
@@ -81,22 +80,148 @@ namespace test3 {
 namespace test4 {
   template <class T> struct X {
     template <class U> void operator+=(U);
-    
+
     template <class V>
     template <class U>
-    friend void X<V>::operator+=(U); // expected-warning {{not supported}}
+    friend void X<V>::operator+=(U);
   };
 
-  void test() {   
+  void test() {
     X<int>() += 1.0;
   }
 }
 
 namespace test5 {
   template<template <class> class T> struct A {
-    template<template <class> class U> friend void A<U>::foo(); // 
expected-warning {{not supported}}
+    template<template <class> class U> friend void A<U>::foo();
   };
 
   template <class> struct B {};
   template class A<B>;
 }
+
+namespace test6 {
+  template <class T> struct A {
+    struct B {
+      static int f();
+    };
+  };
+
+  struct C {
+    int n;
+    template <class T> friend struct A<T>::B;
+  };
+
+  template <class T> int A<T>::B::f() {
+    C c;
+    c.n = 0;
+    return 0;
+  }
+
+  int k = A<int>::B::f();
+}
+
+namespace test7 {
+  template <class T> struct A {
+    struct D {
+      void g();
+    };
+  };
+
+  struct C {
+    template <class T> friend void A<T>::D::g(); // expected-error {{friend 
declaration does not name a member of a class template specialization}}
+  };
+}
+
+namespace test8 {
+  template <class T> struct A {
+    T h();
+  };
+
+  template <> struct A<int> {
+    int h();
+  };
+
+  template <> struct A<float *> {
+    int *h();
+  };
+
+  class C {
+    int n; // expected-note {{implicitly declared private here}}
+    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 {{'n' is a private member of 'test8::C'}}
+    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(); // expected-note {{implicitly declared private here}}
+    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 {{'foo' is a private member of 'test10::C'}}
+      return 0;
+    }
+  };
+}
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp 
b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
new file mode 100644
index 0000000000000..67bb7e19b4aaf
--- /dev/null
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
@@ -0,0 +1,24 @@
+// 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 {{templates can only 
be declared in namespace or class scope}}
+  };
+}
+
+void t2() {
+  struct S {
+    template <class T> friend struct A; // expected-error {{templates cannot 
be declared inside of a local class}}
+  };
+}
+
+void t3() {
+  struct S {
+    template <class T> friend void B<T>::f(); // expected-error {{templates 
cannot be declared inside of a local class}}
+  };
+}
diff --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp 
b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index f98005c7e6fb5..cbd04db3301d6 100644
--- a/clang/test/SemaCXX/many-template-parameter-lists.cpp
+++ b/clang/test/SemaCXX/many-template-parameter-lists.cpp
@@ -5,7 +5,7 @@
 template <class T>
 struct X {
   template <class U>
-  struct A { // expected-note {{not-yet-instantiated member is declared here}}
+  struct A {
     template <class V>
     struct B {
       template <class W>
@@ -28,7 +28,9 @@ struct X {
   template <class X>
   template <class Y>
   template <class Z>
-  friend void A<U>::template B<V>::template C<W>::template D<X>::template 
E<Y>::operator+=(Z); // expected-warning {{not supported}} expected-error {{no 
member 'A' in 'X<int>'; it has not yet been instantiated}}
+  friend void A<U>::template B<V>::template C<W>::template D<X>::template 
E<Y>::operator+=(Z);
+  // expected-error@-1 {{no member 'operator+=' in 'X<int>'; it has not yet 
been instantiated}}
+  // expected-note@-2 {{not-yet-instantiated member is declared here}}
 };
 
 void test() {
diff --git a/clang/test/SemaTemplate/GH71595.cpp 
b/clang/test/SemaTemplate/GH71595.cpp
index daec9410e547a..637f00e886ef7 100644
--- a/clang/test/SemaTemplate/GH71595.cpp
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -20,15 +20,15 @@ class temp {
     template<C<temp> T>
     friend void g(); // expected-error {{friend declaration with a constraint 
that depends on an enclosing template parameter must be a definition}}
 
-    temp();
+    temp(); // expected-note {{implicitly declared private here}}
 };
 
 template<C<temp<int>> T>
 void g() {
-    auto v = temp<T>();
+    auto v = temp<T>(); // expected-error {{calling a private constructor of 
class 'temp<int>'}}
 }
 
 void h() {
     f<int>();
-    g<int>();
+    g<int>(); // expected-note {{in instantiation of function template 
specialization 'g<int>' requested here}}
 }
diff --git a/clang/test/SemaTemplate/ctad.cpp b/clang/test/SemaTemplate/ctad.cpp
index 52ffef980fcdf..6f505d20e89e6 100644
--- a/clang/test/SemaTemplate/ctad.cpp
+++ b/clang/test/SemaTemplate/ctad.cpp
@@ -36,7 +36,7 @@ namespace Access {
   // Once we implement proper support for dependent nested name specifiers in
   // friends, this should still work.
   class Y {
-    template <typename T> friend D<T>::D(T, typename T::type); // 
expected-warning {{dependent nested name specifier}}
+    template <typename T> friend D<T>::D(T, typename T::type);
     struct type {};
   };
   D y = {Y(), {}};

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to